<?php
/**
* Wrapper for Google Visualisation API
* Visualisation Class
*
* wraps all Google Visualisation Types dynamically and
* in a generic way
*
* holds common functions for API to generate Javascript source code
*
* @author Thomas Sch�fer
* @since 2008-06-29
*
*/
class QVizualisationGoogleGraph extends QGoogleGraph {
/**
* version
*
* @var integer
*/
private $vizApi = 1;
protected $vizualisationScope = "google.visualization";
/**
* basic reference link
*
* @var string
*/
private $vizReference = "http://code.google.com/apis/visualization/documentation/gallery/";
/**
* holder of context node => name of visualisation type
*
* @var string
*/
protected $context;
/**
* holder of xml root element
*
* @var string
*/
protected $rootNode = 'root';
/**
* holder of visualisation package name
*
* @var unknown_type
*/
private $package = "";
/**
* holder of data columns
*
* @var array
*/
protected $columns;
/**
* offset of context name in context array
*
* @var integer
*/
protected $contextNode = 1;
/**
* flag for prevent output of div container
*
* @var unknown_type
*/
protected $ignoreContainer = false;
/**
* package usage
*
* @var bool
*/
private $usePackage = true;
/**
* Holder for additional scripts and css
* while render process the add. elements
* will be transfered from a classes package
* property to the register of packages
*
* @var array
*/
protected $registerPackage = array();
protected $methodName;
/**
* constructor:
* - initializes xml objects
* - adds package info
* - binds methods to visualisation type
* - sets default property values
* @return void
*/
public function __construct(){
// identifies context part of class name
$this->context = QTool::make()
->toUnderscore(get_class($this))
->toArray()
->get();
// package = context
$this->package = $this->getContext();
// xml root node
$xml = '<?xml version="1.0" encoding="UTF-8"?><'.$this->rootNode.'></'.$this->rootNode.'>';
// set api section
$this->object["api"] = new SimpleXMLElement($xml);
$api = $this->object["api"];
$apiscript = $api->addChild('script');
$apiscript->addAttribute("type", "text/javascript");
$apiscript->addAttribute("src", "http://www.google.com/jsapi");
// set base section
$this->object["base"] = new SimpleXMLElement($xml);
$object = $this->object["base"];
$script = $object->addChild("script");
$script->addAttribute("type", "text/javascript");
// do ignore if container must not be rendered
if(empty($this->ignoreContainer)){
$this->object["div"] = new SimpleXMLElement($xml);
$object = $this->object["div"];
$object->addChild("div");
// add attributes for local requirements
switch($this->getContext()){
case "map":
case "annotatedtimeline":
$object->div->addAttribute("style", 'width:'.$this->drawProperties["width"].";height:".$this->drawProperties["height"].";");
break;
}
}
$this->initDrawProperties();
}
public function setMethod($name){
$this->methodName = $name;
return $this;
}
/**
* if set then the div container will not be renderer
*
* @return self
*/
public function ignoreContainer(){
$this->ignoreContainer = true;
return $this;
}
/**
* no package usage
*
* @return self
*/
public function noPackage(){
$this->usePackage = false;
return $this;
}
/**
* use package
*
* @return self
*/
public function usePackage(){
$this->usePackage = true;
return $this;
}
/**
* get package name
*
* @return string
*/
public function getPackage(){
return $this->package;
}
/**
* get state of package usage
*
* @return boolean
*/
public function getUsePackage(){
return $this->usePackage;
}
public function getScope() {
return $this->vizualisationScope;
}
/**
* return visualization api version
*
* @return integer
*/
public function getVizApi(){
return $this->vizApi;
}
public function getVisualizationType(){
return $this->vizualisationType;
}
/**
* sets default properties into working array
*
* @return void
*/
public function initDrawProperties(){
foreach($this->drawProperties as $key => $value){
$this->putProperty("drawproperties", array( $key => $value));
}
}
/**
* context/package getter
*
* @return array
*/
public function getContexts(){
return $this->context;
}
/**
* returns the class context name (2nd part of splitted class name)
*
* @return string
*/
public function getContext(){
return $this->context[$this->contextNode];
}
/**
* adds package lines to working array
*
* @return void
*/
public function addPackage($localPackage=true){
$this->putProperty("package","");
if($localPackage) { // usually used by mash ups
if($this->getUsePackage()==true && !isset($this->members)) {
$this->putProperty("package", 'google.load("visualization", "'.$this->getVizApi().'", {packages:["'.$this->package.'"]});');
} elseif($this->getUsePackage()==true && isset($this->members)) {
$packages = array();
foreach($this->members as $member) {
$packages[] = $member->getPackage();
}
$packages = implode('","', $packages);
$this->putProperty("package", 'google.load("visualization", "'.$this->getVizApi().'", {packages:["'.$packages.'"]});');
} else {
$this->putProperty("package", 'google.load("visualization", "'.$this->getVizApi().'");');
}
}
if(isset($this->methodName)) {
$this->putProperty("package", 'google.setOnLoadCallback(draw'.ucfirst($this->methodName).');');
} else {
$this->putProperty("package", 'google.setOnLoadCallback(draw'.ucfirst($this->getPackage()).');');
}
}
/**
* puts and names function outlines for working array
*
* @return void
*/
public function addFunction(){
$this->putProperty("openfunction","");
if(isset($this->methodName)) {
$this->putProperty("openfunction", 'function draw'.ucfirst($this->methodName).'() {');
} else {
$this->putProperty("openfunction", 'function draw'.ucfirst($this->getPackage()).'() {');
}
$this->putProperty("closefunction", '}');
$this->putProperty("closefunction", "\n");
}
/**
* adds columns source to working array
* counts columns
*
* @param array $columns
* @return self
*/
public function addColumns($columns=array()){
$this->columns = count($columns);
$this->putProperty("function", "var data = new google.visualization.DataTable();");
foreach($columns as $column){
switch($this->getContext()){
case "intensitymap":
$this->putProperty("function", "data.addColumn('".$column[0]."','".$column[1]."','".$column[2]."');");
break;
default:
$this->putProperty("function", "data.addColumn('".$column[0]."','".$column[1]."');");
break;
}
}
return $this;
}
/**
* internal: count rows on different contexts
* @param array $values
*
* @return void
*/
private function setAddRows($values){
switch($this->getContext()) {
case "annotatedtimeline":
$i=0;
foreach($values as $value){
if(substr($value[2],0,3)=='new'){
$i++;
}
}
$this->putProperty("function", "data.addRows(".$i.");");
break;
}
switch($this->getContext()){
case "orgchart":
case "piechart":
$this->putProperty("function", "data.addRows(".count($values).");");
break;
case "linechart":
case "barchart":
case "columnchart":
case "areachart":
case "table":
case "scatterchart":
case "gauge":
case "map":
$this->putProperty("function", "data.addRows(".(count($values)/$this->columns).");");
break;
case "intensitymap":
case "motionchart":
$this->putProperty("function", "data.addRows(".ceil(count($values)/$this->columns).");");
break;
}
}
/**
* set values into working array
*
* @param array $values
* @return self
*/
public function setValues($values=array()){
$this->putProperty("function","");
$this->setAddRows($values); // add the row counter
foreach($values as $value){
if(is_string($value[2])) {
$value3 = "'".$value[2]."'" ;
} elseif(is_bool($value[2])) {
$value3 = $value[2]?"true":"false";
} else {
$value3 = (is_null($value[2])?'null':$value[2]);
}
switch($this->getContext()){
case "annotatedtimeline":
case "motionchart":
if(substr($value[0],0,3)=='new'){
$value0 = $value[0];
} elseif(is_string($value[0])||is_bool($value[0])){
$value0 = "'".$value[0]."'";
} else {
$value0 = $value[0];
}
if(substr($value[1],0,3)=='new'){
$value1 = $value[1];
} elseif(is_string($value[1])||is_bool($value[1])){
$value1 = "'".$value[1]."'";
} else {
$value1 = $value[1];
}
if(substr($value[2],0,3)=='new'){
$value2 = $value[2];
$i++;
} elseif(is_string($value[2])||is_bool($value[2])){
$value2 = "'".$value[2]."'";
} else {
$value2 = (is_null($value[2])?'null':$value[2]);
}
$this->putProperty("function", "data.setValue(".$value0.",".$value1.",".$value2.");");
break;
case "map":
case "table":
case "orgchart":
$this->putProperty("function", "data.setCell(".$value[0].",".$value[1].",".$value3.");");
break;
default:
$this->putProperty("function", "data.setValue(".$value[0].",".$value[1].",".$value3.");");
break;
}
}
return $this;
}
/**
* set selection script function
*
* @param string $type
* @param string $option
* @param string $startObject
* @param string $endObject
* @return string
*/
public function getSelection($type, $option, $startObject, $endObject) {
$so = (strpos($startObject,'Control')?$startObject:strtolower($type).$startObject);
$eo = (strpos($endObject,'Control')?$endObject:strtolower($type).$endObject);
return $so.',"'.$option.'",function(){'.$eo.'.setSelection('.$so.'.getSelection());}';
}
/**
* add listener script line
*
* @param string $object
* @param string $listener
* @return self
*/
public function addListener($object, $listener="filterControls") {
$string = array();
$string[] = 'google.visualization.events.addListener(';
$string[] = $object;
$string[] = ');';
$this->putProperty($listener,implode($string),"listen");
return $this;
}
/**
* build javascript data property object
* @return void
*/
private function buildPropertyObject() {
$array = array();
$string = "chart.draw(data, {";
switch($this->getContext()){
case "annotatedtimeline":
$drawproperties = array_reverse($this->getProperty("drawproperties"));
$checkArray = array();
foreach($drawproperties as $row){
foreach($row as $attribute => $value){
$keys = array_keys($this->configuration);
if(in_array($attribute, $keys) && !in_array($attribute, $checkArray)){
$check = $this->checkProperties($attribute, $value);
switch($check){
case "array":
case "literal":
$array[] = $attribute .':'. trim($value);
break;
case "bool":
case "integer":
case "float":
$array[] = $attribute . ":".$value;
break;
default:
switch($attribute) {
// not checked for the moment for correctness
case "zoomEndTime":
case "zoomStartTime":
$array[] = $attribute . ":".$value;
break;
default:
$array[] = $attribute . ":'".$value."'";
break;
}
break;
}
$checkArray[] = $attribute;
}
}
}
$string .= implode(", ", $array);
$string .= "});";
$this->putProperty("function", $string);
break;
default:
$drawproperties = array_reverse($this->getProperty("drawproperties"));
$checkArray = array();
foreach($drawproperties as $row){
foreach($row as $attribute => $value){
$keys = array_keys($this->configuration);
if(in_array($attribute, $keys) && !in_array($attribute, $checkArray)){
$check = $this->checkProperties($attribute, $value);
switch($check){
case "literal":
$array[] =$attribute .':'. trim($value);;
break;
case "bool":
case "integer":
case "float":
$array[] = $attribute . ":".$value;
break;
default:
switch($attribute) {
// not checked for the moment for correctness
case "zoomEndTime":
case "zoomStartTime":
$array[] = $attribute . ":".$value;
break;
default:
$array[] = $attribute . ":'".$value."'";
break;
}
break;
}
}
$checkArray[] = $attribute;
}
}
$string .= implode(", ", $array);
$string .= "});";
$this->putProperty("function", $string);
break;
}
}
public function setDrawProperties($array=array()) {
$this->drawProperties = $array;
return $this;
}
public function buildProperties() {
$string = "{";
$drawproperties = array_reverse($this->getProperty("drawproperties"));
$checkArray = array();
foreach($drawproperties as $row){
foreach($row as $attribute => $value){
$keys = array_keys($this->configuration);
if(in_array($attribute, $keys) && !in_array($attribute, $checkArray)){
$check = $this->checkProperties($attribute, $value);
switch($check){
case "literal":
$array[] =$attribute .':'. trim($value);;
break;
case "bool":
case "integer":
case "float":
$array[] = $attribute . ":".$value;
break;
default:
switch($attribute) {
// not checked for the moment for correctness
case "zoomEndTime":
case "zoomStartTime":
$array[] = $attribute . ":".$value;
break;
default:
$array[] = $attribute . ":'".$value."'";
break;
}
break;
}
}
$checkArray[] = $attribute;
}
}
$string .= implode(", ", $array);
$string .= "}";
return $string;
}
/**
* prepares source to be rendered
* @return void
*/
public function prepare($usePackage=true){
$this->addPackage($usePackage);
$this->addFunction();
if(isset($this->packageSetup)){
$xml = '<?xml version="1.0" encoding="UTF-8"?><'.$this->rootNode.'></'.$this->rootNode.'>';
foreach($this->packageSetup as $element => $attributes){
if(is_array($attributes) and isset($attributes[0])) {
$this->object["addon"][$element] = new SimpleXMLElement($xml);
$this->registerPackage = $element; // register package by name
foreach($attributes as $elm => $attr) {
// new addon
$usePack = $this->object["addon"][$element];
if(isset($attr["text"])) {
$usePackChild = $usePack->addChild($element, $attr["text"]);
} else {
$usePackChild = $usePack->addChild($element);
}
foreach($attr as $attrName => $attrValue){
if($attrName!="text") {
$usePackChild->addAttribute($attrName, $attrValue);
}
}
}
if(!empty($customPackage)) {
$this->object["api"]["script"] = null;
}
} else {
$this->registerPackage = $element; // register package by name
// new addon
$this->object["addon"][$element] = new SimpleXMLElement($xml);
$usePack = $this->object["addon"][$element];
$usePackChild = $usePack->addChild($element); // attributes to the element
foreach($attributes as $attrName => $attrValue){
$usePackChild->addAttribute($attrName, $attrValue);
}
}
}
if(method_exists($this,"loadCustomPackage")) {
$this->object["customPackage"] = new SimpleXMLElement($xml);
$customPackage = $this->object["customPackage"];
$customScript = $customPackage->addChild("script", $this->loadCustomPackage());
$customScript->addAttribute("type", "text/javascript");
}
}
// build container id
if(!$this->hasProperty("id") and empty($this->ignoreContainer)){
$this->setId();
} else {
$this->setId($this->getContext());
}
if($this->getUsePackage()){
$this->putProperty("function", "");
$this->putProperty("function", "var chart = new google.visualization.".$this->vizualisationType."(document.getElementById('".$this->getProperty("id")."'));");
$this->buildPropertyObject();
}
$output = implode("\n", $this->getProperty("package"));
$output .= "\n";
$output .= implode("\n", $this->getProperty("openfunction"));
$output .= "\n";
// append custom javascript code
if(in_array("customAppend", get_class_methods($this))){
$this->customAppend();
}
$output .= implode("\n", $this->getProperty("function"));
$output .= "\n";
$output .= implode("\n", $this->getProperty("closefunction"));
unset($this->properties);
$object = $this->object["base"];
$object->script = $output;
return $this;
}
/**
* rendering
* - prepares js source
* - builds xml
* - converts xml to xhtml
*
* @param string $name
* @param boolean $return
* @return mixed
*/
public function render($name = false, $return = false){
$this->prepare();
$dom_api_node = dom_import_simplexml($this->object["api"]);
if(empty($this->ignoreContainer)){
// build chart type browser container
$dom_div_node = dom_import_simplexml($this->object["div"]);
}
// use base or alt. container as root
$dom_node = !empty($name)
? dom_import_simplexml($this->object[$name])
: dom_import_simplexml($this->object["base"]);
$dom = new DomDocument();
// append add-ons
if(empty($this->usePackage) and isset($this->packageSetup)){
$addons = $this->object["addon"];
if(is_array($addons)) {
foreach($addons as $addon){
$dom_addon = $dom->importNode(dom_import_simplexml($addon), true);
$dom->appendChild($dom_addon);
}
}
if(method_exists($this,"loadCustomPackage") and isset($this->object["customPackage"])) {
$dom_addon = $dom->importNode(dom_import_simplexml($this->object["customPackage"]), true);
$dom->appendChild($dom_addon);
}
} elseif(isset($this->packageSetup)){
$addons = $this->object["addon"];
if(is_array($addons)) {
foreach($addons as $addon){
$dom_addon = $dom->importNode(dom_import_simplexml($addon), true);
$dom->appendChild($dom_addon);
}
}
if(method_exists($this,"loadCustomPackage") and isset($this->object["customPackage"])) {
$dom_addon = $dom->importNode(dom_import_simplexml($this->object["customPackage"]), true);
$dom->appendChild($dom_addon);
}
}
// append presentation container
if(empty($this->ignoreContainer)){
$dom_div_node = $dom->importNode($dom_div_node, true);
$dom->appendChild($dom_div_node);
}
// append api source
$dom_api_node = $dom->importNode($dom_api_node, true);
$dom->appendChild($dom_api_node);
// append defaults
$dom_node = $dom->importNode($dom_node, true);
$dom->appendChild($dom_node);
// strip root node tags for they are not used
$output = str_replace("<".$this->rootNode.">","", $dom->saveHTML());
$output = str_replace("</".$this->rootNode.">","", $output);
if($return) {
return $output;
} else {
echo $output;
}
return false;
}
public function getReferenceLink() {
$link = '<a href="'.$this->vizReference;
$link .= strtolower($this->getContext());
$link .= '.html" target="_blank">Goto Google Visualization Web API Gallery</a>';
return $link;
}
}
|