Published
Edited
Feb 27, 2020
7 forks
19 stars
Insert cell
Changed in target
-
md`# Bubble Map
+
md`# U.S. Population by State, 1790–1990
-
Estimated population by county, 2016. Source: [American Community Survey](https://api.census.gov/data/2016/acs/acs5/cprofile/examples.html).`
+
Note: the U.S. census only recorded population for a given state after it was established as a territory or state; missing data does not imply that the given area was unpopulated! Data: [U.S. Census](https://www.census.gov/population/www/censusdata/pop1790-1990.html)`
Insert cell
Added in target
viewof year = Scrubber(data.years, {delay, loop: false})
Insert cell
Changed in target
chart = { const svg = d3.create("svg")
-
.attr("viewBox", [0, 0, 975, 610]);
+
.attr("viewBox", [0, 0, 975, 610]) .attr("text-anchor", "middle") .attr("font-family", "sans-serif") .attr("font-size", 10);
svg.append("path") .datum(topojson.feature(us, us.objects.nation))
-
.attr("fill", "#ccc")
+
.attr("fill", "#ddd")
.attr("d", path); svg.append("path") .datum(topojson.mesh(us, us.objects.states, (a, b) => a !== b)) .attr("fill", "none") .attr("stroke", "white") .attr("stroke-linejoin", "round") .attr("d", path); const legend = svg.append("g") .attr("fill", "#777") .attr("transform", "translate(925,608)")
-
.attr("text-anchor", "middle") .style("font", "10px sans-serif")
.selectAll("g")
-
.data([1e6, 5e6, 1e7])
+
.data([1e6, 5e6, 15e6])
.join("g"); legend.append("circle") .attr("fill", "none")
-
.attr("stroke", "#ccc")
+
.attr("stroke", "#bbb")
.attr("cy", d => -radius(d)) .attr("r", radius); legend.append("text") .attr("y", d => -2 * radius(d)) .attr("dy", "1.3em")
-
.text(d3.format(".1s"));
+
.text(d3.format("~s"));
-
svg.append("g") .attr("fill", "brown") .attr("fill-opacity", 0.5) .attr("stroke", "#fff") .attr("stroke-width", 0.5) .selectAll("circle") .data(topojson.feature(us, us.objects.counties).features .map(d => (d.value = data.get(d.id), d)) .sort((a, b) => b.value - a.value)) .join("circle") .attr("transform", d => `translate(${path.centroid(d)})`) .attr("r", d => radius(d.value)) .append("title") .text(d => `${d.properties.name} ${format(d.value)}`);
+
let bubble = svg.append("g") .selectAll("g");
-
return svg.node();
+
return Object.assign(svg.node(), { update(year) { const t = svg.transition() .duration(delay) .ease(d3.easeLinear); bubble = bubble .data(data.filter(d => d.year === year), d => d.id) .join( enter => enter.append("g") .attr("transform", d => `translate(${path.centroid(d.feature)})`) .call(g => g.append("circle") .attr("fill", "brown") .attr("fill-opacity", 0.5) .attr("stroke", "currentColor") .attr("r", 0)) .call(g => g.append("text") .attr("dy", "0.35em") .attr("fill-opacity", 0) .attr("y", -2) .text(d => d.feature.properties.name) .call(text => text.append("tspan") .style("font-variant-numeric", "tabular-nums") .attr("x", 0) .attr("y", 12) .text(0)) .transition(t) .attr("fill-opacity", 1)), update => update, exit => exit.call(bubble => bubble.transition(t) .call(g => g.select("circle").attr("r", 0)) .call(g => g.select("text").attr("fill-opacity", 0)) .call(g => g.select("tspan").call(textTween, 0)) .remove()) ); bubble.select("circle").transition(t) .attr("r", d => radius(d.value)); bubble.select("tspan").transition(t) .call(textTween, d => d.value); } });
}
Insert cell
Added in target
function textTween(text, valueof) {
if (typeof valueof !== "function") {
const value = +valueof;
valueof = () => value;
}
return text.textTween(function(d) {
const value = +this.textContent.replace(/,/g, "");
const i = d3.interpolateRound(value, valueof(d));
return t => formatValue(i(t));
});
}
Insert cell
Added in target
chart.update(year)
Insert cell
Added in target
delay = 750
Insert cell
Changed in target
-
data = new Map((await FileAttachment("population.json").json()).slice(1).map(([population, state, county]) => [state + county, +population]))
+
data = { const data = d3.tsvParse(await FileAttachment("population.tsv").text(), (d, i) => i === 0 ? null : d); const years = data.columns.slice(1, -3).map(x => +x).reverse(); return Object.assign(d3.cross(data, years, (d, year) => ({id: d["FIPS code"], feature: states.get(d["FIPS code"]), year, value: +d[year].replace(/,/g, "")})).filter(d => !isNaN(d.value)).sort((a, b) => d3.descending(a.value, b.value)), {years}); }
Insert cell
Added in target
states = new Map(us.objects.states.geometries.map(d => [d.id, topojson.feature(us, d)]))
Insert cell
Changed in target
-
radius = d3.scaleSqrt([0, d3.quantile([...data.values()].sort(d3.ascending), 0.985)], [0, 15])
+
radius = d3.scaleSqrt([0, 2e7], [0, 50])
Insert cell
path = d3.geoPath()
Insert cell
Changed in target
-
format = d3.format(",.0f")
+
formatValue = d3.format(",d")
Insert cell
Changed in target
-
us = FileAttachment("counties-albers-10m.json").json()
+
us = FileAttachment("states-albers-10m.json").json()
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require("d3@5")
Insert cell
Added in target
import {Scrubber} from "@mbostock/scrubber"
Insert cell