Published
Edited
May 6, 2021
Importers
11 stars
Also listed in…
Geo
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
projection = show_stereo ? stereo : d3.geoOrthographic().rotate([0, -15])
Insert cell
centroid = d3.geoCentroid({ type: "MultiPoint", coordinates: points })
Insert cell
stereo = d3.geoStereographic().rotate(centroid.map(d => -d))
Insert cell
stereoCircles = points =>
points.map(d => {
const g = d3
.geoCircle()
.center(d)
.radius((d[2] || 0) + .2)().coordinates[0],
f = [g[0], g[20], g[40]].map(stereo), // sample 3 of the 60 points given by geoCircle
c = circumcenter(f);
return {
x: c[0],
y: c[1],
r: Math.hypot(c[0] - f[0][0], c[1] - f[0][1])
};
})
Insert cell
// inspect
stereoCircles(points)
Insert cell
enclose = points => {
const centroid = d3.geoCentroid({ type: "MultiPoint", coordinates: points });
stereo.rotate(centroid.map(d => -d));

const { x, y, r } = d3.packEnclose(stereoCircles(points));

// the center of the spherical circle can be retrieved with a geoVoronoi of 3 of its points
const f = [[x + r, y], [x - r, y], [x, y + r]].map(stereo.invert),
c = d3.geoVoronoi(f).triangles().features[0].properties.circumcenter;
// for very large lists, c can be on the opposite side, check for that…
if (d3.geoDistance(c, centroid) > Math.PI / 2) {
c[0] += 180;
c[1] *= -1;
}

return { c, r: (d3.geoDistance(c, f[0]) * 180) / Math.PI };
}
Insert cell
// inspect
circle = enclose(points)
Insert cell
height = 500
Insert cell
points = Array.from({ length: N }, () => [
360 * Math.random(),
90 - S * Math.random(),
R * Math.random()
])
Insert cell
function distance2(A, B) {
return (A[0] - B[0])*(A[0] - B[0])+(A[1] - B[1])*(A[1] - B[1]);
}
Insert cell
circumcenter = ([A, B, C]) => {
const a2 = distance2(B, C),
b2 = distance2(C, A),
c2 = distance2(A, B),
// barycentric weights
a = a2 * (b2 + c2 - a2),
b = b2 * (a2 + c2 - b2),
c = c2 * (b2 + a2 - c2),
d = a + b + c;

return [
(A[0] * a + B[0] * b + C[0] * c) / d,
(A[1] * a + B[1] * b + C[1] * c) / d
];
}
Insert cell
d3 = require("d3@5", "d3-geo-voronoi")
Insert cell
import { checkbox, slider } from "@jashkenas/inputs"
Insert cell
import { drag } from "@fil/versor-dragging-with-attitude"
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more