b r a y d e n . o r g / Software

/ WebHome / SparklinesGoogleChartsForTwiki

This Web


WebHome  
Topic List  
Web Statistics 

All Webs


Books
Main
Random
Software
TWiki  

brayden.org


Home
Monthly Digest
Today's Links
Resumé
Reading List
Books RSS
Random RSS
Software RSS

Other


Dale's Blog

currently-reading
TextDrive

Rendering sparklines on TWiki using google charts

Google recently made public their chart creation api. Very simple, very cool. The idea is that you specify chart data and settings in a url, and get back a generated image.

I wrote a little code that renders sparklines-like graphs using the google charts api, from TWiki. In my case I call the code from one of my plugins - but I'm probably the only person on the planet that uses that plugin, so you would need to call it from somewhere else.

The basic idea is simple: create the simplest, smallest legible line graph for a set of data. Small enough that it can serve as just another 'word' in a sentence, yet convey a relatively large set of quantitative information.

My approach was to invent some wiki syntax: ((sparkline)(number1, ..., numberk)), e.g. ((sparkline)(1500.0,700,1244,400,800.0,1921.5,2100.2,500,900,600,800,1000,1921.5,2100.2,500,900,600,800,1000)). This gets rendered as shown in this example. It's all fairly easy.

Our goal is to take markup as shown above, and generate the google api url that will render the graph. That url looks something like this:

http://chart.apis.google.com/chart?
  chs=63x20
  &chd=t:64,17,49,0,23,89,100,5,29,11,23,35,89,100,5,29,11,23,35
  &cht=lc
  &chco=000000
  &chxt=y
  &chxl=0:|400|2100
  &chxs=0,444444,8

Breaking this down:

The reason for adding 25 to the x-size of the chart is to accomodate the axis labels.

To get the image, we wrap the url in an image tag.

Here's the perl code to do all this.

First, the pattern matching.

# ((sparkline)(n1,...nk))
$_[0] =~ s/\(\(sparkline\)\((.*?)\)\)/&make_sparkline($1)/geo;

Now the make_sparkline method.

sub make_sparkline
{
  my ($num_string) = @_;  # string in the form number,number, ...,number
  # input: num1,num2,num3,num4,...,numx
  # output: <img src="http://chart.apis.google.com/chart?params ..."/>
  # params:
  #  chs=kx20       ## where k = 2 * n + 3, where n is length of number list
  #  &chd=t:nnum1,nnum2,nnum3,nnum4,...,nnumx   ## list of normalized numbers
  #  &cht=lc&chco=000000    ## line chart, black line color
  #  &chxt=y&chxl=0:|min|max   ## specify y-axis label min,max=min,max of number list
  #  &chxs=0,444444,8          ## grey axis label, small (8-pixel) font
  #
  # transform to a list of numbers
  # note: numeric-string + number -> number
  my @number_list = map { $_ + 0 } split(/,/, $num_string);
  my ($min_val,$max_val) = find_min_max(@number_list);
  my @normal_list = normalize_spark($min_val, $max_val, @number_list);
  my $values = "t:" . join(',',@normal_list);
  my $width = @normal_list * 2 + 25;
  return "<img src=\"http://chart.apis.google.com/chart?chs=${width}x20&chd=${values}"
  . "&cht=lc&chco=000000&chxt=y&chxl=0:|${min_val}|${max_val}&chxs=0,444444,8\" />";
}

Finally, a couple helper functions.

sub find_min_max
{
  my ($min_val, $max_val) = (10000000,-10000000);
  foreach my $n (@_)
  {
    $min_val = $n if ($n < $min_val);
    $max_val = $n if ($n > $max_val);
  }
  $min_val = int($min_val);
  $max_val = int($max_val);
  return ($min_val,$max_val);
}
sub normalize_spark
{
  my ($min_val, $max_val, @nums) = @_;
  my ($min_to,$max_to) = (0.0,100.0);
  my ($delta_val, $delta_to) = ($max_val - $min_val, $max_to - $min_to);

  return map { int(($_ - $min_val)/$delta_val * $delta_to + $min_to) } @nums;
}

Caveats

The code assumes that max - min value > 1, and that truncating the values to integer is generally OK. So, for small data, pre-scale it to fit those assumptions. The code as written will crash if this assumption is violated.

Really large numbers (positive or negative) will make the y-axis label very wide and will cut into the amount of space for the graph itself. The code could be easily modified to handle this by taking log base 10 of the absolute value of the min and max values and multiplying the larger of the 2 results by about 8 to derive the additional amount to be added to the width of the chart (currently that amount is hard-coded at 25).

 
 
Current Rev: r1.1 - 08 Dec 2007 - 23:33 GMT - DaleBrayden, Revision History:Diffs | r1.1
© 2003-2011 by the contributing authors.