jpgraph.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
  1. <?php
  2. require_once(PHPExcel_Settings::getChartRendererPath().'/jpgraph.php');
  3. /**
  4. * PHPExcel_Chart_Renderer_jpgraph
  5. *
  6. * Copyright (c) 2006 - 2015 PHPExcel
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. *
  22. * @category PHPExcel
  23. * @package PHPExcel_Chart_Renderer
  24. * @copyright Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
  25. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  26. * @version ##VERSION##, ##DATE##
  27. */
  28. class PHPExcel_Chart_Renderer_jpgraph
  29. {
  30. private static $width = 640;
  31. private static $height = 480;
  32. private static $colourSet = array(
  33. 'mediumpurple1', 'palegreen3', 'gold1', 'cadetblue1',
  34. 'darkmagenta', 'coral', 'dodgerblue3', 'eggplant',
  35. 'mediumblue', 'magenta', 'sandybrown', 'cyan',
  36. 'firebrick1', 'forestgreen', 'deeppink4', 'darkolivegreen',
  37. 'goldenrod2'
  38. );
  39. private static $markSet = array(
  40. 'diamond' => MARK_DIAMOND,
  41. 'square' => MARK_SQUARE,
  42. 'triangle' => MARK_UTRIANGLE,
  43. 'x' => MARK_X,
  44. 'star' => MARK_STAR,
  45. 'dot' => MARK_FILLEDCIRCLE,
  46. 'dash' => MARK_DTRIANGLE,
  47. 'circle' => MARK_CIRCLE,
  48. 'plus' => MARK_CROSS
  49. );
  50. private $chart;
  51. private $graph;
  52. private static $plotColour = 0;
  53. private static $plotMark = 0;
  54. private function formatPointMarker($seriesPlot, $markerID)
  55. {
  56. $plotMarkKeys = array_keys(self::$markSet);
  57. if (is_null($markerID)) {
  58. // Use default plot marker (next marker in the series)
  59. self::$plotMark %= count(self::$markSet);
  60. $seriesPlot->mark->SetType(self::$markSet[$plotMarkKeys[self::$plotMark++]]);
  61. } elseif ($markerID !== 'none') {
  62. // Use specified plot marker (if it exists)
  63. if (isset(self::$markSet[$markerID])) {
  64. $seriesPlot->mark->SetType(self::$markSet[$markerID]);
  65. } else {
  66. // If the specified plot marker doesn't exist, use default plot marker (next marker in the series)
  67. self::$plotMark %= count(self::$markSet);
  68. $seriesPlot->mark->SetType(self::$markSet[$plotMarkKeys[self::$plotMark++]]);
  69. }
  70. } else {
  71. // Hide plot marker
  72. $seriesPlot->mark->Hide();
  73. }
  74. $seriesPlot->mark->SetColor(self::$colourSet[self::$plotColour]);
  75. $seriesPlot->mark->SetFillColor(self::$colourSet[self::$plotColour]);
  76. $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
  77. return $seriesPlot;
  78. }
  79. private function formatDataSetLabels($groupID, $datasetLabels, $labelCount, $rotation = '')
  80. {
  81. $datasetLabelFormatCode = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getFormatCode();
  82. if (!is_null($datasetLabelFormatCode)) {
  83. // Retrieve any label formatting code
  84. $datasetLabelFormatCode = stripslashes($datasetLabelFormatCode);
  85. }
  86. $testCurrentIndex = 0;
  87. foreach ($datasetLabels as $i => $datasetLabel) {
  88. if (is_array($datasetLabel)) {
  89. if ($rotation == 'bar') {
  90. $datasetLabels[$i] = implode(" ", $datasetLabel);
  91. } else {
  92. $datasetLabel = array_reverse($datasetLabel);
  93. $datasetLabels[$i] = implode("\n", $datasetLabel);
  94. }
  95. } else {
  96. // Format labels according to any formatting code
  97. if (!is_null($datasetLabelFormatCode)) {
  98. $datasetLabels[$i] = PHPExcel_Style_NumberFormat::toFormattedString($datasetLabel, $datasetLabelFormatCode);
  99. }
  100. }
  101. ++$testCurrentIndex;
  102. }
  103. return $datasetLabels;
  104. }
  105. private function percentageSumCalculation($groupID, $seriesCount)
  106. {
  107. // Adjust our values to a percentage value across all series in the group
  108. for ($i = 0; $i < $seriesCount; ++$i) {
  109. if ($i == 0) {
  110. $sumValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
  111. } else {
  112. $nextValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
  113. foreach ($nextValues as $k => $value) {
  114. if (isset($sumValues[$k])) {
  115. $sumValues[$k] += $value;
  116. } else {
  117. $sumValues[$k] = $value;
  118. }
  119. }
  120. }
  121. }
  122. return $sumValues;
  123. }
  124. private function percentageAdjustValues($dataValues, $sumValues)
  125. {
  126. foreach ($dataValues as $k => $dataValue) {
  127. $dataValues[$k] = $dataValue / $sumValues[$k] * 100;
  128. }
  129. return $dataValues;
  130. }
  131. private function getCaption($captionElement)
  132. {
  133. // Read any caption
  134. $caption = (!is_null($captionElement)) ? $captionElement->getCaption() : null;
  135. // Test if we have a title caption to display
  136. if (!is_null($caption)) {
  137. // If we do, it could be a plain string or an array
  138. if (is_array($caption)) {
  139. // Implode an array to a plain string
  140. $caption = implode('', $caption);
  141. }
  142. }
  143. return $caption;
  144. }
  145. private function renderTitle()
  146. {
  147. $title = $this->getCaption($this->chart->getTitle());
  148. if (!is_null($title)) {
  149. $this->graph->title->Set($title);
  150. }
  151. }
  152. private function renderLegend()
  153. {
  154. $legend = $this->chart->getLegend();
  155. if (!is_null($legend)) {
  156. $legendPosition = $legend->getPosition();
  157. $legendOverlay = $legend->getOverlay();
  158. switch ($legendPosition) {
  159. case 'r':
  160. $this->graph->legend->SetPos(0.01, 0.5, 'right', 'center'); // right
  161. $this->graph->legend->SetColumns(1);
  162. break;
  163. case 'l':
  164. $this->graph->legend->SetPos(0.01, 0.5, 'left', 'center'); // left
  165. $this->graph->legend->SetColumns(1);
  166. break;
  167. case 't':
  168. $this->graph->legend->SetPos(0.5, 0.01, 'center', 'top'); // top
  169. break;
  170. case 'b':
  171. $this->graph->legend->SetPos(0.5, 0.99, 'center', 'bottom'); // bottom
  172. break;
  173. default:
  174. $this->graph->legend->SetPos(0.01, 0.01, 'right', 'top'); // top-right
  175. $this->graph->legend->SetColumns(1);
  176. break;
  177. }
  178. } else {
  179. $this->graph->legend->Hide();
  180. }
  181. }
  182. private function renderCartesianPlotArea($type = 'textlin')
  183. {
  184. $this->graph = new Graph(self::$width, self::$height);
  185. $this->graph->SetScale($type);
  186. $this->renderTitle();
  187. // Rotate for bar rather than column chart
  188. $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex(0)->getPlotDirection();
  189. $reverse = ($rotation == 'bar') ? true : false;
  190. $xAxisLabel = $this->chart->getXAxisLabel();
  191. if (!is_null($xAxisLabel)) {
  192. $title = $this->getCaption($xAxisLabel);
  193. if (!is_null($title)) {
  194. $this->graph->xaxis->SetTitle($title, 'center');
  195. $this->graph->xaxis->title->SetMargin(35);
  196. if ($reverse) {
  197. $this->graph->xaxis->title->SetAngle(90);
  198. $this->graph->xaxis->title->SetMargin(90);
  199. }
  200. }
  201. }
  202. $yAxisLabel = $this->chart->getYAxisLabel();
  203. if (!is_null($yAxisLabel)) {
  204. $title = $this->getCaption($yAxisLabel);
  205. if (!is_null($title)) {
  206. $this->graph->yaxis->SetTitle($title, 'center');
  207. if ($reverse) {
  208. $this->graph->yaxis->title->SetAngle(0);
  209. $this->graph->yaxis->title->SetMargin(-55);
  210. }
  211. }
  212. }
  213. }
  214. private function renderPiePlotArea($doughnut = false)
  215. {
  216. $this->graph = new PieGraph(self::$width, self::$height);
  217. $this->renderTitle();
  218. }
  219. private function renderRadarPlotArea()
  220. {
  221. $this->graph = new RadarGraph(self::$width, self::$height);
  222. $this->graph->SetScale('lin');
  223. $this->renderTitle();
  224. }
  225. private function renderPlotLine($groupID, $filled = false, $combination = false, $dimensions = '2d')
  226. {
  227. $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
  228. $labelCount = count($this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount());
  229. if ($labelCount > 0) {
  230. $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
  231. $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount);
  232. $this->graph->xaxis->SetTickLabels($datasetLabels);
  233. }
  234. $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
  235. $seriesPlots = array();
  236. if ($grouping == 'percentStacked') {
  237. $sumValues = $this->percentageSumCalculation($groupID, $seriesCount);
  238. }
  239. // Loop through each data series in turn
  240. for ($i = 0; $i < $seriesCount; ++$i) {
  241. $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
  242. $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker();
  243. if ($grouping == 'percentStacked') {
  244. $dataValues = $this->percentageAdjustValues($dataValues, $sumValues);
  245. }
  246. // Fill in any missing values in the $dataValues array
  247. $testCurrentIndex = 0;
  248. foreach ($dataValues as $k => $dataValue) {
  249. while ($k != $testCurrentIndex) {
  250. $dataValues[$testCurrentIndex] = null;
  251. ++$testCurrentIndex;
  252. }
  253. ++$testCurrentIndex;
  254. }
  255. $seriesPlot = new LinePlot($dataValues);
  256. if ($combination) {
  257. $seriesPlot->SetBarCenter();
  258. }
  259. if ($filled) {
  260. $seriesPlot->SetFilled(true);
  261. $seriesPlot->SetColor('black');
  262. $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]);
  263. } else {
  264. // Set the appropriate plot marker
  265. $this->formatPointMarker($seriesPlot, $marker);
  266. }
  267. $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
  268. $seriesPlot->SetLegend($dataLabel);
  269. $seriesPlots[] = $seriesPlot;
  270. }
  271. if ($grouping == 'standard') {
  272. $groupPlot = $seriesPlots;
  273. } else {
  274. $groupPlot = new AccLinePlot($seriesPlots);
  275. }
  276. $this->graph->Add($groupPlot);
  277. }
  278. private function renderPlotBar($groupID, $dimensions = '2d')
  279. {
  280. $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotDirection();
  281. // Rotate for bar rather than column chart
  282. if (($groupID == 0) && ($rotation == 'bar')) {
  283. $this->graph->Set90AndMargin();
  284. }
  285. $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
  286. $labelCount = count($this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount());
  287. if ($labelCount > 0) {
  288. $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
  289. $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount, $rotation);
  290. // Rotate for bar rather than column chart
  291. if ($rotation == 'bar') {
  292. $datasetLabels = array_reverse($datasetLabels);
  293. $this->graph->yaxis->SetPos('max');
  294. $this->graph->yaxis->SetLabelAlign('center', 'top');
  295. $this->graph->yaxis->SetLabelSide(SIDE_RIGHT);
  296. }
  297. $this->graph->xaxis->SetTickLabels($datasetLabels);
  298. }
  299. $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
  300. $seriesPlots = array();
  301. if ($grouping == 'percentStacked') {
  302. $sumValues = $this->percentageSumCalculation($groupID, $seriesCount);
  303. }
  304. // Loop through each data series in turn
  305. for ($j = 0; $j < $seriesCount; ++$j) {
  306. $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($j)->getDataValues();
  307. if ($grouping == 'percentStacked') {
  308. $dataValues = $this->percentageAdjustValues($dataValues, $sumValues);
  309. }
  310. // Fill in any missing values in the $dataValues array
  311. $testCurrentIndex = 0;
  312. foreach ($dataValues as $k => $dataValue) {
  313. while ($k != $testCurrentIndex) {
  314. $dataValues[$testCurrentIndex] = null;
  315. ++$testCurrentIndex;
  316. }
  317. ++$testCurrentIndex;
  318. }
  319. // Reverse the $dataValues order for bar rather than column chart
  320. if ($rotation == 'bar') {
  321. $dataValues = array_reverse($dataValues);
  322. }
  323. $seriesPlot = new BarPlot($dataValues);
  324. $seriesPlot->SetColor('black');
  325. $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]);
  326. if ($dimensions == '3d') {
  327. $seriesPlot->SetShadow();
  328. }
  329. if (!$this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)) {
  330. $dataLabel = '';
  331. } else {
  332. $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)->getDataValue();
  333. }
  334. $seriesPlot->SetLegend($dataLabel);
  335. $seriesPlots[] = $seriesPlot;
  336. }
  337. // Reverse the plot order for bar rather than column chart
  338. if (($rotation == 'bar') && (!($grouping == 'percentStacked'))) {
  339. $seriesPlots = array_reverse($seriesPlots);
  340. }
  341. if ($grouping == 'clustered') {
  342. $groupPlot = new GroupBarPlot($seriesPlots);
  343. } elseif ($grouping == 'standard') {
  344. $groupPlot = new GroupBarPlot($seriesPlots);
  345. } else {
  346. $groupPlot = new AccBarPlot($seriesPlots);
  347. if ($dimensions == '3d') {
  348. $groupPlot->SetShadow();
  349. }
  350. }
  351. $this->graph->Add($groupPlot);
  352. }
  353. private function renderPlotScatter($groupID, $bubble)
  354. {
  355. $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
  356. $scatterStyle = $bubbleSize = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
  357. $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
  358. $seriesPlots = array();
  359. // Loop through each data series in turn
  360. for ($i = 0; $i < $seriesCount; ++$i) {
  361. $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues();
  362. $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
  363. foreach ($dataValuesY as $k => $dataValueY) {
  364. $dataValuesY[$k] = $k;
  365. }
  366. $seriesPlot = new ScatterPlot($dataValuesX, $dataValuesY);
  367. if ($scatterStyle == 'lineMarker') {
  368. $seriesPlot->SetLinkPoints();
  369. $seriesPlot->link->SetColor(self::$colourSet[self::$plotColour]);
  370. } elseif ($scatterStyle == 'smoothMarker') {
  371. $spline = new Spline($dataValuesY, $dataValuesX);
  372. list($splineDataY, $splineDataX) = $spline->Get(count($dataValuesX) * self::$width / 20);
  373. $lplot = new LinePlot($splineDataX, $splineDataY);
  374. $lplot->SetColor(self::$colourSet[self::$plotColour]);
  375. $this->graph->Add($lplot);
  376. }
  377. if ($bubble) {
  378. $this->formatPointMarker($seriesPlot, 'dot');
  379. $seriesPlot->mark->SetColor('black');
  380. $seriesPlot->mark->SetSize($bubbleSize);
  381. } else {
  382. $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker();
  383. $this->formatPointMarker($seriesPlot, $marker);
  384. }
  385. $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
  386. $seriesPlot->SetLegend($dataLabel);
  387. $this->graph->Add($seriesPlot);
  388. }
  389. }
  390. private function renderPlotRadar($groupID)
  391. {
  392. $radarStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
  393. $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
  394. $seriesPlots = array();
  395. // Loop through each data series in turn
  396. for ($i = 0; $i < $seriesCount; ++$i) {
  397. $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues();
  398. $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
  399. $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker();
  400. $dataValues = array();
  401. foreach ($dataValuesY as $k => $dataValueY) {
  402. $dataValues[$k] = implode(' ', array_reverse($dataValueY));
  403. }
  404. $tmp = array_shift($dataValues);
  405. $dataValues[] = $tmp;
  406. $tmp = array_shift($dataValuesX);
  407. $dataValuesX[] = $tmp;
  408. $this->graph->SetTitles(array_reverse($dataValues));
  409. $seriesPlot = new RadarPlot(array_reverse($dataValuesX));
  410. $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
  411. $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
  412. if ($radarStyle == 'filled') {
  413. $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour]);
  414. }
  415. $this->formatPointMarker($seriesPlot, $marker);
  416. $seriesPlot->SetLegend($dataLabel);
  417. $this->graph->Add($seriesPlot);
  418. }
  419. }
  420. private function renderPlotContour($groupID)
  421. {
  422. $contourStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
  423. $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
  424. $seriesPlots = array();
  425. $dataValues = array();
  426. // Loop through each data series in turn
  427. for ($i = 0; $i < $seriesCount; ++$i) {
  428. $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues();
  429. $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
  430. $dataValues[$i] = $dataValuesX;
  431. }
  432. $seriesPlot = new ContourPlot($dataValues);
  433. $this->graph->Add($seriesPlot);
  434. }
  435. private function renderPlotStock($groupID)
  436. {
  437. $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
  438. $plotOrder = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotOrder();
  439. $dataValues = array();
  440. // Loop through each data series in turn and build the plot arrays
  441. foreach ($plotOrder as $i => $v) {
  442. $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($v)->getDataValues();
  443. foreach ($dataValuesX as $j => $dataValueX) {
  444. $dataValues[$plotOrder[$i]][$j] = $dataValueX;
  445. }
  446. }
  447. if (empty($dataValues)) {
  448. return;
  449. }
  450. $dataValuesPlot = array();
  451. // Flatten the plot arrays to a single dimensional array to work with jpgraph
  452. for ($j = 0; $j < count($dataValues[0]); ++$j) {
  453. for ($i = 0; $i < $seriesCount; ++$i) {
  454. $dataValuesPlot[] = $dataValues[$i][$j];
  455. }
  456. }
  457. // Set the x-axis labels
  458. $labelCount = count($this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount());
  459. if ($labelCount > 0) {
  460. $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
  461. $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount);
  462. $this->graph->xaxis->SetTickLabels($datasetLabels);
  463. }
  464. $seriesPlot = new StockPlot($dataValuesPlot);
  465. $seriesPlot->SetWidth(20);
  466. $this->graph->Add($seriesPlot);
  467. }
  468. private function renderAreaChart($groupCount, $dimensions = '2d')
  469. {
  470. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_line.php');
  471. $this->renderCartesianPlotArea();
  472. for ($i = 0; $i < $groupCount; ++$i) {
  473. $this->renderPlotLine($i, true, false, $dimensions);
  474. }
  475. }
  476. private function renderLineChart($groupCount, $dimensions = '2d')
  477. {
  478. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_line.php');
  479. $this->renderCartesianPlotArea();
  480. for ($i = 0; $i < $groupCount; ++$i) {
  481. $this->renderPlotLine($i, false, false, $dimensions);
  482. }
  483. }
  484. private function renderBarChart($groupCount, $dimensions = '2d')
  485. {
  486. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_bar.php');
  487. $this->renderCartesianPlotArea();
  488. for ($i = 0; $i < $groupCount; ++$i) {
  489. $this->renderPlotBar($i, $dimensions);
  490. }
  491. }
  492. private function renderScatterChart($groupCount)
  493. {
  494. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_scatter.php');
  495. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_regstat.php');
  496. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_line.php');
  497. $this->renderCartesianPlotArea('linlin');
  498. for ($i = 0; $i < $groupCount; ++$i) {
  499. $this->renderPlotScatter($i, false);
  500. }
  501. }
  502. private function renderBubbleChart($groupCount)
  503. {
  504. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_scatter.php');
  505. $this->renderCartesianPlotArea('linlin');
  506. for ($i = 0; $i < $groupCount; ++$i) {
  507. $this->renderPlotScatter($i, true);
  508. }
  509. }
  510. private function renderPieChart($groupCount, $dimensions = '2d', $doughnut = false, $multiplePlots = false)
  511. {
  512. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_pie.php');
  513. if ($dimensions == '3d') {
  514. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_pie3d.php');
  515. }
  516. $this->renderPiePlotArea($doughnut);
  517. $iLimit = ($multiplePlots) ? $groupCount : 1;
  518. for ($groupID = 0; $groupID < $iLimit; ++$groupID) {
  519. $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
  520. $exploded = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
  521. if ($groupID == 0) {
  522. $labelCount = count($this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount());
  523. if ($labelCount > 0) {
  524. $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
  525. $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount);
  526. }
  527. }
  528. $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
  529. $seriesPlots = array();
  530. // For pie charts, we only display the first series: doughnut charts generally display all series
  531. $jLimit = ($multiplePlots) ? $seriesCount : 1;
  532. // Loop through each data series in turn
  533. for ($j = 0; $j < $jLimit; ++$j) {
  534. $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($j)->getDataValues();
  535. // Fill in any missing values in the $dataValues array
  536. $testCurrentIndex = 0;
  537. foreach ($dataValues as $k => $dataValue) {
  538. while ($k != $testCurrentIndex) {
  539. $dataValues[$testCurrentIndex] = null;
  540. ++$testCurrentIndex;
  541. }
  542. ++$testCurrentIndex;
  543. }
  544. if ($dimensions == '3d') {
  545. $seriesPlot = new PiePlot3D($dataValues);
  546. } else {
  547. if ($doughnut) {
  548. $seriesPlot = new PiePlotC($dataValues);
  549. } else {
  550. $seriesPlot = new PiePlot($dataValues);
  551. }
  552. }
  553. if ($multiplePlots) {
  554. $seriesPlot->SetSize(($jLimit-$j) / ($jLimit * 4));
  555. }
  556. if ($doughnut) {
  557. $seriesPlot->SetMidColor('white');
  558. }
  559. $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
  560. if (count($datasetLabels) > 0) {
  561. $seriesPlot->SetLabels(array_fill(0, count($datasetLabels), ''));
  562. }
  563. if ($dimensions != '3d') {
  564. $seriesPlot->SetGuideLines(false);
  565. }
  566. if ($j == 0) {
  567. if ($exploded) {
  568. $seriesPlot->ExplodeAll();
  569. }
  570. $seriesPlot->SetLegends($datasetLabels);
  571. }
  572. $this->graph->Add($seriesPlot);
  573. }
  574. }
  575. }
  576. private function renderRadarChart($groupCount)
  577. {
  578. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_radar.php');
  579. $this->renderRadarPlotArea();
  580. for ($groupID = 0; $groupID < $groupCount; ++$groupID) {
  581. $this->renderPlotRadar($groupID);
  582. }
  583. }
  584. private function renderStockChart($groupCount)
  585. {
  586. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_stock.php');
  587. $this->renderCartesianPlotArea('intint');
  588. for ($groupID = 0; $groupID < $groupCount; ++$groupID) {
  589. $this->renderPlotStock($groupID);
  590. }
  591. }
  592. private function renderContourChart($groupCount, $dimensions)
  593. {
  594. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_contour.php');
  595. $this->renderCartesianPlotArea('intint');
  596. for ($i = 0; $i < $groupCount; ++$i) {
  597. $this->renderPlotContour($i);
  598. }
  599. }
  600. private function renderCombinationChart($groupCount, $dimensions, $outputDestination)
  601. {
  602. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_line.php');
  603. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_bar.php');
  604. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_scatter.php');
  605. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_regstat.php');
  606. require_once(PHPExcel_Settings::getChartRendererPath().'jpgraph_line.php');
  607. $this->renderCartesianPlotArea();
  608. for ($i = 0; $i < $groupCount; ++$i) {
  609. $dimensions = null;
  610. $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType();
  611. switch ($chartType) {
  612. case 'area3DChart':
  613. $dimensions = '3d';
  614. // no break
  615. case 'areaChart':
  616. $this->renderPlotLine($i, true, true, $dimensions);
  617. break;
  618. case 'bar3DChart':
  619. $dimensions = '3d';
  620. // no break
  621. case 'barChart':
  622. $this->renderPlotBar($i, $dimensions);
  623. break;
  624. case 'line3DChart':
  625. $dimensions = '3d';
  626. // no break
  627. case 'lineChart':
  628. $this->renderPlotLine($i, false, true, $dimensions);
  629. break;
  630. case 'scatterChart':
  631. $this->renderPlotScatter($i, false);
  632. break;
  633. case 'bubbleChart':
  634. $this->renderPlotScatter($i, true);
  635. break;
  636. default:
  637. $this->graph = null;
  638. return false;
  639. }
  640. }
  641. $this->renderLegend();
  642. $this->graph->Stroke($outputDestination);
  643. return true;
  644. }
  645. public function render($outputDestination)
  646. {
  647. self::$plotColour = 0;
  648. $groupCount = $this->chart->getPlotArea()->getPlotGroupCount();
  649. $dimensions = null;
  650. if ($groupCount == 1) {
  651. $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex(0)->getPlotType();
  652. } else {
  653. $chartTypes = array();
  654. for ($i = 0; $i < $groupCount; ++$i) {
  655. $chartTypes[] = $this->chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType();
  656. }
  657. $chartTypes = array_unique($chartTypes);
  658. if (count($chartTypes) == 1) {
  659. $chartType = array_pop($chartTypes);
  660. } elseif (count($chartTypes) == 0) {
  661. echo 'Chart is not yet implemented<br />';
  662. return false;
  663. } else {
  664. return $this->renderCombinationChart($groupCount, $dimensions, $outputDestination);
  665. }
  666. }
  667. switch ($chartType) {
  668. case 'area3DChart':
  669. $dimensions = '3d';
  670. // no break
  671. case 'areaChart':
  672. $this->renderAreaChart($groupCount, $dimensions);
  673. break;
  674. case 'bar3DChart':
  675. $dimensions = '3d';
  676. // no break
  677. case 'barChart':
  678. $this->renderBarChart($groupCount, $dimensions);
  679. break;
  680. case 'line3DChart':
  681. $dimensions = '3d';
  682. // no break
  683. case 'lineChart':
  684. $this->renderLineChart($groupCount, $dimensions);
  685. break;
  686. case 'pie3DChart':
  687. $dimensions = '3d';
  688. // no break
  689. case 'pieChart':
  690. $this->renderPieChart($groupCount, $dimensions, false, false);
  691. break;
  692. case 'doughnut3DChart':
  693. $dimensions = '3d';
  694. // no break
  695. case 'doughnutChart':
  696. $this->renderPieChart($groupCount, $dimensions, true, true);
  697. break;
  698. case 'scatterChart':
  699. $this->renderScatterChart($groupCount);
  700. break;
  701. case 'bubbleChart':
  702. $this->renderBubbleChart($groupCount);
  703. break;
  704. case 'radarChart':
  705. $this->renderRadarChart($groupCount);
  706. break;
  707. case 'surface3DChart':
  708. $dimensions = '3d';
  709. // no break
  710. case 'surfaceChart':
  711. $this->renderContourChart($groupCount, $dimensions);
  712. break;
  713. case 'stockChart':
  714. $this->renderStockChart($groupCount, $dimensions);
  715. break;
  716. default:
  717. echo $chartType.' is not yet implemented<br />';
  718. return false;
  719. }
  720. $this->renderLegend();
  721. $this->graph->Stroke($outputDestination);
  722. return true;
  723. }
  724. /**
  725. * Create a new PHPExcel_Chart_Renderer_jpgraph
  726. */
  727. public function __construct(PHPExcel_Chart $chart)
  728. {
  729. $this->graph = null;
  730. $this->chart = $chart;
  731. }
  732. }