chartGenerator = (chart, data, index) => {
const margin = ({left: 10, right: 10, top: 5 + (index ? 0 : marginTopExtraFirstChart), bottom: 24})
const regionsOrdered = data[1];
const cities = regionsOrdered.map(d => d.entries).flat();
const chartheight = (24 + maxR) * regionsOrdered.length;
const y = d3.scaleBand(regionsOrdered.map(d => d.subregion), [0, chartheight]);
const svg = chart.append("svg")
.attr("width", chartwidth + margin.left + margin.right)
.attr("height", chartheight + margin.top + margin.bottom)
svg.append("style").html(css);
const g = svg.append("g")
.attr("transform", `translate(${[margin.left, margin.top]})`);
g.append("g")
.call((g) => axisGenerator(g, chartheight, y, index));
g.selectAll(".subregion-label")
.data(regionsOrdered)
.join("text")
.attr("class", "subregion-label")
.attr("y", d => y(d.subregion) + y.bandwidth() / 2)
.text(d => d.subregion);
const statesProp = chartwidth <= 520 ? "states_abbr" : "states";
const statesY = chartwidth <= 350 ? 16 : chartwidth <= 520 ? 18 : 20;
const statesDy = chartwidth <= 350 ? 13 : chartwidth <= 520 ? 14 : 16;
g.selectAll(".states-label")
.data(regionsOrdered)
.join("text")
.attr("class", "states-label")
.attr("y", d => y(d.subregion) + y.bandwidth() / 2 + statesY)
.html(d => {
if (d.states.length < 5) {
return d[statesProp].join(", ");
}
else {
const grouper = Math.ceil(d.states.length / 2);
const groups = d3.groups(d[statesProp], (_, i) => i < grouper);
return groups.map((group, i) => `<tspan x=0 dy=${i * 16}>${group[1].join(", ")}</tspan>`)
}
})
g.selectAll(".baseline")
.data(regionsOrdered)
.join("line")
.attr("stroke", "#ccc")
.attr("shape-rendering", "crispEdges")
.attr("transform", d => `translate(0, ${y(d.subregion) + y.bandwidth()})`)
.attr("x2", chartwidth);
g.append("g")
.attr("class", "warts")
.selectAll(".wart")
.data(cities)
.join("path")
.attr("class", "wart")
.attr("d", d => semicircle(x(d.decadal_change), y(d.subregion), r(d.population)))
.attr("fill", d => colorScale(d.decadal_change))
.attr("fill-opacity", 0.8)
.attr("stroke", d => d3.color(colorScale(d.decadal_change)).darker(0.2))
.attr("transform", `translate(0, ${y.bandwidth()})`);
const cityLabel = g.selectAll(".city-label")
.data(dataCityLabels.filter(d => data[0] === d.region))
.join("g")
.attr("class", "city-label")
.attr("transform", d => {
const cx = x(d.decadal_change);
const cy = y(d.subregion) + y.bandwidth();
const radius = r(d.population);
return `translate(${geometric.pointTranslate([cx, cy], d.angle(chartwidth), radius)})`;
})
cityLabel.append("polyline")
.attr("points", d => {
return [[0, 0], geometric.pointTranslate([0, 0], d.angle(chartwidth), 5)]
})
.attr("stroke", d => d3.color(colorScale(d.decadal_change)).darker(0.2))
.style("display", d => d.display ? d.display(chartwidth) : "block")
cityLabel.append("text")
.attr("text-anchor", d => d.textAnchor(chartwidth))
.attr("transform", d => {
return `translate(${geometric.pointTranslate([0, 0], d.angle(chartwidth), 7)})`
})
.text(d => d.city);
return chart;
}