Published
Edited
Nov 10, 2021
2 stars
Insert cell
Insert cell
Insert cell
world = FileAttachment("custom@1.geo-3.json").json()
Insert cell
Insert cell
countryData = world.features
.filter((country) => country.properties.sov_a3 != "US1")
.map((country) => ({
...country.properties,
centroid: d3.geoCentroid(country),
distance: d3.geoDistance(d3.geoCentroid(country), usCentroid),
//path from country center to us center
trail: {
type: "Feature",
geometry: {
type: "LineString",
coordinates: [d3.geoCentroid(country), usCentroid]
}
}
}))
Insert cell
//computed us center is confused by Alaska+Hawaii, so here we just set it ourselves
//https://en.wikipedia.org/wiki/Geographic_center_of_the_United_States
usCentroid = [-103.75, 40]
Insert cell
Insert cell
//for path color based on population
popScale = d3
.scaleLinear()
.domain(d3.extent(countryData, (country) => country.pop_est))
.range([1, 0.5])
Insert cell
//for how many seconds the animaton will take, based on population
durationScale = d3
.scaleLinear()
.domain(d3.extent(countryData, (country) => country.pop_est))
.range([15, 2])
Insert cell
//for converting long-lat to pixels
projection = d3
.geoAzimuthalEqualArea()
.center([0, 0])
.scale(250)
//center on the Americas
.rotate([90, 0, 0])
Insert cell
Insert cell
height = 500
Insert cell
Insert cell
{
//svg area
const svg = d3.create("svg").attr("width", width).attr("height", height);
svg
.append("rect")
.attr("width", width)
.attr("height", height)
.attr("fill", "#555b6e");

//rotated group to hold countries
const g = svg
.append("g")
.attr("transform", "rotate(-90," + width / 2 + "," + height / 2 + ")");

//the function that takes geographic data and converts to SVG paths
const path = d3.geoPath().projection(projection);

//draw countries
g.selectAll("path")
.data(world.features)
.enter()
.append("path")
.attr("d", path)
.attr("stroke", "#bee3db")
.attr("stroke-width", 1)
.attr("fill", "#89b0ae");

//draw arcs
g.selectAll(".arcs")
.data(countryData)
.enter()
.append("path")
.attr("d", (d) => path(d.trail))
.attr("fill", "none")
.attr("stroke-width", 3)
.attr("stroke", (d) => d3.interpolateMagma(popScale(d.pop_est)))
//make stroke dashed
.style("stroke-dasharray", "2 20")
//create animation
.style(
"animation",
(d) => "dash " + durationScale(d.pop_est) + "s linear infinite"
);

return svg.node();
}
Insert cell
Insert cell
html`
<style>
@keyframes dash {
to {
stroke-dashoffset: -100;
}
}
</style>`
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