This example fixes zpconn's block: Spherical alpha shapes, by using d3-geo-voronoi.
We merge the triangles thanks to Jason Davies’s block /jasondavies/1554783.
xxxxxxxxxx
<meta charset="utf-8">
<style>
.point {
stroke: #000;
stroke-width: .5px;
fill: red;
}
.graticule {
stroke: #000;
stroke-width: .25px;
fill: white;
}
.geom {
fill: none;
stroke: #000;
stroke-width: 1.0;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script src="https://unpkg.com/d3-geo-voronoi"></script>
<div id="map"></div>
<script>
var degrees = 180 / Math.PI,
cities = [],
width = 1200,
height = 625;
var projection = d3.geoOrthographic()
.scale(height / 3 - 1);
var path = d3.geoPath()
.projection(projection);
var svg = d3.select("#map")
.append("svg")
.attr("width", width)
.attr("height", height);
svg.append("path")
.datum(d3.geoGraticule())
.attr("class", "graticule")
.attr("d", path);
svg.append("path")
.attr("class", "point");
var update = function () {
var points = cities.map(function (d) {
return d.coordinates;
});
svg.select(".point")
.datum({type: "MultiPoint", coordinates: points})
.attr("d", path);
var alpha = 0.406,
distance2 = function (a, b) {
var d = d3.geoDistance(
a, b
);
return d * d;
},
alpha2 = alpha * alpha;
var tr = d3.geoVoronoi().triangles(points).features
.map(s => s.properties.sites)
.filter(t => distance2(t[0], t[1]) < alpha2 &&
distance2(t[1], t[2]) < alpha2 &&
distance2(t[0], t[2]) < alpha2);
svg.append('path')
.datum({
type: "Polygon",
coordinates: boundary(tr)
})
.attr("d", path)
.attr("class", "geom")
;
// gentle animation
d3.interval(function(elapsed) {
projection.rotate([ elapsed / 150, 0 ]);
svg.selectAll('path')
.attr('d', path);
}, 50);
};
d3.json("cities.json", function (data) {
cities = data;
update();
});
// Computes boundaries of connected triangles, given an array of triangles.
// Jason Davies - https://bl.ocks.org/jasondavies/1554783
function boundary(mesh) {
var counts = {},
edges = {},
r,
result = [];
// Traverse the edges of all triangles and discard any edges that appear twice.
mesh.forEach(function(triangle) {
for (var i = 0; i < 3; i++) {
var edge = [triangle[i], triangle[(i + 1) % 3]].sort(ascendingCoords).map(String);
(edges[edge[0]] = (edges[edge[0]] || [])).push(edge[1]);
(edges[edge[1]] = (edges[edge[1]] || [])).push(edge[0]);
var k = edge.join(":");
if (counts[k]) delete counts[k];
else counts[k] = 1;
}
});
while (1) {
var k = null;
// Pick an arbitrary starting point on a boundary.
for (k in counts) break;
if (k == null) break;
result.push(r = k.split(":").map(function(d) { return d.split(",").map(Number); }));
delete counts[k];
var q = r[1];
while (q[0] !== r[0][0] || q[1] !== r[0][1]) {
var p = q,
qs = edges[p.join(",")],
n = qs.length;
for (var i = 0; i < n; i++) {
q = qs[i].split(",").map(Number);
var edge = [p, q].sort(ascendingCoords).join(":");
if (counts[edge]) {
delete counts[edge];
r.push(q);
break;
}
}
}
}
return result;
}
function ascendingCoords(a, b) {
return a[0] === b[0] ? b[1] - a[1] : b[0] - a[0];
}
</script>
</body>
https://d3js.org/d3.v4.min.js
https://d3js.org/topojson.v2.min.js
https://unpkg.com/d3-geo-voronoi