Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
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 class 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 48 /** 49 * Create a new jpgraph. 50 */ 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 } 305 306 // Loop through each data series in turn 307 for ($i = 0; $i < $seriesCount; ++$i) { 308 $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues(); 309 $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker(); 310 311 if ($grouping == 'percentStacked') { 312 $dataValues = $this->percentageAdjustValues($dataValues, $sumValues); 313 } 314 315 // Fill in any missing values in the $dataValues array 316 $testCurrentIndex = 0; 317 foreach ($dataValues as $k => $dataValue) { 318 while ($k != $testCurrentIndex) { 319 $dataValues[$testCurrentIndex] = null; 320 ++$testCurrentIndex; 321 } 322 ++$testCurrentIndex; 323 } 324 325 $seriesPlot = new LinePlot($dataValues); 326 if ($combination) { 327 $seriesPlot->SetBarCenter(); 328 } 329 330 if ($filled) { 331 $seriesPlot->SetFilled(true); 332 $seriesPlot->SetColor('black'); 333 $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]); 334 } else { 335 // Set the appropriate plot marker 336 $this->formatPointMarker($seriesPlot, $marker); 337 } 338 $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue(); 339 $seriesPlot->SetLegend($dataLabel); 340 341 $seriesPlots[] = $seriesPlot; 342 } 343 344 if ($grouping == 'standard') { 345 $groupPlot = $seriesPlots; 346 } else { 347 $groupPlot = new AccLinePlot($seriesPlots); 348 } 349 $this->graph->Add($groupPlot); 350 } 351 352 private function renderPlotBar($groupID, $dimensions = '2d'): void 353 { 354 $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotDirection(); 355 // Rotate for bar rather than column chart 356 if (($groupID == 0) && ($rotation == 'bar')) { 357 $this->graph->Set90AndMargin(); 358 } 359 $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping(); 360 361 $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount(); 362 if ($labelCount > 0) { 363 $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues(); 364 $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount, $rotation); 365 // Rotate for bar rather than column chart 366 if ($rotation == 'bar') { 367 $datasetLabels = array_reverse($datasetLabels); 368 $this->graph->yaxis->SetPos('max'); 369 $this->graph->yaxis->SetLabelAlign('center', 'top'); 370 $this->graph->yaxis->SetLabelSide(SIDE_RIGHT); 371 } 372 $this->graph->xaxis->SetTickLabels($datasetLabels); 373 } 374 375 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount(); 376 $seriesPlots = []; 377 if ($grouping == 'percentStacked') { 378 $sumValues = $this->percentageSumCalculation($groupID, $seriesCount); 379 } 380 381 // Loop through each data series in turn 382 for ($j = 0; $j < $seriesCount; ++$j) { 383 $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($j)->getDataValues(); 384 if ($grouping == 'percentStacked') { 385 $dataValues = $this->percentageAdjustValues($dataValues, $sumValues); 386 } 387 388 // Fill in any missing values in the $dataValues array 389 $testCurrentIndex = 0; 390 foreach ($dataValues as $k => $dataValue) { 391 while ($k != $testCurrentIndex) { 392 $dataValues[$testCurrentIndex] = null; 393 ++$testCurrentIndex; 394 } 395 ++$testCurrentIndex; 396 } 397 398 // Reverse the $dataValues order for bar rather than column chart 399 if ($rotation == 'bar') { 400 $dataValues = array_reverse($dataValues); 401 } 402 $seriesPlot = new BarPlot($dataValues); 403 $seriesPlot->SetColor('black'); 404 $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]); 405 if ($dimensions == '3d') { 406 $seriesPlot->SetShadow(); 407 } 408 if (!$this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)) { 409 $dataLabel = ''; 410 } else { 411 $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)->getDataValue(); 412 } 413 $seriesPlot->SetLegend($dataLabel); 414 415 $seriesPlots[] = $seriesPlot; 416 } 417 // Reverse the plot order for bar rather than column chart 418 if (($rotation == 'bar') && ($grouping != 'percentStacked')) { 419 $seriesPlots = array_reverse($seriesPlots); 420 } 421 422 if ($grouping == 'clustered') { 423 $groupPlot = new GroupBarPlot($seriesPlots); 424 } elseif ($grouping == 'standard') { 425 $groupPlot = new GroupBarPlot($seriesPlots); 426 } else { 427 $groupPlot = new AccBarPlot($seriesPlots); 428 if ($dimensions == '3d') { 429 $groupPlot->SetShadow(); 430 } 431 } 432 433 $this->graph->Add($groupPlot); 434 } 435 436 private function renderPlotScatter($groupID, $bubble): void 437 { 438 $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping(); 439 $scatterStyle = $bubbleSize = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle(); 440 441 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount(); 442 $seriesPlots = []; 443 444 // Loop through each data series in turn 445 for ($i = 0; $i < $seriesCount; ++$i) { 446 $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues(); 447 $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues(); 448 449 foreach ($dataValuesY as $k => $dataValueY) { 450 $dataValuesY[$k] = $k; 451 } 452 453 $seriesPlot = new ScatterPlot($dataValuesX, $dataValuesY); 454 if ($scatterStyle == 'lineMarker') { 455 $seriesPlot->SetLinkPoints(); 456 $seriesPlot->link->SetColor(self::$colourSet[self::$plotColour]); 457 } elseif ($scatterStyle == 'smoothMarker') { 458 $spline = new Spline($dataValuesY, $dataValuesX); 459 [$splineDataY, $splineDataX] = $spline->Get(count($dataValuesX) * self::$width / 20); 460 $lplot = new LinePlot($splineDataX, $splineDataY); 461 $lplot->SetColor(self::$colourSet[self::$plotColour]); 462 463 $this->graph->Add($lplot); 464 } 465 466 if ($bubble) { 467 $this->formatPointMarker($seriesPlot, 'dot'); 468 $seriesPlot->mark->SetColor('black'); 469 $seriesPlot->mark->SetSize($bubbleSize); 470 } else { 471 $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker(); 472 $this->formatPointMarker($seriesPlot, $marker); 473 } 474 $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue(); 475 $seriesPlot->SetLegend($dataLabel); 476 477 $this->graph->Add($seriesPlot); 478 } 479 } 480 481 private function renderPlotRadar($groupID): void 482 { 483 $radarStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle(); 484 485 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount(); 486 $seriesPlots = []; 487 488 // Loop through each data series in turn 489 for ($i = 0; $i < $seriesCount; ++$i) { 490 $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues(); 491 $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues(); 492 $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker(); 493 494 $dataValues = []; 495 foreach ($dataValuesY as $k => $dataValueY) { 496 $dataValues[$k] = implode(' ', array_reverse($dataValueY)); 497 } 498 $tmp = array_shift($dataValues); 499 $dataValues[] = $tmp; 500 $tmp = array_shift($dataValuesX); 501 $dataValuesX[] = $tmp; 502 503 $this->graph->SetTitles(array_reverse($dataValues)); 504 505 $seriesPlot = new RadarPlot(array_reverse($dataValuesX)); 506 507 $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue(); 508 $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]); 509 if ($radarStyle == 'filled') { 510 $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour]); 511 } 512 $this->formatPointMarker($seriesPlot, $marker); 513 $seriesPlot->SetLegend($dataLabel); 514 515 $this->graph->Add($seriesPlot); 516 } 517 } 518 519 private function renderPlotContour($groupID): void 520 { 521 $contourStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle(); 522 523 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount(); 524 $seriesPlots = []; 525 526 $dataValues = []; 527 // Loop through each data series in turn 528 for ($i = 0; $i < $seriesCount; ++$i) { 529 $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues(); 530 $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues(); 531 532 $dataValues[$i] = $dataValuesX; 533 } 534 $seriesPlot = new ContourPlot($dataValues); 535 536 $this->graph->Add($seriesPlot); 537 } 538 539 private function renderPlotStock($groupID): void 540 { 541 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount(); 542 $plotOrder = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotOrder(); 543 544 $dataValues = []; 545 // Loop through each data series in turn and build the plot arrays 546 foreach ($plotOrder as $i => $v) { 547 $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($v)->getDataValues(); 548 foreach ($dataValuesX as $j => $dataValueX) { 549 $dataValues[$plotOrder[$i]][$j] = $dataValueX; 550 } 551 } 552 if (empty($dataValues)) { 553 return; 554 } 555 556 $dataValuesPlot = []; 557 // Flatten the plot arrays to a single dimensional array to work with jpgraph 558 $jMax = count($dataValues[0]); 559 for ($j = 0; $j < $jMax; ++$j) { 560 for ($i = 0; $i < $seriesCount; ++$i) { 561 $dataValuesPlot[] = $dataValues[$i][$j]; 562 } 563 } 564 565 // Set the x-axis labels 566 $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount(); 567 if ($labelCount > 0) { 568 $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues(); 569 $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount); 570 $this->graph->xaxis->SetTickLabels($datasetLabels); 571 } 572 573 $seriesPlot = new StockPlot($dataValuesPlot); 574 $seriesPlot->SetWidth(20); 575 576 $this->graph->Add($seriesPlot); 577 } 578 579 private function renderAreaChart($groupCount, $dimensions = '2d'): void 580 { 581 $this->renderCartesianPlotArea(); 582 583 for ($i = 0; $i < $groupCount; ++$i) { 584 $this->renderPlotLine($i, true, false, $dimensions); 585 } 586 } 587 588 private function renderLineChart($groupCount, $dimensions = '2d'): void 589 { 590 $this->renderCartesianPlotArea(); 591 592 for ($i = 0; $i < $groupCount; ++$i) { 593 $this->renderPlotLine($i, false, false, $dimensions); 594 } 595 } 596 597 private function renderBarChart($groupCount, $dimensions = '2d'): void 598 { 599 $this->renderCartesianPlotArea(); 600 601 for ($i = 0; $i < $groupCount; ++$i) { 602 $this->renderPlotBar($i, $dimensions); 603 } 604 } 605 606 private function renderScatterChart($groupCount): void 607 { 608 $this->renderCartesianPlotArea('linlin'); 609 610 for ($i = 0; $i < $groupCount; ++$i) { 611 $this->renderPlotScatter($i, false); 612 } 613 } 614 615 private function renderBubbleChart($groupCount): void 616 { 617 $this->renderCartesianPlotArea('linlin'); 618 619 for ($i = 0; $i < $groupCount; ++$i) { 620 $this->renderPlotScatter($i, true); 621 } 622 } 623 624 private function renderPieChart($groupCount, $dimensions = '2d', $doughnut = false, $multiplePlots = false): void 625 { 626 $this->renderPiePlotArea(); 627 628 $iLimit = ($multiplePlots) ? $groupCount : 1; 629 for ($groupID = 0; $groupID < $iLimit; ++$groupID) { 630 $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping(); 631 $exploded = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle(); 632 $datasetLabels = []; 633 if ($groupID == 0) { 634 $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount(); 635 if ($labelCount > 0) { 636 $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues(); 637 $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount); 638 } 639 } 640 641 $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount(); 642 $seriesPlots = []; 643 // For pie charts, we only display the first series: doughnut charts generally display all series 644 $jLimit = ($multiplePlots) ? $seriesCount : 1; 645 // Loop through each data series in turn 646 for ($j = 0; $j < $jLimit; ++$j) { 647 $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($j)->getDataValues(); 648 649 // Fill in any missing values in the $dataValues array 650 $testCurrentIndex = 0; 651 foreach ($dataValues as $k => $dataValue) { 652 while ($k != $testCurrentIndex) { 653 $dataValues[$testCurrentIndex] = null; 654 ++$testCurrentIndex; 655 } 656 ++$testCurrentIndex; 657 } 658 659 if ($dimensions == '3d') { 660 $seriesPlot = new PiePlot3D($dataValues); 661 } else { 662 if ($doughnut) { 663 $seriesPlot = new PiePlotC($dataValues); 664 } else { 665 $seriesPlot = new PiePlot($dataValues); 666 } 667 } 668 669 if ($multiplePlots) { 670 $seriesPlot->SetSize(($jLimit - $j) / ($jLimit * 4)); 671 } 672 673 if ($doughnut) { 674 $seriesPlot->SetMidColor('white'); 675 } 676 677 $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]); 678 if (count($datasetLabels) > 0) { 679 $seriesPlot->SetLabels(array_fill(0, count($datasetLabels), '')); 680 } 681 if ($dimensions != '3d') { 682 $seriesPlot->SetGuideLines(false); 683 } 684 if ($j == 0) { 685 if ($exploded) { 686 $seriesPlot->ExplodeAll(); 687 } 688 $seriesPlot->SetLegends($datasetLabels); 689 } 690 691 $this->graph->Add($seriesPlot); 692 } 693 } 694 } 695 696 private function renderRadarChart($groupCount): void 697 { 698 $this->renderRadarPlotArea(); 699 700 for ($groupID = 0; $groupID < $groupCount; ++$groupID) { 701 $this->renderPlotRadar($groupID); 702 } 703 } 704 705 private function renderStockChart($groupCount): void 706 { 707 $this->renderCartesianPlotArea('intint'); 708 709 for ($groupID = 0; $groupID < $groupCount; ++$groupID) { 710 $this->renderPlotStock($groupID); 711 } 712 } 713 714 private function renderContourChart($groupCount, $dimensions): void 715 { 716 $this->renderCartesianPlotArea('intint'); 717 718 for ($i = 0; $i < $groupCount; ++$i) { 719 $this->renderPlotContour($i); 720 } 721 } 722 723 private function renderCombinationChart($groupCount, $dimensions, $outputDestination) 724 { 725 $this->renderCartesianPlotArea(); 726 727 for ($i = 0; $i < $groupCount; ++$i) { 728 $dimensions = null; 729 $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType(); 730 switch ($chartType) { 731 case 'area3DChart': 732 $dimensions = '3d'; 733 // no break 734 case 'areaChart': 735 $this->renderPlotLine($i, true, true, $dimensions); 736 737 break; 738 case 'bar3DChart': 739 $dimensions = '3d'; 740 // no break 741 case 'barChart': 742 $this->renderPlotBar($i, $dimensions); 743 744 break; 745 case 'line3DChart': 746 $dimensions = '3d'; 747 // no break 748 case 'lineChart': 749 $this->renderPlotLine($i, false, true, $dimensions); 750 751 break; 752 case 'scatterChart': 753 $this->renderPlotScatter($i, false); 754 755 break; 756 case 'bubbleChart': 757 $this->renderPlotScatter($i, true); 758 759 break; 760 default: 761 $this->graph = null; 762 763 return false; 764 } 765 } 766 767 $this->renderLegend(); 768 769 $this->graph->Stroke($outputDestination); 770 771 return true; 772 } 773 774 public function render($outputDestination) 775 { 776 self::$plotColour = 0; 777 778 $groupCount = $this->chart->getPlotArea()->getPlotGroupCount(); 779 780 $dimensions = null; 781 if ($groupCount == 1) { 782 $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex(0)->getPlotType(); 783 } else { 784 $chartTypes = []; 785 for ($i = 0; $i < $groupCount; ++$i) { 786 $chartTypes[] = $this->chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType(); 787 } 788 $chartTypes = array_unique($chartTypes); 789 if (count($chartTypes) == 1) { 790 $chartType = array_pop($chartTypes); 791 } elseif (count($chartTypes) == 0) { 792 echo 'Chart is not yet implemented<br />'; 793 794 return false; 795 } else { 796 return $this->renderCombinationChart($groupCount, $dimensions, $outputDestination); 797 } 798 } 799 800 switch ($chartType) { 801 case 'area3DChart': 802 $dimensions = '3d'; 803 // no break 804 case 'areaChart': 805 $this->renderAreaChart($groupCount, $dimensions); 806 807 break; 808 case 'bar3DChart': 809 $dimensions = '3d'; 810 // no break 811 case 'barChart': 812 $this->renderBarChart($groupCount, $dimensions); 813 814 break; 815 case 'line3DChart': 816 $dimensions = '3d'; 817 // no break 818 case 'lineChart': 819 $this->renderLineChart($groupCount, $dimensions); 820 821 break; 822 case 'pie3DChart': 823 $dimensions = '3d'; 824 // no break 825 case 'pieChart': 826 $this->renderPieChart($groupCount, $dimensions, false, false); 827 828 break; 829 case 'doughnut3DChart': 830 $dimensions = '3d'; 831 // no break 832 case 'doughnutChart': 833 $this->renderPieChart($groupCount, $dimensions, true, true); 834 835 break; 836 case 'scatterChart': 837 $this->renderScatterChart($groupCount); 838 839 break; 840 case 'bubbleChart': 841 $this->renderBubbleChart($groupCount); 842 843 break; 844 case 'radarChart': 845 $this->renderRadarChart($groupCount); 846 847 break; 848 case 'surface3DChart': 849 $dimensions = '3d'; 850 // no break 851 case 'surfaceChart': 852 $this->renderContourChart($groupCount, $dimensions); 853 854 break; 855 case 'stockChart': 856 $this->renderStockChart($groupCount); 857 858 break; 859 default: 860 echo $chartType . ' is not yet implemented<br />'; 861 862 return false; 863 } 864 $this->renderLegend(); 865 866 $this->graph->Stroke($outputDestination); 867 868 return true; 869 } 870 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body