function clippedVoronoi(points, padding = 0) {
const coords = points.features.map(d3.geoCentroid);
const X = d3.extent(coords, (d) => d[0]);
const Y = d3.extent(coords, (d) => d[1]);
const projection = d3
.geoEquirectangular()
.fitExtent(
[
[X[0], Y[0]],
[X[1], Y[1]]
],
points
)
.clipExtent([
[X[0] - padding, Y[0] - padding],
[X[1] + padding, Y[1] + padding]
]);
const path = d3.geoPath(projection);
return {
type: "FeatureCollection",
features: d3
.geoVoronoi(points)
.polygons()
.features.map((feature) => {
let p = path(feature);
if (p) {
const planarRing = p
.replace(/^M|Z$/g, "")
.split("L")
.map((d) => d.split(",").map((d) => +d));
planarRing.push(planarRing[0]);
const sphericalRing = d3.pairs(planarRing).flatMap(([a, b]) => {
const dist = Math.hypot(a[0] - b[0], a[1] - b[1]);
const inter = d3.interpolate(a, b);
const steps = Math.ceil(dist);
return d3
.range(steps)
.map((i) => projection.invert(inter(i / steps)));
});
sphericalRing.push(sphericalRing[0]);
return {
...feature,
geometry: { type: "Polygon", coordinates: [sphericalRing] }
};
}
})
};
}