Published
Edited
Apr 24, 2021
7 stars
Insert cell
Insert cell
attitude = require("attitude@^0.2")
Insert cell
Insert cell
canvas = {
const context = DOM.context2d(width, height);
const projection = d3
.geoOrthographic()
.fitExtent([[10, 10], [width - 10, height - 10]], sphere);
const path = d3.geoPath(projection, context);

function render(country, arc) {
context.clearRect(0, 0, width, height);
context.beginPath(),
path(land),
(context.fillStyle = "#ccc"),
context.fill();
context.beginPath(),
path(country),
(context.fillStyle = "#f00"),
context.fill();
context.beginPath(),
path(borders),
(context.strokeStyle = "#fff"),
(context.lineWidth = 0.5),
context.stroke();
context.beginPath(),
path(sphere),
(context.strokeStyle = "#000"),
(context.lineWidth = 1.5),
context.stroke();
context.beginPath(), path(arc), context.stroke();
return context.canvas;
}

let p1 = [0, 0],
r1 = [0, 0, 0];
for (const country of countries) {
mutable name = names.get(country.id);
yield render(country);

// p1 is the current centroid, p2 the centroid we want to go to
const p2 = d3.geoCentroid(country);
// r1 is the current rotation, r2 the rotation we want to go to
const r2 = [-p2[0], tilt - p2[1], 0];
// ip: interpolate centroids
const ip = d3.geoInterpolate(p1, p2);
// ir: interpolate rotations
const ir = attitude(r1).interpolateTo(attitude(r2));

await d3
.transition()
.duration(1250)
.tween("render", () => t => {
projection.rotate(ir(t).angles());
render(country, { type: "LineString", coordinates: [p1, ip(t)] });
})
.transition()
.tween("render", () => t => {
render(country, { type: "LineString", coordinates: [ip(t), p2] });
})
.end();

(p1 = p2), (r1 = r2);
}
}
Insert cell
mutable name = ""
Insert cell
height = Math.min(width, 720)
Insert cell
tilt = -30
Insert cell
sphere = ({type: "Sphere"})
Insert cell
names = new Map(await d3.tsv("https://cdn.jsdelivr.net/npm/world-atlas@1/world/110m.tsv", ({iso_n3, name_long}) => [iso_n3, name_long]))
Insert cell
countries = topojson.feature(world, world.objects.countries).features
Insert cell
borders = topojson.mesh(world, world.objects.countries, (a, b) => a !== b)
Insert cell
land = topojson.feature(world, world.objects.land)
Insert cell
world = d3.json("https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json")
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require("d3@5")
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