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