PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.4.7
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.4.7
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
visualizer / vendor / phpoffice / phpspreadsheet / src / PhpSpreadsheet / Chart / Renderer / JpGraph.php

JpGraph.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.4.7, at vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php

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