/libs/sparkline/lib/Sparkline_Line.php

https://github.com/quarkness/piwik · PHP · 286 lines · 192 code · 46 blank · 48 comment · 20 complexity · 42786b6bca1d2d8edbe617e18c290ddc MD5 · raw file

  1. <?php
  2. /*
  3. * Sparkline PHP Graphing Library
  4. * Copyright 2004 James Byers <jbyers@gmail.com>
  5. * http://sparkline.org
  6. *
  7. * Dual-licensed under the BSD (LICENSE-BSD.txt) and GPL (LICENSE-GPL.txt)
  8. * licenses.
  9. *
  10. * $Id: Sparkline_Line.php,v 1.10 2008/03/11 19:12:49 jbyers Exp $
  11. *
  12. */
  13. require_once dirname(__FILE__).'/Sparkline.php';
  14. class Sparkline_Line extends Sparkline {
  15. var $dataSeries;
  16. var $dataSeriesStats;
  17. var $dataSeriesConverted;
  18. var $yMin;
  19. var $yMax;
  20. var $featurePoint;
  21. ////////////////////////////////////////////////////////////////////////////
  22. // constructor
  23. //
  24. function Sparkline_Line($catch_errors = true) {
  25. parent::Sparkline($catch_errors);
  26. $this->dataSeries = array();
  27. $this->dataSeriesStats = array();
  28. $this->dataSeriesConverted = array();
  29. $this->featurePoint = array();
  30. } // function Sparkline
  31. ////////////////////////////////////////////////////////////////////////////
  32. // data setting
  33. //
  34. function SetData($x, $y, $series = 1) {
  35. if(!is_numeric($x)) {
  36. $x = trim($x);
  37. }
  38. if(!is_numeric($y)) {
  39. $y = trim($y);
  40. }
  41. $this->Debug("Sparkline_Line :: SetData($x, $y, $series)", DEBUG_SET);
  42. if (!is_numeric($x) ||
  43. !is_numeric($y)) {
  44. $this->Debug("Sparkline_Line :: SetData rejected values($x, $y) in series $series", DEBUG_WARNING);
  45. return false;
  46. } // if
  47. $this->dataSeries[$series][$x] = $y;
  48. if (!isset($this->dataSeriesStats[$series]['yMin']) ||
  49. $y < $this->dataSeriesStats[$series]['yMin']) {
  50. $this->dataSeriesStats[$series]['yMin'] = $y;
  51. }
  52. if (!isset($this->dataSeriesStats[$series]['xMin']) ||
  53. $x < $this->dataSeriesStats[$series]['xMin']) {
  54. $this->dataSeriesStats[$series]['xMin'] = $x;
  55. }
  56. if (!isset($this->dataSeriesStats[$series]['yMax']) ||
  57. $y > $this->dataSeriesStats[$series]['yMax']) {
  58. $this->dataSeriesStats[$series]['yMax'] = $y;
  59. }
  60. if (!isset($this->dataSeriesStats[$series]['xMax']) ||
  61. $x > $this->dataSeriesStats[$series]['xMax']) {
  62. $this->dataSeriesStats[$series]['xMax'] = $x;
  63. }
  64. } // function SetData
  65. function SetYMin($value) {
  66. $this->Debug("Sparkline_Line :: SetYMin($value)", DEBUG_SET);
  67. $this->yMin = $value;
  68. } // function SetYMin
  69. function SetYMax($value) {
  70. $this->Debug("Sparkline_Line :: SetYMax($value)", DEBUG_SET);
  71. $this->yMax = $value;
  72. } // function SetYMin
  73. function ConvertDataSeries($series, $xBound, $yBound) {
  74. $this->Debug("Sparkline_Line :: ConvertDataSeries($series, $xBound, $yBound)", DEBUG_CALLS);
  75. if (!isset($this->yMin)) {
  76. $this->yMin = $this->dataSeriesStats[$series]['yMin'];
  77. }
  78. if (!isset($this->xMin)) {
  79. $this->xMin = $this->dataSeriesStats[$series]['XMin'];
  80. }
  81. if (!isset($this->yMax)) {
  82. $this->yMax = $this->dataSeriesStats[$series]['yMax'];
  83. }
  84. if (!isset($this->xMax)) {
  85. $this->xMax = $this->dataSeriesStats[$series]['xMax'];
  86. }
  87. $this->yRange = $this->yMax + ($this->yMin * -1);
  88. for ($i = 0; $i < sizeof($this->dataSeries[$series]); $i++) {
  89. $y = round(($this->dataSeries[$series][$i] + ($this->yMin * -1)) * (($yBound-1) / $this->yRange));
  90. $x = round($i * $xBound / (sizeof($this->dataSeries[$series])));
  91. $this->dataSeriesConverted[$series][] = array($x, $y);
  92. $this->Debug("Sparkline :: ConvertDataSeries series $series value $i ($x, $y)", DEBUG_SET);
  93. }
  94. } // function ConvertDataSeries
  95. ////////////////////////////////////////////////////////////////////////////
  96. // features
  97. //
  98. function SetFeaturePoint($x, $y, $color, $diameter, $text = '', $position = TEXT_TOP, $font = FONT_1) {
  99. $this->Debug("Sparkline_Line :: SetFeaturePoint($x, $y, '$color', $diameter, '$text')", DEBUG_CALLS);
  100. $this->featurePoint[] = array('ptX' => $x,
  101. 'ptY' => $y,
  102. 'color' => $color,
  103. 'diameter' => $diameter,
  104. 'text' => $text,
  105. 'textpos' => $position,
  106. 'font' => $font);
  107. } // function SetFeaturePoint
  108. ////////////////////////////////////////////////////////////////////////////
  109. // low quality rendering
  110. //
  111. function Render($x, $y) {
  112. $this->Debug("Sparkline_Line :: Render($x, $y)", DEBUG_CALLS);
  113. if (!parent::Init($x, $y)) {
  114. return false;
  115. }
  116. // convert based on graphAreaPx bounds
  117. //
  118. $this->ConvertDataSeries(1, $this->GetGraphWidth(), $this->GetGraphHeight());
  119. // stats debugging
  120. //
  121. $this->Debug('Sparkline_Line :: Draw' .
  122. ' series: 1 min: ' . $this->dataSeriesStats[1]['yMin'] .
  123. ' max: ' . $this->dataSeriesStats[1]['yMax'] .
  124. ' offset: ' . ($this->dataSeriesStats[1]['yMin'] * -1) .
  125. ' height: ' . $this->GetGraphHeight() + 1 .
  126. ' yfactor: ' . ($this->GetGraphHeight() / ($this->dataSeriesStats[1]['yMax'] + ($this->dataSeriesStats[1]['yMin'] * -1))));
  127. $this->Debug('Sparkline_Line :: Draw' .
  128. ' drawing area:' .
  129. ' (' . $this->graphAreaPx[0][0] . ',' . $this->graphAreaPx[0][1] . '), ' .
  130. ' (' . $this->graphAreaPx[1][0] . ',' . $this->graphAreaPx[1][1] . ')');
  131. $this->DrawBackground();
  132. // draw graph
  133. //
  134. for ($i = 0; $i < sizeof($this->dataSeriesConverted[1]) - 1; $i++) {
  135. $this->DrawLine($this->dataSeriesConverted[1][$i][0] + $this->graphAreaPx[0][0],
  136. $this->dataSeriesConverted[1][$i][1] + $this->graphAreaPx[0][1],
  137. $this->dataSeriesConverted[1][$i+1][0] + $this->graphAreaPx[0][0],
  138. $this->dataSeriesConverted[1][$i+1][1] + $this->graphAreaPx[0][1],
  139. 'black');
  140. }
  141. // draw features
  142. //
  143. while (list(, $v) = each($this->featurePoint)) {
  144. $pxY = round(($v['ptY'] + ($this->yMin * -1)) * ($this->GetGraphHeight() / $this->yRange));
  145. $pxX = round($v['ptX'] * $this->GetGraphWidth() / sizeof($this->dataSeries[1]));
  146. $this->DrawCircleFilled($pxX + $this->graphAreaPx[0][0],
  147. $pxY + $this->graphAreaPx[0][1],
  148. $v['diameter'],
  149. $v['color'],
  150. $this->imageHandle);
  151. $this->DrawTextRelative($v['text'],
  152. $pxX + $this->graphAreaPx[0][0],
  153. $pxY + $this->graphAreaPx[0][1],
  154. $v['color'],
  155. $v['textpos'],
  156. round($v['diameter'] / 2),
  157. $v['font'],
  158. $this->imageHandle);
  159. }
  160. } // function Render
  161. ////////////////////////////////////////////////////////////////////////////
  162. // high quality rendering
  163. //
  164. function RenderResampled($x, $y) {
  165. $this->Debug("Sparkline_Line :: RenderResampled($x, $y)", DEBUG_CALLS);
  166. if (!parent::Init($x, $y)) {
  167. return false;
  168. }
  169. // draw background on standard image in case of resample blit miss
  170. //
  171. $this->DrawBackground($this->imageHandle);
  172. // convert based on virtual canvas: x based on size of dataset, y scaled proportionately
  173. // if size of data set is small, default to 4X target canvas size
  174. //
  175. $xVC = max(sizeof($this->dataSeries[1]), 4 * $x);
  176. $yVC = floor($xVC * ($this->GetGraphHeight() / $this->GetGraphWidth()));
  177. $this->ConvertDataSeries(1, $xVC, $yVC);
  178. // stats debugging
  179. //
  180. $this->Debug('Sparkline_Line :: DrawResampled' .
  181. ' series: 1 min: ' . $this->dataSeriesStats[1]['yMin'] .
  182. ' max: ' . $this->dataSeriesStats[1]['yMax'] .
  183. ' offset: ' . ($this->dataSeriesStats[1]['yMin'] * -1) .
  184. ' height: ' . $this->GetGraphHeight() .
  185. ' yfactor: ' . ($this->GetGraphHeight() / ($this->dataSeriesStats[1]['yMax'] + ($this->dataSeriesStats[1]['yMin'] * -1))), DEBUG_STATS);
  186. $this->Debug('Sparkline_Line :: DrawResampled' .
  187. ' drawing area:' .
  188. ' (' . $this->graphAreaPx[0][0] . ',' . $this->graphAreaPx[0][1] . '), ' .
  189. ' (' . $this->graphAreaPx[1][0] . ',' . $this->graphAreaPx[1][1] . ')');
  190. // create virtual image
  191. // allocate colors
  192. // draw background, graph
  193. // resample and blit onto original graph
  194. //
  195. $imageVCHandle = $this->CreateImageHandle($xVC, $yVC);
  196. while (list($k, $v) = each($this->colorList)) {
  197. $this->SetColorHandle($k, $this->DrawColorAllocate($k, $imageVCHandle));
  198. }
  199. reset($this->colorList);
  200. $this->DrawBackground($imageVCHandle);
  201. for ($i = 0; $i < sizeof($this->dataSeriesConverted[1]) - 1; $i++) {
  202. $this->DrawLine($this->dataSeriesConverted[1][$i][0],
  203. $this->dataSeriesConverted[1][$i][1],
  204. $this->dataSeriesConverted[1][$i+1][0],
  205. $this->dataSeriesConverted[1][$i+1][1],
  206. 'black',
  207. $this->GetLineSize(),
  208. $imageVCHandle);
  209. }
  210. $this->DrawImageCopyResampled($this->imageHandle,
  211. $imageVCHandle,
  212. $this->graphAreaPx[0][0], // dest x
  213. $this->GetImageHeight() - $this->graphAreaPx[1][1], // dest y
  214. 0, 0, // src x, y
  215. $this->GetGraphWidth(), // dest width
  216. $this->GetGraphHeight(), // dest height
  217. $xVC, // src width
  218. $yVC); // src height
  219. // draw features
  220. //
  221. while (list(, $v) = each($this->featurePoint)) {
  222. $pxY = round(($v['ptY'] + ($this->yMin * -1)) * ($this->GetGraphHeight() / $this->yRange));
  223. $pxX = round($v['ptX'] * $this->GetGraphWidth() / sizeof($this->dataSeries[1]));
  224. $this->DrawCircleFilled($pxX + $this->graphAreaPx[0][0],
  225. $pxY + $this->graphAreaPx[0][1],
  226. $v['diameter'],
  227. $v['color'],
  228. $this->imageHandle);
  229. $this->DrawTextRelative($v['text'],
  230. $pxX + $this->graphAreaPx[0][0],
  231. $pxY + $this->graphAreaPx[0][1],
  232. $v['color'],
  233. $v['textpos'],
  234. round($v['diameter'] / 2),
  235. $v['font'],
  236. $this->imageHandle);
  237. }
  238. } // function RenderResampled
  239. } // class Sparkline_Line
  240. ?>