3 // Extracted from CodingTeam for the Jappix project.
5 // This file is a part of CodingTeam. Take a look at <http://codingteam.org>.
6 // Copyright © 2007-2010 Erwan Briand <erwan@codingteam.net>
8 // This program is free software: you can redistribute it and/or modify it
9 // under the terms of the GNU Affero General Public License as published by
10 // the Free Software Foundation, version 3 only.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
15 // License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 * This file contains the DrawSVGChart class.
29 private $datas, $legend, $link, $xml_object, $svg,
30 $xml_elements, $evolution;
33 function createChart($datas=array(), $legend=array(), $link,
34 $evolution=FALSE, $type='others')
36 $this->has_errors = FALSE;
39 // One or two data arrays
40 if (isset($datas[0]) && is_array($datas[0]))
42 $datas_number = count($datas[0]);
44 if ($datas_number >= 1)
45 $max = max($datas[0]);
47 $this->has_errors = TRUE;
51 $datas_number = count($datas);
53 if ($datas_number >= 1)
56 $this->has_errors = TRUE;
59 // Set the width of the chart
60 if ($datas_number * 55 > 400)
61 $width = $datas_number * 55;
66 $this->datas = $datas;
67 $this->legend = $legend;
69 $this->evolution = $evolution;
71 $this->xml_elements = array();
83 $scale[4] = ceil($max / 20) * 20;
84 $scale[3] = $scale[4] * 3/4;
85 $scale[2] = $scale[4] * 2/4;
86 $scale[1] = $scale[4] * 1/4;
89 if ($scale[4] == 0 || $max == 0)
90 $this->has_errors = TRUE;
92 if ($this->has_errors)
95 $this->xml_object = new DOMDocument('1.0', 'utf-8');
97 // Process the static file host prefix
101 $static_prefix = HOST_STATIC.'/php';
103 // Add the stylesheet
104 $style = $this->xml_object->createProcessingInstruction("xml-stylesheet",
105 "type='text/css' href='".getFiles(genHash(getVersion()), '', 'css', '', 'stats-svg.css')."'");
106 $this->xml_object->appendChild($style);
108 // Create the root SVG element
109 $this->svg = $this->xml_object->createElement('svg');
110 $this->svg->setAttribute('xmlns:svg', 'http://www.w3.org/2000/svg');
111 $this->svg->setAttribute('xmlns', 'http://www.w3.org/2000/svg');
112 $this->svg->setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
113 $this->svg->setAttribute('version', '1.1');
114 $this->svg->setAttribute('width', $width);
115 $this->svg->setAttribute('height', $height);
116 $this->svg->setAttribute('id', 'svg');
117 $this->xml_object->appendChild($this->svg);
119 // Create a definition
120 $this->xml_elements['basic_defs'] = $this->xml_object->createElement('defs');
121 $path = $this->xml_object->createElement('path');
122 $path->setAttribute('id', 'mark');
123 $path->setAttribute('d', 'M 0,234 v 4 ');
124 $path->setAttribute('stroke', '#596171');
125 $path->setAttribute('stroke-width', '2px');
126 $this->xml_elements['basic_defs']->appendChild($path);
128 // Create the static background
129 $this->xml_elements['static_background'] = $this->xml_object->createElement('g');
130 $this->xml_elements['static_background']->setAttribute('class', 'static-background');
136 $this->drawTable($scale, $width);
139 $this->drawChart($scale, $width);
142 function drawLegend()
147 foreach ($this->legend as $item)
149 $val_path = $pstart + 11;
150 $val_text = $tstart + 10;
152 // Create the legend line
153 $path = $this->xml_object->createElement('path');
154 $path->setAttribute('d', 'M 40, '.$val_path.' L 55, '.$val_path);
155 $path->setAttribute('id', 'legendline');
156 $path->setAttribute('stroke', $item[0]);
157 $path->setAttribute('stroke-width', '2px');
159 // Create the legend text
160 $text = $this->xml_object->createElement('text', $item[1]);
161 $text->setAttribute('x', 57);
162 $text->setAttribute('y', $val_text);
163 $text->setAttribute('text-anchor', 'start');
164 $text->setAttribute('id', 'reftext');
165 $text->setAttribute('fill', $item[0]);
166 $text->setAttribute('font-size', '11px');
167 $text->setAttribute('font-family', "'DejaVu sans', Verdana, sans-serif");
170 $this->xml_elements['static_background']->appendChild($path);
171 $this->xml_elements['static_background']->appendChild($text);
178 function drawTable($scale, $width)
184 foreach ($scale as $level)
188 if(($type == 'share') || ($type == 'others'))
189 $level = formatBytes($level);
198 $path = $this->xml_object->createElement('path');
199 $path->setAttribute('d', 'M 38, '.$m.' L '.$width.', '.$m);
200 $path->setAttribute('stroke', $color);
201 $path->setAttribute('stroke-width', '1px');
203 $text = $this->xml_object->createElement('text', $level);
204 $text->setAttribute('x', 34);
205 $text->setAttribute('y', ($m + 3));
206 $text->setAttribute('text-anchor', 'end');
207 $text->setAttribute('class', 'refleft');
209 $this->xml_elements['static_background']->appendChild($path);
210 $this->xml_elements['static_background']->appendChild($text);
217 $text = $this->xml_object->createElement('text', 0);
218 $text->setAttribute('x', 34);
219 $text->setAttribute('y', 236);
220 $text->setAttribute('text-anchor', 'end');
221 $text->setAttribute('class', 'refleft');
223 $this->xml_elements['static_background']->appendChild($text);
226 function drawChart($scale, $width)
228 if (isset($this->datas[0]) && is_array($this->datas[0]))
230 $foreached_datas = $this->datas[0];
231 $onlykeys_datas = array_keys($this->datas[0]);
232 $secondary_datas = array_keys($this->datas[1]);
236 $foreached_datas = $this->datas;
237 $onlykeys_datas = array_keys($this->datas);
238 $secondary_datas = FALSE;
241 // Create graphics data
242 $defs = $this->xml_object->createElement('defs');
244 $rect = $this->xml_object->createElement('rect');
245 $rect->setAttribute('id', 'focusbar');
246 $rect->setAttribute('width', 14);
247 $rect->setAttribute('height', 211);
248 $rect->setAttribute('x', -20);
249 $rect->setAttribute('y', 34);
250 $rect->setAttribute('style', 'fill: black; opacity: 0;');
251 $defs->appendChild($rect);
253 $path = $this->xml_object->createElement('path');
254 $path->setAttribute('id', 'bubble');
256 if ($this->evolution)
257 $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');
258 elseif ($secondary_datas)
259 $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');
261 $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');
263 $path->setAttribute('fill', 'none');
264 $path->setAttribute('fill-opacity', '0.85');
265 $path->setAttribute('pointer-events', 'none');
266 $path->setAttribute('stroke-linejoin', 'round');
267 $path->setAttribute('stroke', 'none');
268 $path->setAttribute('stroke-opacity', '0.8');
269 $path->setAttribute('stroke-width', '1px');
270 $defs->appendChild($path);
272 $rect = $this->xml_object->createElement('rect');
273 $rect->setAttribute('id', 'graphicbar');
274 $rect->setAttribute('width', '12');
275 $rect->setAttribute('height', '200');
276 $rect->setAttribute('rx', '2');
277 $rect->setAttribute('ry', '1');
278 $rect->setAttribute('fill', '#6C84C0');
279 $rect->setAttribute('fill-opacity', '0.6');
280 $rect->setAttribute('stroke', '#5276A9');
281 $rect->setAttribute('stroke-width', '1px');
282 $defs->appendChild($rect);
284 $rect = $this->xml_object->createElement('rect');
285 $rect->setAttribute('style', 'fill:#8B2323');
286 $rect->setAttribute('id', 'rectpoint');
287 $rect->setAttribute('width', 4);
288 $rect->setAttribute('height', 4);
289 $defs->appendChild($rect);
291 $this->xml_elements['chart_defs'] = $defs;
292 $global_g = $this->xml_object->createElement('g');
304 foreach ($foreached_datas as $key => $data)
309 $top = 233 - ceil($data / ($scale[4] / 100) * 2);
313 elseif (!$secondary_datas)
314 $bubble_top = ($top - 42);
315 elseif ($secondary_datas)
316 $bubble_top = ($top - 10);
320 if(($type == 'share') || ($type == 'others'))
321 $value = formatBytes($data);
325 // Create the chart with datas
326 $g = $this->xml_object->createElement('g');
327 $g->setAttribute('transform', 'translate('.$x.')');
329 $duse = $this->xml_object->createElement('use');
330 $duse->setAttribute('xlink:href', '#mark');
331 $duse->setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
332 $g->appendChild($duse);
334 $data_g = $this->xml_object->createElement('g');
335 $data_g->setAttribute('class', 'gbar');
339 $text = $this->xml_object->createElement('text');
341 $link = $this->xml_object->createElement('a', mb_substr(filterSpecialXML($onlykeys_datas[$element]), 0, 7));
342 $link->setAttribute('xlink:href', str_replace('{data}', filterSpecialXML($onlykeys_datas[$element]), $this->link));
343 $link->setAttribute('target', '_main');
344 $text->appendChild($link);
347 $text = $this->xml_object->createElement('text', mb_substr(filterSpecialXML($onlykeys_datas[$element]), 0, 7));
349 $text->setAttribute('class', 'reftext');
350 $text->setAttribute('y', 248);
351 $text->setAttribute('text-anchor', 'middle');
353 $data_g->appendChild($text);
355 $uselink = $this->xml_object->createElement('use');
356 $uselink->setAttribute('xlink:href', '#focusbar');
357 $uselink->setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
358 $data_g->appendChild($uselink);
360 if (!$this->evolution)
362 $rect = $this->xml_object->createElement('rect');
363 $rect->setAttribute('class', 'bluebar');
364 $rect->setAttribute('height', (233 - $top));
365 $rect->setAttribute('width', 13);
366 $rect->setAttribute('x', -6);
367 $rect->setAttribute('y', $top);
368 $rect->setAttribute('fill', $this->legend[0][0]);
369 $rect->setAttribute('fill-opacity', '0.6');
370 $data_g->appendChild($rect);
374 $use = $this->xml_object->createElement('use');
375 $use->setAttribute('xlink:href', '#rectpoint');
376 $use->setAttribute('y', ($top - 1));
377 $use->setAttribute('x', -2);
378 $data_g->appendChild($use);
381 $chart_defs .= 'L '.$x.' '.$top.' ';
383 $chart_defs .= 'M '.$xprevious.' '.$tprevious.' L '.$x.' '.$top.' ';
389 if ($secondary_datas && isset($secondary_datas[$element]))
391 $datalink = $secondary_datas[$element];
392 $dataval = $this->datas[1][$datalink];
393 $stop = 233 - ceil($dataval / ($scale[4] / 100) * 2);
395 $rect = $this->xml_object->createElement('rect');
396 $rect->setAttribute('class', 'redbar');
397 $rect->setAttribute('height', (233 - $stop));
398 $rect->setAttribute('width', 13);
399 $rect->setAttribute('x', -6);
400 $rect->setAttribute('y', $stop);
401 $rect->setAttribute('fill', $this->legend[1][0]);
402 $rect->setAttribute('fill-opacity', '0.7');
403 $data_g->appendChild($rect);
406 if (!$this->evolution)
408 $path = $this->xml_object->createElement('path');
409 $path->setAttribute('stroke', '#5276A9');
410 $path->setAttribute('stroke-width', '2px');
411 $path->setAttribute('fill', 'none');
412 $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');
413 $data_g->appendChild($path);
416 $uselink = $this->xml_object->createElement('use');
417 $uselink->setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
418 $uselink->setAttribute('xlink:href', '#bubble');
419 $uselink->setAttribute('y', $bubble_top);
421 if (!$secondary_datas)
422 $uselink->setAttribute('x', -42);
424 $data_g->appendChild($uselink);
426 $text = $this->xml_object->createElement('text', $value);
427 $text->setAttribute('class', 'bubbletextblue');
428 $text->setAttribute('x', -10);
430 if (!$secondary_datas)
431 $text->setAttribute('y', ($bubble_top + 20));
433 $text->setAttribute('y', ($bubble_top - 27));
435 $text->setAttribute('fill', 'none');
436 $data_g->appendChild($text);
438 if ($secondary_datas && isset($secondary_datas[$element]))
440 $text = $this->xml_object->createElement('text', $dataval);
441 $text->setAttribute('class', 'bubbletextred');
442 $text->setAttribute('x', -10);
443 $text->setAttribute('y', ($bubble_top - 11));
444 $text->setAttribute('fill', 'none');
445 $data_g->appendChild($text);
448 $g->appendChild($data_g);
449 $global_g->appendChild($g);
451 $x_base = $x_base + 50;
452 $y_base = $y_base + 20;
456 if ($this->evolution)
458 $path = $this->xml_object->createElement('path');
459 $path->setAttribute('d', $chart_defs);
460 $path->setAttribute('stroke', $this->legend[0][0]);
461 $path->setAttribute('stroke-width', '1px');
462 $path->setAttribute('fill', 'none');
463 $this->xml_elements['evolution_path'] = $path;
466 $this->xml_elements['global_g'] = $global_g;
468 $path = $this->xml_object->createElement('path');
469 $path->setAttribute('d', 'M 38,233 h '.$width);
470 $path->setAttribute('stroke', '#2F4F77');
471 $path->setAttribute('stroke-width', '2px');
472 $path->setAttribute('pointer-events', 'none');
473 $this->xml_elements['final_path'] = $path;
476 function has_errors()
478 return $this->has_errors;
481 function getXMLOutput()
483 if (isset($this->xml_object))
485 // Add SVG elements to the DOM object
486 foreach($this->xml_elements as $element)
487 $this->svg->appendChild($element);
490 $this->xml_object->formatOutput = true;
491 return $this->xml_object->saveXML();