jpgraph.php 34 KB

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