Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active March 8, 2024 10:40
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save mbostock/fb6c1e5ff700f9713a9dc2f0fd392c35 to your computer and use it in GitHub Desktop.
Save mbostock/fb6c1e5ff700f9713a9dc2f0fd392c35 to your computer and use it in GitHub Desktop.
geo2svg
license: gpl-3.0
height: 1100
border: no
.DS_Store
node_modules
cb_*
<!DOCTYPE html>
<img src="topo.svg" width="960" height="1100">
Display the source blob
Display the rendered blob
Raw
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="960" height="1100" viewBox="0 0 960 1100" fill="none">
<g transform="translate(540,40)" font-size="10" font-family="sans-serif" text-anchor="middle">
<rect height="8" x="0" width="6" fill="#fff7ec"></rect>
<rect height="8" x="6" width="13" fill="#fee8c8"></rect>
<rect height="8" x="19" width="23" fill="#fdd49e"></rect>
<rect height="8" x="42" width="42" fill="#fdbb84"></rect>
<rect height="8" x="84" width="49" fill="#fc8d59"></rect>
<rect height="8" x="133" width="56" fill="#ef6548"></rect>
<rect height="8" x="189" width="78" fill="#d7301f"></rect>
<rect height="8" x="267" width="110" fill="#b30000"></rect>
<rect height="8" x="377" width="23" fill="#7f0000"></rect>
<text x="0" y="-6" fill="#000" text-anchor="start" font-weight="bold">Population per square mile</text>
<g transform="translate(6,0)">
<line stroke="#000" y2="13" x1="0.5" x2="0.5"></line>
<text fill="#000" y="16" x="0.5" dy="0.71em">1</text>
</g>
<g transform="translate(19,0)">
<line stroke="#000" y2="13" x1="0.5" x2="0.5"></line>
<text fill="#000" y="16" x="0.5" dy="0.71em">10</text>
</g>
<g transform="translate(42,0)">
<line stroke="#000" y2="13" x1="0.5" x2="0.5"></line>
<text fill="#000" y="16" x="0.5" dy="0.71em">50</text>
</g>
<g transform="translate(84,0)">
<line stroke="#000" y2="13" x1="0.5" x2="0.5"></line>
<text fill="#000" y="16" x="0.5" dy="0.71em">200</text>
</g>
<g transform="translate(133,0)">
<line stroke="#000" y2="13" x1="0.5" x2="0.5"></line>
<text fill="#000" y="16" x="0.5" dy="0.71em">500</text>
</g>
<g transform="translate(189,0)">
<line stroke="#000" y2="13" x1="0.5" x2="0.5"></line>
<text fill="#000" y="16" x="0.5" dy="0.71em">1,000</text>
</g>
<g transform="translate(267,0)">
<line stroke="#000" y2="13" x1="0.5" x2="0.5"></line>
<text fill="#000" y="16" x="0.5" dy="0.71em">2,000</text>
</g>
<g transform="translate(377,0)">
<line stroke="#000" y2="13" x1="0.5" x2="0.5"></line>
<text fill="#000" y="16" x="0.5" dy="0.71em">4,000</text>
</g>
</g>
</svg>
{
"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.1",
"d3-scale-chromatic": "^1.1.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 block group boundaries.
# Extract the shapefile (.shp) and dBASE (.dbf).
if [ ! -f cb_${YEAR}_${STATE}_bg_500k.shp ]; then
curl -o cb_${YEAR}_${STATE}_bg_500k.zip \
"http://www2.census.gov/geo/tiger/GENZ${YEAR}/shp/cb_${YEAR}_${STATE}_bg_500k.zip"
unzip -o \
cb_${YEAR}_${STATE}_bg_500k.zip \
cb_${YEAR}_${STATE}_bg_500k.shp \
cb_${YEAR}_${STATE}_bg_500k.dbf
fi
# Download the list of counties.
if [ ! -f cb_${YEAR}_${STATE}_counties.json ]; then
curl -o cb_${YEAR}_${STATE}_counties.json \
"http://api.census.gov/data/${YEAR}/acs5?get=NAME&for=county:*&in=state:${STATE}&key=${CENSUS_KEY}"
fi
# Download the census block group population estimates for each county.
if [ ! -f cb_${YEAR}_${STATE}_bg_B01003.ndjson ]; then
for COUNTY in $(ndjson-cat cb_${YEAR}_${STATE}_counties.json \
| ndjson-split \
| tail -n +2 \
| ndjson-map 'd[2]' \
| cut -c 2-4); do
echo ${COUNTY}
if [ ! -f cb_${YEAR}_${STATE}_${COUNTY}_bg_B01003.json ]; then
curl -o cb_${YEAR}_${STATE}_${COUNTY}_bg_B01003.json \
"http://api.census.gov/data/${YEAR}/acs5?get=B01003_001E&for=block+group:*&in=state:${STATE}+county:${COUNTY}&key=${CENSUS_KEY}"
fi
ndjson-cat cb_${YEAR}_${STATE}_${COUNTY}_bg_B01003.json \
| ndjson-split \
| tail -n +2 \
>> cb_${YEAR}_${STATE}_bg_B01003.ndjson
done
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 \
blockgroups=<(ndjson-join 'd.id' \
<(shp2json cb_${YEAR}_${STATE}_bg_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') \
<(cat cb_${YEAR}_${STATE}_bg_B01003.ndjson \
| ndjson-map '{id: d[2] + d[3] + d[4], 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=blockgroups \
| topomerge --mesh -f 'a !== b' counties=counties \
| topomerge -k 'd.properties.density' blockgroups=blockgroups \
| toposimplify -p 1 -f \
> topo.json
# Convert to SVG (while dropping the last line).
cat \
<(topo2geo -n \
< topo.json blockgroups=- \
| ndjson-map -r d3=d3-scale-chromatic '(d.properties.title = d.id, d.properties.fill = d3.schemeOrRd[9][d.id], d)') \
<(topo2geo -n \
< topo.json counties=- \
| ndjson-map '(d.properties.stroke = "black", d.properties.strokeOpacity = 0.3, d)') \
| geo2svg --stroke=none -n -p 1 -w ${WIDTH} -h ${HEIGHT} \
| sed '$d' \
> topo.svg
# Insert the legend.
tail -n +4 \
< legend.svg \
>> topo.svg
rm topo.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.
@sa-mm
Copy link

sa-mm commented Feb 8, 2017

Problems occur when running npm install. Node complains in the following way:

npm ERR! Darwin 16.4.0
npm ERR! argv "/usr/local/Cellar/node/7.5.0/bin/node" "/usr/local/bin/npm" "install"
npm ERR! node v7.5.0
npm ERR! npm  v4.1.2
npm ERR! path /Users/me/Documents/Programming/d3/geo2svg/node_modules/topojson/node_modules/topojson-simplify/bin/toposimplify
npm ERR! code ENOENT
npm ERR! errno -2
npm ERR! syscall chmod

npm ERR! enoent ENOENT: no such file or directory, chmod '/Users/me/Documents/Programming/d3/geo2svg/node_modules/topojson/node_modules/topojson-simplify/bin/toposimplify'
npm ERR! enoent ENOENT: no such file or directory, chmod '/Users/me/Documents/Programming/d3/geo2svg/node_modules/topojson/node_modules/topojson-simplify/bin/toposimplify'
npm ERR! enoent This is most likely not a problem with npm itself
npm ERR! enoent and is related to npm not being able to find a file.
npm ERR! enoent 

npm ERR! Please include the following file with any support request:
npm ERR!     /Users/me/Documents/Programming/d3/geo2svg/npm-debug.log

I can supply a debug.log if you're interested. If one follows the tutorial and installs the dependencies globally, the prepublish script works just fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment