2014-03-12 14:52:47 +01:00
< ? php
// Extracted from CodingTeam for the Jappix project.
// This file is a part of CodingTeam. Take a look at <http://codingteam.org>.
// Copyright © 2007-2010 Erwan Briand <erwan@codingteam.net>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, version 3 only.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
// License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/**
* @ file
* This file contains the DrawSVGChart class .
*/
/**
* DrawSVGChart class
*/
class DrawSVGChart {
private $datas , $legend , $link , $xml_object , $svg ,
$xml_elements , $evolution ;
public $has_errors ;
function createChart ( $datas = array (), $legend = array (), $link ,
$evolution = FALSE , $type = 'others' )
{
$this -> has_errors = FALSE ;
$max = 0 ;
// One or two data arrays
if ( isset ( $datas [ 0 ]) && is_array ( $datas [ 0 ]))
{
$datas_number = count ( $datas [ 0 ]);
if ( $datas_number >= 1 )
$max = max ( $datas [ 0 ]);
else
$this -> has_errors = TRUE ;
}
else
{
$datas_number = count ( $datas );
if ( $datas_number >= 1 )
$max = max ( $datas );
else
$this -> has_errors = TRUE ;
}
// Set the width of the chart
if ( $datas_number * 55 > 400 )
$width = $datas_number * 55 ;
else
$width = 400 ;
$height = 250 ;
$this -> datas = $datas ;
$this -> legend = $legend ;
$this -> link = $link ;
$this -> evolution = $evolution ;
$this -> type = $type ;
$this -> xml_elements = array ();
// Scale
if ( $max <= 20 )
{
$scale [ 4 ] = 20 ;
$scale [ 3 ] = 15 ;
$scale [ 2 ] = 10 ;
$scale [ 1 ] = 5 ;
}
else
{
$scale [ 4 ] = ceil ( $max / 20 ) * 20 ;
$scale [ 3 ] = $scale [ 4 ] * 3 / 4 ;
$scale [ 2 ] = $scale [ 4 ] * 2 / 4 ;
$scale [ 1 ] = $scale [ 4 ] * 1 / 4 ;
}
if ( $scale [ 4 ] == 0 || $max == 0 )
$this -> has_errors = TRUE ;
if ( $this -> has_errors )
return TRUE ;
$this -> xml_object = new DOMDocument ( '1.0' , 'utf-8' );
2014-11-25 23:42:38 +01:00
2014-03-12 14:52:47 +01:00
// Process the static file host prefix
$static_prefix = '.' ;
2014-11-25 23:42:38 +01:00
2014-03-12 14:52:47 +01:00
if ( hasStatic ())
$static_prefix = HOST_STATIC . '/php' ;
2014-11-25 23:42:38 +01:00
2014-03-12 14:52:47 +01:00
// Add the stylesheet
$style = $this -> xml_object -> createProcessingInstruction ( " xml-stylesheet " ,
" type='text/css' href=' " . getFiles ( genHash ( getVersion ()), '' , 'css' , '' , 'stats-svg.css' ) . " ' " );
$this -> xml_object -> appendChild ( $style );
// Create the root SVG element
$this -> svg = $this -> xml_object -> createElement ( 'svg' );
$this -> svg -> setAttribute ( 'xmlns:svg' , 'http://www.w3.org/2000/svg' );
$this -> svg -> setAttribute ( 'xmlns' , 'http://www.w3.org/2000/svg' );
$this -> svg -> setAttribute ( 'xmlns:xlink' , 'http://www.w3.org/1999/xlink' );
$this -> svg -> setAttribute ( 'version' , '1.1' );
$this -> svg -> setAttribute ( 'width' , $width );
$this -> svg -> setAttribute ( 'height' , $height );
$this -> svg -> setAttribute ( 'id' , 'svg' );
$this -> xml_object -> appendChild ( $this -> svg );
// Create a definition
$this -> xml_elements [ 'basic_defs' ] = $this -> xml_object -> createElement ( 'defs' );
$path = $this -> xml_object -> createElement ( 'path' );
$path -> setAttribute ( 'id' , 'mark' );
$path -> setAttribute ( 'd' , 'M 0,234 v 4 ' );
$path -> setAttribute ( 'stroke' , '#596171' );
$path -> setAttribute ( 'stroke-width' , '2px' );
$this -> xml_elements [ 'basic_defs' ] -> appendChild ( $path );
// Create the static background
$this -> xml_elements [ 'static_background' ] = $this -> xml_object -> createElement ( 'g' );
$this -> xml_elements [ 'static_background' ] -> setAttribute ( 'class' , 'static-background' );
// Draw the legend
$this -> drawLegend ();
// Draw the table
$this -> drawTable ( $scale , $width );
// Draw the chart
$this -> drawChart ( $scale , $width );
}
function drawLegend ()
{
$pstart = 3 ;
$tstart = 7 ;
foreach ( $this -> legend as $item )
{
$val_path = $pstart + 11 ;
$val_text = $tstart + 10 ;
// Create the legend line
$path = $this -> xml_object -> createElement ( 'path' );
$path -> setAttribute ( 'd' , 'M 40, ' . $val_path . ' L 55, ' . $val_path );
$path -> setAttribute ( 'id' , 'legendline' );
$path -> setAttribute ( 'stroke' , $item [ 0 ]);
$path -> setAttribute ( 'stroke-width' , '2px' );
// Create the legend text
$text = $this -> xml_object -> createElement ( 'text' , $item [ 1 ]);
$text -> setAttribute ( 'x' , 57 );
$text -> setAttribute ( 'y' , $val_text );
$text -> setAttribute ( 'text-anchor' , 'start' );
$text -> setAttribute ( 'id' , 'reftext' );
$text -> setAttribute ( 'fill' , $item [ 0 ]);
$text -> setAttribute ( 'font-size' , '11px' );
$text -> setAttribute ( 'font-family' , " 'DejaVu sans', Verdana, sans-serif " );
// Append elemets
$this -> xml_elements [ 'static_background' ] -> appendChild ( $path );
$this -> xml_elements [ 'static_background' ] -> appendChild ( $text );
$pstart = $val_path ;
$tstart = $val_text ;
}
}
function drawTable ( $scale , $width )
{
// Create left scale
$top = TRUE ;
$start = - 17 ;
foreach ( $scale as $level )
{
$type = $this -> type ;
2014-11-25 23:42:38 +01:00
2014-03-12 14:52:47 +01:00
if (( $type == 'share' ) || ( $type == 'others' ))
$level = formatBytes ( $level );
2014-11-25 23:42:38 +01:00
2014-03-12 14:52:47 +01:00
if ( $top )
$color = '#CED0D5' ;
else
$color = '#EAEAEA' ;
$m = $start + 50 ;
$path = $this -> xml_object -> createElement ( 'path' );
$path -> setAttribute ( 'd' , 'M 38, ' . $m . ' L ' . $width . ', ' . $m );
$path -> setAttribute ( 'stroke' , $color );
$path -> setAttribute ( 'stroke-width' , '1px' );
$text = $this -> xml_object -> createElement ( 'text' , $level );
$text -> setAttribute ( 'x' , 34 );
$text -> setAttribute ( 'y' , ( $m + 3 ));
$text -> setAttribute ( 'text-anchor' , 'end' );
$text -> setAttribute ( 'class' , 'refleft' );
$this -> xml_elements [ 'static_background' ] -> appendChild ( $path );
$this -> xml_elements [ 'static_background' ] -> appendChild ( $text );
$top = FALSE ;
$start = $m ;
}
// Add zero
$text = $this -> xml_object -> createElement ( 'text' , 0 );
$text -> setAttribute ( 'x' , 34 );
$text -> setAttribute ( 'y' , 236 );
$text -> setAttribute ( 'text-anchor' , 'end' );
$text -> setAttribute ( 'class' , 'refleft' );
$this -> xml_elements [ 'static_background' ] -> appendChild ( $text );
}
function drawChart ( $scale , $width )
{
if ( isset ( $this -> datas [ 0 ]) && is_array ( $this -> datas [ 0 ]))
{
$foreached_datas = $this -> datas [ 0 ];
$onlykeys_datas = array_keys ( $this -> datas [ 0 ]);
$secondary_datas = array_keys ( $this -> datas [ 1 ]);
}
else
{
$foreached_datas = $this -> datas ;
$onlykeys_datas = array_keys ( $this -> datas );
$secondary_datas = FALSE ;
}
// Create graphics data
$defs = $this -> xml_object -> createElement ( 'defs' );
$rect = $this -> xml_object -> createElement ( 'rect' );
$rect -> setAttribute ( 'id' , 'focusbar' );
$rect -> setAttribute ( 'width' , 14 );
$rect -> setAttribute ( 'height' , 211 );
$rect -> setAttribute ( 'x' , - 20 );
$rect -> setAttribute ( 'y' , 34 );
$rect -> setAttribute ( 'style' , 'fill: black; opacity: 0;' );
$defs -> appendChild ( $rect );
$path = $this -> xml_object -> createElement ( 'path' );
$path -> setAttribute ( 'id' , 'bubble' );
if ( $this -> evolution )
$path -> setAttribute ( 'd' , 'M 4.7871575,0.5 L 39.084404,0.5 C 41.459488,0.5 43.371561,2.73 43.371561,5.5 L 43.371561,25.49999 L 43.30,31.05 L 4.7871575,30.49999 C 2.412072,30.49999 0.5,28.26999 0.5,25.49999 L 0.5,5.5 C 0.5,2.73 2.412072,0.5 4.7871575,0.5 z' );
elseif ( $secondary_datas )
$path -> setAttribute ( 'd' , 'M 1,0 v 8 l -6,-10 c -1.5,-2 -1.5,-2 -6,-2 h -36 c -3,0 -6,-3 -6,-6 v -28 c 0,-3 3,-6 6,-6 h 43 c 3,0 6,3 6,6 z' );
else
$path -> setAttribute ( 'd' , 'M 4.7871575,0.5 L 39.084404,0.5 C 41.459488,0.5 43.371561,2.73 43.371561,5.5 L 43.371561,25.49999 C 43.371561,27.07677 43.83887,41.00777 42.990767,40.95796 C 42.137828,40.90787 37.97451,30.49999 36.951406,30.49999 L 4.7871575,30.49999 C 2.412072,30.49999 0.5,28.26999 0.5,25.49999 L 0.5,5.5 C 0.5,2.73 2.412072,0.5 4.7871575,0.5 z' );
$path -> setAttribute ( 'fill' , 'none' );
$path -> setAttribute ( 'fill-opacity' , '0.85' );
$path -> setAttribute ( 'pointer-events' , 'none' );
$path -> setAttribute ( 'stroke-linejoin' , 'round' );
$path -> setAttribute ( 'stroke' , 'none' );
$path -> setAttribute ( 'stroke-opacity' , '0.8' );
$path -> setAttribute ( 'stroke-width' , '1px' );
$defs -> appendChild ( $path );
$rect = $this -> xml_object -> createElement ( 'rect' );
$rect -> setAttribute ( 'id' , 'graphicbar' );
$rect -> setAttribute ( 'width' , '12' );
$rect -> setAttribute ( 'height' , '200' );
$rect -> setAttribute ( 'rx' , '2' );
$rect -> setAttribute ( 'ry' , '1' );
$rect -> setAttribute ( 'fill' , '#6C84C0' );
$rect -> setAttribute ( 'fill-opacity' , '0.6' );
$rect -> setAttribute ( 'stroke' , '#5276A9' );
$rect -> setAttribute ( 'stroke-width' , '1px' );
$defs -> appendChild ( $rect );
$rect = $this -> xml_object -> createElement ( 'rect' );
$rect -> setAttribute ( 'style' , 'fill:#8B2323' );
$rect -> setAttribute ( 'id' , 'rectpoint' );
$rect -> setAttribute ( 'width' , 4 );
$rect -> setAttribute ( 'height' , 4 );
$defs -> appendChild ( $rect );
$this -> xml_elements [ 'chart_defs' ] = $defs ;
$global_g = $this -> xml_object -> createElement ( 'g' );
// Calc
$x_base = 35 ;
$y_base = 20 ;
$start = 18 ;
$element = 0 ;
$chart_defs = '' ;
$xprevious = 38 ;
$tprevious = 233 ;
foreach ( $foreached_datas as $key => $data )
{
$x = 27 + $x_base ;
$y = 107 + $y_base ;
$top = 233 - ceil ( $data / ( $scale [ 4 ] / 100 ) * 2 );
if ( $top <= 50 )
$bubble_top = 55 ;
elseif ( ! $secondary_datas )
$bubble_top = ( $top - 42 );
elseif ( $secondary_datas )
$bubble_top = ( $top - 10 );
$type = $this -> type ;
if (( $type == 'share' ) || ( $type == 'others' ))
$value = formatBytes ( $data );
else
$value = $data ;
2014-11-25 23:42:38 +01:00
2014-03-12 14:52:47 +01:00
// Create the chart with datas
$g = $this -> xml_object -> createElement ( 'g' );
$g -> setAttribute ( 'transform' , 'translate(' . $x . ')' );
$duse = $this -> xml_object -> createElement ( 'use' );
$duse -> setAttribute ( 'xlink:href' , '#mark' );
$duse -> setAttribute ( 'xmlns:xlink' , 'http://www.w3.org/1999/xlink' );
$g -> appendChild ( $duse );
$data_g = $this -> xml_object -> createElement ( 'g' );
$data_g -> setAttribute ( 'class' , 'gbar' );
if ( $this -> link )
{
2014-11-25 23:42:38 +01:00
$text = $this -> xml_object -> createElement ( 'text' );
2014-03-12 14:52:47 +01:00
$link = $this -> xml_object -> createElement ( 'a' , mb_substr ( filterSpecialXML ( $onlykeys_datas [ $element ]), 0 , 7 ));
$link -> setAttribute ( 'xlink:href' , str_replace ( '{data}' , filterSpecialXML ( $onlykeys_datas [ $element ]), $this -> link ));
$link -> setAttribute ( 'target' , '_main' );
$text -> appendChild ( $link );
}
else
$text = $this -> xml_object -> createElement ( 'text' , mb_substr ( filterSpecialXML ( $onlykeys_datas [ $element ]), 0 , 7 ));
$text -> setAttribute ( 'class' , 'reftext' );
$text -> setAttribute ( 'y' , 248 );
$text -> setAttribute ( 'text-anchor' , 'middle' );
$data_g -> appendChild ( $text );
$uselink = $this -> xml_object -> createElement ( 'use' );
$uselink -> setAttribute ( 'xlink:href' , '#focusbar' );
$uselink -> setAttribute ( 'xmlns:xlink' , 'http://www.w3.org/1999/xlink' );
$data_g -> appendChild ( $uselink );
if ( ! $this -> evolution )
{
$rect = $this -> xml_object -> createElement ( 'rect' );
$rect -> setAttribute ( 'class' , 'bluebar' );
$rect -> setAttribute ( 'height' , ( 233 - $top ));
$rect -> setAttribute ( 'width' , 13 );
$rect -> setAttribute ( 'x' , - 6 );
$rect -> setAttribute ( 'y' , $top );
$rect -> setAttribute ( 'fill' , $this -> legend [ 0 ][ 0 ]);
$rect -> setAttribute ( 'fill-opacity' , '0.6' );
$data_g -> appendChild ( $rect );
}
else
{
$use = $this -> xml_object -> createElement ( 'use' );
$use -> setAttribute ( 'xlink:href' , '#rectpoint' );
$use -> setAttribute ( 'y' , ( $top - 1 ));
$use -> setAttribute ( 'x' , - 2 );
$data_g -> appendChild ( $use );
if ( $x != ( 35 + 27 ))
$chart_defs .= 'L ' . $x . ' ' . $top . ' ' ;
else
$chart_defs .= 'M ' . $xprevious . ' ' . $tprevious . ' L ' . $x . ' ' . $top . ' ' ;
$xprevious = $x ;
$tprevious = $top ;
}
if ( $secondary_datas && isset ( $secondary_datas [ $element ]))
{
$datalink = $secondary_datas [ $element ];
$dataval = $this -> datas [ 1 ][ $datalink ];
$stop = 233 - ceil ( $dataval / ( $scale [ 4 ] / 100 ) * 2 );
$rect = $this -> xml_object -> createElement ( 'rect' );
$rect -> setAttribute ( 'class' , 'redbar' );
$rect -> setAttribute ( 'height' , ( 233 - $stop ));
$rect -> setAttribute ( 'width' , 13 );
$rect -> setAttribute ( 'x' , - 6 );
$rect -> setAttribute ( 'y' , $stop );
$rect -> setAttribute ( 'fill' , $this -> legend [ 1 ][ 0 ]);
$rect -> setAttribute ( 'fill-opacity' , '0.7' );
$data_g -> appendChild ( $rect );
}
if ( ! $this -> evolution )
{
$path = $this -> xml_object -> createElement ( 'path' );
$path -> setAttribute ( 'stroke' , '#5276A9' );
$path -> setAttribute ( 'stroke-width' , '2px' );
$path -> setAttribute ( 'fill' , 'none' );
$path -> setAttribute ( 'd' , 'M -7,233 v -' . ( 232 - $top ) . ' c 0,-1 1,-1 1,-1 h 12 c 1,0 2,0 2,1 v ' . ( 232 - $top ) . ' z' );
$data_g -> appendChild ( $path );
}
$uselink = $this -> xml_object -> createElement ( 'use' );
$uselink -> setAttribute ( 'xmlns:xlink' , 'http://www.w3.org/1999/xlink' );
$uselink -> setAttribute ( 'xlink:href' , '#bubble' );
$uselink -> setAttribute ( 'y' , $bubble_top );
if ( ! $secondary_datas )
$uselink -> setAttribute ( 'x' , - 42 );
$data_g -> appendChild ( $uselink );
$text = $this -> xml_object -> createElement ( 'text' , $value );
$text -> setAttribute ( 'class' , 'bubbletextblue' );
$text -> setAttribute ( 'x' , - 10 );
if ( ! $secondary_datas )
$text -> setAttribute ( 'y' , ( $bubble_top + 20 ));
else
$text -> setAttribute ( 'y' , ( $bubble_top - 27 ));
$text -> setAttribute ( 'fill' , 'none' );
$data_g -> appendChild ( $text );
if ( $secondary_datas && isset ( $secondary_datas [ $element ]))
{
$text = $this -> xml_object -> createElement ( 'text' , $dataval );
$text -> setAttribute ( 'class' , 'bubbletextred' );
$text -> setAttribute ( 'x' , - 10 );
$text -> setAttribute ( 'y' , ( $bubble_top - 11 ));
$text -> setAttribute ( 'fill' , 'none' );
$data_g -> appendChild ( $text );
}
$g -> appendChild ( $data_g );
$global_g -> appendChild ( $g );
$x_base = $x_base + 50 ;
$y_base = $y_base + 20 ;
2014-11-25 23:42:38 +01:00
$element ++ ;
2014-03-12 14:52:47 +01:00
}
if ( $this -> evolution )
{
$path = $this -> xml_object -> createElement ( 'path' );
$path -> setAttribute ( 'd' , $chart_defs );
$path -> setAttribute ( 'stroke' , $this -> legend [ 0 ][ 0 ]);
$path -> setAttribute ( 'stroke-width' , '1px' );
$path -> setAttribute ( 'fill' , 'none' );
$this -> xml_elements [ 'evolution_path' ] = $path ;
}
$this -> xml_elements [ 'global_g' ] = $global_g ;
$path = $this -> xml_object -> createElement ( 'path' );
$path -> setAttribute ( 'd' , 'M 38,233 h ' . $width );
$path -> setAttribute ( 'stroke' , '#2F4F77' );
$path -> setAttribute ( 'stroke-width' , '2px' );
$path -> setAttribute ( 'pointer-events' , 'none' );
$this -> xml_elements [ 'final_path' ] = $path ;
}
function has_errors ()
{
return $this -> has_errors ;
}
function getXMLOutput ()
{
if ( isset ( $this -> xml_object ))
{
// Add SVG elements to the DOM object
foreach ( $this -> xml_elements as $element )
$this -> svg -> appendChild ( $element );
// Return the XML
2014-11-25 23:42:38 +01:00
$this -> xml_object -> formatOutput = true ;
2014-03-12 14:52:47 +01:00
return $this -> xml_object -> saveXML ();
}
}
}
?>