Skip to content

Choropleth Tutorial

zack-krejci edited this page Oct 7, 2012 · 8 revisions

One of the more common tasks any mapmaker encounters is building choropleth maps. A lot of their popularity is owed to their usefulness. By encoding a value with a color, they give a useful way of showing an extra data dimension on a map. In this tutorial we walk through the steps of creating a choropleth map dynamically at runtime.

Let's say that we have map of the US in GeoJSON format with population for each state in a file called states.json. As usual we need to load the map and the data:

wiggle.ready (function () {
    var map = new wiggle.Map ();

    $.ajax ({
        url: 'states.json',
        dataType: 'json',
        success: function (data) {
            var states = wiggle.io.GeoJSON (data);
            map.append (states);
        }),
        error: function (xhr, message) {
            console.log (message);
        }
    });
});

Pretty basic so far, but this will give us our map and set us up for the next part. First, we will need a set of colors. Using [Color Brewer] (http://colorbrewer2.org/), I chose:

var ramp = [
    wiggle.util.icolor (69, 117, 180, 225),
    wiggle.util.icolor  (145, 191, 219, 225),
    wiggle.util.icolor  (224, 243, 248, 225),
    wiggle.util.icolor  (254, 224, 144, 225),
    wiggle.util.icolor  (252, 141, 89, 225),
    wiggle.util.icolor  (215, 48, 39, 225),
];

Ok, now we just need to apply this color ramp to the polygons on the map. To do this, we need to mention [Layer Selectors] (https://github.com/dotskapes/wigglemaps/wiki/Layer-Selectors). These work a lot like jQuery selectors. Each layer selector represents a collection of features within a layer. They're chainable and methods work on every feature selected.

For example

states.features ()

will give us the entire set of features within states. If we wanted to do something more interesting, with them, we could write

var blue = wiggle.util.icolor (0, 0, 255, 255);
states.features ().style ('fill', blue);

to set the color of all states blue. In principle we could chain these together, so

var blue = wiggle.util.icolor  (0, 0, 255, 255);
var red = wiggle.util.icolor  (255, 0, 0, 255);
states.features ().style ('fill', blue).style ('stroke', red);

would set the color of the states blue with a red outline (but we won't do that because that would be really ugly).

One more point. The style methods can use functions as a second argument for fine-grained control over each feature. So

states.features ().style ('fill', function (index, feature) {
    if (feature.attr['name'] == 'Wisconsin')
        return red;
    else
        return blue;
});

will iterate through each state, coloring Wisconsin red and everything else blue.

To map US population to a color, we have to know the bounds of data. Fortunately, such a method exists:

var bounds = states.features ().bounds ();

which will tell us the max and min of the data. In this basic example, we want to do is distribute the colors uniformly among the data values (a more advanced application may use quantiles). So if we have some value in the variable val, we could find the correct color index by:

var tol = (bounds.max - bounds.min) * .05;
var raw = (val - bounds.min + tol) / (bounds.max - bounds.min + 2 * tol) * ramp.length;
var i = Math.floor (raw);

(Note that we're using the variable tol here to pad the edges of the distribution just a little in case some numerical precision issue on the bounds that causes the index to overflow or underflow.)

All that remains now is to actually write down how to compute the mapping of colors. This should do it:

$.ajax ({
    url: 'states.json',
    dataType: 'json',
    success: function (data) {
        var states = wiggle.io.GeoJSON (data);
        var bounds = states.features ().bounds ();
        states.features ().style ('fill', function (index, feature) {
            var val = feature.attr['population'];
            var tol = (bounds.max - bounds.min) * .05;
            var raw = (val - bounds.min + tol) / (bounds.max - bounds.min + 2 * tol) * ramp.length;
            var i = Math.floor (raw);
            return ramp[i];
         });
        map.append (states);
    }),
    error: function (xhr, message) {
        console.log (message);
    }
});
Clone this wiki locally