map = {
let localNodes = shapesWithData
.map((d) => {
return {
id: d.id,
metric: d.metric,
name: d.properties.name,
state: d.state,
r: rScale(d.metric),
population: d.population,
p: rPopScale(d.population),
cx: d.centroid ? d.centroid[0] : width / 2,
cy: d.centroid ? d.centroid[1] : mapHeight / 2,
x: d.centroid ? d.centroid[0] : width / 2,
y: d.centroid ? d.centroid[1] : mapHeight / 2
};
})
.filter((d) => !isNaN(d.r));
let simulation = d3
.forceSimulation(localNodes)
.force(
"collide",
d3
.forceCollide()
.radius((d) => d.p * 1.09)
.strength(0.9)
)
.force(
"x",
d3.forceX((d) => {
return d.cx;
})
)
.force(
"y",
d3.forceY((d) => {
return d.cy;
})
);
simulation.tick(250);
const svg = d3
.select(DOM.svg(mapWidth, mapHeight))
.attr("viewBox", `-25, -25, ${mapWidth + 50}, ${mapHeight + 60}`)
.attr("preserveAspectRatio", "xMidYMid slice")
.style("width", "100%")
.style("height", "auto")
.style("font-size", 12)
.style("background-color", "#111");
let node = svg.node();
svg
.append("rect")
.attr("width", mapWidth)
.attr("height", mapHeight)
.attr("fill", "#111");
svg
.append("g")
.selectAll("path")
.data(shapesWithData)
.join("path")
.attr("fill", (d) => (d.metric ? "#222" : "#000"))
.attr("d", path);
svg
.append("g")
.attr("stroke-width", 1)
.selectAll("path")
.data(stateShapes)
.join("path")
.style("stroke", (d, i) => {
return "#666";
})
.style("fill", "none")
.attr("d", path);
let centroids = svg
.append("g")
.selectAll("circle.centroid")
.data(localNodes)
.join("circle")
.classed("centroid", true)
.sort((a, b) => {
return b.r - a.r;
})
.attr("id", (d) => slugify(d.name + " " + d.state))
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y)
.attr("r", (d) => d.p)
.attr("stroke-width", 0.5)
.attr("fill-opacity", 0.96)
.attr("fill", (d, i) => {
if (!d.metric) return "none";
else return colorScale(d.metric || 0);
})
.on("click", (d) => {
console.log("circle", d);
});
return node;
}