Published
Edited
Jan 8, 2022
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
distantViews = {
// same parameters as `rotate`: lambda, phi, gamma
// distance needs to be high
const afroEurope = [-35, -13, 0];
const asia = [-115, -5, 0];
const pacific = [-205, 8, 0];
const newWorld = [95, -3.7, 0];
return {afroEurope, asia, pacific, newWorld};
}
Insert cell
closeupViews = {
// same parameters as `rotate`: lambda, phi, gamma
// distance = 2 (1 in the slider)
const na = [100.36, -42.55, 0];
const sa = [61.09, 19.64, 0];
const africa = [-15.27, -1.09, 0];
const wEurasia = [-40, -36, 0];
const eEurasia = [-106.91, -36, 0];
const austronesia = [-137.45, 17.45, 0];
const polynesia = [-205.33, 13.09, 0];
return {na, sa, africa, wEurasia, eEurasia, austronesia, polynesia};
}
Insert cell
Insert cell
projection = d3.geoSatellite()
.scale(camera.scale)
.translate([width / 2, height / 2])
.rotate(rotate)
.tilt(camera.tilt)
.distance(camera.distance)
.preclip(preclip)
.precision(0.1)
Insert cell
preclip = {
const distance = camera.distance;
const tilt = camera.tilt * Math.PI / 180;
const alpha = Math.acos(distance * Math.cos(tilt) * 0.999);
const clipDistance = geoClipCircle(Math.acos(1 / distance) - 1e-6);
return alpha ? geoPipeline(
clipDistance,
geoRotatePhi(Math.PI + tilt),
geoClipCircle(Math.PI - alpha),
geoRotatePhi(-Math.PI - tilt)
) : clipDistance;
}
Insert cell
function geoPipeline(...transforms) {
return sink => {
for (let i = transforms.length - 1; i >= 0; --i) {
sink = transforms[i](sink);
}
return sink;
};
}
Insert cell
geoClipCircle = d3.geoClipCircle
Insert cell
function geoRotatePhi(deltaPhi) {
const cosDeltaPhi = Math.cos(deltaPhi);
const sinDeltaPhi = Math.sin(deltaPhi);
return sink => ({
point(lambda, phi) {
const cosPhi = Math.cos(phi);
const x = Math.cos(lambda) * cosPhi;
const y = Math.sin(lambda) * cosPhi;
const z = Math.sin(phi);
const k = z * cosDeltaPhi + x * sinDeltaPhi;
sink.point(Math.atan2(y, x * cosDeltaPhi - z * sinDeltaPhi), Math.asin(k));
},
lineStart() { sink.lineStart(); },
lineEnd() { sink.lineEnd(); },
polygonStart() { sink.polygonStart(); },
polygonEnd() { sink.polygonEnd(); },
sphere() { sink.sphere(); }
});
}
Insert cell
height = width
Insert cell
outline = ({type: "Sphere"})
Insert cell
graticule = d3.geoGraticule10()
Insert cell
land = Generators.observe(notify => {
const mousedown = event => { event.target.form && notify(land110); };
const mouseup = event => { event.target.form && notify(land50); };
notify(land50);
window.addEventListener("mousedown", mousedown);
window.addEventListener("mouseup", mouseup);
return () => {
window.removeEventListener("mousedown", mousedown);
window.removeEventListener("mouseup", mouseup);
};
})
Insert cell
land50 = fetch("https://cdn.jsdelivr.net/npm/world-atlas@2/countries-50m.json")
.then(response => response.json())
.then(topology => topojson.feature(topology, topology.objects.countries))
Insert cell
land110 = fetch("https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json")
.then(response => response.json())
.then(topology => topojson.feature(topology, topology.objects.countries))
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require("d3-geo@2", "d3-geo-projection@3")
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