Small multiples choropleth map. Color shows the annual percent change in the number of manufacturing jobs. Red is a decline, blue is growth.
Three recessions are covered in this timespan: July 1990 to March 1991, March 2001 to November 2001, and December 2007 to June 2009 (see NBER’s business cycle dates).
In a real world situation you wouldn’t want to have the user’s browser
do all the computation needed to draw these maps.
The maps are rendered in <canvas>
so it would be fairly straightforward to pre-render them as images
and to serve those instead.
<html>
<head>
<style>
html {
font-family: monospace;
}
th, .row-header {
font-size: 20px;
font-weight: normal;
}
.row-header {
padding: 20px;
}
</style>
</head>
<body>
<h4 class="loading-text">
Loading... (This takes a long time. Has to render 300,000 tiny polygons.
This would be pre-rendered in a normal situation)
</h4>
<script src="//d3js.org/d3.v3.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-queue.v2.min.js"></script>
<script>
var queue = d3_queue.queue();
var width = 210,
height = 125;
var colorScale = d3.scale.threshold()
.domain([-0.2, -0.1, -0.025, 0.025, 0.1, 0.2])
.range(["#b2182b", "#ef8a62", "#fddbc7", "#f7f7f7", "#d1e5f0", "#67a9cf", "#2166ac"]);
var projection = d3.geo.albersUsa()
.scale(250)
.translate([width/2, height/2]);
var path = d3.geo.path()
.projection(projection);
var table = d3.select("body").append("table");
queue
.defer(d3.json, "us.json")
.defer(d3.json, "manufacturing.json")
.await(ready);
function ready(error, us, manufacturing) {
if (error) throw error;
var nested = d3.nest()
.key(function(d) { return d.year; })
.key(function(d) { return d.qtr; })
.rollup(function(d) { return d3.map(d, function(d) { return d.fips; })})
.entries(manufacturing);
var counties = topojson.feature(us, us.objects.counties);
counties.features
.forEach(function(feature) {
feature.properties.centroid = path.centroid(feature);
});
table.append("thead").selectAll("th")
.data(["", "Q1", "Q2", "Q3", "Q4"])
.enter().append("th")
.text(function(d) { return d; });
var tr = table.selectAll("tr")
.data(nested, function(d) { return d.key; })
.enter().append("tr");
tr.append("td")
.attr("class", "row-header")
.text(function(d) { return d.key; });
var td = tr.selectAll(".map").data(function(d) { return d.values; })
.enter().append("td")
.attr("class", "map");
var canvases = td.append("canvas")
.attr("width", width)
.attr("height", height)
.each(render);
d3.select(".loading-text").remove();
function render(d) {
var data = d.values;
var context = d3.select(this).node().getContext("2d");
var color = function(d) {
if (data.has(d.id)) {
var value = data.get(d.id).pct_change_emp;
return value ? colorScale(value) : "#fff";
}
return "#fff";
};
// Want the maps to render sequentially. Use setTimeout to give the
// browser a break in between drawing each map.
window.setTimeout(function() {
drawMap(context, color);
}, 500);
}
function drawMap(context, color) {
path.context(context);
context.strokeStyle = "#fff";
context.lineWidth = 0.1;
counties.features.forEach(function(d) {
context.beginPath()
path(d);
context.fillStyle = color(d);
context.fill();
context.stroke();
});
}
}
</script>
</body>
</html>