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