Combining D3 and Ember to Build Interactive Maps
The Javascript world is exploding with new libraries that let us build really interactive applications. The image on the left is a static image showing how each US State voted in the 2008 Presidential Election - I suspect we’ll be seeing a lot more of this map in the coming months!
Let’s look at how we can draw that map ourselves and make it interactive using: * SVG - a vector graphics format. That will let us draw an good looking map using publicly available data about the geography of each state. * D3.js - a JavaScript library for manipulating documents based on data. Its super fast and will help us interact with the SVG map. * Ember.js - a JavaScript framework for creating ambitious web applications that eliminates boilerplate and provides a standard application architecture. Its going to keep us disciplined and our code organized.
An outline map of the States in the USA
We’ll get stated building an Ember app to draw a map of the USA with each state outlined.
First, we’ll need to know what each State looks like. This information is available to download from http://data.jquerygeo.com/usastates.json.
This is a GeoJSON format which Thomas Newton points out in his post Positioning and Scaling maps in D3 is a format that works really well with D3 since it is data that can be rendered into SVG by our code. Each State’s boundaries are defined in complete detail with polygons that a cartographer has drawn out for us. This works for rectangular States like Colorodo or Wyoming and amazingly also works for those with lots of coastline like Louisiana or Alaska (although the polygons are much more complicated).
Now, to create an Ember app we need to create our application and we’ll assign the GeoJSON data into a bindable property.
Next we’ll add some handlebars expressions to our html page telling it to display the map as an Ember View (we’ll create that in a minute).
Finally we write the view
Whew that is a bit of code so let’s dig in. This is where we use d3
to create the svg that looks like a map of the USA.
d3
(Data-Driven Documents) works by binding data to your DOM then applying tranformations to that data.
In this case our data is the GeoJSON file and our tranformations turn it into an SVG map.
didInsertElement
is called by Ember when the view is inserted into the page’s DOM.
- The first two lines use
d3.select
to add the<svg><g id="regions">
elements to the page on this view’s portion of the page. - The remaining use
d3.selectAll.data.enter
to loop through the features (each State) and add a<path>
element. - Looping through each feature (ie. State), we make use of the rest of the code and set the
d
attribute to the value of thepath
property. Thepath
logic scales the map and uses thealbersUsa
projection which moves Alaska and Hawaii to the bottom left where we usually see them on maps.
Looking at code is fun but what does it look like? Below is the page we just built and the you can see that it does, in fact, look like a map of the USA with each State outlined.
Coloring the States red or blue
A red/blue map isn’t much use when all the States are white so let’s color them in.
First we’ll need some data with the election results so we know what color each State should be. There’s a source from Google at http://code.google.com/p/general-election-2008-data/source/browse/trunk/json/votes/2008. I found the format to be a little hard to work with so I processed it into a simpler json file with just the information we’ll need. It looks like:
To use it we’ll save it in our Ember Application.
We use the javascript map
function to turn our json data into ember objects that we can put into an Ember ArrayProxy
object.
For this example we don’t really need these ember objects but in a real application we’d probably get some requirements that would make them useful.
For instance if we wanted the colors to update on election night as new results come in.
Since we are using the StateResult
and StateResults
objects, we need to define them next.
Both objects are pretty simple. In fact they would both be empty except for the findByName
accessor we define on StateResults
.
We’ll see the need for that in a minute when we look at how the view decides whether to color a State red or blue.
This takes us to the view which enhances what we had before:
Most of the view is the same, all that’s different is the way we set the fill on each State (“feature” in d3 terms).
.attr('fill', this.updateFillFor);
- Instead of setting the “fill” attribute to ‘white’ for all States we now provide a function to make the decision differently for each State. D3 will call the function with the current State as an argument (it actually passes the D3 “datum” object).updateFillFor
- This function uses thefindByName
function we defined inStateResults
to match the “geographical” State with the “election result” State. They are separate because each came from a different external json source. Once we have the stateResult object we look at the winner and know to return either “red” or “blue”.
We can see it all working together right here and yes it does look like all the other red/blue election maps you’ve seen.
This is just the beginning of what you can do by combining D3.js with Ember.js. Next I plan to write about how you could add behavior like clicking to select a State and let Ember show details about that State.