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:
- chs : x/y size of the chart in pixels. x = 2 * length + 25
- chd : the chart data, normalized to the range 0..100
- cht : chart type is a line chart
- chco : line color is black
- chxt : the y axis will be labelled
- chxl : the y axis labels, showing the top and bottom values of the data range
- chxs : axis title color is grey, fontsize is 8 pixels
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;
}
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).