map-presentation



map-presentation

1 3


map-presentation

Slide deck and example code about maps!

On Github Boulder-Denver-d3-vis / map-presentation

Boulder/Denver D3 And Data Vis

January 30th, 2014

Get the Slides Here:

http://bit.ly/1nrw23J

Github Project

I'm Erik

I'm here to talk about maps.

Why Maps?

  • They engage visual processing
  • Spatial relationships become clear
  • Exploration is encouraged
  • Multi-scale
  • Density!

Examples:

  • Inhof's Maps of Switzerland's commute
  • FlowingData's Pizza Map
  • Ocean currents globe
  • Wind over the US
  • Maps of language/politics in Ukraine

Types of maps

  • 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.

Take a look

Chloropleth

  • 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!

Let's make a map!

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.

    Here's the CSV.

Get some Geometry

  • D3 deals best with two formats: GeoJson (link) and TopoJson (link)
    • Both formats are open standards, TopoJson eliminates duplicated segments and uses a fixed-precision number format for much reduced size.

We have states, and we have data. Let's draw a map!

Setup

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.");
      });
    });

Step 1

Draw the States:

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)

Result

A Brief Word on Projections

  • 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.

Associate our data

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
});

Color!

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!

Finished!

  • 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.

Learning More

Exercises

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)

Thanks!

Questions?