Monthly Archive for February, 2010

JavaScript InfoVis Toolkit Customizations

I had the chance to play with the JavaScript InfoVis Toolkit lately. It’s nice to be able to use the Toolkit instead of developing it. The main reason I built the Toolkit in the first place was to create specific visualizations I was needing for MusicTrails. At the end I got to code lots of features but didn’t have the chance to play with them for a long time.

I hope these examples can demonstrate that the Toolkit was really built upon the concepts of Modularity, Extensibility and Composability.

Stacked Bar Charts

One of the things I wanted to do for some time now was to adapt the Spacetree visualization to show Bar Charts.
By watching this example we can already tell that the Spacetree can be used to represent something similar to a Bar Chart. For the next example I created a BarChart class that uses a Spacetree with some special node rendering function to plot bars for each node:

What’s also interesting about this widget is that the custom node implementation I made allows it to show stacked values:

Stacked Bar Charts are useful when aggregated results are as meaninful information as knowing the specific value of each analyzed element. The JQuery team used these kind of charts for measuring performance in different browsers for different versions of JQuery. As you can a see in the next picture, the overall performance comparison is as useful as the specific browser performance improvements data.

Implementation
In order to make Stacked Bar Charts I stored multivalued information into the nodes’ data property and then accessed it to render each node like this:

//Here we implement custom node rendering types for the ST
ST.Plot.NodeTypes.implement({
  'stacked': function(node, canvas) {
    var pos = node.pos.getc(true), nconfig = this.node, data = node.data;
    var cond = nconfig.overridable && data;
    var width  = cond && data.$width || nconfig.width;
    var height = cond && data.$height || nconfig.height;
    var algnPos = this.getAlignedPos(pos, width, height);
    var valueArray = data.valueArray;

    var ctx = canvas.getCtx();
    ctx.save();
    if(valueArray) {
     for(var i=0, l=valueArray.length, acum=0; i<l; i++) {
      var rgb = valueArray[i].color.hexToRgb(true);
      var rgbdark = rgb.map(function(e) { return (e * .3) >> 0; });

      var lgradient = ctx.createLinearGradient(algnPos.x, algnPos.y + acum,
        algnPos.x + width -1, algnPos.y + acum + (valueArray[i].hvalue || 0));

      lgradient.addColorStop(0, rgb.rgbToHex());
      lgradient.addColorStop(0.5, rgb.rgbToHex());
      lgradient.addColorStop(1, rgbdark.rgbToHex());

      ctx.fillStyle = lgradient;
      ctx.fillRect(algnPos.x, algnPos.y + acum, width, valueArray[i].hvalue || 0);
    }
    ctx.restore();
  }
});

That code also uses linear gradients to render nice gradients for each stacked bar.

Pie Charts + TreeMaps = Awesome TreeMaps

When lots of elements need to be compared the Stacked Bar Chart visualization can be confusing. This is due to the fact that each bar gets thinner and the aspect ratio for each bar tends to be very high. I wrote about the aspect ratio problem some time ago, and I also showed that it could be solved by using Squarified TreeMaps to show hierarchical structures in constrained space.
This is OK for replacing Bar Charts, but what about Stacked Bar Charts? Should we subdivide each TreeMap cell into the number of stacked elements? I didn’t find that solution very appealing: for each TreeMap node its subnodes would have the same color, same name, but different values. It seems like redundant information.
Instead, I opted to create Pie Charts inside each TreeMap node. Pie Charts are useful to compare values where the whole information also has a meaning.

Here’s an example I did with the same data collected from the second Stacked Bar Chart image (click on the image to enlarge):



Each TreeMap cell is proportional to the amount of aggregate information for each element. The Pie Chart compares the specific information of each element.
While the previous example isn’t too useful, this next example collects more data and thus makes this visualization more suitable (also click to enlarge):



Implementation
By taking a look at this example we can see that we can make Pie Charts by using RGraphs and adding a special node rendering function. We also know that we can make Squarified TreeMaps by looking at this example.

So how can we combine these two visualizations?

The TreeMap visualization accepts a controller method that is triggered on element creation. This means that for each created treemap node a callback is used that can post-process each TreeMap node. This method is called onCreateElement and I use it to append a pie chart for each TreeMap element like this:

onCreateElement: function(content, node, isLeaf, leaf) {
  if(isLeaf && node.data.valueArray) {
    var w = leaf.offsetWidth, h = leaf.offsetHeight;
    //create a canvas with unique id
    //and append it to the leaf TreeMap element
    var c = new Canvas("piechartcanvas_" + TMPieWidget.count++, {
      injectInto: leaf,
      width: w,
      height: h - 2*tm.config.titleHeight
    });
    //create a RGraph with nodepie node rendering
    //function
    var rg = new RGraph(c, {
        Node: {
            'overridable': true,
             'type': 'nodepie'
        },
        Edge: {
            'overridable': true
        },
        //Parent-children distance
        levelDistance: ((w > h? h : w) / 2) - 2*tm.config.titleHeight,  

        //Add styles to node labels on label creation
        onCreateLabel: function(domElement, node){
            domElement.innerHTML = '';//node.name;
            if(node.data.$aw)
                domElement.innerHTML += " " + node.data.$aw;
            var style = domElement.style;
            style.fontSize = "0.8em";
            style.color = "#fff";
        },
        //Add some offset to the labels when placed.
        onPlaceLabel: function(domElement, node){
            var r = rg.graph.getNode(rg.root);
            var style = domElement.style;
            var dw = domElement.offsetWidth;
            if(r.data.count == 1) {
              var dh = domElement.offsetHeight;
              style.left = (w/2 - dw / 2) + 'px';
              style.top = (h/2 - dh) + 'px';
            } else {
              var left = parseInt(style.left);
              style.left = (left - dw / 2) + 'px';
            }
        }
    });
    rg.loadJSON(that.createJSONPie(node));
    rg.refresh();
  }
}

And that’s it!

I hope these customizations inspired you enough to create your own wacky visualizations with the JavaScript InfoVis Toolkit. I honestly encourage all Toolkit users to try to extend the library with new crazy ideas and features; most of the library design was targeted at that!

PS: Some people posted a job offer at the JavaScript InfoVis Toolkit Google Group that you might find useful. To check or post about job offerings related to visualization or the JavaScript InfoVis Toolkit please join the group!

Voronoi Tessellation

This is going to be the first of a couple of posts related to Voronoi Tessellations, Centroidal Voronoi Tessellations and Voronoi TreeMaps. In this post I’ll explain what a Voronoi Tessellation is, what can it be used for, and also I’ll describe an interesting algorithm for creating a Voronoi Tessellation given a set of points (or sites as I’ll call them from now on).

What is a Voronoi Tessellation?

Given a set P := {p1, …, pn} of sites, a Voronoi Tessellation is a subdivision of the space into n cells, one for each site in P, with the property that a point q lies in the cell corresponding to a site pi iff d(pi, q) < d(pj, q) for i distinct from j. The segments in a Voronoi Tessellation correspond to all points in the plane equidistant to the two nearest sites.
Voronoi Tessellations have applications in computer science, chemistry, etc. but I’m most interested in Voronoi Diagrams for constructing Voronoi TreeMaps (more on that in later posts).

How do I create a Voronoi Tessellation?

One algorithm for creating Voronoi Tessellations was discovered by Steven Fortune in 1986.

This algorithm is described as a plane sweep algorithm. Fortune’s Algorithm maintains both a sweep line (in red) and a beach line (in black) which move through the plane as the algorithm progresses.

The structures used in this algorithm are an EventQueue, EdgeList and a binary Tree that tracks the state of the beach line.

The EventQueue stores two distinct type of events that are related to changes in the beach line. These events happen when:

  • A site s crosses the sweep line: in this case a new parabola with minimum at s is added to the beach line. A Voronoi Edge is born.
  • A circle that touches three sites staying behind the sweep line is found and is tangent to the sweep line (see image below). A Voronoi Vertex is found.

At each of these stages the EdgeList is updated until the algorithm is completed.

Implementations

I haven’t found any interesting implementation of this algorithm in JavaScript. Actually, I didn’t find a working implementation of this algorithm in JavaScript. There’s this version of a JavaScript applet for making Voronoi Tessellations, but it doesn’t seem to work right (the corners of the image generally hold two or more sites in the same cell). Also, the code was based in a C# implementation and isn’t very pretty. Then there’s this blog that mentions the algorithm but there’s no visible implementation, just a JQuery demo that animates parabolas when a mouse crosses a site, but no Voronoi Diagram.

So I made my own implementation, which I tried to make as similar as possible to the original C implementation from Steven Fortune. If you click on the image you can see it in action. Just refresh the page for new Voronoi Tessellations of random positioned sites.



Any comments or advices about how to make this implementation better are welcomed!