Public
Edited
Apr 6, 2023
Insert cell
Insert cell
map = {
let w = d3.min([width, 1000]);
let h = 1.25 * w;
let padding = 10;
let svg = d3
.create("svg")
.attr("viewBox", [0, 0, w, h])
.style("max-width", `${w}px`);

let projection = d3.geoCylindricalEqualArea().parallel(30);
projection.fitExtent(
[
[padding, padding],
[w - padding, h - padding]
],
africaMapData.boundary
);
let path = d3.geoPath().projection(projection);
let color_scale = d3
.scaleQuantile()
.domain(Object.entries(populationData).map((o) => o[1]))
.range(d3.schemeBlues[8]);

const strokeColor = d3
.color(color_scale(d3.max(Object.entries(populationData).map((o) => o[1]))))
.darker(1);

// Boundaries
svg
.append("g")
.selectAll("path")
.data(africaMapData.boundary.features)
.join("path")
.attr("d", path)
.attr("fill-opacity", "0")
.attr("stroke", "black")
.attr("stroke-width", 6)
.attr("pointer-events", "none");

// Countries
let countries = svg
.append("g")
.selectAll("path")
.data(africaMapData.countries.features)
.join("path")
.attr("d", path)
.attr(
"fill",
(o) => color_scale(populationData[o.properties.ISO_A3]) ?? "red"
)
.attr("stroke", strokeColor)
.attr("stroke-width", 1)
.on("pointerenter", function () {
let clone = d3
.select(this)
.clone(true)
.classed("extrude", true)
.attr("fill", d3.color(d3.select(this).style("fill")).darker(2))
.attr("pointer-events", "none")
.attr("stroke", "none")
.attr("transform", `translate(0,0)`)
.raise();

const extrusionHeight = 6;
for (let i = 0; i < extrusionHeight; ++i) {
clone = clone
.attr("transform", `translate(0,-${i})`)
.clone(true)
.raise();
}
clone
.attr("fill", d3.select(this).style("fill"))
.attr("stroke", strokeColor)
.attr("stroke-width", 1);
})
.on("pointerleave", function () {
d3.select(this).attr("filter", "none");
d3.selectAll(".extrude").remove();
})
.each(function (o) {
tippy(this, {
theme: "light-border",
allowHTML: true,
content: `<span>${o.properties.ADMIN}: ${
populationData[o.properties.ISO_A3] ?? "data unavailable"
}</span>`
});
});

svg.node().scale = color_scale;

return svg.node();
}
Insert cell
Insert cell
africaMapRawData = FileAttachment("africa-mapshaper-01.json").json()
Insert cell
africaMapData = {
let data = await FileAttachment("africa-mapshaper-01.json").json();
let countries = topojson.feature(data, data.objects.countries);
let boundary = topojson.feature(data, data.objects.boundary);
return { countries, boundary };
}
Insert cell
d3 = require("d3", "d3-geo-projection")
Insert cell
populationRawData = FileAttachment("population-data.csv").csv()
Insert cell
populationData = {
const africaIds = africaMapData.countries.features.map(
(o) => o.properties.ISO_A3
);

let output = {};
populationRawData.forEach((o) => {
if (africaIds.includes(o.cca3)) {
output[o.cca3] = parseFloat(o.density);
}
});
return output;
}
Insert cell
tippy = require("tippy.js@6")

Insert cell
<!-- The Tippy JS style sheet used for the light-border theme -->
<link rel="stylesheet" href="${await require.resolve(
`tippy.js/themes/dark-border.css`
)}">
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