Building Bullet Charts In Protovis
What Is A Bullet Chart?
Bullet Charts are a best-practices derived replacement for so-called gauge and -meter charts (think gas gauge and thermometer). They were first proposed by Stephen Few of Perceptual Edge and the original specification (PDF) is available on his website. Bullet charts, as defined by Mr. Few encode both qualitative (Good, Bad, Ugly) and quantitative data as typically found in a gauge-type chart into a much smaller space with a much higher data density. While not quite as data-dense as Mr. Tufte's sparklines, Bullet charts are small enough to be used effectively in a dashboard visualization.
At their core, bullet charts are an extension of a typical bar chart and seem to have their ideological roots in thermometer-style graphics.
Bullet charts may be either horizontal or vertical in orientation, the examples here are horizontally oriented.
These charts can be confusing, so please read the specification first.
Bullet Charts & Protovis
After my initial prototype, I got a lot of help from Jamie Love over on the Protovis group to wrap the bullet chart up into packaged and reusable function. Jason Davies also helped identify an issue that was preventing the image from rendering in WebKit based browsers - many kudos to Jamie and Jason for their help!
The Data Objects
The first of the data objects is 'bcd' and bcd is shorthand for 'bullet chart data', so this object supports all of the data that will be encoded on the bullet chart. bcd has the following attributes:
- bcd (bullet chart data object)
- range: this is the overall range of your data set, typically starting at 0 and ending at N.
- min: the minimum acceptable value. This property marks the 'bad' zone on the bullet chart.
- accpt: this is the incremental range from the upper bound of min to the lower bound of 'target'. Actual values in this range are barely acceptable.
- goal: goal is the value desired for a given metric. As in, we have a goal of $3 million dollars in revenue this quarter.
- prev: this is the previous (or last reported) actual value.
- actuals: the current measured value of the metric.
- runrate: runrate is the forecasted finish value of the metric. this property is special in that if it exceeds the maximum in 'range', the bullet chart will be recalibrated against runrate to allow the full range of new values to be seen.
- mag: the order of magnitude of your data. mag is used to format your data to be more readable. In other words, if your value is 6,000,000,000 then the order of magnitude is 1e9 and labels will be written as 6B (as in 6 billion).
- title: the title of the chart, displayed above the bullet chart graphic.
- subtitle: if your measure or metric needs more meta information then it can be placed on the subtitle which is displayed below the actual value label.
- prefix: if your measure needs a prefix (typically a monetary prefix like '$'), set it here.
- vnorm: an array to set the normal color values for the actuals and runrate marks.
- vwarn: an array to set color the alert or warning color values for for the actuals and runrate marks.
- bcp (bullet chart parameters object)
- maxwidth: the maximum width (in pixels) of the bullet chart.
- maxheight: the maximum height (in pixels) of the bullet chart.
- barheight: the height of the actuals bar (and the runrate bar).
The bullet-chart.js file supports 4 main tasks: namespace, data transformation, protovis configuration and protovis loading.
- namespace: puts the bullet chart into the unique name space bchart.
- data transformation: takes the data from the object properties and transforms them for presentation in the protovis canvas.
- protovis configuration: takes the transformed data and uses it to configure the protvis graphic.
- protovis loading: places the call to the protivis render() function to actually build and display the graphic.
there is a small helper function in bullet-chart.js called 'draw.bc()' which wraps bchart.draw(bcp, bcd) in a TRY/CATCH. The predominate reason for this function is to display a more friendly error message to IE users, but it may also catch other errors. So when you are developing a bullet chart it is recommended to call bchart.draw directly to prevent TRY/CATCH from hiding protovis-related errors.
There are two helper functions in this build. The first, as mentioned above is 'draw.bc' and its main function is to display a more friendly error to IE users (yes, a detect would probably be more accurate but currently, IE always throws an error when trying to load protovis so this is good enough). The other function is myObject.clone() (as in var myobjnew = myobj.clone();) and can be used to clone an existing bcd or bcp object so that the data on any unique instance of bcd or bcp is available down the line. I'm using this function in the charts above because the third bullet chart -- RPM -- is derived from the data of the first two and I didn't want to have to calculate and hard code its value.