javascriptjsonvisualizationprotovis

Interactive area chart using Protovis


This is quite a daunting project to a Protovis newcomer, but maybe you could help me split it into digestible chunks?

What I would like to build is an "interactive Area chart", as sketched here: https://i.sstatic.net/7bs9W.png

First of all, it's the data ... I have data for provinces in Excel:

Province   Year  10  100  1000  10000 
A          1970  2   4    6     3 
A          1971  3   6    8     5 
B          1970  6   9    12    6 
B          1971  4   8    11    8 
....       ...   .   .    .     .

For each province and year, I would like to be able to draw an area chart:

vis.add(pv.Area) 
    .data(data.ProvinceA[1970]) 
    .bottom(1) 
    .interpolate("basis") 
    .left(function(d) x(d.x)) 
    .height(function(d) y(d.y)) 
    .fillStyle("rgb(21,173,210)") 
    .anchor("top").add(pv.Line) 
    .lineWidth(3); 

Then I would like to add 2 types of interactivity:

  1. Selection of Province
  2. Time slider

Together, the selection checkboxes and the time slider determine which areas are visible at any given time. If, for example, Province A is selected and the year is 1984, only that area is displayed. If the time slider is now dragged, the corresponding years are now displayed for Province A. If another Province is checked, the areas are overlayed and both areas are redrawn when the time slider moves.

Protovis questions:

  1. How do I format the data (province, year, x, y) for this application?
  2. How do I achieve the binding of checkboxes to area?
  3. How do I implement the time slider? Within Protovis or like an external component with listeners that trigger re-rendering of the graph?

Solution

  • Formatting data: The first step is to get it into JSON, using some external tool (I really like Google Refine for this, though it's a pretty big tool if this is all you need it for - try Mr. Data Converter for a quick and dirty option). These tools will probably give you data as a JSON object, like this:

    `[{"Province":"A", "Year":"1970", "10":2, "100":4, "1000":6, "10000":3}, ...]`
    

    Once you have the data available as JSON, you'll want to get it into shape for your vis. You're going to want to pass each pv.Area an array of values - from your description it looks like you want the [10, 100, 1000, 10000] values. Protovis has a lot of tools for manipulating data - see the pv.Nest operator. There are lots of ways you might approach this - I might do this:

    data = pv.nest(data)
        .key(function(x) {return x.Province})
        .key(function(x) {return x.Year})
        .rollup(function(v) {
            return [v[0]['10'], v[0]['100'], v[0]['1000'], v[0]['10000']];
        });
    

    which gives you an object like:

    { 
        A: {
            1970: [2,4,6,3]
            // ...
        },
        // ...
    }
    

    This sets you up for the interface elements. Keep the array of checked Provinces and the current year in global variables:

    var currentProvinces = ['A', 'B', ...];
    var currentYear = 1970;
    

    and set up your area to reference those variables:

    // a containing panel to help with layout and data
    var panel = vis.add(pv.Panel)
        .data(function() currentProvinces); // making this a function allows it to 
                                            // be re-evaluated later
    // the area itself
    var area = panel.add(pv.Area)
        .data(function(province) data[province][currentYear]);
        // plus more area settings as needed
    

    Now use some other library - I'm partial to jQuery, with jQuery UI for the slider - to create your interface elements. The onchange function for each element just needs to set the corresponding global variable and call vis.render() (assuming your root panel is called vis). This should be pretty simple - see here for a Protovis example using jQuery UI to make a time slider very similar to what you have in mind.