Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
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 [$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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body