plotdata); } public function current() { return current($this->plotdata); } public function key() { return key($this->plotdata); } public function next() { return next($this->plotdata); } public function valid() { return key($this->plotdata) ? TRUE : FALSE; } public function count() { return count($this->plotdata); } // Chart Types private $cht = array( // Line // 'line' => 'lc', // 'sparkline' => 'ls', // 'line_xy' => 'lxy', // Bar // 'horizontal_bar' => 'bhs', 'vertical_bar' => 'bvs', // 'horizontal_bar_grp' => 'bhg', // 'vertical_bar_grp' => 'bvg', // Pie // 'pie' => 'p', // 'pie_3d' => 'p3', // 'pie_concentric' => 'pc', // Venn // 'venn' => 'v', // Scatter // 'scatter' => 's', // Radar // 'radar' => 'r', // 'radar_fill' => 'rs', // Maps // 'map' => 't', // Google-o-meter // 'google_o_meter' => 'gom', // QR // 'qr' => 'qr', ); public function __construct($type) { if (empty($this->cht[$type])) throw new Kohana_Exception('Unknown chart type :type for :class',array(':type'=>$type,':class'=>get_class($this))); $this->chart_type = $this->cht[$type]; } public function __set($key,$value) { switch ($key) { case 'div': $this->div_mode = $value; break; case 'encode': // Encoding options are t=none,s=simple,e=extend if (! in_array($value,array('t','s','e'))) throw new Kohana_Exception('Unknown encoding type :type',array(':type'=>$value)); $this->data_encode = $value; break; case 'title': $this->chart_title = $value; break; default: throw new Kohana_Exception('Unknown variable :key (:value) for :class',array(':key'=>$key,':value'=>$value,':class'=>get_class($this))); } } public function __toString() { try { return $this->render(); } catch (Exception $e) { echo Debug::vars($e); } } /** * Return an instance of this class * * @return GoogleChart */ public static function factory($type) { return new GoogleChart($type); } public function series($data) { // Quick Sanity check that we have the right indexes foreach (array('data','axis') as $k) if (! isset($data[$k])) throw new Kohana_Exception('Missing key :key',array(':key'=>$k)); // Quick check that we have the right types if (! in_array($data['axis'],array('x','r'))) throw new Kohana_Exception('Unknown data type :type',array(':type'=>$data['axis'])); if (is_array($data['data'])) { foreach ($data['data'] as $title => $values) { $this->numseries++; $this->chartdata['legend'][$this->numseries] = $title; $this->chartdata['axis'][$data['axis']][] = $this->numseries; if (is_array($values)) foreach ($values as $k => $v) $this->plotdata[$k][$data['axis']][$this->numseries] = $v; else throw new Kohana_Exception('Series data needs to be an array'); } } else { throw new Kohana_Exception('Series data needs to be an array'); } } private function seriescolors() { $return = array(); $i = 0; for ($j=0;$j<$this->numseries;$j++) { array_push($return,$this->series_colors[$i++]); // Reset $i if we exceed our available colors if ($i > count($this->series_colors)) $i = 0; } return $return; } public function series_colors() { return implode(',',$this->seriescolors()); } public function series_chm() { if (! isset($this->chartdata['axis']['r'])) return ''; $return = array(); $c = $this->seriescolors(); foreach ($this->chartdata['axis']['r'] as $v) array_push($return,sprintf('%s,%s,%s,%s,%s,%s','D',$c[$v-1],$v-1,0,2,2));// @todo 'D,0,2,2' May need to be configurable return implode('|',$return); } public function series_x_labels() { // @todo Why the 0:? return '0:|'.implode('|',array_keys($this->plotdata)); } public function series_scaling() { $return = array(); foreach ($this->chartdata['max'] as $k => $v) array_push($return,sprintf('%s,%s',empty($this->chartdata['min']) ? 0 : $this->chartdata['min'],$v)); return implode(',',$return); } public function series_scale() { $return = array(); $i = 1; // @todo need to add min values foreach ($this->chartdata['max'] as $k => $v) array_push($return,sprintf('%s,0,%s,0',$i++,$v)); return implode('|',$return); } public function series_legend() { return implode('|',$this->chartdata['legend']); } public function series_data() { $return = $sreturn = $invs = array(); $sd = $max = array(); foreach ($this->plotdata as $label => $seriesdata) foreach ($seriesdata as $type => $data) { foreach ($data as $key => $value) { $sd[$key][$label] = $value; $max[$key] = $this->chartdata['max'][$type]; $invs[$type][$key] = TRUE; } } foreach ($sd as $k => $v) array_push($sreturn,$this->encode($v,$max[$k])); // @todo This might need some more work, when used with different graph types. if (count($invs) > 1) $prefix = sprintf('%s:',count($invs['x'])); else $prefix = ':'; // If encoding is text, we need to separate the series with a | if ($this->data_encode == 't') return $prefix.implode('|',$sreturn); else return $prefix.implode(',',$sreturn); } private function render() { $output = ''; // Testing if ($this->div_mode) { $output .= '
GoogleChart
'; $output .= '
'; } // Render $output .= sprintf('%s',$this->url,http_build_query($this->build()),_('Traffic Summary')); // $output .= Debug::vars($this->build()); // $output .= Debug::vars($this->chartdata); // $output .= Debug::vars($this->plotdata); // Toggle the display of the chart. // @todo This JS should be placed elsewhere for HTML validation if ($this->div_mode) { $output .= '
'; $output .= ''; } return $output; } public function html_table($vertical=TRUE,$class=array()) { if (! count($this)) return sprintf('
%s
',_('No Data')); $output = sprintf('', isset($class['table']) ? $class['table'] : 'class="google-data-table"'); if ($vertical) { $output .= ''; $output .= ''; foreach ($this->chartdata['axis'] as $axis => $adetails) foreach ($adetails as $k) $output .= sprintf('',$this->chartdata['legend'][$k]); $output .= ''; foreach ($this as $k => $v) { $output .= ''; $output .= sprintf('',$k); foreach ($this->chartdata['axis'] as $axis => $adetails) foreach ($adetails as $k) $output .= sprintf('',$v[$axis][$k]); $output .= ''; } // Horizontal table } else { $output .= ''; $output .= ''; foreach ($this as $k => $v) $output .= sprintf('',$k); $output .= ''; foreach ($this->chartdata['axis'] as $axis => $adetails) foreach ($adetails as $id) { $output .= ''; $output .= sprintf('',$this->chartdata['legend'][$id]); foreach ($this as $k => $v) $output .= sprintf('',$v[$axis][$id]); $output .= ''; } } $output .= '
 %s
%s%s
 %s
%s%s
'; return $output; } /** * Some pre-render processing */ private function process() { $max = array(); // Work out our maximum number for each series. foreach ($this->plotdata as $label => $seriesdata) { foreach ($seriesdata as $type => $data) { $c = 0; foreach ($data as $value) $c += $value; // Scale up our max, so we get some extra space at the top of our chart. // @todo This should be configurable. $c *= 1.1; if (! isset($this->chartdata['max'][$type])) $this->chartdata['max'][$type] = $c; else $this->chartdata['max'][$type] = ($c > $this->chartdata['max'][$type]) ? $c : $this->chartdata['max'][$type]; } } } /** * Build the chart */ private function build() { if ($this->plotdata) { $this->process(); return array( 'chf'=>'bg,s,FFFFFF00', 'cht'=>$this->chart_type, 'chs'=>$this->chart_size, 'chtt'=>$this->chart_title, 'chbh'=>'a', // @todo This might need to be calculated, valid options (a,r); 'chg'=>'7.7,12.5,1,5', // @todo This should be calculated 'chco'=>$this->series_colors(), 'chdl'=>$this->series_legend(), 'chd'=>$this->data_encode.$this->series_data(), // 'chds'=>$this->series_scaling(), 'chm'=>$this->series_chm(), 'chxt'=>'x,y,r', // @todo configurable? 'chxl'=>$this->series_x_labels(), 'chxr'=>$this->series_scale(), ); } else return array(); } /** * Encode the series data */ private function encode($data,$max=NULL) { $table = array(); $table['simple'] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $table['extend'] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.'; $size = array(); $size['simple'] = strlen($table['simple']); $size['extend'] = strlen($table['extend']); if (is_null($max) OR $max == 0) $max = max($data) > 0 ? max($data) : 1; $encode = ''; switch ($this->data_encode) { case 't' : return join(',',$data); case 's' : foreach ($data as $v) if ($v > -1) $encode .= substr($table['simple'],($size['simple']-1)*($v/$max),1); else $encode .= '_'; break; case 'e' : foreach ($data as $v) { # Convert to a 0-4095 data range $y = 4095*$v/$max; $first = substr($table['extend'],floor($y/$size['extend']),1); $second = substr($table['extend'],$y%$size['extend'],1); $encode .= "$first$second"; } break; } return $encode; } } ?>