PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.12.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.12.1
5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / vendor / szymach / c-pchart / src / Data.php
matomo / app / vendor / szymach / c-pchart / src Last commit date
Barcode 1 year ago Chart 1 year ago BaseDraw.php 1 year ago Cache.php 1 year ago Data.php 1 year ago Draw.php 1 year ago Image.php 1 year ago
Data.php
1158 lines
1 <?php
2
3 namespace CpChart;
4
5 use Exception;
6 use RuntimeException;
7 /**
8 * Data - class to manipulate data arrays
9 *
10 * Version : 2.1.4
11 * Made by : Jean-Damien POGOLOTTI
12 * Last Update : 19/01/2014
13 *
14 * This file can be distributed under the license you can find at :
15 *
16 * http://www.pchart.net/license
17 *
18 * You can find the whole class documentation on the pChart web site.
19 */
20 class Data
21 {
22 /**
23 * @var array
24 */
25 public $Data = [];
26 /**
27 * @var array
28 */
29 public $Palette = ["0" => ["R" => 188, "G" => 224, "B" => 46, "Alpha" => 100], "1" => ["R" => 224, "G" => 100, "B" => 46, "Alpha" => 100], "2" => ["R" => 224, "G" => 214, "B" => 46, "Alpha" => 100], "3" => ["R" => 46, "G" => 151, "B" => 224, "Alpha" => 100], "4" => ["R" => 176, "G" => 46, "B" => 224, "Alpha" => 100], "5" => ["R" => 224, "G" => 46, "B" => 117, "Alpha" => 100], "6" => ["R" => 92, "G" => 224, "B" => 46, "Alpha" => 100], "7" => ["R" => 224, "G" => 176, "B" => 46, "Alpha" => 100]];
30 public function __construct()
31 {
32 $this->Data["XAxisDisplay"] = AXIS_FORMAT_DEFAULT;
33 $this->Data["XAxisFormat"] = null;
34 $this->Data["XAxisName"] = null;
35 $this->Data["XAxisUnit"] = null;
36 $this->Data["Abscissa"] = null;
37 $this->Data["AbsicssaPosition"] = AXIS_POSITION_BOTTOM;
38 $this->Data["Axis"][0]["Display"] = AXIS_FORMAT_DEFAULT;
39 $this->Data["Axis"][0]["Position"] = AXIS_POSITION_LEFT;
40 $this->Data["Axis"][0]["Identity"] = AXIS_Y;
41 }
42 /**
43 * Add a single point or an array to the given serie
44 * @param mixed $Values
45 * @param string $SerieName
46 * @return int
47 */
48 public function addPoints($Values, $SerieName = "Serie1")
49 {
50 if (!isset($this->Data["Series"][$SerieName])) {
51 $this->initialise($SerieName);
52 }
53 if (is_array($Values)) {
54 foreach ($Values as $Value) {
55 $this->Data["Series"][$SerieName]["Data"][] = $Value;
56 }
57 } else {
58 $this->Data["Series"][$SerieName]["Data"][] = $Values;
59 }
60 if ($Values != VOID) {
61 $StrippedData = $this->stripVOID($this->Data["Series"][$SerieName]["Data"]);
62 if (empty($StrippedData)) {
63 $this->Data["Series"][$SerieName]["Max"] = 0;
64 $this->Data["Series"][$SerieName]["Min"] = 0;
65 return 0;
66 }
67 $this->Data["Series"][$SerieName]["Max"] = max($StrippedData);
68 $this->Data["Series"][$SerieName]["Min"] = min($StrippedData);
69 }
70 }
71 /**
72 * Strip VOID values
73 * @param mixed $Values
74 * @return array
75 */
76 public function stripVOID($Values)
77 {
78 if (!is_array($Values)) {
79 return [];
80 }
81 $Result = [];
82 foreach ($Values as $Value) {
83 if ($Value != VOID) {
84 $Result[] = $Value;
85 }
86 }
87 return $Result;
88 }
89 /**
90 * Return the number of values contained in a given serie
91 * @param string $Serie
92 * @return int
93 */
94 public function getSerieCount($Serie)
95 {
96 if (isset($this->Data["Series"][$Serie]["Data"])) {
97 return sizeof($this->Data["Series"][$Serie]["Data"]);
98 }
99 return 0;
100 }
101 /**
102 * Remove a serie from the pData object
103 * @param mixed $Series
104 */
105 public function removeSerie($Series)
106 {
107 if (!is_array($Series)) {
108 $Series = $this->convertToArray($Series);
109 }
110 foreach ($Series as $Serie) {
111 if (isset($this->Data["Series"][$Serie])) {
112 unset($this->Data["Series"][$Serie]);
113 }
114 }
115 }
116 /**
117 * Return a value from given serie & index
118 * @param string $Serie
119 * @param int $Index
120 * @return mixed
121 */
122 public function getValueAt($Serie, $Index = 0)
123 {
124 if (isset($this->Data["Series"][$Serie]["Data"][$Index])) {
125 return $this->Data["Series"][$Serie]["Data"][$Index];
126 }
127 return null;
128 }
129 /**
130 * Return the values array
131 * @param string $Serie
132 * @return mixed
133 */
134 public function getValues($Serie)
135 {
136 if (isset($this->Data["Series"][$Serie]["Data"])) {
137 return $this->Data["Series"][$Serie]["Data"];
138 }
139 return null;
140 }
141 /**
142 * Reverse the values in the given serie
143 * @param mixed $Series
144 */
145 public function reverseSerie($Series)
146 {
147 if (!is_array($Series)) {
148 $Series = $this->convertToArray($Series);
149 }
150 foreach ($Series as $Serie) {
151 if (isset($this->Data["Series"][$Serie]["Data"])) {
152 $this->Data["Series"][$Serie]["Data"] = array_reverse($this->Data["Series"][$Serie]["Data"]);
153 }
154 }
155 }
156 /**
157 * Return the sum of the serie values
158 * @param string $Serie
159 * @return int|null
160 */
161 public function getSum($Serie)
162 {
163 if (isset($this->Data["Series"][$Serie])) {
164 return array_sum($this->Data["Series"][$Serie]["Data"]);
165 }
166 return null;
167 }
168 /**
169 * Return the max value of a given serie
170 * @param string $Serie
171 * @return mixed
172 */
173 public function getMax($Serie)
174 {
175 if (isset($this->Data["Series"][$Serie]["Max"])) {
176 return $this->Data["Series"][$Serie]["Max"];
177 }
178 return null;
179 }
180 /**
181 * @param string $Serie
182 * @return mixed
183 */
184 public function getMin($Serie)
185 {
186 if (isset($this->Data["Series"][$Serie]["Min"])) {
187 return $this->Data["Series"][$Serie]["Min"];
188 }
189 return null;
190 }
191 /**
192 * Set the description of a given serie
193 * @param mixed $Series
194 * @param string $Shape
195 */
196 public function setSerieShape($Series, $Shape = SERIE_SHAPE_FILLEDCIRCLE)
197 {
198 if (!is_array($Series)) {
199 $Series = $this->convertToArray($Series);
200 }
201 foreach ($Series as $Serie) {
202 if (isset($this->Data["Series"][$Serie])) {
203 $this->Data["Series"][$Serie]["Shape"] = $Shape;
204 }
205 }
206 }
207 /**
208 * Set the description of a given serie
209 * @param string|array $Series
210 * @param string $Description
211 */
212 public function setSerieDescription($Series, $Description = "My serie")
213 {
214 if (!is_array($Series)) {
215 $Series = $this->convertToArray($Series);
216 }
217 foreach ($Series as $Serie) {
218 if (isset($this->Data["Series"][$Serie])) {
219 $this->Data["Series"][$Serie]["Description"] = $Description;
220 }
221 }
222 }
223 /**
224 * Set a serie as "drawable" while calling a rendering public function
225 * @param string|array $Series
226 * @param boolean $Drawable
227 */
228 public function setSerieDrawable($Series, $Drawable = \true)
229 {
230 if (!is_array($Series)) {
231 $Series = $this->convertToArray($Series);
232 }
233 foreach ($Series as $Serie) {
234 if (isset($this->Data["Series"][$Serie])) {
235 $this->Data["Series"][$Serie]["isDrawable"] = $Drawable;
236 }
237 }
238 }
239 /**
240 * Set the icon associated to a given serie
241 * @param mixed $Series
242 * @param mixed $Picture
243 */
244 public function setSeriePicture($Series, $Picture = null)
245 {
246 if (!is_array($Series)) {
247 $Series = $this->convertToArray($Series);
248 }
249 foreach ($Series as $Serie) {
250 if (isset($this->Data["Series"][$Serie])) {
251 $this->Data["Series"][$Serie]["Picture"] = $Picture;
252 }
253 }
254 }
255 /**
256 * Set the name of the X Axis
257 * @param string $Name
258 */
259 public function setXAxisName($Name)
260 {
261 $this->Data["XAxisName"] = $Name;
262 }
263 /**
264 * Set the display mode of the X Axis
265 * @param int $Mode
266 * @param array $Format
267 */
268 public function setXAxisDisplay($Mode, $Format = null)
269 {
270 $this->Data["XAxisDisplay"] = $Mode;
271 $this->Data["XAxisFormat"] = $Format;
272 }
273 /**
274 * Set the unit that will be displayed on the X axis
275 * @param string $Unit
276 */
277 public function setXAxisUnit($Unit)
278 {
279 $this->Data["XAxisUnit"] = $Unit;
280 }
281 /**
282 * Set the serie that will be used as abscissa
283 * @param string $Serie
284 */
285 public function setAbscissa($Serie)
286 {
287 if (isset($this->Data["Series"][$Serie])) {
288 $this->Data["Abscissa"] = $Serie;
289 }
290 }
291 /**
292 * Set the position of the abscissa axis
293 * @param int $Position
294 */
295 public function setAbsicssaPosition($Position = AXIS_POSITION_BOTTOM)
296 {
297 $this->Data["AbsicssaPosition"] = $Position;
298 }
299 /**
300 * Set the name of the abscissa axis
301 * @param string $Name
302 */
303 public function setAbscissaName($Name)
304 {
305 $this->Data["AbscissaName"] = $Name;
306 }
307 /**
308 * Create a scatter group specified in X and Y data series
309 * @param string $SerieX
310 * @param string $SerieY
311 * @param int $ID
312 */
313 public function setScatterSerie($SerieX, $SerieY, $ID = 0)
314 {
315 if (isset($this->Data["Series"][$SerieX]) && isset($this->Data["Series"][$SerieY])) {
316 $this->initScatterSerie($ID);
317 $this->Data["ScatterSeries"][$ID]["X"] = $SerieX;
318 $this->Data["ScatterSeries"][$ID]["Y"] = $SerieY;
319 }
320 }
321 /**
322 * Set the shape of a given sctatter serie
323 * @param int $ID
324 * @param int $Shape
325 */
326 public function setScatterSerieShape($ID, $Shape = SERIE_SHAPE_FILLEDCIRCLE)
327 {
328 if (isset($this->Data["ScatterSeries"][$ID])) {
329 $this->Data["ScatterSeries"][$ID]["Shape"] = $Shape;
330 }
331 }
332 /**
333 * Set the description of a given scatter serie
334 * @param int $ID
335 * @param string $Description
336 */
337 public function setScatterSerieDescription($ID, $Description = "My serie")
338 {
339 if (isset($this->Data["ScatterSeries"][$ID])) {
340 $this->Data["ScatterSeries"][$ID]["Description"] = $Description;
341 }
342 }
343 /**
344 * Set the icon associated to a given scatter serie
345 * @param int $ID
346 * @param mixed $Picture
347 */
348 public function setScatterSeriePicture($ID, $Picture = null)
349 {
350 if (isset($this->Data["ScatterSeries"][$ID])) {
351 $this->Data["ScatterSeries"][$ID]["Picture"] = $Picture;
352 }
353 }
354 /**
355 * Set a scatter serie as "drawable" while calling a rendering public function
356 * @param int $ID
357 * @param boolean $Drawable
358 */
359 public function setScatterSerieDrawable($ID, $Drawable = \true)
360 {
361 if (isset($this->Data["ScatterSeries"][$ID])) {
362 $this->Data["ScatterSeries"][$ID]["isDrawable"] = $Drawable;
363 }
364 }
365 /**
366 * Define if a scatter serie should be draw with ticks
367 * @param int $ID
368 * @param int $Width
369 */
370 public function setScatterSerieTicks($ID, $Width = 0)
371 {
372 if (isset($this->Data["ScatterSeries"][$ID])) {
373 $this->Data["ScatterSeries"][$ID]["Ticks"] = $Width;
374 }
375 }
376 /**
377 * Define if a scatter serie should be draw with a special weight
378 * @param int $ID
379 * @param int $Weight
380 */
381 public function setScatterSerieWeight($ID, $Weight = 0)
382 {
383 if (isset($this->Data["ScatterSeries"][$ID])) {
384 $this->Data["ScatterSeries"][$ID]["Weight"] = $Weight;
385 }
386 }
387 /**
388 * Associate a color to a scatter serie
389 * @param int $ID
390 * @param array $Format
391 */
392 public function setScatterSerieColor($ID, array $Format)
393 {
394 $R = isset($Format["R"]) ? $Format["R"] : 0;
395 $G = isset($Format["G"]) ? $Format["G"] : 0;
396 $B = isset($Format["B"]) ? $Format["B"] : 0;
397 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
398 if (isset($this->Data["ScatterSeries"][$ID])) {
399 $this->Data["ScatterSeries"][$ID]["Color"]["R"] = $R;
400 $this->Data["ScatterSeries"][$ID]["Color"]["G"] = $G;
401 $this->Data["ScatterSeries"][$ID]["Color"]["B"] = $B;
402 $this->Data["ScatterSeries"][$ID]["Color"]["Alpha"] = $Alpha;
403 }
404 }
405 /**
406 * Compute the series limits for an individual and global point of view
407 * @return array
408 */
409 public function limits()
410 {
411 $GlobalMin = ABSOLUTE_MAX;
412 $GlobalMax = ABSOLUTE_MIN;
413 foreach (array_keys($this->Data["Series"]) as $Key) {
414 if ($this->Data["Abscissa"] != $Key && $this->Data["Series"][$Key]["isDrawable"] == \true) {
415 if ($GlobalMin > $this->Data["Series"][$Key]["Min"]) {
416 $GlobalMin = $this->Data["Series"][$Key]["Min"];
417 }
418 if ($GlobalMax < $this->Data["Series"][$Key]["Max"]) {
419 $GlobalMax = $this->Data["Series"][$Key]["Max"];
420 }
421 }
422 }
423 $this->Data["Min"] = $GlobalMin;
424 $this->Data["Max"] = $GlobalMax;
425 return [$GlobalMin, $GlobalMax];
426 }
427 /**
428 * Mark all series as drawable
429 */
430 public function drawAll()
431 {
432 foreach (array_keys($this->Data["Series"]) as $Key) {
433 if ($this->Data["Abscissa"] != $Key) {
434 $this->Data["Series"][$Key]["isDrawable"] = \true;
435 }
436 }
437 }
438 /**
439 * Return the average value of the given serie
440 * @param string $Serie
441 * @return int|null
442 */
443 public function getSerieAverage($Serie)
444 {
445 if (isset($this->Data["Series"][$Serie])) {
446 $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]);
447 return array_sum($SerieData) / sizeof($SerieData);
448 }
449 return null;
450 }
451 /**
452 * Return the geometric mean of the given serie
453 * @param string $Serie
454 * @return int|null
455 */
456 public function getGeometricMean($Serie)
457 {
458 if (isset($this->Data["Series"][$Serie])) {
459 $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]);
460 $Seriesum = 1;
461 foreach ($SerieData as $Value) {
462 $Seriesum = $Seriesum * $Value;
463 }
464 return pow($Seriesum, 1 / sizeof($SerieData));
465 }
466 return null;
467 }
468 /**
469 * Return the harmonic mean of the given serie
470 * @param string $Serie
471 * @return int|null
472 */
473 public function getHarmonicMean($Serie)
474 {
475 if (isset($this->Data["Series"][$Serie])) {
476 $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]);
477 $Seriesum = 0;
478 foreach ($SerieData as $Value) {
479 $Seriesum = $Seriesum + 1 / $Value;
480 }
481 return sizeof($SerieData) / $Seriesum;
482 }
483 return null;
484 }
485 /**
486 * Return the standard deviation of the given serie
487 * @param string $Serie
488 * @return double|null
489 */
490 public function getStandardDeviation($Serie)
491 {
492 if (isset($this->Data["Series"][$Serie])) {
493 $Average = $this->getSerieAverage($Serie);
494 $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]);
495 $DeviationSum = 0;
496 foreach ($SerieData as $Key => $Value) {
497 $DeviationSum = $DeviationSum + ($Value - $Average) * ($Value - $Average);
498 }
499 return sqrt($DeviationSum / count($SerieData));
500 }
501 return null;
502 }
503 /**
504 * Return the Coefficient of variation of the given serie
505 * @param string $Serie
506 * @return float|null
507 */
508 public function getCoefficientOfVariation($Serie)
509 {
510 if (isset($this->Data["Series"][$Serie])) {
511 $Average = $this->getSerieAverage($Serie);
512 $StandardDeviation = $this->getStandardDeviation($Serie);
513 if ($StandardDeviation != 0) {
514 return $StandardDeviation / $Average;
515 }
516 }
517 return null;
518 }
519 /**
520 * Return the median value of the given serie
521 * @param string $Serie
522 * @return int|float
523 */
524 public function getSerieMedian($Serie)
525 {
526 if (isset($this->Data["Series"][$Serie])) {
527 $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]);
528 sort($SerieData);
529 $SerieCenter = floor(sizeof($SerieData) / 2);
530 if (isset($SerieData[$SerieCenter])) {
531 return $SerieData[$SerieCenter];
532 }
533 }
534 return null;
535 }
536 /**
537 * Return the x th percentil of the given serie
538 * @param string $Serie
539 * @param int $Percentil
540 * @return int|float| null
541 */
542 public function getSeriePercentile($Serie = "Serie1", $Percentil = 95)
543 {
544 if (!isset($this->Data["Series"][$Serie]["Data"])) {
545 return null;
546 }
547 $Values = count($this->Data["Series"][$Serie]["Data"]) - 1;
548 if ($Values < 0) {
549 $Values = 0;
550 }
551 $PercentilID = floor($Values / 100 * $Percentil + 0.5);
552 $SortedValues = $this->Data["Series"][$Serie]["Data"];
553 sort($SortedValues);
554 if (is_numeric($SortedValues[$PercentilID])) {
555 return $SortedValues[$PercentilID];
556 }
557 return null;
558 }
559 /**
560 * Add random values to a given serie
561 * @param string $SerieName
562 * @param array $Options
563 */
564 public function addRandomValues($SerieName = "Serie1", array $Options = [])
565 {
566 $Values = isset($Options["Values"]) ? $Options["Values"] : 20;
567 $Min = isset($Options["Min"]) ? $Options["Min"] : 0;
568 $Max = isset($Options["Max"]) ? $Options["Max"] : 100;
569 $withFloat = isset($Options["withFloat"]) ? $Options["withFloat"] : \false;
570 for ($i = 0; $i <= $Values; $i++) {
571 $Value = $withFloat ? rand($Min * 100, $Max * 100) / 100 : rand($Min, $Max);
572 $this->addPoints($Value, $SerieName);
573 }
574 }
575 /**
576 * Test if we have valid data
577 * @return boolean|null
578 */
579 public function containsData()
580 {
581 if (!isset($this->Data["Series"])) {
582 return \false;
583 }
584 foreach (array_keys($this->Data["Series"]) as $Key) {
585 if ($this->Data["Abscissa"] != $Key && $this->Data["Series"][$Key]["isDrawable"] == \true) {
586 return \true;
587 }
588 }
589 return null;
590 }
591 /**
592 * Set the display mode of an Axis
593 * @param int $AxisID
594 * @param int $Mode
595 * @param array $Format
596 */
597 public function setAxisDisplay($AxisID, $Mode = AXIS_FORMAT_DEFAULT, $Format = null)
598 {
599 if (isset($this->Data["Axis"][$AxisID])) {
600 $this->Data["Axis"][$AxisID]["Display"] = $Mode;
601 if ($Format != null) {
602 $this->Data["Axis"][$AxisID]["Format"] = $Format;
603 }
604 }
605 }
606 /**
607 * Set the position of an Axis
608 * @param int $AxisID
609 * @param int $Position
610 */
611 public function setAxisPosition($AxisID, $Position = AXIS_POSITION_LEFT)
612 {
613 if (isset($this->Data["Axis"][$AxisID])) {
614 $this->Data["Axis"][$AxisID]["Position"] = $Position;
615 }
616 }
617 /**
618 * Associate an unit to an axis
619 * @param int $AxisID
620 * @param string $Unit
621 */
622 public function setAxisUnit($AxisID, $Unit)
623 {
624 if (isset($this->Data["Axis"][$AxisID])) {
625 $this->Data["Axis"][$AxisID]["Unit"] = $Unit;
626 }
627 }
628 /**
629 * Associate a name to an axis
630 * @param int $AxisID
631 * @param string $Name
632 */
633 public function setAxisName($AxisID, $Name)
634 {
635 if (isset($this->Data["Axis"][$AxisID])) {
636 $this->Data["Axis"][$AxisID]["Name"] = $Name;
637 }
638 }
639 /**
640 * Associate a color to an axis
641 * @param int $AxisID
642 * @param array $Format
643 */
644 public function setAxisColor($AxisID, array $Format)
645 {
646 $R = isset($Format["R"]) ? $Format["R"] : 0;
647 $G = isset($Format["G"]) ? $Format["G"] : 0;
648 $B = isset($Format["B"]) ? $Format["B"] : 0;
649 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
650 if (isset($this->Data["Axis"][$AxisID])) {
651 $this->Data["Axis"][$AxisID]["Color"]["R"] = $R;
652 $this->Data["Axis"][$AxisID]["Color"]["G"] = $G;
653 $this->Data["Axis"][$AxisID]["Color"]["B"] = $B;
654 $this->Data["Axis"][$AxisID]["Color"]["Alpha"] = $Alpha;
655 }
656 }
657 /**
658 * Design an axis as X or Y member
659 * @param int $AxisID
660 * @param int $Identity
661 */
662 public function setAxisXY($AxisID, $Identity = AXIS_Y)
663 {
664 if (isset($this->Data["Axis"][$AxisID])) {
665 $this->Data["Axis"][$AxisID]["Identity"] = $Identity;
666 }
667 }
668 /**
669 * Associate one data serie with one axis
670 * @param mixed $Series
671 * @param int $AxisID
672 */
673 public function setSerieOnAxis($Series, $AxisID)
674 {
675 if (!is_array($Series)) {
676 $Series = $this->convertToArray($Series);
677 }
678 foreach ($Series as $Serie) {
679 $PreviousAxis = $this->Data["Series"][$Serie]["Axis"];
680 /* Create missing axis */
681 if (!isset($this->Data["Axis"][$AxisID])) {
682 $this->Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_LEFT;
683 $this->Data["Axis"][$AxisID]["Identity"] = AXIS_Y;
684 }
685 $this->Data["Series"][$Serie]["Axis"] = $AxisID;
686 /* Cleanup unused axis */
687 $Found = \false;
688 foreach ($this->Data["Series"] as $Values) {
689 if ($Values["Axis"] == $PreviousAxis) {
690 $Found = \true;
691 }
692 }
693 if (!$Found) {
694 unset($this->Data["Axis"][$PreviousAxis]);
695 }
696 }
697 }
698 /**
699 * Define if a serie should be draw with ticks
700 * @param mixed $Series
701 * @param int $Width
702 */
703 public function setSerieTicks($Series, $Width = 0)
704 {
705 if (!is_array($Series)) {
706 $Series = $this->convertToArray($Series);
707 }
708 foreach ($Series as $Serie) {
709 if (isset($this->Data["Series"][$Serie])) {
710 $this->Data["Series"][$Serie]["Ticks"] = $Width;
711 }
712 }
713 }
714 /**
715 * Define if a serie should be draw with a special weight
716 * @param mixed $Series
717 * @param int $Weight
718 */
719 public function setSerieWeight($Series, $Weight = 0)
720 {
721 if (!is_array($Series)) {
722 $Series = $this->convertToArray($Series);
723 }
724 foreach ($Series as $Serie) {
725 if (isset($this->Data["Series"][$Serie])) {
726 $this->Data["Series"][$Serie]["Weight"] = $Weight;
727 }
728 }
729 }
730 /**
731 * Returns the palette of the given serie
732 * @param type $Serie
733 * @return null
734 */
735 public function getSeriePalette($Serie)
736 {
737 if (!isset($this->Data["Series"][$Serie])) {
738 return null;
739 }
740 $Result = [];
741 $Result["R"] = $this->Data["Series"][$Serie]["Color"]["R"];
742 $Result["G"] = $this->Data["Series"][$Serie]["Color"]["G"];
743 $Result["B"] = $this->Data["Series"][$Serie]["Color"]["B"];
744 $Result["Alpha"] = $this->Data["Series"][$Serie]["Color"]["Alpha"];
745 return $Result;
746 }
747 /**
748 * Set the color of one serie
749 * @param mixed $Series
750 * @param array $Format
751 */
752 public function setPalette($Series, array $Format = [])
753 {
754 if (!is_array($Series)) {
755 $Series = $this->convertToArray($Series);
756 }
757 foreach ($Series as $Key => $Serie) {
758 $R = isset($Format["R"]) ? $Format["R"] : 0;
759 $G = isset($Format["G"]) ? $Format["G"] : 0;
760 $B = isset($Format["B"]) ? $Format["B"] : 0;
761 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
762 if (isset($this->Data["Series"][$Serie])) {
763 $OldR = $this->Data["Series"][$Serie]["Color"]["R"];
764 $OldG = $this->Data["Series"][$Serie]["Color"]["G"];
765 $OldB = $this->Data["Series"][$Serie]["Color"]["B"];
766 $this->Data["Series"][$Serie]["Color"]["R"] = $R;
767 $this->Data["Series"][$Serie]["Color"]["G"] = $G;
768 $this->Data["Series"][$Serie]["Color"]["B"] = $B;
769 $this->Data["Series"][$Serie]["Color"]["Alpha"] = $Alpha;
770 /* Do reverse processing on the internal palette array */
771 foreach ($this->Palette as $Key => $Value) {
772 if ($Value["R"] == $OldR && $Value["G"] == $OldG && $Value["B"] == $OldB) {
773 $this->Palette[$Key]["R"] = $R;
774 $this->Palette[$Key]["G"] = $G;
775 $this->Palette[$Key]["B"] = $B;
776 $this->Palette[$Key]["Alpha"] = $Alpha;
777 }
778 }
779 }
780 }
781 }
782 /**
783 * Load a palette file
784 * @param string $FileName
785 * @param boolean $Overwrite
786 * @throws Exception
787 */
788 public function loadPalette($FileName, $Overwrite = \false)
789 {
790 $path = file_exists($FileName) ? $FileName : sprintf('%s/../resources/palettes/%s', __DIR__, ltrim($FileName, '/'));
791 $fileHandle = @fopen($path, "r");
792 if (!$fileHandle) {
793 throw new Exception(sprintf('The requested palette "%s" was not found at path "%s"!', $FileName, $path));
794 }
795 if ($Overwrite) {
796 $this->Palette = [];
797 }
798 while (!feof($fileHandle)) {
799 $line = fgets($fileHandle, 4096);
800 if (\false === $line) {
801 continue;
802 }
803 $row = explode(',', $line);
804 if (empty($row)) {
805 continue;
806 }
807 if (count($row) !== 4) {
808 throw new RuntimeException(sprintf('A palette row must supply R, G, B and Alpha components, %s given!', var_export($row, \true)));
809 }
810 list($R, $G, $B, $Alpha) = $row;
811 $ID = count($this->Palette);
812 $this->Palette[$ID] = ["R" => trim($R), "G" => trim($G), "B" => trim($B), "Alpha" => trim($Alpha)];
813 }
814 fclose($fileHandle);
815 /* Apply changes to current series */
816 $ID = 0;
817 if (isset($this->Data["Series"])) {
818 foreach ($this->Data["Series"] as $Key => $Value) {
819 if (!isset($this->Palette[$ID])) {
820 $this->Data["Series"][$Key]["Color"] = ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 0];
821 } else {
822 $this->Data["Series"][$Key]["Color"] = $this->Palette[$ID];
823 }
824 $ID++;
825 }
826 }
827 }
828 /**
829 * Initialise a given scatter serie
830 * @param int $ID
831 * @return null
832 */
833 public function initScatterSerie($ID)
834 {
835 if (isset($this->Data["ScatterSeries"][$ID])) {
836 return null;
837 }
838 $this->Data["ScatterSeries"][$ID]["Description"] = "Scatter " . $ID;
839 $this->Data["ScatterSeries"][$ID]["isDrawable"] = \true;
840 $this->Data["ScatterSeries"][$ID]["Picture"] = null;
841 $this->Data["ScatterSeries"][$ID]["Ticks"] = 0;
842 $this->Data["ScatterSeries"][$ID]["Weight"] = 0;
843 if (isset($this->Palette[$ID])) {
844 $this->Data["ScatterSeries"][$ID]["Color"] = $this->Palette[$ID];
845 } else {
846 $this->Data["ScatterSeries"][$ID]["Color"]["R"] = rand(0, 255);
847 $this->Data["ScatterSeries"][$ID]["Color"]["G"] = rand(0, 255);
848 $this->Data["ScatterSeries"][$ID]["Color"]["B"] = rand(0, 255);
849 $this->Data["ScatterSeries"][$ID]["Color"]["Alpha"] = 100;
850 }
851 }
852 /**
853 * Initialise a given serie
854 * @param string $Serie
855 */
856 public function initialise($Serie)
857 {
858 $ID = 0;
859 if (isset($this->Data["Series"])) {
860 $ID = count($this->Data["Series"]);
861 }
862 $this->Data["Series"][$Serie]["Description"] = $Serie;
863 $this->Data["Series"][$Serie]["isDrawable"] = \true;
864 $this->Data["Series"][$Serie]["Picture"] = null;
865 $this->Data["Series"][$Serie]["Max"] = null;
866 $this->Data["Series"][$Serie]["Min"] = null;
867 $this->Data["Series"][$Serie]["Axis"] = 0;
868 $this->Data["Series"][$Serie]["Ticks"] = 0;
869 $this->Data["Series"][$Serie]["Weight"] = 0;
870 $this->Data["Series"][$Serie]["Shape"] = SERIE_SHAPE_FILLEDCIRCLE;
871 if (isset($this->Palette[$ID])) {
872 $this->Data["Series"][$Serie]["Color"] = $this->Palette[$ID];
873 } else {
874 $this->Data["Series"][$Serie]["Color"]["R"] = rand(0, 255);
875 $this->Data["Series"][$Serie]["Color"]["G"] = rand(0, 255);
876 $this->Data["Series"][$Serie]["Color"]["B"] = rand(0, 255);
877 $this->Data["Series"][$Serie]["Color"]["Alpha"] = 100;
878 }
879 $this->Data["Series"][$Serie]["Data"] = [];
880 }
881 /**
882 * @param int $NormalizationFactor
883 * @param mixed $UnitChange
884 * @param int $Round
885 */
886 public function normalize($NormalizationFactor = 100, $UnitChange = null, $Round = 1)
887 {
888 $Abscissa = $this->Data["Abscissa"];
889 $SelectedSeries = [];
890 $MaxVal = 0;
891 foreach (array_keys($this->Data["Axis"]) as $AxisID) {
892 if ($UnitChange != null) {
893 $this->Data["Axis"][$AxisID]["Unit"] = $UnitChange;
894 }
895 foreach ($this->Data["Series"] as $SerieName => $Serie) {
896 if ($Serie["Axis"] == $AxisID && $Serie["isDrawable"] == \true && $SerieName != $Abscissa) {
897 $SelectedSeries[$SerieName] = $SerieName;
898 if (count($Serie["Data"]) > $MaxVal) {
899 $MaxVal = count($Serie["Data"]);
900 }
901 }
902 }
903 }
904 for ($i = 0; $i <= $MaxVal - 1; $i++) {
905 $Factor = 0;
906 foreach ($SelectedSeries as $Key => $SerieName) {
907 $Value = $this->Data["Series"][$SerieName]["Data"][$i];
908 if ($Value != VOID) {
909 $Factor = $Factor + abs($Value);
910 }
911 }
912 if ($Factor != 0) {
913 $Factor = $NormalizationFactor / $Factor;
914 foreach ($SelectedSeries as $Key => $SerieName) {
915 $Value = $this->Data["Series"][$SerieName]["Data"][$i];
916 if ($Value != VOID && $Factor != $NormalizationFactor) {
917 $this->Data["Series"][$SerieName]["Data"][$i] = round(abs($Value) * $Factor, $Round);
918 } elseif ($Value == VOID || $Value == 0) {
919 $this->Data["Series"][$SerieName]["Data"][$i] = VOID;
920 } elseif ($Factor == $NormalizationFactor) {
921 $this->Data["Series"][$SerieName]["Data"][$i] = $NormalizationFactor;
922 }
923 }
924 }
925 }
926 foreach ($SelectedSeries as $Key => $SerieName) {
927 $this->Data["Series"][$SerieName]["Max"] = max($this->stripVOID($this->Data["Series"][$SerieName]["Data"]));
928 $this->Data["Series"][$SerieName]["Min"] = min($this->stripVOID($this->Data["Series"][$SerieName]["Data"]));
929 }
930 }
931 /**
932 * Load data from a CSV (or similar) data source
933 * @param string $FileName
934 * @param array $Options
935 */
936 public function importFromCSV($FileName, array $Options = [])
937 {
938 $Delimiter = isset($Options["Delimiter"]) ? $Options["Delimiter"] : ",";
939 $GotHeader = isset($Options["GotHeader"]) ? $Options["GotHeader"] : \false;
940 $SkipColumns = isset($Options["SkipColumns"]) ? $Options["SkipColumns"] : [-1];
941 $DefaultSerieName = isset($Options["DefaultSerieName"]) ? $Options["DefaultSerieName"] : "Serie";
942 $Handle = @fopen($FileName, "r");
943 if ($Handle) {
944 $HeaderParsed = \false;
945 $SerieNames = [];
946 while (!feof($Handle)) {
947 $Buffer = fgets($Handle, 4096);
948 $Buffer = str_replace(chr(10), "", $Buffer);
949 $Buffer = str_replace(chr(13), "", $Buffer);
950 $Values = preg_split("/" . $Delimiter . "/", $Buffer);
951 if ($Buffer != "") {
952 if ($GotHeader && !$HeaderParsed) {
953 foreach ($Values as $Key => $Name) {
954 if (!in_array($Key, $SkipColumns)) {
955 $SerieNames[$Key] = $Name;
956 }
957 }
958 $HeaderParsed = \true;
959 } else {
960 if (!count($SerieNames)) {
961 foreach ($Values as $Key => $Name) {
962 if (!in_array($Key, $SkipColumns)) {
963 $SerieNames[$Key] = $DefaultSerieName . $Key;
964 }
965 }
966 }
967 foreach ($Values as $Key => $Value) {
968 if (!in_array($Key, $SkipColumns)) {
969 $this->addPoints($Value, $SerieNames[$Key]);
970 }
971 }
972 }
973 }
974 }
975 fclose($Handle);
976 }
977 }
978 /**
979 * Create a dataset based on a formula
980 *
981 * @param string $SerieName
982 * @param string $Formula
983 * @param array $Options
984 * @return null
985 */
986 public function createFunctionSerie($SerieName, $Formula = "", array $Options = [])
987 {
988 $MinX = isset($Options["MinX"]) ? $Options["MinX"] : -10;
989 $MaxX = isset($Options["MaxX"]) ? $Options["MaxX"] : 10;
990 $XStep = isset($Options["XStep"]) ? $Options["XStep"] : 1;
991 $AutoDescription = isset($Options["AutoDescription"]) ? $Options["AutoDescription"] : \false;
992 $RecordAbscissa = isset($Options["RecordAbscissa"]) ? $Options["RecordAbscissa"] : \false;
993 $AbscissaSerie = isset($Options["AbscissaSerie"]) ? $Options["AbscissaSerie"] : "Abscissa";
994 if ($Formula == "") {
995 return null;
996 }
997 $Result = [];
998 $Abscissa = [];
999 for ($i = $MinX; $i <= $MaxX; $i = $i + $XStep) {
1000 $Expression = "\$return = '!'.(" . str_replace("z", $i, $Formula) . ");";
1001 if (@eval($Expression) === \false) {
1002 $return = VOID;
1003 }
1004 if ($return == "!") {
1005 $return = VOID;
1006 } else {
1007 $return = $this->right($return, strlen($return) - 1);
1008 }
1009 if ($return == "NAN") {
1010 $return = VOID;
1011 }
1012 if ($return == "INF") {
1013 $return = VOID;
1014 }
1015 if ($return == "-INF") {
1016 $return = VOID;
1017 }
1018 $Abscissa[] = $i;
1019 $Result[] = $return;
1020 }
1021 $this->addPoints($Result, $SerieName);
1022 if ($AutoDescription) {
1023 $this->setSerieDescription($SerieName, $Formula);
1024 }
1025 if ($RecordAbscissa) {
1026 $this->addPoints($Abscissa, $AbscissaSerie);
1027 }
1028 }
1029 /**
1030 * @param mixed $Series
1031 */
1032 public function negateValues($Series)
1033 {
1034 if (!is_array($Series)) {
1035 $Series = $this->convertToArray($Series);
1036 }
1037 foreach ($Series as $Key => $SerieName) {
1038 if (isset($this->Data["Series"][$SerieName])) {
1039 $Data = [];
1040 foreach ($this->Data["Series"][$SerieName]["Data"] as $Key => $Value) {
1041 if ($Value == VOID) {
1042 $Data[] = VOID;
1043 } else {
1044 $Data[] = -$Value;
1045 }
1046 }
1047 $this->Data["Series"][$SerieName]["Data"] = $Data;
1048 $this->Data["Series"][$SerieName]["Max"] = max($this->stripVOID($this->Data["Series"][$SerieName]["Data"]));
1049 $this->Data["Series"][$SerieName]["Min"] = min($this->stripVOID($this->Data["Series"][$SerieName]["Data"]));
1050 }
1051 }
1052 }
1053 /**
1054 * Return the data & configuration of the series
1055 * @return array
1056 */
1057 public function getData()
1058 {
1059 return $this->Data;
1060 }
1061 /**
1062 * Save a palette element
1063 *
1064 * @param integer $ID
1065 * @param string $Color
1066 */
1067 public function savePalette($ID, $Color)
1068 {
1069 $this->Palette[$ID] = $Color;
1070 }
1071 /**
1072 * Return the palette of the series
1073 * @return array
1074 */
1075 public function getPalette()
1076 {
1077 return $this->Palette;
1078 }
1079 /**
1080 * Called by the scaling algorithm to save the config
1081 * @param mixed $Axis
1082 */
1083 public function saveAxisConfig($Axis)
1084 {
1085 $this->Data["Axis"] = $Axis;
1086 }
1087 /**
1088 * Save the Y Margin if set
1089 * @param mixed $Value
1090 */
1091 public function saveYMargin($Value)
1092 {
1093 $this->Data["YMargin"] = $Value;
1094 }
1095 /**
1096 * Save extended configuration to the pData object
1097 * @param string $Tag
1098 * @param mixed $Values
1099 */
1100 public function saveExtendedData($Tag, $Values)
1101 {
1102 $this->Data["Extended"][$Tag] = $Values;
1103 }
1104 /**
1105 * Called by the scaling algorithm to save the orientation of the scale
1106 * @param mixed $Orientation
1107 */
1108 public function saveOrientation($Orientation)
1109 {
1110 $this->Data["Orientation"] = $Orientation;
1111 }
1112 /**
1113 * Convert a string to a single elements array
1114 * @param mixed $Value
1115 * @return array
1116 */
1117 public function convertToArray($Value)
1118 {
1119 return [$Value];
1120 }
1121 /**
1122 * Class string wrapper
1123 * @return string
1124 */
1125 public function __toString()
1126 {
1127 return "pData object.";
1128 }
1129 /**
1130 * @param string $value
1131 * @param int $NbChar
1132 * @return string
1133 */
1134 public function left($value, $NbChar)
1135 {
1136 return substr($value, 0, $NbChar);
1137 }
1138 /**
1139 * @param string $value
1140 * @param int $NbChar
1141 * @return string
1142 */
1143 public function right($value, $NbChar)
1144 {
1145 return substr($value, strlen($value) - $NbChar, $NbChar);
1146 }
1147 /**
1148 * @param string $value
1149 * @param int $Depart
1150 * @param int $NbChar
1151 * @return string
1152 */
1153 public function mid($value, $Depart, $NbChar)
1154 {
1155 return substr($value, $Depart - 1, $NbChar);
1156 }
1157 }
1158