Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active November 13, 2016 16:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mbostock/670b9fe0577a29c39a1803f59c628769 to your computer and use it in GitHub Desktop.
Save mbostock/670b9fe0577a29c39a1803f59c628769 to your computer and use it in GitHub Desktop.
Merged Choropleth
license: gpl-3.0
height: 1100
border: no
.DS_Store
node_modules
cb_*
<!DOCTYPE html>
<svg width="960" height="1100"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var path = d3.geoPath();
var color = d3.scaleThreshold()
.domain([1, 10, 50, 200, 500, 1000, 2000, 4000])
.range(d3.schemeOrRd[9]);
var x = d3.scaleSqrt()
.domain([0, 4500])
.rangeRound([440, 950]);
var g = svg.append("g")
.attr("class", "key")
.attr("transform", "translate(0,40)");
g.selectAll("rect")
.data(color.range().map(function(d) {
d = color.invertExtent(d);
if (d[0] == null) d[0] = x.domain()[0];
if (d[1] == null) d[1] = x.domain()[1];
return d;
}))
.enter().append("rect")
.attr("height", 8)
.attr("x", function(d) { return x(d[0]); })
.attr("width", function(d) { return x(d[1]) - x(d[0]); })
.attr("fill", function(d) { return color(d[0]); });
g.append("text")
.attr("class", "caption")
.attr("x", x.range()[0])
.attr("y", -6)
.attr("fill", "#000")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("Population per square mile");
g.call(d3.axisBottom(x)
.tickSize(13)
.tickValues(color.domain()))
.select(".domain")
.remove();
d3.json("topo.json", function(error, topology) {
if (error) throw error;
svg.append("g")
.selectAll("path")
.data(topojson.feature(topology, topology.objects.tracts).features)
.enter().append("path")
.attr("fill", function(d) { return d3.schemeOrRd[9][d.id]; })
.attr("d", path);
svg.append("path")
.datum(topojson.feature(topology, topology.objects.counties))
.attr("fill", "none")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.3)
.attr("d", path);
});
</script>
{
"private": true,
"license": "gpl-3.0",
"author": {
"name": "Mike Bostock",
"url": "https://bost.ocks.org/mike"
},
"scripts": {
"prepublish": "bash prepublish"
},
"devDependencies": {
"d3-array": "^1.0.1",
"d3-geo-projection": "^1.2.0",
"ndjson-cli": "^0.3.0",
"shapefile": "^0.5.8",
"topojson": "^2.0.0",
"topojson-client": "^2.1.0",
"topojson-simplify": "^2.0.0"
}
}
#!/bin/bash
# EPSG:3310 California Albers
PROJECTION='d3.geoAlbers().parallels([34, 40.5]).rotate([120, 0])'
# The state FIPS code.
STATE=06
# The ACS 5-Year Estimate vintage.
YEAR=2014
# The display size.
WIDTH=960
HEIGHT=1100
# Download the census tract boundaries.
# Extract the shapefile (.shp) and dBASE (.dbf).
if [ ! -f cb_${YEAR}_${STATE}_tract_500k.shp ]; then
curl -o cb_${YEAR}_${STATE}_tract_500k.zip \
"http://www2.census.gov/geo/tiger/GENZ${YEAR}/shp/cb_${YEAR}_${STATE}_tract_500k.zip"
unzip -o \
cb_${YEAR}_${STATE}_tract_500k.zip \
cb_${YEAR}_${STATE}_tract_500k.shp \
cb_${YEAR}_${STATE}_tract_500k.dbf
fi
# Download the census tract population estimates.
if [ ! -f cb_${YEAR}_${STATE}_tract_B01003.json ]; then
curl -o cb_${YEAR}_${STATE}_tract_B01003.json \
"http://api.census.gov/data/${YEAR}/acs5?get=B01003_001E&for=tract:*&in=state:${STATE}"
fi
# 1. Convert to GeoJSON.
# 2. Project.
# 3. Join with the census data.
# 4. Compute the population density.
# 5. Simplify.
# 6. Compute the county borders.
geo2topo -n \
tracts=<(ndjson-join 'd.id' \
<(shp2json cb_${YEAR}_${STATE}_tract_500k.shp \
| geoproject "${PROJECTION}.fitExtent([[10, 10], [${WIDTH} - 10, ${HEIGHT} - 10]], d)" \
| ndjson-split 'd.features' \
| ndjson-map 'd.id = d.properties.GEOID.slice(2), d') \
<(ndjson-cat cb_${YEAR}_${STATE}_tract_B01003.json \
| ndjson-split 'd.slice(1)' \
| ndjson-map '{id: d[2] + d[3], B01003: +d[0]}') \
| ndjson-map -r d3=d3-array 'd[0].properties = {density: d3.bisect([1, 10, 50, 200, 500, 1000, 2000, 4000], (d[1].B01003 / d[0].properties.ALAND || 0) * 2589975.2356)}, d[0]') \
| topomerge -k 'd.id.slice(0, 3)' counties=tracts \
| topomerge --mesh -f 'a !== b' counties=counties \
| topomerge -k 'd.properties.density' tracts=tracts \
| toposimplify -p 1 -f \
> topo.json
# Re-compute the topology as a further optimization.
# This consolidates unique sequences of arcs.
# https://github.com/topojson/topojson-simplify/issues/4
topo2geo \
< topo.json \
tracts=tracts.json \
counties=counties.json
geo2topo \
tracts=tracts.json \
counties=counties.json \
| topoquantize 1e5 \
> topo.json
rm tracts.json counties.json
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment