A graphical toolkit for visualization
Protovis
Overview
Examples
Documentation
Download
Index

Property Chaining

Absolute References

A stacked area chart can be produced using property chaining, where a new mark's properties are defined in terms of an existing one:

var vis = new pv.Panel()
    .width(150)
    .height(150);

var area = vis.add(pv.Area)
    .data([1, 1.2, 1.7, 1.5, .7, .5, .2])
    .bottom(0)
    .height(function(d) d * 40)
    .left(function() this.index * 25)
    .fillStyle("steelblue");

area.add(pv.Area)
    .data([.4, .2, .8, 1.2, 1.5, 1.1, .8])
    .bottom(function() area.bottom() + area.height())
    .fillStyle("lightsteelblue");

vis.render();

This example actually combines inheritance with property chaining. Another example:

var vis = new pv.Panel()
    .width(150)
    .height(150);

var area = vis.add(pv.Area)
    .data([1, 1.2, 1.7, 1.5, .7, .5, .2])
    .left(0)
    .width(function(d) d * 40)
    .bottom(function() this.index * 25);

area.add(pv.Area)
    .data([.4, .2, .8, 1.2, 1.5, 1.1, .8])
    .left(function() area.left() + area.width())
  .add(pv.Line)
    .strokeStyle("white");

vis.render();

Although straightforward, property chaining can be verbose.

Relative References

Using parent

The most common way marks may refer to previously-computed properties is by looking at their parent.

Using proto

Description forthcoming. Note: requires that proto is visible and rendered. Also, if a property function that references this.proto is inherited, the reference changes!

Stacked area charts can also be specified using panels. As above, the panel data is a two-dimensional array, each element an array of values for the area. To offset the bottom of the area, we refer to the cousin, which is the corresponding area instance in the previous instance of the parent panel:

new pv.Panel()
    .width(150)
    .height(150)
  .add(pv.Panel)
    .data([[1, 1.2, 1.7, 1.5, .7, .5, .2],
           [.4, .2, .8, 1.2, 1.5, 1.1, .8],
           [.4, .6, .8, .7, .6, .5, .8]])
  .add(pv.Area)
    .data(function(d) d)
    .bottom(function() {
        var c = this.cousin();
        return c ? c.bottom + c.height : 0;
      })
    .height(function(d) d * 40)
    .left(function() this.index * 25)
  .root.render();

Of course, this is a bit convoluted. The convenient stack layout presents an automatic alternative, but it would be nice to design something a bit more robust (for instance, that calculates the offset in data space rather than pixel space). The technique of using the cousin can break if some of the panel instances are invisible, or in conjunction with inheritance.

Copyright 2010 Stanford Visualization Group