Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
/*
3
pDraw - class to manipulate data arrays
4
 
5
Version     : 2.1.4
6
Made by     : Jean-Damien POGOLOTTI
7
Last Update : 19/01/2014
8
 
9
This file can be distributed under the license you can find at :
10
 
11
http://www.pchart.net/license
12
 
13
You can find the whole class documentation on the pChart web site.
14
*/
15
/* Axis configuration */
16
define("AXIS_FORMAT_DEFAULT", 680001);
17
define("AXIS_FORMAT_TIME", 680002);
18
define("AXIS_FORMAT_DATE", 680003);
19
define("AXIS_FORMAT_METRIC", 680004);
20
define("AXIS_FORMAT_CURRENCY", 680005);
21
define("AXIS_FORMAT_TRAFFIC", 680006);
22
define("AXIS_FORMAT_CUSTOM", 680007);
23
/* Axis position */
24
define("AXIS_POSITION_LEFT", 681001);
25
define("AXIS_POSITION_RIGHT", 681002);
26
define("AXIS_POSITION_TOP", 681001);
27
define("AXIS_POSITION_BOTTOM", 681002);
28
/* Families of data points */
29
define("SERIE_SHAPE_FILLEDCIRCLE", 681011);
30
define("SERIE_SHAPE_FILLEDTRIANGLE", 681012);
31
define("SERIE_SHAPE_FILLEDSQUARE", 681013);
32
define("SERIE_SHAPE_FILLEDDIAMOND", 681017);
33
define("SERIE_SHAPE_CIRCLE", 681014);
34
define("SERIE_SHAPE_TRIANGLE", 681015);
35
define("SERIE_SHAPE_SQUARE", 681016);
36
define("SERIE_SHAPE_DIAMOND", 681018);
37
/* Axis position */
38
define("AXIS_X", 682001);
39
define("AXIS_Y", 682002);
40
/* Define value limits */
41
define("ABSOLUTE_MIN", -10000000000000);
42
define("ABSOLUTE_MAX", 10000000000000);
43
/* Replacement to the PHP NULL keyword */
44
define("VOID", 0.123456789);
45
/* Euro symbol for GD fonts */
46
define("EURO_SYMBOL", utf8_encode("&#8364;"));
47
 
48
/* pData class definition */
49
class pData
50
{
51
	var $Data;
52
	var $Palette = [
53
		"0" => ["R" => 188,"G" => 224,"B" => 46,"Alpha" => 100],
54
		"1" => ["R" => 224,"G" => 100,"B" => 46,"Alpha" => 100],
55
		"2" => ["R" => 224,"G" => 214,"B" => 46,"Alpha" => 100],
56
		"3" => ["R" => 46,"G" => 151,"B" => 224,"Alpha" => 100],
57
		"4" => ["R" => 176,"G" => 46,"B" => 224,"Alpha" => 100],
58
		"5" => ["R" => 224,"G" => 46,"B" => 117,"Alpha" => 100],
59
		"6" => ["R" => 92,"G" => 224,"B" => 46,	"Alpha" => 100],
60
		"7" => ["R" => 224,"G" => 176,"B" => 46,"Alpha" => 100]
61
	];
62
	/* Class creator */
63
	function __construct()
64
	{
65
		$this->Data = [
66
			"XAxisDisplay" => AXIS_FORMAT_DEFAULT,
67
			"XAxisFormat" => NULL,
68
			"XAxisName" => NULL,
69
			"XAxisUnit" => NULL,
70
			"Abscissa" => NULL,
71
			"AbsicssaPosition" => AXIS_POSITION_BOTTOM,
72
			"Axis" => [0 => [
73
					"Display" => AXIS_FORMAT_DEFAULT,
74
					"Position" => AXIS_POSITION_LEFT,
75
					"Identity" => AXIS_Y
76
				]
77
			]
78
		];
79
	}
80
 
81
	/* Add a single point or an array to the given serie */
82
	function addPoints($Values, $SerieName = "Serie1")
83
	{
84
		if (!isset($this->Data["Series"][$SerieName])){
85
			$this->initialise($SerieName);
86
		}
87
 
88
		if (is_array($Values)) {
89
			foreach($Values as $Key => $Value) {
90
				$this->Data["Series"][$SerieName]["Data"][] = $Value;
91
			}
92
		} else {
93
			$this->Data["Series"][$SerieName]["Data"][] = $Values;
94
		}
95
 
96
		if ($Values != VOID) {
97
			$StrippedData = $this->stripVOID($this->Data["Series"][$SerieName]["Data"]);
98
			if (empty($StrippedData)) {
99
				$this->Data["Series"][$SerieName]["Max"] = 0;
100
				$this->Data["Series"][$SerieName]["Min"] = 0;
101
			} else {
102
				$this->Data["Series"][$SerieName]["Max"] = max($StrippedData);
103
				$this->Data["Series"][$SerieName]["Min"] = min($StrippedData);
104
			}
105
		}
106
	}
107
 
108
	/* Strip VOID values */
109
	function stripVOID($Values)
110
	{
111
		if (!is_array($Values)) {
112
			return [];
113
		}
114
 
115
		$Result = [];
116
		foreach($Values as $Key => $Value) {
117
			($Value != VOID) AND $Result[] = $Value;
118
		}
119
 
120
		return $Result;
121
	}
122
 
123
	/* Return the number of values contained in a given serie */
124
	function getSerieCount($Serie)
125
	{
126
		return (isset($this->Data["Series"][$Serie]["Data"])) ? count($this->Data["Series"][$Serie]["Data"]) : 0;
127
	}
128
 
129
	/* Remove a serie from the pData object */
130
	function removeSerie($Series)
131
	{
132
 
133
		$Series = $this->convertToArray($Series);
134
 
135
		foreach($Series as $Key => $Serie) {
136
			if (isset($this->Data["Series"][$Serie])) {
137
				unset($this->Data["Series"][$Serie]);
138
			}
139
		}
140
	}
141
 
142
	/* Return a value from given serie & index */
143
	function getValueAt($Serie, $Index = 0)
144
	{
145
		return (isset($this->Data["Series"][$Serie]["Data"][$Index])) ? $this->Data["Series"][$Serie]["Data"][$Index] : NULL;
146
	}
147
 
148
	/* Return the values array */
149
	function getValues($Serie)
150
	{
151
		return (isset($this->Data["Series"][$Serie]["Data"])) ? $this->Data["Series"][$Serie]["Data"] : NULL;
152
	}
153
 
154
	/* Reverse the values in the given serie */
155
	function reverseSerie($Series)
156
	{
157
 
158
		$Series = $this->convertToArray($Series);
159
 
160
		foreach($Series as $Key => $Serie) {
161
			if (isset($this->Data["Series"][$Serie]["Data"])) {
162
				$this->Data["Series"][$Serie]["Data"] = array_reverse($this->Data["Series"][$Serie]["Data"]);
163
			}
164
		}
165
	}
166
 
167
	/* Return the sum of the serie values */
168
	function getSum($Serie)
169
	{
170
		return (isset($this->Data["Series"][$Serie])) ? array_sum($this->Data["Series"][$Serie]["Data"]) : NULL;
171
	}
172
 
173
	/* Return the max value of a given serie */
174
	function getMax($Serie)
175
	{
176
		return (isset($this->Data["Series"][$Serie]["Max"])) ? $this->Data["Series"][$Serie]["Max"] : NULL;
177
	}
178
 
179
	/* Return the min value of a given serie */
180
	function getMin($Serie)
181
	{
182
		return (isset($this->Data["Series"][$Serie]["Min"])) ? $this->Data["Series"][$Serie]["Min"] : NULL;
183
	}
184
 
185
	/* Set the description of a given serie */
186
	function setSerieShape($Series, $Shape = SERIE_SHAPE_FILLEDCIRCLE)
187
	{
188
		$Series = $this->convertToArray($Series);
189
 
190
		foreach($Series as $Key => $Serie) {
191
			if (isset($this->Data["Series"][$Serie])) {
192
				$this->Data["Series"][$Serie]["Shape"] = $Shape;
193
			}
194
		}
195
	}
196
 
197
	/* Set the description of a given serie */
198
	function setSerieDescription($Series, $Description = "My serie")
199
	{
200
		$Series = $this->convertToArray($Series);
201
 
202
		foreach($Series as $Key => $Serie) {
203
			if (isset($this->Data["Series"][$Serie])) {
204
				$this->Data["Series"][$Serie]["Description"] = $Description;
205
			}
206
		}
207
	}
208
 
209
	/* Set a serie as "drawable" while calling a rendering function */
210
	function setSerieDrawable($Series, $Drawable = TRUE)
211
	{
212
		$Series = $this->convertToArray($Series);
213
 
214
		foreach($Series as $Key => $Serie) {
215
			if (isset($this->Data["Series"][$Serie])) {
216
				$this->Data["Series"][$Serie]["isDrawable"] = $Drawable;
217
			}
218
		}
219
	}
220
 
221
	/* Set the icon associated to a given serie */
222
	function setSeriePicture($Series, $Picture = NULL)
223
	{
224
		$Series = $this->convertToArray($Series);
225
 
226
		foreach($Series as $Key => $Serie) {
227
			if (isset($this->Data["Series"][$Serie])) {
228
				$this->Data["Series"][$Serie]["Picture"] = $Picture;
229
			}
230
		}
231
	}
232
 
233
	/* Set the name of the X Axis */
234
	function setXAxisName($Name)
235
	{
236
		$this->Data["XAxisName"] = $Name;
237
	}
238
 
239
	/* Set the display mode of the  X Axis */
240
	function setXAxisDisplay($Mode, $Format = NULL)
241
	{
242
		$this->Data["XAxisDisplay"] = $Mode;
243
		$this->Data["XAxisFormat"] = $Format;
244
	}
245
 
246
	/* Set the unit that will be displayed on the X axis */
247
	function setXAxisUnit($Unit)
248
	{
249
		$this->Data["XAxisUnit"] = $Unit;
250
	}
251
 
252
	/* Set the serie that will be used as abscissa */
253
	function setAbscissa($Serie)
254
	{
255
		if (isset($this->Data["Series"][$Serie])) {
256
			$this->Data["Abscissa"] = $Serie;
257
		}
258
	}
259
 
260
	function setAbsicssaPosition($Position = AXIS_POSITION_BOTTOM)
261
	{
262
		$this->Data["AbsicssaPosition"] = $Position;
263
	}
264
 
265
	/* Set the name of the abscissa axis */
266
	function setAbscissaName($Name)
267
	{
268
		$this->Data["AbscissaName"] = $Name;
269
	}
270
 
271
	/* Create a scatter group specifyin X and Y data series */
272
	function setScatterSerie($SerieX, $SerieY, $ID = 0)
273
	{
274
		if (isset($this->Data["Series"][$SerieX]) && isset($this->Data["Series"][$SerieY])) {
275
			$this->initScatterSerie($ID);
276
			$this->Data["ScatterSeries"][$ID]["X"] = $SerieX;
277
			$this->Data["ScatterSeries"][$ID]["Y"] = $SerieY;
278
		}
279
	}
280
 
281
	/* Set the shape of a given sctatter serie */
282
	function setScatterSerieShape($ID, $Shape = SERIE_SHAPE_FILLEDCIRCLE)
283
	{
284
		if (isset($this->Data["ScatterSeries"][$ID])) {
285
			$this->Data["ScatterSeries"][$ID]["Shape"] = $Shape;
286
		}
287
	}
288
 
289
	/* Set the description of a given scatter serie */
290
	function setScatterSerieDescription($ID, $Description = "My serie")
291
	{
292
		if (isset($this->Data["ScatterSeries"][$ID])) {
293
			$this->Data["ScatterSeries"][$ID]["Description"] = $Description;
294
		}
295
	}
296
 
297
	/* Set the icon associated to a given scatter serie */
298
	function setScatterSeriePicture($ID, $Picture = NULL)
299
	{
300
		if (isset($this->Data["ScatterSeries"][$ID])) {
301
			$this->Data["ScatterSeries"][$ID]["Picture"] = $Picture;
302
		}
303
	}
304
 
305
	/* Set a scatter serie as "drawable" while calling a rendering function */
306
	function setScatterSerieDrawable($ID, $Drawable = TRUE)
307
	{
308
		if (isset($this->Data["ScatterSeries"][$ID])) {
309
			$this->Data["ScatterSeries"][$ID]["isDrawable"] = $Drawable;
310
		}
311
	}
312
 
313
	/* Define if a scatter serie should be draw with ticks */
314
	function setScatterSerieTicks($ID, $Width = 0)
315
	{
316
		if (isset($this->Data["ScatterSeries"][$ID])) {
317
			$this->Data["ScatterSeries"][$ID]["Ticks"] = $Width;
318
		}
319
	}
320
 
321
	/* Define if a scatter serie should be draw with a special weight */
322
	function setScatterSerieWeight($ID, $Weight = 0)
323
	{
324
		if (isset($this->Data["ScatterSeries"][$ID])) {
325
			$this->Data["ScatterSeries"][$ID]["Weight"] = $Weight;
326
		}
327
	}
328
 
329
	/* Associate a color to a scatter serie */
330
	function setScatterSerieColor($ID, array $Format)
331
	{
332
		if (isset($this->Data["ScatterSeries"][$ID])) {
333
			$this->Data["ScatterSeries"][$ID]["Color"] = [
334
				"R" => isset($Format["R"]) ? $Format["R"] : 0,
335
				"G" => isset($Format["G"]) ? $Format["G"] : 0,
336
				"B" => isset($Format["B"]) ? $Format["B"] : 0,
337
				"Alpha" => isset($Format["Alpha"]) ? $Format["Alpha"] : 100
338
			];
339
		}
340
	}
341
 
342
	/* Compute the series limits for an individual and global point of view */
343
	function limits()
344
	{
345
		$GlobalMin = ABSOLUTE_MAX;
346
		$GlobalMax = ABSOLUTE_MIN;
347
		foreach($this->Data["Series"] as $Key => $Value) {
348
			if ($this->Data["Abscissa"] != $Key && $this->Data["Series"][$Key]["isDrawable"] == TRUE) {
349
				if ($GlobalMin > $this->Data["Series"][$Key]["Min"]) {
350
					$GlobalMin = $this->Data["Series"][$Key]["Min"];
351
				}
352
 
353
				if ($GlobalMax < $this->Data["Series"][$Key]["Max"]) {
354
					$GlobalMax = $this->Data["Series"][$Key]["Max"];
355
				}
356
			}
357
		}
358
 
359
		$this->Data["Min"] = $GlobalMin;
360
		$this->Data["Max"] = $GlobalMax;
361
		return [$GlobalMin,$GlobalMax];
362
	}
363
 
364
	/* Mark all series as drawable */
365
	function drawAll()
366
	{
367
		foreach($this->Data["Series"] as $Key => $Value) {
368
			if ($this->Data["Abscissa"] != $Key) {
369
				$this->Data["Series"][$Key]["isDrawable"] = TRUE;
370
			}
371
		}
372
	}
373
 
374
	/* Return the average value of the given serie */
375
	function getSerieAverage($Serie)
376
	{
377
		if (isset($this->Data["Series"][$Serie])) {
378
			$SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]);
379
			return (array_sum($SerieData) / count($SerieData));
380
		} else {
381
			return NULL;
382
		}
383
	}
384
 
385
	/* Return the geometric mean of the given serie */
386
	function getGeometricMean($Serie)
387
	{
388
		if (isset($this->Data["Series"][$Serie])) {
389
			$SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]);
390
			$Seriesum = 1;
391
			foreach($SerieData as $Key => $Value) {
392
				$Seriesum = $Seriesum * $Value;
393
			}
394
 
395
			return (pow($Seriesum, 1 / count($SerieData)));
396
		} else {
397
			return NULL;
398
		}
399
	}
400
 
401
	/* Return the harmonic mean of the given serie */
402
	function getHarmonicMean($Serie)
403
	{
404
		if (isset($this->Data["Series"][$Serie])) {
405
			$SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]);
406
			$Seriesum = 0;
407
			foreach($SerieData as $Key => $Value) {
408
				$Seriesum = $Seriesum + 1 / $Value;
409
			}
410
 
411
			return (count($SerieData) / $Seriesum);
412
		} else {
413
			return NULL;
414
		}
415
	}
416
 
417
	/* Return the standard deviation of the given serie */
418
	function getStandardDeviation($Serie)
419
	{
420
		if (isset($this->Data["Series"][$Serie])) {
421
			$Average = $this->getSerieAverage($Serie);
422
			$SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]);
423
			$DeviationSum = 0;
424
			foreach($SerieData as $Key => $Value) {
425
				$DeviationSum = $DeviationSum + ($Value - $Average) * ($Value - $Average);
426
			}
427
 
428
			return sqrt($DeviationSum / count($SerieData));
429
		} else {
430
			return NULL;
431
		}
432
	}
433
 
434
	/* Return the Coefficient of variation of the given serie */
435
	function getCoefficientOfVariation($Serie)
436
	{
437
		if (isset($this->Data["Series"][$Serie])) {
438
			$Average = $this->getSerieAverage($Serie);
439
			$StandardDeviation = $this->getStandardDeviation($Serie);
440
			return ($StandardDeviation != 0) ? ($StandardDeviation / $Average) : NULL;
441
		} else {
442
			return NULL;
443
		}
444
	}
445
 
446
	/* Return the median value of the given serie */
447
	function getSerieMedian($Serie)
448
	{
449
		if (isset($this->Data["Series"][$Serie])) {
450
			$SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]);
451
			sort($SerieData);
452
			$SerieCenter = floor(count($SerieData) / 2);
453
			return (isset($SerieData[$SerieCenter])) ? $SerieData[$SerieCenter] : NULL;
454
		} else {
455
			return NULL;
456
		}
457
	}
458
 
459
	/* Return the x th percentil of the given serie */
460
	function getSeriePercentile($Serie = "Serie1", $Percentil = 95)
461
	{
462
		if (!isset($this->Data["Series"][$Serie]["Data"])) {
463
			return NULL;
464
		}
465
 
466
		$Values = count($this->Data["Series"][$Serie]["Data"]) - 1;
467
		($Values < 0) AND $Values = 0;
468
		$PercentilID = floor(($Values / 100) * $Percentil + .5);
469
		$SortedValues = $this->Data["Series"][$Serie]["Data"];
470
		sort($SortedValues);
471
		return (is_numeric($SortedValues[$PercentilID])) ? $SortedValues[$PercentilID] : NULL;
472
	}
473
 
474
	/* Add random values to a given serie */
475
	function addRandomValues($SerieName = "Serie1", array $Options = [])
476
	{
477
		$Values = isset($Options["Values"]) ? $Options["Values"] : 20;
478
		$Min = isset($Options["Min"]) ? $Options["Min"] : 0;
479
		$Max = isset($Options["Max"]) ? $Options["Max"] : 100;
480
		$withFloat = isset($Options["withFloat"]) ? $Options["withFloat"] : FALSE;
481
		for ($i = 0; $i <= $Values; $i++) {
482
			$Value = ($withFloat) ? (rand($Min * 100, $Max * 100) / 100) : rand($Min, $Max);
483
			$this->addPoints($Value, $SerieName);
484
		}
485
	}
486
 
487
	/* Test if we have valid data */
488
	function containsData()
489
	{
490
		if (!isset($this->Data["Series"])) {
491
			return FALSE;
492
		}
493
 
494
		foreach($this->Data["Series"] as $Key => $Value) {
495
			if ($this->Data["Abscissa"] != $Key && $this->Data["Series"][$Key]["isDrawable"] == TRUE) {
496
				return TRUE;
497
			}
498
		}
499
 
500
		return FALSE;
501
	}
502
 
503
	/* Set the display mode of an Axis */
504
	function setAxisDisplay($AxisID, $Mode = AXIS_FORMAT_DEFAULT, $Format = NULL)
505
	{
506
		if (isset($this->Data["Axis"][$AxisID])) {
507
			$this->Data["Axis"][$AxisID]["Display"] = $Mode;
508
			if ($Format != NULL) {
509
				$this->Data["Axis"][$AxisID]["Format"] = $Format;
510
			}
511
		}
512
	}
513
 
514
	/* Set the position of an Axis */
515
	function setAxisPosition($AxisID, $Position = AXIS_POSITION_LEFT)
516
	{
517
		if (isset($this->Data["Axis"][$AxisID])) {
518
			$this->Data["Axis"][$AxisID]["Position"] = $Position;
519
		}
520
	}
521
 
522
	/* Associate an unit to an axis */
523
	function setAxisUnit($AxisID, $Unit)
524
	{
525
		if (isset($this->Data["Axis"][$AxisID])) {
526
			$this->Data["Axis"][$AxisID]["Unit"] = $Unit;
527
		}
528
	}
529
 
530
	/* Associate a name to an axis */
531
	function setAxisName($AxisID, $Name)
532
	{
533
		if (isset($this->Data["Axis"][$AxisID])) {
534
			$this->Data["Axis"][$AxisID]["Name"] = $Name;
535
		}
536
	}
537
 
538
	/* Associate a color to an axis */
539
	function setAxisColor($AxisID, array $Format)
540
	{
541
		if (isset($this->Data["Axis"][$AxisID])) {
542
			$this->Data["Axis"][$AxisID]["Color"] = [
543
				"R" => isset($Format["R"]) ? $Format["R"] : 0,
544
				"G" => isset($Format["G"]) ? $Format["G"] : 0,
545
				"B" => isset($Format["B"]) ? $Format["B"] : 0,
546
				"Alpha" => isset($Format["Alpha"]) ? $Format["Alpha"] : 100
547
			];
548
		}
549
	}
550
 
551
	/* Design an axis as X or Y member */
552
	function setAxisXY($AxisID, $Identity = AXIS_Y)
553
	{
554
		if (isset($this->Data["Axis"][$AxisID])) {
555
			$this->Data["Axis"][$AxisID]["Identity"] = $Identity;
556
		}
557
	}
558
 
559
	/* Associate one data serie with one axis */
560
	function setSerieOnAxis($Series, $AxisID)
561
	{
562
		$Series = $this->convertToArray($Series);
563
 
564
		foreach($Series as $Key => $Serie) {
565
			$PreviousAxis = $this->Data["Series"][$Serie]["Axis"];
566
			/* Create missing axis */
567
			if (!isset($this->Data["Axis"][$AxisID])) {
568
				$this->Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_LEFT;
569
				$this->Data["Axis"][$AxisID]["Identity"] = AXIS_Y;
570
			}
571
 
572
			$this->Data["Series"][$Serie]["Axis"] = $AxisID;
573
			/* Cleanup unused axis */
574
			$Found = FALSE;
575
			foreach($this->Data["Series"] as $SerieName => $Values) {
576
				if ($Values["Axis"] == $PreviousAxis) {
577
					$Found = TRUE;
578
				}
579
			}
580
 
581
			if (!$Found) {
582
				unset($this->Data["Axis"][$PreviousAxis]);
583
			}
584
		}
585
	}
586
 
587
	/* Define if a serie should be draw with ticks */
588
	function setSerieTicks($Series, $Width = 0)
589
	{
590
		$Series = $this->convertToArray($Series);
591
 
592
		foreach($Series as $Key => $Serie) {
593
			if (isset($this->Data["Series"][$Serie])) {
594
				$this->Data["Series"][$Serie]["Ticks"] = $Width;
595
			}
596
		}
597
	}
598
 
599
	/* Define if a serie should be draw with a special weight */
600
	function setSerieWeight($Series, $Weight = 0)
601
	{
602
		$Series = $this->convertToArray($Series);
603
 
604
		foreach($Series as $Key => $Serie) {
605
			if (isset($this->Data["Series"][$Serie])) {
606
				$this->Data["Series"][$Serie]["Weight"] = $Weight;
607
			}
608
		}
609
	}
610
 
611
	/* Returns the palette of the given serie */
612
	function getSeriePalette($Serie)
613
	{
614
		if (!isset($this->Data["Series"][$Serie])) {
615
			return NULL;
616
		} else {
617
			return [
618
				"R" => $this->Data["Series"][$Serie]["Color"]["R"],
619
				"G" => $this->Data["Series"][$Serie]["Color"]["G"],
620
				"B" => $this->Data["Series"][$Serie]["Color"]["B"],
621
				"Alpha" => $this->Data["Series"][$Serie]["Color"]["Alpha"]
622
			];
623
		}
624
 
625
	}
626
 
627
	/* Set the color of one serie */
628
	function setPalette($Series, array $Format = [])
629
	{
630
		$Series = $this->convertToArray($Series);
631
		$R = isset($Format["R"]) ? $Format["R"] : 0;
632
		$G = isset($Format["G"]) ? $Format["G"] : 0;
633
		$B = isset($Format["B"]) ? $Format["B"] : 0;
634
		$Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
635
 
636
		foreach($Series as $Key => $Serie) {
637
			if (isset($this->Data["Series"][$Serie])) {
638
				$OldR = $this->Data["Series"][$Serie]["Color"]["R"];
639
				$OldG = $this->Data["Series"][$Serie]["Color"]["G"];
640
				$OldB = $this->Data["Series"][$Serie]["Color"]["B"];
641
				$this->Data["Series"][$Serie]["Color"]["R"] = $R;
642
				$this->Data["Series"][$Serie]["Color"]["G"] = $G;
643
				$this->Data["Series"][$Serie]["Color"]["B"] = $B;
644
				$this->Data["Series"][$Serie]["Color"]["Alpha"] = $Alpha;
645
				/* Do reverse processing on the internal palette array */
646
				foreach($this->Palette as $Key => $Value) {
647
					if ($Value["R"] == $OldR && $Value["G"] == $OldG && $Value["B"] == $OldB) {
648
						$this->Palette[$Key]["R"] = $R;
649
						$this->Palette[$Key]["G"] = $G;
650
						$this->Palette[$Key]["B"] = $B;
651
						$this->Palette[$Key]["Alpha"] = $Alpha;
652
					}
653
				}
654
			}
655
		}
656
	}
657
 
658
	/* Load a palette file */
659
	function loadPalette($FileName, $Overwrite = FALSE)
660
	{
661
		if (!file_exists($FileName)) {
662
			die("Palette not found");
663
		}
664
 
665
		$buffer = file_get_contents($FileName);
666
		if ($buffer === false) {
667
			die("Invalid palette");
668
		}
669
 
670
		if ($Overwrite) {
671
			$this->Palette = [];
672
		}
673
 
674
		$lines = explode("\n", $buffer);
675
		$ID = 0;
676
 
677
		foreach($lines as $line){
678
			$pal = explode(",", $line);
679
			if (count($pal) == 4) {
680
				list($R, $G, $B, $Alpha) = $pal;
681
				$this->Palette[$ID] = ["R" => intval($R),"G" => intval($G),"B" => intval($B),"Alpha" => intval($Alpha)];
682
				$ID++;
683
			}
684
		}
685
 
686
		/* Apply changes to current series */
687
		$ID = 0;
688
		if (isset($this->Data["Series"])) {
689
			foreach($this->Data["Series"] as $Key => $Value) {
690
				$this->Data["Series"][$Key]["Color"] = (!isset($this->Palette[$ID])) ? ["R" => 0,"G" => 0,"B" => 0,"Alpha" => 0] : $this->Palette[$ID];
691
				$ID++;
692
			}
693
		}
694
 
695
	}
696
 
697
	/* Initialise a given scatter serie */
698
	function initScatterSerie($ID)
699
	{
700
		if (isset($this->Data["ScatterSeries"][$ID])) {
701
			return 0;
702
		}
703
 
704
		$this->Data["ScatterSeries"][$ID] = [
705
			"Description" => "Scatter " . $ID,
706
			"isDrawable" => TRUE,
707
			"Picture" => NULL,
708
			"Ticks" => 0,
709
			"Weight" => 0,
710
			"Color" => (isset($this->Palette[$ID])) ? $this->Palette[$ID] : ["R" => rand(0, 255), "B" => rand(0, 255), "G" => rand(0, 255), "Alpha" => 100]
711
		];
712
	}
713
 
714
	/* Initialise a given serie */
715
	function initialise($Serie)
716
	{
717
		$ID = (isset($this->Data["Series"])) ? count($this->Data["Series"]) : 0;
718
 
719
		$this->Data["Series"][$Serie] = [
720
			"Description" => $Serie,
721
			"isDrawable" => TRUE,
722
			"Picture" => NULL,
723
			"Max" => NULL,
724
			"Min" => NULL,
725
			"Axis" => 0,
726
			"Ticks" => 0,
727
			"Weight" => 0,
728
			"Shape" => SERIE_SHAPE_FILLEDCIRCLE,
729
			"Color" => (isset($this->Palette[$ID])) ? $this->Palette[$ID] : ["R" => rand(0, 255), "B" => rand(0, 255), "G" => rand(0, 255), "Alpha" => 100]
730
		];
731
	}
732
 
733
	function normalize($NormalizationFactor = 100, $UnitChange = NULL, $Round = 1)
734
	{
735
		$Abscissa = $this->Data["Abscissa"];
736
		$SelectedSeries = [];
737
		$MaxVal = 0;
738
		foreach($this->Data["Axis"] as $AxisID => $Axis) {
739
 
740
			($UnitChange != NULL) AND $this->Data["Axis"][$AxisID]["Unit"] = $UnitChange;
741
 
742
			foreach($this->Data["Series"] as $SerieName => $Serie) {
743
				if ($Serie["Axis"] == $AxisID && $Serie["isDrawable"] == TRUE && $SerieName != $Abscissa) {
744
					$SelectedSeries[$SerieName] = $SerieName;
745
					if (count($Serie["Data"]) > $MaxVal) {
746
						$MaxVal = count($Serie["Data"]);
747
					}
748
				}
749
			}
750
		}
751
 
752
		for ($i = 0; $i <= $MaxVal - 1; $i++) {
753
			$Factor = 0;
754
			foreach($SelectedSeries as $Key => $SerieName) {
755
				$Value = $this->Data["Series"][$SerieName]["Data"][$i];
756
				($Value != VOID) AND $Factor = $Factor + abs($Value);
757
			}
758
 
759
			if ($Factor != 0) {
760
				$Factor = $NormalizationFactor / $Factor;
761
				foreach($SelectedSeries as $Key => $SerieName) {
762
					$Value = $this->Data["Series"][$SerieName]["Data"][$i];
763
					if ($Value != VOID && $Factor != $NormalizationFactor) {
764
						$this->Data["Series"][$SerieName]["Data"][$i] = round(abs($Value) * $Factor, $Round);
765
					} elseif ($Value == VOID || $Value == 0) {
766
						$this->Data["Series"][$SerieName]["Data"][$i] = VOID;
767
					} elseif ($Factor == $NormalizationFactor) {
768
						$this->Data["Series"][$SerieName]["Data"][$i] = $NormalizationFactor;
769
					}
770
				}
771
			}
772
		}
773
 
774
		foreach($SelectedSeries as $Key => $SerieName) {
775
			$data = $this->stripVOID($this->Data["Series"][$SerieName]["Data"]);
776
			$this->Data["Series"][$SerieName]["Max"] = max($data);
777
			$this->Data["Series"][$SerieName]["Min"] = min($data);
778
		}
779
	}
780
 
781
	/* Load data from a CSV (or similar) data source */
782
	function importFromCSV($FileName, array $Options = [])
783
	{
784
		$Delimiter = isset($Options["Delimiter"]) ? $Options["Delimiter"] : ",";
785
		$GotHeader = isset($Options["GotHeader"]) ? $Options["GotHeader"] : FALSE;
786
		$SkipColumns = isset($Options["SkipColumns"]) ? $Options["SkipColumns"] : [-1];
787
		$DefaultSerieName = isset($Options["DefaultSerieName"]) ? $Options["DefaultSerieName"] : "Serie";
788
		$Handle = fopen($FileName, "r");
789
		if ($Handle) {
790
			$HeaderParsed = FALSE;
791
			$SerieNames = [];
792
			while (!feof($Handle)) {
793
				$Buffer = fgets($Handle, 4096);
794
				$Buffer = str_replace([chr(10),chr(13)], ["",""], $Buffer); # TODO consider stream_get_line
795
				$Values = preg_split("/" . $Delimiter . "/", $Buffer); #TODO consider explode
796
				if ($Buffer != "") {
797
					if ($GotHeader && !$HeaderParsed) {
798
						foreach($Values as $Key => $Name) {
799
							(!in_array($Key, $SkipColumns)) AND $SerieNames[$Key] = $Name;
800
						}
801
 
802
						$HeaderParsed = TRUE;
803
					} else {
804
						if (count($SerieNames) == 0) {
805
							foreach($Values as $Key => $Name) {
806
								(!in_array($Key, $SkipColumns)) AND $SerieNames[$Key] = $DefaultSerieName . $Key;
807
							}
808
						}
809
 
810
						foreach($Values as $Key => $Value) {
811
							(!in_array($Key, $SkipColumns)) AND $this->addPoints($Value, $SerieNames[$Key]);
812
						}
813
					}
814
				} # $Buffer != ""
815
			} # while
816
 
817
			fclose($Handle);
818
		}
819
	}
820
 
821
	/* Create a dataset based on a formula */
822
	function createFunctionSerie($SerieName, $Formula = "", array $Options = [])
823
	{
824
 
825
		if ($Formula == "") {
826
			return 0;
827
		}
828
 
829
		$MinX = isset($Options["MinX"]) ? $Options["MinX"] : -10;
830
		$MaxX = isset($Options["MaxX"]) ? $Options["MaxX"] : 10;
831
		$XStep = isset($Options["XStep"]) ? $Options["XStep"] : 1;
832
		$AutoDescription = isset($Options["AutoDescription"]) ? $Options["AutoDescription"] : FALSE;
833
		$RecordAbscissa = isset($Options["RecordAbscissa"]) ? $Options["RecordAbscissa"] : FALSE;
834
		$AbscissaSerie = isset($Options["AbscissaSerie"]) ? $Options["AbscissaSerie"] : "Abscissa";
835
 
836
		$Result = [];
837
		$Abscissa = [];
838
 
839
		for ($i = $MinX; $i <= $MaxX; $i = $i + $XStep) {
840
 
841
			$Expression = "\$return = '!'.(" . str_replace("z", $i, $Formula) . ");";
842
 
843
			if (substr($Expression, -4, 4) == "/0);"){ # Division by zero in ..\pData.class.php(849) : eval()'d code on line 1
844
				$return = VOID;
845
			} else {
846
				if (eval($Expression) === FALSE) {
847
					$return = VOID;
848
				}
849
 
850
				$return = ($return == "!") ? VOID : $this->right($return, strlen($return) - 1);
851
 
852
				if (in_array($return, ["NAN", "INF", "-INF"])){
853
					$return = VOID;
854
				}
855
			}
856
 
857
			$Abscissa[] = $i;
858
			$Result[] = $return;
859
		}
860
 
861
		$this->addPoints($Result, $SerieName);
862
		if ($AutoDescription) {
863
			$this->setSerieDescription($SerieName, $Formula);
864
		}
865
 
866
		if ($RecordAbscissa) {
867
			$this->addPoints($Abscissa, $AbscissaSerie);
868
		}
869
 
870
	}
871
 
872
	function negateValues($Series)
873
	{
874
		$Series = $this->convertToArray($Series);
875
 
876
		foreach($Series as $Key => $SerieName) {
877
			if (isset($this->Data["Series"][$SerieName])) {
878
				$Data = [];
879
				foreach($this->Data["Series"][$SerieName]["Data"] as $Key => $Value) {
880
					$Data[] = ($Value == VOID) ? VOID : - $Value;
881
				}
882
 
883
				$this->Data["Series"][$SerieName]["Data"] = $Data;
884
				$Data = $this->stripVOID($Data);
885
				$this->Data["Series"][$SerieName]["Max"] = max($Data);
886
				$this->Data["Series"][$SerieName]["Min"] = min($Data);
887
			}
888
		}
889
	}
890
 
891
	/* Return the data & configuration of the series */
892
	function getData()
893
	{
894
		return $this->Data;
895
	}
896
 
897
	/* Save a palette element */
898
	function savePalette($ID, $Color)
899
	{
900
		$this->Palette[$ID] = $Color;
901
	}
902
 
903
	/* Return the palette of the series */
904
	function getPalette()
905
	{
906
		return $this->Palette;
907
	}
908
 
909
	/* Called by the scaling algorithm to save the config */
910
	function saveAxisConfig($Axis)
911
	{
912
		$this->Data["Axis"] = $Axis;
913
	}
914
 
915
	/* Save the Y Margin if set */
916
	function saveYMargin($Value)
917
	{
918
		$this->Data["YMargin"] = $Value;
919
	}
920
 
921
	/* Save extended configuration to the pData object */
922
	function saveExtendedData($Tag, $Values)
923
	{
924
		$this->Data["Extended"][$Tag] = $Values;
925
	}
926
 
927
	/* Called by the scaling algorithm to save the orientation of the scale */
928
	function saveOrientation($Orientation)
929
	{
930
		$this->Data["Orientation"] = $Orientation;
931
	}
932
 
933
	/* Convert a string to a single elements array */
934
	function convertToArray($Value)
935
	{
936
		return (is_array($Value)) ? $Value : [$Value];
937
	}
938
 
939
	/* Class string wrapper */
940
	function __toString()
941
	{
942
		return "pData object.";
943
	}
944
 
945
	function left($value, $NbChar)
946
	{
947
		return substr($value, 0, $NbChar);
948
	}
949
 
950
	function right($value, $NbChar)
951
	{
952
		return substr($value, strlen($value) - $NbChar, $NbChar);
953
	}
954
 
955
	function mid($value, $Depart, $NbChar)
956
	{
957
		return substr($value, $Depart - 1, $NbChar);
958
	}
959
}
960
 
961
?>