ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
JpGraph.php
Go to the documentation of this file.
1<?php
2
4
5use AccBarPlot;
6use AccLinePlot;
7use BarPlot;
8use ContourPlot;
9use Graph;
10use GroupBarPlot;
11use LinePlot;
14use PieGraph;
15use PiePlot;
16use PiePlot3D;
17use PiePlotC;
18use RadarGraph;
19use RadarPlot;
20use ScatterPlot;
21use Spline;
22use StockPlot;
23
24class JpGraph implements IRenderer
25{
26 private static $width = 640;
27
28 private static $height = 480;
29
30 private static $colourSet = [
31 'mediumpurple1', 'palegreen3', 'gold1', 'cadetblue1',
32 'darkmagenta', 'coral', 'dodgerblue3', 'eggplant',
33 'mediumblue', 'magenta', 'sandybrown', 'cyan',
34 'firebrick1', 'forestgreen', 'deeppink4', 'darkolivegreen',
35 'goldenrod2',
36 ];
37
38 private static $markSet;
39
40 private $chart;
41
42 private $graph;
43
44 private static $plotColour = 0;
45
46 private static $plotMark = 0;
47
51 public function __construct(Chart $chart)
52 {
53 self::init();
54 $this->graph = null;
55 $this->chart = $chart;
56 }
57
58 private static function init(): void
59 {
60 static $loaded = false;
61 if ($loaded) {
62 return;
63 }
64
65 \JpGraph\JpGraph::load();
66 \JpGraph\JpGraph::module('bar');
67 \JpGraph\JpGraph::module('contour');
68 \JpGraph\JpGraph::module('line');
69 \JpGraph\JpGraph::module('pie');
70 \JpGraph\JpGraph::module('pie3d');
71 \JpGraph\JpGraph::module('radar');
72 \JpGraph\JpGraph::module('regstat');
73 \JpGraph\JpGraph::module('scatter');
74 \JpGraph\JpGraph::module('stock');
75
76 self::$markSet = [
77 'diamond' => MARK_DIAMOND,
78 'square' => MARK_SQUARE,
79 'triangle' => MARK_UTRIANGLE,
80 'x' => MARK_X,
81 'star' => MARK_STAR,
82 'dot' => MARK_FILLEDCIRCLE,
83 'dash' => MARK_DTRIANGLE,
84 'circle' => MARK_CIRCLE,
85 'plus' => MARK_CROSS,
86 ];
87
88 $loaded = true;
89 }
90
91 private function formatPointMarker($seriesPlot, $markerID)
92 {
93 $plotMarkKeys = array_keys(self::$markSet);
94 if ($markerID === null) {
95 // Use default plot marker (next marker in the series)
96 self::$plotMark %= count(self::$markSet);
97 $seriesPlot->mark->SetType(self::$markSet[$plotMarkKeys[self::$plotMark++]]);
98 } elseif ($markerID !== 'none') {
99 // Use specified plot marker (if it exists)
100 if (isset(self::$markSet[$markerID])) {
101 $seriesPlot->mark->SetType(self::$markSet[$markerID]);
102 } else {
103 // If the specified plot marker doesn't exist, use default plot marker (next marker in the series)
104 self::$plotMark %= count(self::$markSet);
105 $seriesPlot->mark->SetType(self::$markSet[$plotMarkKeys[self::$plotMark++]]);
106 }
107 } else {
108 // Hide plot marker
109 $seriesPlot->mark->Hide();
110 }
111 $seriesPlot->mark->SetColor(self::$colourSet[self::$plotColour]);
112 $seriesPlot->mark->SetFillColor(self::$colourSet[self::$plotColour]);
113 $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
114
115 return $seriesPlot;
116 }
117
118 private function formatDataSetLabels($groupID, $datasetLabels, $labelCount, $rotation = '')
119 {
120 $datasetLabelFormatCode = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getFormatCode();
121 if ($datasetLabelFormatCode !== null) {
122 // Retrieve any label formatting code
123 $datasetLabelFormatCode = stripslashes($datasetLabelFormatCode);
124 }
125
126 $testCurrentIndex = 0;
127 foreach ($datasetLabels as $i => $datasetLabel) {
128 if (is_array($datasetLabel)) {
129 if ($rotation == 'bar') {
130 $datasetLabels[$i] = implode(' ', $datasetLabel);
131 } else {
132 $datasetLabel = array_reverse($datasetLabel);
133 $datasetLabels[$i] = implode("\n", $datasetLabel);
134 }
135 } else {
136 // Format labels according to any formatting code
137 if ($datasetLabelFormatCode !== null) {
138 $datasetLabels[$i] = NumberFormat::toFormattedString($datasetLabel, $datasetLabelFormatCode);
139 }
140 }
141 ++$testCurrentIndex;
142 }
143
144 return $datasetLabels;
145 }
146
147 private function percentageSumCalculation($groupID, $seriesCount)
148 {
149 $sumValues = [];
150 // Adjust our values to a percentage value across all series in the group
151 for ($i = 0; $i < $seriesCount; ++$i) {
152 if ($i == 0) {
153 $sumValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
154 } else {
155 $nextValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
156 foreach ($nextValues as $k => $value) {
157 if (isset($sumValues[$k])) {
158 $sumValues[$k] += $value;
159 } else {
160 $sumValues[$k] = $value;
161 }
162 }
163 }
164 }
165
166 return $sumValues;
167 }
168
169 private function percentageAdjustValues($dataValues, $sumValues)
170 {
171 foreach ($dataValues as $k => $dataValue) {
172 $dataValues[$k] = $dataValue / $sumValues[$k] * 100;
173 }
174
175 return $dataValues;
176 }
177
178 private function getCaption($captionElement)
179 {
180 // Read any caption
181 $caption = ($captionElement !== null) ? $captionElement->getCaption() : null;
182 // Test if we have a title caption to display
183 if ($caption !== null) {
184 // If we do, it could be a plain string or an array
185 if (is_array($caption)) {
186 // Implode an array to a plain string
187 $caption = implode('', $caption);
188 }
189 }
190
191 return $caption;
192 }
193
194 private function renderTitle(): void
195 {
196 $title = $this->getCaption($this->chart->getTitle());
197 if ($title !== null) {
198 $this->graph->title->Set($title);
199 }
200 }
201
202 private function renderLegend(): void
203 {
204 $legend = $this->chart->getLegend();
205 if ($legend !== null) {
206 $legendPosition = $legend->getPosition();
207 switch ($legendPosition) {
208 case 'r':
209 $this->graph->legend->SetPos(0.01, 0.5, 'right', 'center'); // right
210 $this->graph->legend->SetColumns(1);
211
212 break;
213 case 'l':
214 $this->graph->legend->SetPos(0.01, 0.5, 'left', 'center'); // left
215 $this->graph->legend->SetColumns(1);
216
217 break;
218 case 't':
219 $this->graph->legend->SetPos(0.5, 0.01, 'center', 'top'); // top
220
221 break;
222 case 'b':
223 $this->graph->legend->SetPos(0.5, 0.99, 'center', 'bottom'); // bottom
224
225 break;
226 default:
227 $this->graph->legend->SetPos(0.01, 0.01, 'right', 'top'); // top-right
228 $this->graph->legend->SetColumns(1);
229
230 break;
231 }
232 } else {
233 $this->graph->legend->Hide();
234 }
235 }
236
237 private function renderCartesianPlotArea($type = 'textlin'): void
238 {
239 $this->graph = new Graph(self::$width, self::$height);
240 $this->graph->SetScale($type);
241
242 $this->renderTitle();
243
244 // Rotate for bar rather than column chart
245 $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex(0)->getPlotDirection();
246 $reverse = $rotation == 'bar';
247
248 $xAxisLabel = $this->chart->getXAxisLabel();
249 if ($xAxisLabel !== null) {
250 $title = $this->getCaption($xAxisLabel);
251 if ($title !== null) {
252 $this->graph->xaxis->SetTitle($title, 'center');
253 $this->graph->xaxis->title->SetMargin(35);
254 if ($reverse) {
255 $this->graph->xaxis->title->SetAngle(90);
256 $this->graph->xaxis->title->SetMargin(90);
257 }
258 }
259 }
260
261 $yAxisLabel = $this->chart->getYAxisLabel();
262 if ($yAxisLabel !== null) {
263 $title = $this->getCaption($yAxisLabel);
264 if ($title !== null) {
265 $this->graph->yaxis->SetTitle($title, 'center');
266 if ($reverse) {
267 $this->graph->yaxis->title->SetAngle(0);
268 $this->graph->yaxis->title->SetMargin(-55);
269 }
270 }
271 }
272 }
273
274 private function renderPiePlotArea(): void
275 {
276 $this->graph = new PieGraph(self::$width, self::$height);
277
278 $this->renderTitle();
279 }
280
281 private function renderRadarPlotArea(): void
282 {
283 $this->graph = new RadarGraph(self::$width, self::$height);
284 $this->graph->SetScale('lin');
285
286 $this->renderTitle();
287 }
288
289 private function renderPlotLine($groupID, $filled = false, $combination = false, $dimensions = '2d'): void
290 {
291 $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
292
293 $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
294 if ($labelCount > 0) {
295 $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
296 $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount);
297 $this->graph->xaxis->SetTickLabels($datasetLabels);
298 }
299
300 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
301 $seriesPlots = [];
302 if ($grouping == 'percentStacked') {
303 $sumValues = $this->percentageSumCalculation($groupID, $seriesCount);
304 } else {
305 $sumValues = [];
306 }
307
308 // Loop through each data series in turn
309 for ($i = 0; $i < $seriesCount; ++$i) {
310 $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
311 $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker();
312
313 if ($grouping == 'percentStacked') {
314 $dataValues = $this->percentageAdjustValues($dataValues, $sumValues);
315 }
316
317 // Fill in any missing values in the $dataValues array
318 $testCurrentIndex = 0;
319 foreach ($dataValues as $k => $dataValue) {
320 while ($k != $testCurrentIndex) {
321 $dataValues[$testCurrentIndex] = null;
322 ++$testCurrentIndex;
323 }
324 ++$testCurrentIndex;
325 }
326
327 $seriesPlot = new LinePlot($dataValues);
328 if ($combination) {
329 $seriesPlot->SetBarCenter();
330 }
331
332 if ($filled) {
333 $seriesPlot->SetFilled(true);
334 $seriesPlot->SetColor('black');
335 $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]);
336 } else {
337 // Set the appropriate plot marker
338 $this->formatPointMarker($seriesPlot, $marker);
339 }
340 $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
341 $seriesPlot->SetLegend($dataLabel);
342
343 $seriesPlots[] = $seriesPlot;
344 }
345
346 if ($grouping == 'standard') {
347 $groupPlot = $seriesPlots;
348 } else {
349 $groupPlot = new AccLinePlot($seriesPlots);
350 }
351 $this->graph->Add($groupPlot);
352 }
353
354 private function renderPlotBar($groupID, $dimensions = '2d'): void
355 {
356 $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotDirection();
357 // Rotate for bar rather than column chart
358 if (($groupID == 0) && ($rotation == 'bar')) {
359 $this->graph->Set90AndMargin();
360 }
361 $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
362
363 $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
364 if ($labelCount > 0) {
365 $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
366 $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount, $rotation);
367 // Rotate for bar rather than column chart
368 if ($rotation == 'bar') {
369 $datasetLabels = array_reverse($datasetLabels);
370 $this->graph->yaxis->SetPos('max');
371 $this->graph->yaxis->SetLabelAlign('center', 'top');
372 $this->graph->yaxis->SetLabelSide(SIDE_RIGHT);
373 }
374 $this->graph->xaxis->SetTickLabels($datasetLabels);
375 }
376
377 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
378 $seriesPlots = [];
379 if ($grouping == 'percentStacked') {
380 $sumValues = $this->percentageSumCalculation($groupID, $seriesCount);
381 } else {
382 $sumValues = [];
383 }
384
385 // Loop through each data series in turn
386 for ($j = 0; $j < $seriesCount; ++$j) {
387 $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($j)->getDataValues();
388 if ($grouping == 'percentStacked') {
389 $dataValues = $this->percentageAdjustValues($dataValues, $sumValues);
390 }
391
392 // Fill in any missing values in the $dataValues array
393 $testCurrentIndex = 0;
394 foreach ($dataValues as $k => $dataValue) {
395 while ($k != $testCurrentIndex) {
396 $dataValues[$testCurrentIndex] = null;
397 ++$testCurrentIndex;
398 }
399 ++$testCurrentIndex;
400 }
401
402 // Reverse the $dataValues order for bar rather than column chart
403 if ($rotation == 'bar') {
404 $dataValues = array_reverse($dataValues);
405 }
406 $seriesPlot = new BarPlot($dataValues);
407 $seriesPlot->SetColor('black');
408 $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]);
409 if ($dimensions == '3d') {
410 $seriesPlot->SetShadow();
411 }
412 if (!$this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)) {
413 $dataLabel = '';
414 } else {
415 $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)->getDataValue();
416 }
417 $seriesPlot->SetLegend($dataLabel);
418
419 $seriesPlots[] = $seriesPlot;
420 }
421 // Reverse the plot order for bar rather than column chart
422 if (($rotation == 'bar') && ($grouping != 'percentStacked')) {
423 $seriesPlots = array_reverse($seriesPlots);
424 }
425
426 if ($grouping == 'clustered') {
427 $groupPlot = new GroupBarPlot($seriesPlots);
428 } elseif ($grouping == 'standard') {
429 $groupPlot = new GroupBarPlot($seriesPlots);
430 } else {
431 $groupPlot = new AccBarPlot($seriesPlots);
432 if ($dimensions == '3d') {
433 $groupPlot->SetShadow();
434 }
435 }
436
437 $this->graph->Add($groupPlot);
438 }
439
440 private function renderPlotScatter($groupID, $bubble): void
441 {
442 $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
443 $scatterStyle = $bubbleSize = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
444
445 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
446 $seriesPlots = [];
447
448 // Loop through each data series in turn
449 for ($i = 0; $i < $seriesCount; ++$i) {
450 $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues();
451 $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
452
453 foreach ($dataValuesY as $k => $dataValueY) {
454 $dataValuesY[$k] = $k;
455 }
456
457 $seriesPlot = new ScatterPlot($dataValuesX, $dataValuesY);
458 if ($scatterStyle == 'lineMarker') {
459 $seriesPlot->SetLinkPoints();
460 $seriesPlot->link->SetColor(self::$colourSet[self::$plotColour]);
461 } elseif ($scatterStyle == 'smoothMarker') {
462 $spline = new Spline($dataValuesY, $dataValuesX);
463 [$splineDataY, $splineDataX] = $spline->Get(count($dataValuesX) * self::$width / 20);
464 $lplot = new LinePlot($splineDataX, $splineDataY);
465 $lplot->SetColor(self::$colourSet[self::$plotColour]);
466
467 $this->graph->Add($lplot);
468 }
469
470 if ($bubble) {
471 $this->formatPointMarker($seriesPlot, 'dot');
472 $seriesPlot->mark->SetColor('black');
473 $seriesPlot->mark->SetSize($bubbleSize);
474 } else {
475 $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker();
476 $this->formatPointMarker($seriesPlot, $marker);
477 }
478 $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
479 $seriesPlot->SetLegend($dataLabel);
480
481 $this->graph->Add($seriesPlot);
482 }
483 }
484
485 private function renderPlotRadar($groupID): void
486 {
487 $radarStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
488
489 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
490 $seriesPlots = [];
491
492 // Loop through each data series in turn
493 for ($i = 0; $i < $seriesCount; ++$i) {
494 $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues();
495 $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
496 $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker();
497
498 $dataValues = [];
499 foreach ($dataValuesY as $k => $dataValueY) {
500 $dataValues[$k] = implode(' ', array_reverse($dataValueY));
501 }
502 $tmp = array_shift($dataValues);
503 $dataValues[] = $tmp;
504 $tmp = array_shift($dataValuesX);
505 $dataValuesX[] = $tmp;
506
507 $this->graph->SetTitles(array_reverse($dataValues));
508
509 $seriesPlot = new RadarPlot(array_reverse($dataValuesX));
510
511 $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
512 $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
513 if ($radarStyle == 'filled') {
514 $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour]);
515 }
516 $this->formatPointMarker($seriesPlot, $marker);
517 $seriesPlot->SetLegend($dataLabel);
518
519 $this->graph->Add($seriesPlot);
520 }
521 }
522
523 private function renderPlotContour($groupID): void
524 {
525 $contourStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
526
527 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
528 $seriesPlots = [];
529
530 $dataValues = [];
531 // Loop through each data series in turn
532 for ($i = 0; $i < $seriesCount; ++$i) {
533 $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues();
534 $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
535
536 $dataValues[$i] = $dataValuesX;
537 }
538 $seriesPlot = new ContourPlot($dataValues);
539
540 $this->graph->Add($seriesPlot);
541 }
542
543 private function renderPlotStock($groupID): void
544 {
545 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
546 $plotOrder = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotOrder();
547
548 $dataValues = [];
549 // Loop through each data series in turn and build the plot arrays
550 foreach ($plotOrder as $i => $v) {
551 $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($v)->getDataValues();
552 foreach ($dataValuesX as $j => $dataValueX) {
553 $dataValues[$plotOrder[$i]][$j] = $dataValueX;
554 }
555 }
556 if (empty($dataValues)) {
557 return;
558 }
559
560 $dataValuesPlot = [];
561 // Flatten the plot arrays to a single dimensional array to work with jpgraph
562 $jMax = count($dataValues[0]);
563 for ($j = 0; $j < $jMax; ++$j) {
564 for ($i = 0; $i < $seriesCount; ++$i) {
565 $dataValuesPlot[] = $dataValues[$i][$j];
566 }
567 }
568
569 // Set the x-axis labels
570 $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
571 if ($labelCount > 0) {
572 $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
573 $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount);
574 $this->graph->xaxis->SetTickLabels($datasetLabels);
575 }
576
577 $seriesPlot = new StockPlot($dataValuesPlot);
578 $seriesPlot->SetWidth(20);
579
580 $this->graph->Add($seriesPlot);
581 }
582
583 private function renderAreaChart($groupCount, $dimensions = '2d'): void
584 {
586
587 for ($i = 0; $i < $groupCount; ++$i) {
588 $this->renderPlotLine($i, true, false, $dimensions);
589 }
590 }
591
592 private function renderLineChart($groupCount, $dimensions = '2d'): void
593 {
595
596 for ($i = 0; $i < $groupCount; ++$i) {
597 $this->renderPlotLine($i, false, false, $dimensions);
598 }
599 }
600
601 private function renderBarChart($groupCount, $dimensions = '2d'): void
602 {
604
605 for ($i = 0; $i < $groupCount; ++$i) {
606 $this->renderPlotBar($i, $dimensions);
607 }
608 }
609
610 private function renderScatterChart($groupCount): void
611 {
612 $this->renderCartesianPlotArea('linlin');
613
614 for ($i = 0; $i < $groupCount; ++$i) {
615 $this->renderPlotScatter($i, false);
616 }
617 }
618
619 private function renderBubbleChart($groupCount): void
620 {
621 $this->renderCartesianPlotArea('linlin');
622
623 for ($i = 0; $i < $groupCount; ++$i) {
624 $this->renderPlotScatter($i, true);
625 }
626 }
627
628 private function renderPieChart($groupCount, $dimensions = '2d', $doughnut = false, $multiplePlots = false): void
629 {
630 $this->renderPiePlotArea();
631
632 $iLimit = ($multiplePlots) ? $groupCount : 1;
633 for ($groupID = 0; $groupID < $iLimit; ++$groupID) {
634 $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
635 $exploded = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
636 $datasetLabels = [];
637 if ($groupID == 0) {
638 $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
639 if ($labelCount > 0) {
640 $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
641 $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount);
642 }
643 }
644
645 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
646 $seriesPlots = [];
647 // For pie charts, we only display the first series: doughnut charts generally display all series
648 $jLimit = ($multiplePlots) ? $seriesCount : 1;
649 // Loop through each data series in turn
650 for ($j = 0; $j < $jLimit; ++$j) {
651 $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($j)->getDataValues();
652
653 // Fill in any missing values in the $dataValues array
654 $testCurrentIndex = 0;
655 foreach ($dataValues as $k => $dataValue) {
656 while ($k != $testCurrentIndex) {
657 $dataValues[$testCurrentIndex] = null;
658 ++$testCurrentIndex;
659 }
660 ++$testCurrentIndex;
661 }
662
663 if ($dimensions == '3d') {
664 $seriesPlot = new PiePlot3D($dataValues);
665 } else {
666 if ($doughnut) {
667 $seriesPlot = new PiePlotC($dataValues);
668 } else {
669 $seriesPlot = new PiePlot($dataValues);
670 }
671 }
672
673 if ($multiplePlots) {
674 $seriesPlot->SetSize(($jLimit - $j) / ($jLimit * 4));
675 }
676
677 if ($doughnut) {
678 $seriesPlot->SetMidColor('white');
679 }
680
681 $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
682 if (count($datasetLabels) > 0) {
683 $seriesPlot->SetLabels(array_fill(0, count($datasetLabels), ''));
684 }
685 if ($dimensions != '3d') {
686 $seriesPlot->SetGuideLines(false);
687 }
688 if ($j == 0) {
689 if ($exploded) {
690 $seriesPlot->ExplodeAll();
691 }
692 $seriesPlot->SetLegends($datasetLabels);
693 }
694
695 $this->graph->Add($seriesPlot);
696 }
697 }
698 }
699
700 private function renderRadarChart($groupCount): void
701 {
702 $this->renderRadarPlotArea();
703
704 for ($groupID = 0; $groupID < $groupCount; ++$groupID) {
705 $this->renderPlotRadar($groupID);
706 }
707 }
708
709 private function renderStockChart($groupCount): void
710 {
711 $this->renderCartesianPlotArea('intint');
712
713 for ($groupID = 0; $groupID < $groupCount; ++$groupID) {
714 $this->renderPlotStock($groupID);
715 }
716 }
717
718 private function renderContourChart($groupCount, $dimensions): void
719 {
720 $this->renderCartesianPlotArea('intint');
721
722 for ($i = 0; $i < $groupCount; ++$i) {
723 $this->renderPlotContour($i);
724 }
725 }
726
727 private function renderCombinationChart($groupCount, $dimensions, $outputDestination)
728 {
730
731 for ($i = 0; $i < $groupCount; ++$i) {
732 $dimensions = null;
733 $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType();
734 switch ($chartType) {
735 case 'area3DChart':
736 $dimensions = '3d';
737 // no break
738 case 'areaChart':
739 $this->renderPlotLine($i, true, true, $dimensions);
740
741 break;
742 case 'bar3DChart':
743 $dimensions = '3d';
744 // no break
745 case 'barChart':
746 $this->renderPlotBar($i, $dimensions);
747
748 break;
749 case 'line3DChart':
750 $dimensions = '3d';
751 // no break
752 case 'lineChart':
753 $this->renderPlotLine($i, false, true, $dimensions);
754
755 break;
756 case 'scatterChart':
757 $this->renderPlotScatter($i, false);
758
759 break;
760 case 'bubbleChart':
761 $this->renderPlotScatter($i, true);
762
763 break;
764 default:
765 $this->graph = null;
766
767 return false;
768 }
769 }
770
771 $this->renderLegend();
772
773 $this->graph->Stroke($outputDestination);
774
775 return true;
776 }
777
778 public function render($outputDestination)
779 {
780 self::$plotColour = 0;
781
782 $groupCount = $this->chart->getPlotArea()->getPlotGroupCount();
783
784 $dimensions = null;
785 if ($groupCount == 1) {
786 $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex(0)->getPlotType();
787 } else {
788 $chartTypes = [];
789 for ($i = 0; $i < $groupCount; ++$i) {
790 $chartTypes[] = $this->chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType();
791 }
792 $chartTypes = array_unique($chartTypes);
793 if (count($chartTypes) == 1) {
794 $chartType = array_pop($chartTypes);
795 } elseif (count($chartTypes) == 0) {
796 echo 'Chart is not yet implemented<br />';
797
798 return false;
799 } else {
800 return $this->renderCombinationChart($groupCount, $dimensions, $outputDestination);
801 }
802 }
803
804 switch ($chartType) {
805 case 'area3DChart':
806 $dimensions = '3d';
807 // no break
808 case 'areaChart':
809 $this->renderAreaChart($groupCount, $dimensions);
810
811 break;
812 case 'bar3DChart':
813 $dimensions = '3d';
814 // no break
815 case 'barChart':
816 $this->renderBarChart($groupCount, $dimensions);
817
818 break;
819 case 'line3DChart':
820 $dimensions = '3d';
821 // no break
822 case 'lineChart':
823 $this->renderLineChart($groupCount, $dimensions);
824
825 break;
826 case 'pie3DChart':
827 $dimensions = '3d';
828 // no break
829 case 'pieChart':
830 $this->renderPieChart($groupCount, $dimensions, false, false);
831
832 break;
833 case 'doughnut3DChart':
834 $dimensions = '3d';
835 // no break
836 case 'doughnutChart':
837 $this->renderPieChart($groupCount, $dimensions, true, true);
838
839 break;
840 case 'scatterChart':
841 $this->renderScatterChart($groupCount);
842
843 break;
844 case 'bubbleChart':
845 $this->renderBubbleChart($groupCount);
846
847 break;
848 case 'radarChart':
849 $this->renderRadarChart($groupCount);
850
851 break;
852 case 'surface3DChart':
853 $dimensions = '3d';
854 // no break
855 case 'surfaceChart':
856 $this->renderContourChart($groupCount, $dimensions);
857
858 break;
859 case 'stockChart':
860 $this->renderStockChart($groupCount);
861
862 break;
863 default:
864 echo $chartType . ' is not yet implemented<br />';
865
866 return false;
867 }
868 $this->renderLegend();
869
870 $this->graph->Stroke($outputDestination);
871
872 return true;
873 }
874}
An exception for terminatinating execution or to throw for unit testing.
__construct(Chart $chart)
Create a new jpgraph.
Definition: JpGraph.php:51
percentageSumCalculation($groupID, $seriesCount)
Definition: JpGraph.php:147
render($outputDestination)
Render the chart to given file (or stream).
Definition: JpGraph.php:778
percentageAdjustValues($dataValues, $sumValues)
Definition: JpGraph.php:169
renderPieChart($groupCount, $dimensions='2d', $doughnut=false, $multiplePlots=false)
Definition: JpGraph.php:628
renderPlotBar($groupID, $dimensions='2d')
Definition: JpGraph.php:354
formatPointMarker($seriesPlot, $markerID)
Definition: JpGraph.php:91
renderCombinationChart($groupCount, $dimensions, $outputDestination)
Definition: JpGraph.php:727
renderLineChart($groupCount, $dimensions='2d')
Definition: JpGraph.php:592
renderContourChart($groupCount, $dimensions)
Definition: JpGraph.php:718
renderAreaChart($groupCount, $dimensions='2d')
Definition: JpGraph.php:583
formatDataSetLabels($groupID, $datasetLabels, $labelCount, $rotation='')
Definition: JpGraph.php:118
renderBarChart($groupCount, $dimensions='2d')
Definition: JpGraph.php:601
renderPlotLine($groupID, $filled=false, $combination=false, $dimensions='2d')
Definition: JpGraph.php:289
static toFormattedString($value, $format, $callBack=null)
Convert a value in a pre-defined format to a PHP string.
$i
Definition: disco.tpl.php:19
$legend
$type