Google Charts API Simple and Extended Encoders in PHP

Google’s charting API has been around for quite a while now, but I’ve only just needed to actually look at it. It became immediately obvious that I needed a PHP encoding function, so off to google I went. Though I found several implementations, they were all incomplete or deficient in one way or another (and it didn’t help that there was an error in google’s extended encoding docs), so I’ve written my own based on several different ones. Both simple and extended encoders support automatic scaling, inflated maximum and lower-bound truncation, so you can pretty much stuff whatever data you like in, with no particular regard for pre-scaling and you’ll get a usable result out. They have an identical interface, so you can use either encoding interchangeably according to the output resolution you need (contrary to popular belief, the encoding to use has very little to do with the range of values you need to graph). By default, the full range of possible values is used as it just seems silly not to. I deliberately omit the ‘s:’ and ‘e:’ prefixes so that you can call these functions for multiple data series, and I include a function that does just that. You still need to generate your own URLs and other formatting, but that’s a different problem. Read on for the code…

/**
 * Encode an array of integers according to Google's Simple Encoding
 * Scales to largest value in array if $max is not set, truncates lower bound if $min is set
 * Does NOT set the 's:' prefix so it can be called when creating multiple data series
 * @link http://code.google.com/apis/chart/formats.html#simple
 * @param array $values
 * @param integer $max
 * @return string
 * @author Marcus Bointon 
 */
function googleSimpleEncode($values, $max = -1, $min = 0) {
	$encoding = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
	$chartdata = '';
	$rangemax = 61;
	if ($max < 0) {
		$max = max($values);
	}
	if ($max < max($values)) {
		$max = max($values);
	}
	$range = $max - $min;
	$scale = $rangemax / $range;
	foreach ($values as $k => $v) {
		if (!is_null($v) and $v - $min >= 0) {
			$chartdata .= $encoding[floor(($v - $min) * $scale)];
		} else { 
			$chartdata .= '_';
		} 
	}
	return $chartdata;
}

/**
 * Encode an array of integers according to Google's Extended Encoding
 * Scales to largest value in array if $max is not set, truncates lower bound if $min is set
 * Does NOT set the 'e:' prefix so it can be called when creating multiple data series
 * @link http://code.google.com/apis/chart/formats.html#extended
 * @param array $values
 * @param integer $max
 * @param integer $min
 * @return string 
 * @author Marcus Bointon 
 */
function googleExtendedEncode($values, $max = -1, $min = 0) {
	$encoding = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.';
	$rangemax = 4095;
	$chartdata = '';
	if ($max < 0) {
		$max = max($values);
	}
	//If max is smaller than the largest value, it will go beyond range allowed by the encoding (0..4095)
	if ($max < max($values)) {
		$max = max($values);
	}
	$range = $max - $min;
	$scale = $rangemax / $range;
	foreach ($values as $k => $v){
		if (!is_null($v) and $v >= $min && $v <= $max) {
			$scaledvalue = ($v - $min) * $scale;
			$chartdata .= $encoding[floor($scaledvalue / 64)].$encoding[$scaledvalue % 64];
		} else {
			$chartdata .= '__'; // Value out of max range;
		}
	}
	return($chartdata);
}

/**
 * Encode an array of one or more integer arrays into one of the Google Charts API formats
 * @link http://code.google.com/apis/chart/formats.html
 * @param array $valuearrays An array of data series arrays you want to plot
 * @param string $encoding 's' or 'e' for simple or extended encoding respectively
 * @param integer $max Maximum value to scale to (-1 for auto-scaling, the default)
 * @param integer $min Minimum value to truncate to
 * @param boolean $commonscaling Whether to use the same scale for all series, or calculate each one separately
 * @return string 
 * @author Marcus Bointon 
 */
function googleMultiEncode($valuearrays, $encoding = 's', $max = -1, $min = 0, $commonscale = true) {
	if ($encoding != 'e') {
		$encoding = 's';
	}
	$chartdata = "$encoding:";
	//Check that max value is valid (if given, must be >= largest value)
	$amax = maxInArrays($valuearrays);
	if ($max > 0 and $amax > $max) {
		$max = $amax;
	}
	if ($max < 0 and $commonscale) {
		$max = $amax;
	}
	$charts = array();
	foreach($valuearrays as $array) {
		if ($encoding == 's') {
			$charts[] = googleSimpleEncode($array, $max, $min);
		} else {
			$charts[] = googleExtendedEncode($array, $max, $min);
		}
	}
	$chartdata .= implode(',', $charts);
	return($chartdata);
}

/**
 * Get the largest value in an array of integer arrays
 * @param array $arrays
 * @return integer
 */
function maxInArrays($arrays) {
	$amax = -1;
	foreach($arrays as $array) {
		if ($amax < max($array)) {
			$amax = max($array);
		}
	}
	return $amax;
}

And here's a simple example using it:

$a = array(array(12,39,57,45,51,27),array(1,19,27,53,61,60),array(10,1,87,23,6,18));
$max = maxInArrays($a);
$m = googleMultiEncode($a, 'e', -1, 0, true);
echo "http://chart.apis.google.com/chart?cht=lc&chxt=y&chs=400x280&chxr=0,0,$max&chd=$m\n";
$m = googleMultiEncode($a, 's', -1, 0, true);
echo "http://chart.apis.google.com/chart?cht=lc&chxt=y&chs=400x280&chxr=0,0,$max&chd=$m\n";

Try to guess which is simple and which is extended encoding!
example graphexample graph

As you can see there's no real difference between the encodings for low-res data like this, so now that it's so easy to switch between them, you can just pick which is the most appropriate, trading off resolution for URL length. The separate maxInArrays functions is useful to extract the max value in your series ready to drop into your axis value. Here's an example to show how the choice of encoding is completely independent of your value range:

$a = array(10000,20000,30000,40000,50000);
$m = googleSimpleEncode($a);
$max = max($a);
echo "http://chart.apis.google.com/chart?cht=bvs&chxt=y&chbh=a&chs=200x100&chxr=0,0,$max&chd=s:$m\n";

example graph

Please leave a comment or trackback if you you find this useful or have any bug reports, suggestions or other comments.

Credit to Google for their JavaScript version that I started with, and Ben Dodson and Felipe Barone for their versions, and to Lorna Jane for her Genshi in Serendipity tip.

Update 27th Sept 2011: Craig Stanton pointed out that missing values were not handled nicely and suggested a fix, which I've incorporated.

6 Replies to “Google Charts API Simple and Extended Encoders in PHP”

  1. While you set a value for $max, you didn’t actually pass it in to the function, so it’s scaling to a max value of 41, not 61. Do this instead:

    $m = googleSimpleEncode($a, $max);
  2. $a = array(39, 43, 40, 41);
    $max = 61;
    $m = googleSimpleEncode($a);
    echo $m;

    returns ’3956’.Should return ’nrop’

    Help me please. Thanks.

  3. You must have made a mistake. This gives me ‘nrop’:

    $a = array(39, 43, 40, 41); $max = 61; $m = googleSimpleEncode($a,$max); echo $m;

Leave a Reply