On Github Boulder-Denver-d3-vis / map-presentation
Most maps are intended to show geography, but thematic maps attach extra information to the display.
The first thematic map was created by Jocodus Hondius in 1607.
It is called Designatio Orbis Cristiani and shows how major religions were spread around the known world.
A chloropleth is a thematic map that shades regions according to the value of some piece of data.
The first known chloropleth was made by Baron Pierre Doupin in 1826, showing population desnsities of various regions of France. Here it is.
It remains a very effective way to communicate data associated with geography. Let's make one!
We'll use D3 and make a simple chloropleth map.
Step 1: find some data.
We'll use Census Bureau educational attainment by state: Census link
From this, we have education completion (High School, Bachelor's, Advanced) in 6 different years, by state.
Step 2: convert to CSV (or some other useable format) and clean it up.
We'll get an example from Mike: http://bl.ocks.org/mbostock/4090848
He has the state and county geometry in TopoJson here: http://bl.ocks.org/mbostock/raw/4090846/us.json
We have states, and we have data. Let's draw a map!
Fetch the CSV with d3.csv() and the TopoJson with d3.json()
Loop over the data and turn strings into numbers:
var fields, svg = d3.select("#container").append("svg"); fields = [ "Code", "1990-advanced", "1990-bachelors", "1990-hs", "1990-not-hs", "2000-advanced", "2000-bachelors", "2000-hs", "2006-advanced", "2006-bachelors", "2006-hs", "2007-advanced", "2007-bachelors", "2007-hs", "2008-advanced", "2008-bachelors", "2008-hs", "2009-advanced", "2009-bachelors", "2009-hs"]; d3.json("data/us.json", function(us) { d3.csv("data/education.csv", function(edu) { edu.forEach(function(d) { fields.forEach(function(field) { d[field] = parseFloat(d[field]); }); }) // Append some text to prove we got what we want. svg.append("text").attr("transform", "translate(0,20)") .text("We have " + edu.length + " edu entries."); }); });
Set a projection, and configure it.
var width = 960, height = 500; var projection = d3.geo.albersUsa() .scale(1000).translate([width / 2, height / 2]); var path = d3.geo.path().projection(projection);
Draw some lines!
svg.append("g") .classed("states", true) .selectAll("path") .data(topojson.feature(us, us.objects.states).features) .enter() .append("path") .attr("d", path)
A geographical projection is the formula applied to the spherical earth in order to get a flat, 2-D representation.
There are lots of different ways to do this, with tradeoffs for each one.
We're going to use Albers USA, an equal-area conic projection centered over the USA. (Wikipedia) We have a little shape distortion, but minimal area distortion.
We need to associate our education data with each state.
Fortunately we have an ID field that is shared between the two.
All our fields need to be numbers, and findable by ID.
edu.forEach(function(d) { fields.forEach(function(field) { d[field] = parseFloat(d[field]); // Interpret as numbers }); dataById[d.Code] = d; // Make an entry for each ID, from Code in the CSV. })
Add each state's data to the feature it's associated with:
states = topojson.feature(us, us.objects.states).features; states.forEach(function(d) { d.data = dataById[d.id]; // Get the data for each state and store it on the feature object });
Create a linear scale between two shades.
scale = d3.scale.linear() .domain(d3.extent(edu, function(d,i) { return d[field];})) .range(["#ffeda0", "#f03b20"]) .interpolate(d3.interpolateHcl);
We wrap setting a fill color in a function that takes a field and an associated scale.
This lets us do things like add a dropdown to pick which piece of data we want to display.
function colorStates(field, scale) { d3.selectAll(".states path") .attr("fill", function(d) { // We need to check if data exists, othewise color grey return d.data ? scale(d.data[field]) : "#333"; })
Let's take a look!
We've gotten data, mapped it to geography, and drawn a chloropleth.
There's lots more we can do with this data and make this map better.
Some suggestions for follow-up work.
Add a data-set dropdown (also update title)
Add a legend showing what percentages are indicated by each color
Mouseover popups with more in-depth data.
Zoom and pan
A data summary table (min/max for each set, identify outlier states)