function applyLloydsAlgorithm(data, maxIter = 20, rows = 10000, cols = 10000) {
let centroids = data.features.map(
(feature) => turf.centroid(feature).geometry.coordinates
);
let positions = centroids;
let bbox = turf.bbox(turf.points(positions));
for (let iter = 0; iter < maxIter; iter++) {
var options = {
bbox: bbox
};
let voronoi = turf.voronoi(turf.points(positions), options);
let newPositions = voronoi.features.map(
(feature) => turf.centroid(feature).geometry.coordinates
);
let maxPosChange = d3.max(newPositions, (pos, i) =>
turf.distance(pos, positions[i], { units: "meters" })
);
if (maxPosChange < 1e-6) break;
positions = newPositions;
}
let xExtent = d3.extent(positions, (d) => d[0]);
let yExtent = d3.extent(positions, (d) => d[1]);
let xBinSize = (xExtent[1] - xExtent[0]) / cols;
let yBinSize = (yExtent[1] - yExtent[0]) / rows;
positions = positions.map((pos) => {
let i = Math.floor((pos[0] - xExtent[0]) / xBinSize);
let j = Math.floor((pos[1] - yExtent[0]) / yBinSize);
return [
xExtent[0] + (i + 0.5) * xBinSize,
yExtent[0] + (j + 0.5) * yBinSize
];
});
let updatedData = {
type: "FeatureCollection",
features: positions.map((pos, i) => ({
type: "Feature",
geometry: {
type: "Point",
coordinates: pos
},
properties: data.features[i].properties
}))
};
return updatedData;
}