PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / libraries / vendor / PhpSpreadsheet / Chart / Renderer / JpGraphRendererBase.php

JpGraphRendererBase.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Chart/Renderer/JpGraphRendererBase.php

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