Passed
Push — master ( 46fdc8...4b16f3 )
by Mark
15:13 queued 06:31
created

JpGraphRendererBase::renderPlotRadar()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 34
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 4

Importance

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