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);
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");
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();
}