Published
Edited
Oct 23, 2020
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const width = 975;
const height = 610;

const zoom = d3.zoom()
.scaleExtent([1, 8])
.on("zoom", zoomed);

const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.on("click", reset);

const g = svg.append("g");

svg.on("wheel", e => e.preventDefault());

let o;
const counties = g.append("g")
.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features.filter(d => d.id.slice(-5,-3,) !== "72"))
.join("path")
.attr("fill", d => ((o = dataMap.get(d.id)) && o[method] ) >= threshold.threshold ? "blue" : "#F0F0C2")
.attr("d", path)
.attr("stroke", "#ccc")
.attr("stroke-width", 0.4)
.attr("stroke-linejoin", "round")

const states = g.append("g")
.attr("fill", "transparent")
.attr("cursor", "pointer")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.join("path")
.on("click", clicked)
.attr("stroke", "#aaa")
.attr("stroke-width", 1)
.attr("stroke-linejoin", "round")
.attr("d", path);
svg.call(zoom);

function reset() {
states.transition().style("stroke", null);
svg.transition().duration(750).call(
zoom.transform,
d3.zoomIdentity,
d3.zoomTransform(svg.node()).invert([width / 2, height / 2])
);
}

function clicked(event, d) {
const [[x0, y0], [x1, y1]] = path.bounds(d);
event.stopPropagation();
states.transition().style("stroke", null);
d3.select(this).transition().style("stroke", "red");
d3.select(this).raise();
svg.transition().duration(750).call(
zoom.transform,
d3.zoomIdentity
.translate(width / 2, height / 2)
.scale(Math.min(8, 0.9 / Math.max((x1 - x0) / width, (y1 - y0) / height)))
.translate(-(x0 + x1) / 2, -(y0 + y1) / 2),
d3.pointer(event, svg.node())
);
}

function zoomed(event) {
const {transform} = event;
g.attr("transform", transform);
counties.attr("stroke-width", 0.4 * Math.sqrt(1 / transform.k));
states.attr("stroke-width", Math.sqrt(1 / transform.k));
}

let second;
if (method === "density") {
second = `${d3.format(".2p")(threshold.area / totalarea)} of the area`;
} else {
second = `${d3.format(".2p")(threshold.counties / totalcounties)} of the counties`;
}

return md`## ${d3.format(".2p")(threshold.population / totalpop)} of the U.S. Population (${d3.format(",.0f") (threshold.population)} people) lives in ${second}

${svg.node()} ${html`<style>h2 { max-width: 100% !important; text-align: center }</style>`}`
}
Insert cell
alldata = d3.csvParse(await FileAttachment("ACSDT5Y2017.B01003_data_with_overlays_2020-10-22T115540.csv").text())
Insert cell
landdata = new Map(d3.csvParse(await FileAttachment("LND01.csv").text()).map(d => [d.STCOU, d.LND110210D]))
Insert cell
data = alldata.slice(1).filter(d => d.GEO_ID.slice(-5, -3) !== "72").map(d => {
let code = d.GEO_ID.slice(-5);
if (code === "02158") {
code = "02270"
}
if (code === "46102") {
code = "46113"
}
return [
d.GEO_ID.slice(-5),
{
population: +d.B01003_001E,
density: landdata.has(code) ? +d.B01003_001E / +landdata.get(code) : null,
area: landdata.has(code) ? +landdata.get(code) : 0,
...d
}
]
}).sort((a, b) => b[1][method] - a[1][method])
Insert cell
Insert cell
totalpop = data.reduce((a, b) => a + b[1].population, 0)
Insert cell
totalarea = data.reduce((a, b) => a + b[1].area, 0)
Insert cell
totalcounties = data.length;
Insert cell
absolute_target = totalpop * proportion
Insert cell
Insert cell
threshold = calculateThreshold()
Insert cell
path = d3.geoPath()
Insert cell
states = new Map(us.objects.states.geometries.map(d => [d.id, d.properties]))
Insert cell
us = FileAttachment("counties-albers-10m.json").json()
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require("d3@6", "d3-array@2")
Insert cell
Insert cell
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