Published
Edited
Aug 27, 2020
5 forks
Importers
30 stars
Insert cell
Insert cell
Insert cell
Insert cell
map = {
const context = DOM.context2d(width, height);
const path = d3.geoPath(projection, context);
context.save();
context.beginPath(), path(outline), context.clip(), context.fillStyle = "#fff", context.fillRect(0, 0, width, height);
context.beginPath(), path(graticule), context.strokeStyle = "#ccc", context.stroke();
context.beginPath(), path(land), context.fillStyle = "#000", context.fill();
context.restore();
context.beginPath(), path(outline), context.strokeStyle = "#000", context.stroke();
return context.canvas;
}
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/land-50m.json")
.then(response => response.json())
.then(topology => topojson.feature(topology, topology.objects.land))
Insert cell
land110 = fetch("https://cdn.jsdelivr.net/npm/world-atlas@2/land-110m.json")
.then(response => response.json())
.then(topology => topojson.feature(topology, topology.objects.land))
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