PageRenderTime 59ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/tags/rel-1_4_5rc1-20050616/locales/support/smstats/includes/jpgraph.php

#
PHP | 6945 lines | 5184 code | 746 blank | 1015 comment | 734 complexity | bf336ee178fb802dccf704ee72537186 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. //=======================================================================
  3. // File: JPGRAPH.PHP
  4. // Description: PHP4 Graph Plotting library. Base module.
  5. // Created: 2001-01-08
  6. // Author: Johan Persson (johanp@aditus.nu)
  7. // Ver: $Id: jpgraph.php 8819 2005-02-08 11:35:07Z tokul $
  8. //
  9. // License: This code is released under QPL 1.0
  10. // Copyright (C) 2001,2002 Johan Persson
  11. //========================================================================
  12. //------------------------------------------------------------------------
  13. // Directories. Must be updated to reflect your installation
  14. //------------------------------------------------------------------------
  15. // The full absolute name of the directory to be used to store the
  16. // cached image files. This directory will not be used if the USE_CACHE
  17. // define (furter down) is false. If you enable the cache please note that
  18. // this directory MUST be readable and writable for the process running PHP.
  19. // Must end with '/'
  20. DEFINE("CACHE_DIR","/tmp/jpgraph_cache/");
  21. // Directory for jpGraph TTF fonts. Must end with '/'
  22. // Note: The fonts must follow the naming conventions as
  23. // used by the supplied TTF fonts in JpGraph.
  24. DEFINE("TTF_DIR","/home/www/smstats.topolis.inet/stats/fonts/");
  25. // Cache directory specification for use with CSIM graphs that are
  26. // using the cache.
  27. // The directory must be the filesysystem name as seen by PHP
  28. // and the 'http' version must be the same directory but as
  29. // seen by the HTTP server relative to the 'htdocs' ddirectory.
  30. // If a relative path is specified it is take to be relative from where
  31. // the image script is executed.
  32. // Note: The default setting is to create a subdirectory in the
  33. // directory from where the image script is executed and store all files
  34. // there. As ususal this directory must be writeable by PHP.
  35. DEFINE("CSIMCACHE_DIR","csimcache/");
  36. DEFINE("CSIMCACHE_HTTP_DIR","csimcache/");
  37. //------------------------------------------------------------------------
  38. // Various JpGraph Settings. PLEASE adjust accordingly to you
  39. // system setup. Note that cache functionlity is turned off by
  40. // default (Enable by setting USE_CACHE to true)
  41. //------------------------------------------------------------------------
  42. // Specify if we should use GD 2.x or GD 1.x
  43. // If you have GD 2.x you MUST set this flag to true
  44. // If you don't have GD2 installed this must be set to false!
  45. DEFINE("USE_LIBRARY_GD2",true);
  46. // Should the image be a truecolor image?
  47. // Note 1: Can only be used with GD 2.0.2 and above.
  48. // Note 2: GD 2.0.1 + PHP 4.0.6 on Win32 crashes when trying to use
  49. // trucolor. Truecolor support is to be considered alpha since GD 2.x
  50. // is still not considered stable (especially on Win32).
  51. // Note 3: MUST be enabled to get background images working with GD2
  52. // Note 4: If enabled then truetype fonts will look very ugly
  53. // => You can't have both background images and truetype fonts in the same
  54. // image until these bugs has been fixed in GD 2.01
  55. DEFINE('USE_TRUECOLOR',true);
  56. // Should the cache be used at all? By setting this to false no
  57. // files will be generated in the cache directory.
  58. // The difference from READ_CACHE being that setting READ_CACHE to
  59. // false will still create the image in the cache directory
  60. // just not use it. By setting USE_CACHE=false no files will even
  61. // be generated in the cache directory.
  62. DEFINE("USE_CACHE",false);
  63. // Should we try to find an image in the cache before generating it?
  64. // Set this define to false to bypass the reading of the cache and always
  65. // regenerate the image. Note that even if reading the cache is
  66. // disabled the cached will still be updated with the newly generated
  67. // image. Set also "USE_CACHE" below.
  68. DEFINE("READ_CACHE",false);
  69. // Deafult graphic format set to "auto" which will automatically
  70. // choose the best available format in the order png,gif,jpg
  71. // (The supported format depends on what your PHP installation supports)
  72. DEFINE("DEFAULT_GFORMAT","auto");
  73. // Determine if the error handler should be image based or purely
  74. // text based. Image based makes it easier since the script will
  75. // always return an image even in case of errors.
  76. DEFINE("USE_IMAGE_ERROR_HANDLER",true);
  77. // If the color palette is full should JpGraph try to allocate
  78. // the closest match? If you plan on using background image or
  79. // gradient fills it might be a good idea to enable this.
  80. // If not you will otherwise get an error saying that the color palette is
  81. // exhausted. The drawback of using approximations is that the colors
  82. // might not be exactly what you specified.
  83. // Note1: This does only apply to paletted images, not truecolor
  84. // images since they don't have the limitations of maximum number
  85. // of colors.
  86. DEFINE("USE_APPROX_COLORS",true);
  87. // Special unicode language support
  88. DEFINE("LANGUAGE_CYRILLIC",false);
  89. // If you are setting this config to true the conversion
  90. // will assume that the input text is windows 1251, if
  91. // false it will assume koi8-r
  92. DEFINE("CYRILLIC_FROM_WINDOWS",false);
  93. // Should usage of deprecated functions and parameters give a fatal error?
  94. // (Useful to check if code is future proof.)
  95. DEFINE("ERR_DEPRECATED",false);
  96. // Should the time taken to generate each picture be branded to the lower
  97. // left in corner in each generated image? Useful for performace measurements
  98. // generating graphs
  99. DEFINE("BRAND_TIMING",false);
  100. // What format should be used for the timing string?
  101. DEFINE("BRAND_TIME_FORMAT","Generated in: %01.3fs");
  102. //------------------------------------------------------------------------
  103. // The following constants should rarely have to be changed !
  104. //------------------------------------------------------------------------
  105. // What group should the cached file belong to
  106. // (Set to "" will give the default group for the "PHP-user")
  107. // Please note that the Apache user must be a member of the
  108. // specified group since otherwise it is impossible for Apache
  109. // to set the specified group.
  110. DEFINE("CACHE_FILE_GROUP","wwwadmin");
  111. // What permissions should the cached file have
  112. // (Set to "" will give the default persmissions for the "PHP-user")
  113. DEFINE("CACHE_FILE_MOD",0664);
  114. // Decide if we should use the bresenham circle algorithm or the
  115. // built in Arc(). Bresenham gives better visual apperance of circles
  116. // but is more CPU intensive and slower then the built in Arc() function
  117. // in GD. Turned off by default for speed
  118. DEFINE("USE_BRESENHAM",false);
  119. // Enable some extra debug information for CSIM etc to be shown.
  120. // (Should only be changed if your first name is Johan and you
  121. // happen to know what you are doing!!)
  122. DEFINE("JPG_DEBUG",false);
  123. // Special file name to indicate that we only want to calc
  124. // the image map in the call to Graph::Stroke() used
  125. // internally from the GetHTMLCSIM() method.
  126. DEFINE("_CSIM_SPECIALFILE","_csim_special_");
  127. // HTTP GET argument that is used with image map
  128. // to indicate to the script to just generate the image
  129. // and not the full CSIM HTML page.
  130. DEFINE("_CSIM_DISPLAY","_jpg_csimd");
  131. // Special filename for Graph::Stroke(). If this filename is given
  132. // then the image will NOT be streamed to browser of file. Instead the
  133. // Stroke call will return the handler for the created GD image.
  134. DEFINE("_IMG_HANDLER","__handle");
  135. //------------------------------------------------------------------
  136. // Constants which are used as parameters for the method calls
  137. //------------------------------------------------------------------
  138. // TTF Font families
  139. DEFINE("FF_COURIER",10);
  140. DEFINE("FF_VERDANA",11);
  141. DEFINE("FF_TIMES",12);
  142. DEFINE("FF_COMIC",14);
  143. DEFINE("FF_ARIAL",15);
  144. DEFINE("FF_GEORGIA",16);
  145. DEFINE("FF_TREBUCHE",17);
  146. //
  147. DEFINE("FF_BOOK",91); // Deprecated fonts from 1.9
  148. DEFINE("FF_HANDWRT",92); // Deprecated fonts from 1.9
  149. // TTF Font styles
  150. DEFINE("FS_NORMAL",1);
  151. DEFINE("FS_BOLD",2);
  152. DEFINE("FS_ITALIC",3);
  153. DEFINE("FS_BOLDIT",4);
  154. DEFINE("FS_BOLDITALIC",4);
  155. //Definitions for internal font, new style
  156. DEFINE("FF_FONT0",1);
  157. DEFINE("FF_FONT1",2);
  158. DEFINE("FF_FONT2",4);
  159. //Definitions for internal font, old style
  160. // (Only defined here to be able to generate an error mesage
  161. // when used)
  162. DEFINE("FONT0",99); // Deprecated from 1.2
  163. DEFINE("FONT1",98); // Deprecated from 1.2
  164. DEFINE("FONT1_BOLD",97); // Deprecated from 1.2
  165. DEFINE("FONT2",96); // Deprecated from 1.2
  166. DEFINE("FONT2_BOLD",95); // Deprecated from 1.2
  167. // Tick density
  168. DEFINE("TICKD_DENSE",1);
  169. DEFINE("TICKD_NORMAL",2);
  170. DEFINE("TICKD_SPARSE",3);
  171. DEFINE("TICKD_VERYSPARSE",4);
  172. // Side for ticks and labels.
  173. DEFINE("SIDE_LEFT",-1);
  174. DEFINE("SIDE_RIGHT",1);
  175. DEFINE("SIDE_DOWN",-1);
  176. DEFINE("SIDE_UP",1);
  177. // Legend type stacked vertical or horizontal
  178. DEFINE("LEGEND_VERT",0);
  179. DEFINE("LEGEND_HOR",1);
  180. // Mark types for plot marks
  181. DEFINE("MARK_SQUARE",1);
  182. DEFINE("MARK_UTRIANGLE",2);
  183. DEFINE("MARK_DTRIANGLE",3);
  184. DEFINE("MARK_DIAMOND",4);
  185. DEFINE("MARK_CIRCLE",5);
  186. DEFINE("MARK_FILLEDCIRCLE",6);
  187. DEFINE("MARK_CROSS",7);
  188. DEFINE("MARK_STAR",8);
  189. DEFINE("MARK_X",9);
  190. // Styles for gradient color fill
  191. DEFINE("GRAD_VER",1);
  192. DEFINE("GRAD_HOR",2);
  193. DEFINE("GRAD_MIDHOR",3);
  194. DEFINE("GRAD_MIDVER",4);
  195. DEFINE("GRAD_CENTER",5);
  196. DEFINE("GRAD_WIDE_MIDVER",6);
  197. DEFINE("GRAD_WIDE_MIDHOR",7);
  198. // Inline defines
  199. DEFINE("INLINE_YES",1);
  200. DEFINE("INLINE_NO",0);
  201. // Format for background images
  202. DEFINE("BGIMG_FILLPLOT",1);
  203. DEFINE("BGIMG_FILLFRAME",2);
  204. DEFINE("BGIMG_COPY",3);
  205. DEFINE("BGIMG_CENTER",4);
  206. // Depth of objects
  207. DEFINE("DEPTH_BACK",0);
  208. DEFINE("DEPTH_FRONT",1);
  209. // Direction
  210. DEFINE("VERTICAL",1);
  211. DEFINE("HORIZONTAL",0);
  212. // Constants for types of static bands in plot area
  213. DEFINE("BAND_RDIAG",1); // Right diagonal lines
  214. DEFINE("BAND_LDIAG",2); // Left diagonal lines
  215. DEFINE("BAND_SOLID",3); // Solid one color
  216. DEFINE("BAND_VLINE",4); // Vertical lines
  217. DEFINE("BAND_HLINE",5); // Horizontal lines
  218. DEFINE("BAND_3DPLANE",6); // "3D" Plane
  219. DEFINE("BAND_HVCROSS",7); // Vertical/Hor crosses
  220. DEFINE("BAND_DIAGCROSS",8); // Diagonal crosses
  221. // Axis styles for scientific style axis
  222. DEFINE('AXSTYLE_SIMPLE',1);
  223. DEFINE('AXSTYLE_BOXIN',2);
  224. DEFINE('AXSTYLE_BOXOUT',3);
  225. //
  226. // First of all set up a default error handler
  227. //
  228. //=============================================================
  229. // The default trivial text error handler.
  230. //=============================================================
  231. class JpGraphErrObject {
  232. function JpGraphErrObject() {
  233. // Empty. Reserved for future use
  234. }
  235. // If aHalt is true then execution can't continue. Typical used for
  236. // fatal errors
  237. function Raise($aMsg,$aHalt=true) {
  238. $aMsg = "<b>JpGraph Error:</b> ".$aMsg;
  239. if( $aHalt )
  240. die($aMsg);
  241. else
  242. echo $aMsg."<p>";
  243. }
  244. }
  245. //==============================================================
  246. // An image based error handler
  247. //==============================================================
  248. class JpGraphErrObjectImg {
  249. function Raise($aMsg,$aHalt=true) {
  250. if( headers_sent() ) {
  251. // Special case for headers already sent error. Dont
  252. // return an image since it can't be displayed
  253. die("<b>JpGraph Error:</b> ".$aMsg);
  254. }
  255. // Create an image that contains the error text.
  256. $w=450; $h=110;
  257. $img = new Image($w,$h);
  258. $img->SetColor("darkred");
  259. $img->Rectangle(0,0,$w-1,$h-1);
  260. $img->SetFont(FF_FONT1,FS_BOLD);
  261. $img->StrokeText(10,20,"JpGraph Error:");
  262. $img->SetColor("black");
  263. $img->SetFont(FF_FONT1,FS_NORMAL);
  264. $txt = new Text(wordwrap($aMsg,70),10,20);
  265. $txt->Align("left","top");
  266. $txt->Stroke($img);
  267. $img->Headers();
  268. $img->Stream();
  269. die();
  270. }
  271. }
  272. //
  273. // A wrapper class that is used to access the specified error object
  274. // (to hide the global error parameter and avoid having a GLOBAL directive
  275. // in all methods.
  276. //
  277. class JpGraphError {
  278. function Install($aErrObject) {
  279. GLOBAL $__jpg_err;
  280. $__jpg_err = $aErrObject;
  281. }
  282. function Raise($aMsg,$aHalt=true){
  283. GLOBAL $__jpg_err;
  284. $tmp = new $__jpg_err;
  285. $tmp->Raise($aMsg,$aHalt);
  286. }
  287. }
  288. //
  289. // ... and install the default error handler
  290. //
  291. if( USE_IMAGE_ERROR_HANDLER ) {
  292. JpGraphError::Install("JpGraphErrObjectImg");
  293. }
  294. else {
  295. JpGraphError::Install("JpGraphErrObject");
  296. }
  297. //
  298. //Check if there were any warnings, perhaps some wrong includes by the
  299. //user
  300. //
  301. if( isset($GLOBALS['php_errormsg']) ) {
  302. JpGraphError::Raise("<b>General PHP error:</b><br>".$GLOBALS['php_errormsg']);
  303. }
  304. //
  305. // Routine to determine if GD1 or GD2 is installed
  306. // At the moment this is used to verify that the user
  307. // really has GD2 if he has set USE_GD2 to true.
  308. //
  309. function CheckGDVersion() {
  310. ob_start();
  311. phpinfo(8); // Just get the modules loaded
  312. $a = ob_get_contents();
  313. ob_end_clean();
  314. if( preg_match('/.*GD Version.*(1[0-9|\.]+).*/',$a,$m) ) {
  315. $r=1;$v=$m[1];
  316. }
  317. elseif( preg_match('/.*GD Version.*(2[0-9|\.]+).*/',$a,$m) ) {
  318. $r=2;$v=$m[1];
  319. }
  320. else {
  321. $r=0;$v=$m[1];
  322. }
  323. return $r;
  324. }
  325. //
  326. // Check what version of the GD library the user has choosen to use. If the user
  327. // has choosen GD2 we also check that GD2 really exists.
  328. // (This might lock a bit strange but in order to avoid calling the CheckGDVersion
  329. // since it takes roughly 20ms we only call it for real in the case where the user
  330. // tries to use GD2. We then make sure that the user really have GD2 installed))
  331. $gdversion = 1;
  332. if( USE_LIBRARY_GD2 ) {
  333. $gdversion = CheckGDVersion();
  334. if( $gdversion == 2 ) {
  335. $GLOBALS['gd2'] = true;
  336. $GLOBALS['copyfunc'] = 'imagecopyresampled';
  337. }
  338. else
  339. JpGraphError::Raise('You have selected GD2 but you do not appear to have GD2 installed!');
  340. } elseif( $gdversion > 0 && function_exists('imagetypes')) {
  341. $GLOBALS['gd2'] = false;
  342. $GLOBALS['copyfunc'] = 'imagecopyresized';
  343. }
  344. else {
  345. JpGraphError::Raise(" Your PHP installation does not seem to
  346. have the required GD library.
  347. Please see the PHP documentation on how to install and enable the GD library.");
  348. }
  349. // Usefull mathematical function
  350. function sign($a) {if( $a>=0) return 1; else return -1;}
  351. // Utility function to generate an image name based on the filename we
  352. // are running from and assuming we use auto detection of graphic format
  353. // (top level), i.e it is safe to call this function
  354. // from a script that uses JpGraph
  355. function GenImgName() {
  356. global $HTTP_SERVER_VARS;
  357. $supported = imagetypes();
  358. if( $supported & IMG_PNG )
  359. $img_format="png";
  360. elseif( $supported & IMG_GIF )
  361. $img_format="gif";
  362. elseif( $supported & IMG_JPG )
  363. $img_format="jpeg";
  364. if( !isset($HTTP_SERVER_VARS['PHP_SELF']) )
  365. JpGraphError::Raise(" Can't access PHP_SELF, PHP global variable. You can't run PHP from command line
  366. if you want to use the 'auto' naming of cache or image files.");
  367. $fname=basename($HTTP_SERVER_VARS['PHP_SELF']);
  368. // Replace the ".php" extension with the image format extension
  369. return substr($fname,0,strlen($fname)-4).".".$img_format;
  370. }
  371. class LanguageConv {
  372. // Translate iso encoding to unicode
  373. function iso2uni ($isoline){
  374. for ($i=0; $i < strlen($isoline); $i++){
  375. $thischar=substr($isoline,$i,1);
  376. $charcode=ord($thischar);
  377. $uniline.=($charcode>175) ? "&#" . (1040+($charcode-176)). ";" : $thischar;
  378. }
  379. return $uniline;
  380. }
  381. function ToCyrillic($aTxt) {
  382. if( CYRILLIC_FROM_WINDOWS ) {
  383. $aTxt = convert_cyr_string($aTxt, "w", "k");
  384. }
  385. $isostring = convert_cyr_string($aTxt, "k", "i");
  386. $unistring = LanguageConv::iso2uni($isostring);
  387. return $unistring;
  388. }
  389. }
  390. //===================================================
  391. // CLASS JpgTimer
  392. // Description: General timing utility class to handle
  393. // timne measurement of generating graphs. Multiple
  394. // timers can be started by pushing new on a stack.
  395. //===================================================
  396. class JpgTimer {
  397. var $start;
  398. var $idx;
  399. //---------------
  400. // CONSTRUCTOR
  401. function JpgTimer() {
  402. $this->idx=0;
  403. }
  404. //---------------
  405. // PUBLIC METHODS
  406. // Push a new timer start on stack
  407. function Push() {
  408. list($ms,$s)=explode(" ",microtime());
  409. $this->start[$this->idx++]=floor($ms*1000) + 1000*$s;
  410. }
  411. // Pop the latest timer start and return the diff with the
  412. // current time
  413. function Pop() {
  414. assert($this->idx>0);
  415. list($ms,$s)=explode(" ",microtime());
  416. $etime=floor($ms*1000) + (1000*$s);
  417. $this->idx--;
  418. return $etime-$this->start[$this->idx];
  419. }
  420. } // Class
  421. $gJpgBrandTiming = BRAND_TIMING;
  422. $gDateLocale = new DateLocale();
  423. $gJpgDateLocale = new DateLocale();
  424. //===================================================
  425. // CLASS DateLocale
  426. // Description: Hold localized text used in dates
  427. // ToDOo: Rewrite this to use the real local locale
  428. // instead.
  429. //===================================================
  430. class DateLocale {
  431. var $iLocale = ''; // environmental locale be used by default
  432. var $iDayAbb = null;
  433. var $iShortDay = null;
  434. var $iShortMonth = null;
  435. var $iMonthName = null;
  436. //---------------
  437. // CONSTRUCTOR
  438. function DateLocale() {
  439. settype($this->iDayAbb, 'array');
  440. settype($this->iShortDay, 'array');
  441. settype($this->iShortMonth, 'array');
  442. settype($this->iMonthName, 'array');
  443. $this->Set($this->iLocale);
  444. }
  445. //---------------
  446. // PUBLIC METHODS
  447. function Set($aLocale) {
  448. if ( in_array($aLocale, array_keys($this->iDayAbb)) ){
  449. $this->iLocale = $aLocale;
  450. return TRUE; // already cahced nothing else to do!
  451. }
  452. $pLocale = setlocale(LC_TIME, 0); // get current locale for LC_TIME
  453. if ( !setlocale(LC_TIME, $aLocale) ){
  454. JpGraphError::Raise("Unsupported locale ($aLocale)");
  455. return FALSE;
  456. }
  457. $this->iLocale = $aLocale;
  458. for ( $i = 0, $ofs = 0 - strftime('%w'); $i < 7; $i++, $ofs++ ){
  459. $day = strftime('%a', strtotime("$ofs day"));
  460. $day{0} = strtoupper($day{0});
  461. $this->iDayAbb[$aLocale][]= $day{0};
  462. $this->iShortDay[$aLocale][]= $day;
  463. }
  464. for($i=1; $i<=12; ++$i) {
  465. list($short ,$full) = explode('|', strftime("%b|%B",strtotime("2001-$i-01")));
  466. $this->iShortMonth[$aLocale][] = ucfirst($short);
  467. $this->iMonthName [$aLocale][] = ucfirst($full);
  468. }
  469. setlocale(LC_TIME, $pLocale);
  470. return TRUE;
  471. }
  472. function GetDayAbb() {
  473. return $this->iDayAbb[$this->iLocale];
  474. }
  475. function GetShortDay() {
  476. return $this->iShortDay[$this->iLocale];
  477. }
  478. function GetShortMonth() {
  479. return $this->iShortMonth[$this->iLocale];
  480. }
  481. function GetShortMonthName($aNbr) {
  482. return $this->iShortMonth[$this->iLocale][$aNbr];
  483. }
  484. function GetLongMonthName($aNbr) {
  485. return $this->iMonthName[$this->iLocale][$aNbr];
  486. }
  487. function GetMonth() {
  488. return $this->iMonthName[$this->iLocale];
  489. }
  490. }
  491. //===================================================
  492. // CLASS FuncGenerator
  493. // Description: Utility class to help generate data for function plots.
  494. // The class supports both parametric and regular functions.
  495. //===================================================
  496. class FuncGenerator {
  497. var $iFunc='',$iXFunc='',$iMin,$iMax,$iStepSize;
  498. function FuncGenerator($aFunc,$aXFunc='') {
  499. $this->iFunc = $aFunc;
  500. $this->iXFunc = $aXFunc;
  501. }
  502. function E($aXMin,$aXMax,$aSteps=50) {
  503. $this->iMin = $aXMin;
  504. $this->iMax = $aXMax;
  505. $this->iStepSize = ($aXMax-$aXMin)/$aSteps;
  506. if( $this->iXFunc != '' )
  507. $t = 'for($i='.$aXMin.'; $i<='.$aXMax.'; $i += '.$this->iStepSize.') {$ya[]='.$this->iFunc.';$xa[]='.$this->iXFunc.';}';
  508. elseif( $this->iFunc != '' )
  509. $t = 'for($x='.$aXMin.'; $x<='.$aXMax.'; $x += '.$this->iStepSize.') {$ya[]='.$this->iFunc.';$xa[]=$x;} $x='.$aXMax.';$ya[]='.$this->iFunc.';$xa[]=$x;';
  510. else
  511. JpGraphError::Raise('FuncGenerator : No function specified. ');
  512. @eval($t);
  513. // If there is an error in the function specifcation this is the only
  514. // way we can discover that.
  515. if( empty($xa) || empty($ya) )
  516. JpGraphError::Raise('FuncGenerator : Syntax error in function specification ');
  517. return array($xa,$ya);
  518. }
  519. }
  520. //=======================================================
  521. // CLASS Footer
  522. // Description: Encapsulates the footer line in the Graph
  523. //
  524. //=======================================================
  525. class Footer {
  526. var $left,$center,$right;
  527. var $iLeftMargin = 3;
  528. var $iRightMargin = 3;
  529. var $iBottomMargin = 3;
  530. function Footer() {
  531. $this->left = new Text();
  532. $this->left->ParagraphAlign('left');
  533. $this->center = new Text();
  534. $this->center->ParagraphAlign('center');
  535. $this->right = new Text();
  536. $this->right->ParagraphAlign('right');
  537. }
  538. function Stroke($aImg) {
  539. $y = $aImg->height - $this->iBottomMargin;
  540. $x = $this->iLeftMargin;
  541. $this->left->Align('left','bottom');
  542. $this->left->Stroke($aImg,$x,$y);
  543. $x = ($aImg->width - $this->iLeftMargin - $this->iRightMargin)/2;
  544. $this->center->Align('center','bottom');
  545. $this->center->Stroke($aImg,$x,$y);
  546. $x = $aImg->width - $this->iRightMargin;
  547. $this->right->Align('right','bottom');
  548. $this->right->Stroke($aImg,$x,$y);
  549. }
  550. }
  551. //===================================================
  552. // CLASS Graph
  553. // Description: Main class to handle graphs
  554. //===================================================
  555. class Graph {
  556. var $cache=null; // Cache object (singleton)
  557. var $img=null; // Img object (singleton)
  558. var $plots=array(); // Array of all plot object in the graph (for Y 1 axis)
  559. var $y2plots=array();// Array of all plot object in the graph (for Y 2 axis)
  560. var $xscale=null; // X Scale object (could be instance of LinearScale or LogScale
  561. var $yscale=null,$y2scale=null;
  562. var $cache_name; // File name to be used for the current graph in the cache directory
  563. var $xgrid=null; // X Grid object (linear or logarithmic)
  564. var $ygrid=null,$y2grid=null; //dito for Y
  565. var $doframe=true,$frame_color=array(0,0,0), $frame_weight=1; // Frame around graph
  566. var $boxed=false, $box_color=array(0,0,0), $box_weight=1; // Box around plot area
  567. var $doshadow=false,$shadow_width=4,$shadow_color=array(102,102,102); // Shadow for graph
  568. var $xaxis=null; // X-axis (instane of Axis class)
  569. var $yaxis=null, $y2axis=null; // Y axis (instance of Axis class)
  570. var $margin_color=array(198,198,198); // Margin coor of graph
  571. var $plotarea_color=array(255,255,255); // Plot area color
  572. var $title,$subtitle,$subsubtitle; // Title and subtitle(s) text object
  573. var $axtype="linlin"; // Type of axis
  574. var $xtick_factor; // Factot to determine the maximum number of ticks depending on the plot with
  575. var $texts=null; // Text object to ge shown in the graph
  576. var $lines=null;
  577. var $bands=null;
  578. var $text_scale_off=0; // Text scale offset in world coordinates
  579. var $background_image="",$background_image_type=-1,$background_image_format="png";
  580. var $background_image_bright=0,$background_image_contr=0,$background_image_sat=0;
  581. var $image_bright=0, $image_contr=0, $image_sat=0;
  582. var $inline;
  583. var $showcsim=0,$csimcolor="red"; //debug stuff, draw the csim boundaris on the image if <>0
  584. var $grid_depth=DEPTH_BACK; // Draw grid under all plots as default
  585. var $iAxisStyle = AXSTYLE_SIMPLE;
  586. var $iCSIMdisplay=false,$iHasStroked = false;
  587. var $footer;
  588. var $csimcachename = '', $csimcachetimeout = 0;
  589. //---------------
  590. // CONSTRUCTOR
  591. // aWIdth Width in pixels of image
  592. // aHeight Height in pixels of image
  593. // aCachedName Name for image file in cache directory
  594. // aTimeOut Timeout in minutes for image in cache
  595. // aInline If true the image is streamed back in the call to Stroke()
  596. // If false the image is just created in the cache
  597. function Graph($aWidth=300,$aHeight=200,$aCachedName="",$aTimeOut=0,$aInline=true) {
  598. GLOBAL $gJpgBrandTiming;
  599. // If timing is used create a new timing object
  600. if( $gJpgBrandTiming ) {
  601. global $tim;
  602. $tim = new JpgTimer();
  603. $tim->Push();
  604. }
  605. // Automatically generate the image file name based on the name of the script that
  606. // generates the graph
  607. if( $aCachedName=="auto" )
  608. $aCachedName=GenImgName();
  609. // Should the image be streamed back to the browser or only to the cache?
  610. $this->inline=$aInline;
  611. $this->img = new RotImage($aWidth,$aHeight);
  612. $this->cache = new ImgStreamCache($this->img);
  613. $this->cache->SetTimeOut($aTimeOut);
  614. $this->title = new Text();
  615. $this->title->ParagraphAlign('center');
  616. $this->title->SetFont(FF_FONT2,FS_BOLD);
  617. $this->title->SetMargin(3);
  618. $this->subtitle = new Text();
  619. $this->subtitle->ParagraphAlign('center');
  620. $this->subsubtitle = new Text();
  621. $this->subsubtitle->ParagraphAlign('center');
  622. $this->legend = new Legend();
  623. $this->footer = new Footer();
  624. // If the cached version exist just read it directly from the
  625. // cache, stream it back to browser and exit
  626. if( $aCachedName!="" && READ_CACHE && $aInline )
  627. if( $this->cache->GetAndStream($aCachedName) ) {
  628. exit();
  629. }
  630. $this->cache_name = $aCachedName;
  631. $this->SetTickDensity(); // Normal density
  632. }
  633. //---------------
  634. // PUBLIC METHODS
  635. // Should the grid be in front or back of the plot?
  636. function SetGridDepth($aDepth) {
  637. $this->grid_depth=$aDepth;
  638. }
  639. // Specify graph angle 0-360 degrees.
  640. function SetAngle($aAngle) {
  641. $this->img->SetAngle($aAngle);
  642. }
  643. // Shortcut to image margin
  644. function SetMargin($lm,$rm,$tm,$bm) {
  645. $this->img->SetMargin($lm,$rm,$tm,$bm);
  646. }
  647. // Add a plot object to the graph
  648. function Add(&$aPlot) {
  649. if( $aPlot == null )
  650. JpGraphError::Raise("<b></b> Graph::Add() You tried to add a null plot to the graph.");
  651. if( is_array($aPlot) && count($aPlot) > 0 )
  652. $cl = get_class($aPlot[0]);
  653. else
  654. $cl = get_class($aPlot);
  655. if( $cl == 'text' )
  656. $this->AddText($aPlot);
  657. elseif( $cl == 'plotline' )
  658. $this->AddLine($aPlot);
  659. elseif( $cl == 'plotband' )
  660. $this->AddBand($aPlot);
  661. else
  662. $this->plots[] = &$aPlot;
  663. }
  664. // Add plot to second Y-scale
  665. function AddY2(&$aPlot) {
  666. if( $aPlot == null )
  667. JpGraphError::Raise("<b></b> Graph::AddY2() You tried to add a null plot to the graph.");
  668. $this->y2plots[] = &$aPlot;
  669. }
  670. // Add text object to the graph
  671. function AddText(&$aTxt) {
  672. if( $aTxt == null )
  673. JpGraphError::Raise("<b></b> Graph::AddText() You tried to add a null text to the graph.");
  674. if( is_array($aTxt) ) {
  675. for($i=0; $i < count($aTxt); ++$i )
  676. $this->texts[]=&$aTxt[$i];
  677. }
  678. else
  679. $this->texts[] = &$aTxt;
  680. }
  681. // Add a line object (class PlotLine) to the graph
  682. function AddLine(&$aLine) {
  683. if( $aLine == null )
  684. JpGraphError::Raise("<b></b> Graph::AddLine() You tried to add a null line to the graph.");
  685. if( is_array($aLine) ) {
  686. for($i=0; $i<count($aLine); ++$i )
  687. $this->lines[]=&$aLine[$i];
  688. }
  689. else
  690. $this->lines[] = &$aLine;
  691. }
  692. // Add vertical or horizontal band
  693. function AddBand(&$aBand) {
  694. if( $aBand == null )
  695. JpGraphError::Raise(" Graph::AddBand() You tried to add a null band to the graph.");
  696. if( is_array($aBand) ) {
  697. for($i=0; $i<count($aBand); ++$i )
  698. $this->bands[] = &$aBand[$i];
  699. }
  700. else
  701. $this->bands[] = &$aBand;
  702. }
  703. // Specify a background image
  704. function SetBackgroundImage($aFileName,$aBgType=BGIMG_FILLPLOT,$aImgFormat="auto") {
  705. if( $GLOBALS['gd2'] && !USE_TRUECOLOR ) {
  706. JpGraphError::Raise("You are using GD 2.x and are trying to use a background images on a non truecolor image. To use background images with GD 2.x you <b>must</b> enable truecolor by setting the USE_TRUECOLOR constant to TRUE. Due to a bug in GD 2.0.1 using any truetype fonts with truecolor images will result in very poor quality fonts.");
  707. }
  708. // Get extension to determine image type
  709. if( $aImgFormat == "auto" ) {
  710. $e = explode('.',$aFileName);
  711. if( !$e ) {
  712. JpGraphError::Raise('Incorrect file name for Graph::SetBackgroundImage() : '.$aFileName);
  713. exit();
  714. }
  715. if( strtolower($e[1]) == 'png' )
  716. $aImgFormat = 'png';
  717. elseif( strtolower($e[1]) == 'jpg' || strtolower($e[1]) == 'jpeg')
  718. $aImgFormat = 'jpg';
  719. elseif( strtolower($e[1]) == 'gif' )
  720. $aImgFormat = 'gif';
  721. else {
  722. JpGraphError::Raise('Unknown file extension in Graph::SetBackgroundImage : '.$aFileName);
  723. exit();
  724. }
  725. }
  726. $this->background_image = $aFileName;
  727. $this->background_image_type=$aBgType;
  728. $this->background_image_format=$aImgFormat;
  729. }
  730. // Adjust brightness and constrast for background image
  731. function AdjBackgroundImage($aBright,$aContr=0,$aSat=0) {
  732. $this->background_image_bright=$aBright;
  733. $this->background_image_contr=$aContr;
  734. $this->background_image_sat=$aSat;
  735. }
  736. // Adjust brightness and constrast for image
  737. function AdjImage($aBright,$aContr=0,$aSat=0) {
  738. $this->image_bright=$aBright;
  739. $this->image_contr=$aContr;
  740. $this->image_sat=$aSat;
  741. }
  742. // Specify axis style (boxed or single)
  743. function SetAxisStyle($aStyle) {
  744. $this->iAxisStyle = $aStyle ;
  745. }
  746. // Set a frame around the plot area
  747. function SetBox($aDrawPlotFrame=true,$aPlotFrameColor=array(0,0,0),$aPlotFrameWeight=1) {
  748. $this->boxed = $aDrawPlotFrame;
  749. $this->box_weight = $aPlotFrameWeight;
  750. $this->box_color = $aPlotFrameColor;
  751. }
  752. // Specify color for the plotarea (not the margins)
  753. function SetColor($aColor) {
  754. $this->plotarea_color=$aColor;
  755. }
  756. // Specify color for the margins (all areas outside the plotarea)
  757. function SetMarginColor($aColor) {
  758. $this->margin_color=$aColor;
  759. }
  760. // Set a frame around the entire image
  761. function SetFrame($aDrawImgFrame=true,$aImgFrameColor=array(0,0,0),$aImgFrameWeight=1) {
  762. $this->doframe = $aDrawImgFrame;
  763. $this->frame_color = $aImgFrameColor;
  764. $this->frame_weight = $aImgFrameWeight;
  765. }
  766. // Set the shadow around the whole image
  767. function SetShadow($aShowShadow=true,$aShadowWidth=5,$aShadowColor=array(102,102,102)) {
  768. $this->doshadow = $aShowShadow;
  769. $this->shadow_color = $aShadowColor;
  770. $this->shadow_width = $aShadowWidth;
  771. $this->footer->iBottomMargin += $aShadowWidth;
  772. $this->footer->iRightMargin += $aShadowWidth;
  773. }
  774. // Specify x,y scale. Note that if you manually specify the scale
  775. // you must also specify the tick distance with a call to Ticks::Set()
  776. function SetScale($aAxisType,$aYMin=1,$aYMax=1,$aXMin=1,$aXMax=1) {
  777. $this->axtype = $aAxisType;
  778. $yt=substr($aAxisType,-3,3);
  779. if( $yt=="lin" )
  780. $this->yscale = new LinearScale($aYMin,$aYMax);
  781. elseif( $yt == "int" ) {
  782. $this->yscale = new LinearScale($aYMin,$aYMax);
  783. $this->yscale->SetIntScale();
  784. }
  785. elseif( $yt=="log" )
  786. $this->yscale = new LogScale($aYMin,$aYMax);
  787. else
  788. JpGraphError::Raise("Unknown scale specification for Y-scale. ($aAxisType)");
  789. $xt=substr($aAxisType,0,3);
  790. if( $xt == "lin" || $xt == "tex" ) {
  791. $this->xscale = new LinearScale($aXMin,$aXMax,"x");
  792. $this->xscale->textscale = ($xt == "tex");
  793. }
  794. elseif( $xt == "int" ) {
  795. $this->xscale = new LinearScale($aXMin,$aXMax,"x");
  796. $this->xscale->SetIntScale();
  797. }
  798. elseif( $xt == "log" )
  799. $this->xscale = new LogScale($aXMin,$aXMax,"x");
  800. else
  801. JpGraphError::Raise(" Unknown scale specification for X-scale. ($aAxisType)");
  802. $this->xscale->Init($this->img);
  803. $this->yscale->Init($this->img);
  804. $this->xaxis = new Axis($this->img,$this->xscale);
  805. $this->yaxis = new Axis($this->img,$this->yscale);
  806. $this->xgrid = new Grid($this->xaxis);
  807. $this->ygrid = new Grid($this->yaxis);
  808. $this->ygrid->Show();
  809. }
  810. // Specify secondary Y scale
  811. function SetY2Scale($aAxisType="lin",$aY2Min=1,$aY2Max=1) {
  812. if( $aAxisType=="lin" )
  813. $this->y2scale = new LinearScale($aY2Min,$aY2Max);
  814. elseif( $aAxisType == "int" ) {
  815. $this->y2scale = new LinearScale($aY2Min,$aY2Max);
  816. $this->y2scale->SetIntScale();
  817. }
  818. elseif( $aAxisType=="log" ) {
  819. $this->y2scale = new LogScale($aY2Min,$aY2Max);
  820. }
  821. else JpGraphError::Raise("JpGraph: Unsupported Y2 axis type: $axtype<br>");
  822. $this->y2scale->Init($this->img);
  823. $this->y2axis = new Axis($this->img,$this->y2scale);
  824. $this->y2axis->scale->ticks->SetDirection(SIDE_LEFT);
  825. $this->y2axis->SetLabelPos(SIDE_RIGHT);
  826. // Deafult position is the max x-value
  827. $this->y2grid = new Grid($this->y2axis);
  828. }
  829. // Specify density of ticks when autoscaling 'normal', 'dense', 'sparse', 'verysparse'
  830. // The dividing factor have been determined heuristically according to my aesthetic
  831. // sense (or lack off) y.m.m.v !
  832. function SetTickDensity($aYDensity=TICKD_NORMAL,$aXDensity=TICKD_NORMAL) {
  833. $this->xtick_factor=30;
  834. $this->ytick_factor=25;
  835. switch( $aYDensity ) {
  836. case TICKD_DENSE:
  837. $this->ytick_factor=12;
  838. break;
  839. case TICKD_NORMAL:
  840. $this->ytick_factor=25;
  841. break;
  842. case TICKD_SPARSE:
  843. $this->ytick_factor=40;
  844. break;
  845. case TICKD_VERYSPARSE:
  846. $this->ytick_factor=100;
  847. break;
  848. default:
  849. JpGraphError::Raise("JpGraph: Unsupported Tick density: $densy");
  850. }
  851. switch( $aXDensity ) {
  852. case TICKD_DENSE:
  853. $this->xtick_factor=15;
  854. break;
  855. case TICKD_NORMAL:
  856. $this->xtick_factor=30;
  857. break;
  858. case TICKD_SPARSE:
  859. $this->xtick_factor=45;
  860. break;
  861. case TICKD_VERYSPARSE:
  862. $this->xtick_factor=60;
  863. break;
  864. default:
  865. JpGraphError::Raise("JpGraph: Unsupported Tick density: $densx");
  866. }
  867. }
  868. // Get a string of all image map areas
  869. function GetCSIMareas() {
  870. if( !$this->iHasStroked )
  871. $this->Stroke(_CSIM_SPECIALFILE);
  872. $csim='';
  873. $n = count($this->plots);
  874. for( $i=0; $i<$n; ++$i )
  875. $csim .= $this->plots[$i]->GetCSIMareas();
  876. $n = count($this->y2plots);
  877. for( $i=0; $i<$n; ++$i )
  878. $csim .= $this->y2plots[$i]->GetCSIMareas();
  879. $csim .= $this->legend->GetCSIMAreas();
  880. return $csim;
  881. }
  882. // Get a complete <MAP>..</MAP> tag for the final image map
  883. function GetHTMLImageMap($aMapName) {
  884. $im = "<MAP NAME=\"$aMapName\">\n";
  885. $im .= $this->GetCSIMareas();
  886. $im .= "</MAP>";
  887. return $im;
  888. }
  889. function CheckCSIMCache($aCacheName,$aTimeOut=60) {
  890. $this->csimcachename = CSIMCACHE_DIR.$aCacheName;
  891. $this->csimcachetimeout = $aTimeOut;
  892. // First determine if we need to check for a cached version
  893. // This differs from the standard cache in the sense that the
  894. // image and CSIM map HTML file is written relative to the directory
  895. // the script executes in and not the specified cache directory.
  896. // The reason for this is that the cache directory is not necessarily
  897. // accessible from the HTTP server.
  898. if( $this->csimcachename != '' ) {
  899. $dir = dirname($this->csimcachename);
  900. $base = basename($this->csimcachename);
  901. $base = strtok($base,'.');
  902. $suffix = strtok('.');
  903. $basecsim = $dir.'/'.$base.'_csim_.html';
  904. $baseimg = $dir.'/'.$base.'.'.$this->img->img_format;
  905. $timedout=false;
  906. // Does it exist at all ?
  907. if( file_exists($basecsim) && file_exists($baseimg) ) {
  908. // Check that it hasn't timed out
  909. $diff=time()-filemtime($basecsim);
  910. if( $this->csimcachetimeout>0 && ($diff > $this->csimcachetimeout*60) ) {
  911. $timedout=true;
  912. @unlink($basecsim);
  913. @unlink($baseimg);
  914. }
  915. else {
  916. if ($fh = @fopen($basecsim, "r")) {
  917. fpassthru($fh);
  918. exit();
  919. }
  920. else
  921. JpGraphError::Raise(" Can't open cached CSIM \"$basecsim\" for reading.");
  922. }
  923. }
  924. }
  925. return false;
  926. }
  927. function StrokeCSIM($aScriptName='',$aCSIMName='',$aBorder=0) {
  928. GLOBAL $HTTP_GET_VARS;
  929. if( $aCSIMName=='' ) {
  930. // create a random map name
  931. srand ((double) microtime() * 1000000);
  932. $r = rand(0,100000);
  933. $aCSIMName='__mapname'.$r.'__';
  934. }
  935. if( empty($HTTP_GET_VARS[_CSIM_DISPLAY]) ) {
  936. // First determine if we need to check for a cached version
  937. // This differs from the standard cache in the sense that the
  938. // image and CSIM map HTML file is written relative to the directory
  939. // the script executes in and not the specified cache directory.
  940. // The reason for this is that the cache directory is not necessarily
  941. // accessible from the HTTP server.
  942. if( $this->csimcachename != '' ) {
  943. $dir = dirname($this->csimcachename);
  944. $base = basename($this->csimcachename);
  945. $base = strtok($base,'.');
  946. $suffix = strtok('.');
  947. $basecsim = $dir.'/'.$base.'_csim_.html';
  948. $baseimg = $base.'.'.$this->img->img_format;
  949. // Check that apache can write to directory specified
  950. if( file_exists($dir) && !is_writeable($dir) ) {
  951. JpgraphError::Raise('Apache/PHP does not have permission to write to the CSIM cache directory ('.$dir.'). Check permissions.');
  952. }
  953. // Make sure directory exists
  954. $this->cache->MakeDirs($dir);
  955. // Write the image file
  956. $this->Stroke(CSIMCACHE_DIR.$baseimg);
  957. // Construct wrapper HTML and write to file and send it back to browser
  958. $htmlwrap = $this->GetHTMLImageMap($aCSIMName)."\n".
  959. '<img src="'.CSIMCACHE_HTTP_DIR.$baseimg.'" ISMAP USEMAP="#'.$aCSIMName.'" border=$aBorder>'."\n";
  960. if($fh = @fopen($basecsim,'w') ) {
  961. fwrite($fh,$htmlwrap);
  962. fclose($fh);
  963. echo $htmlwrap;
  964. }
  965. else
  966. JpGraphError::Raise(" Can't write CSIM \"$basecsim\" for writing. Check free space and permissions.");
  967. }
  968. else {
  969. if( $aScriptName=='' ) {
  970. JpGraphError::Raise('Missing script name in call to StrokeCSIM(). You must specify the name of the actual image script as the first parameter to StrokeCSIM().');
  971. exit();
  972. }
  973. // Construct the HTML wrapper page
  974. // Get all user defined URL arguments
  975. reset($HTTP_GET_VARS);
  976. // This is a JPGRAPH internal defined that prevents
  977. // us from recursively coming here again
  978. $urlarg='?'._CSIM_DISPLAY.'=1';
  979. while( list($key,$value) = each($HTTP_GET_VARS) ) {
  980. $urlarg .= '&'.$key.'='.$value;
  981. }
  982. echo $this->GetHTMLImageMap($aCSIMName);
  983. echo "<img src='".$aScriptName.$urlarg."' ISMAP USEMAP='#".$aCSIMName."' border=$aBorder>";
  984. }
  985. }
  986. else {
  987. $this->Stroke();
  988. }
  989. }
  990. // Stroke the graph
  991. // $aStrokeFileName If != "" the image will be written to this file and NOT
  992. // streamed back to the browser
  993. function Stroke($aStrokeFileName="") {
  994. // If the filename is the predefined value = '_csim_special_'
  995. // we assume that the call to stroke only needs to do enough
  996. // to correctly generate the CSIM maps.
  997. // We use this variable to skip things we don't strictly need
  998. // to do to generate the image map to improve performance
  999. // a best we can. Therefor you will see a lot of tests !$_csim in the
  1000. // code below.
  1001. $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
  1002. // We need to know if we have stroked the plot in the
  1003. // GetCSIMareas. Otherwise the CSIM hasn't been generated
  1004. // and in the case of GetCSIM called before stroke to generate
  1005. // CSIM without storing an image to disk GetCSIM must call Stroke.
  1006. $this->iHasStroked = true;
  1007. // Do any pre-stroke adjustment that is needed by the different plot types
  1008. // (i.e bar plots want's to add an offset to the x-labels etc)
  1009. for($i=0; $i<count($this->plots) ; ++$i ) {
  1010. $this->plots[$i]->PreStrokeAdjust($this);
  1011. $this->plots[$i]->Legend($this);
  1012. }
  1013. // Any plots on the second Y scale?
  1014. if( $this->y2scale != null ) {
  1015. for($i=0; $i<count($this->y2plots) ; ++$i ) {
  1016. $this->y2plots[$i]->PreStrokeAdjust($this);
  1017. $this->y2plots[$i]->Legend($this);
  1018. }
  1019. }
  1020. // Bail out if any of the Y-axis not been specified and
  1021. // has no plots. (This means it is impossible to do autoscaling and
  1022. // no other scale was given so we can't possible draw anything). If you use manual
  1023. // scaling you also have to supply the tick steps as well.
  1024. if( (!$this->yscale->IsSpecified() && count($this->plots)==0) ||
  1025. ($this->y2scale!=null && !$this->y2scale->IsSpecified() && count($this->y2plots)==0) ) {
  1026. $e = "Can't draw unspecified Y-scale.<br>\nYou have either:<br>\n";
  1027. $e .= "1. Specified an Y axis for autoscaling but have not supplied any plots<br>\n";
  1028. $e .= "2. Specified a scale manually but have forgot to specify the tick steps";
  1029. JpGraphError::Raise($e);
  1030. }
  1031. // Bail out if no plots and no specified X-scale
  1032. if( (!$this->xscale->IsSpecified() && count($this->plots)==0 && count($this->y2plots)==0) )
  1033. JpGraphError::Raise("<strong>JpGraph: Can't draw unspecified X-scale.</strong><br>No plots.<br>");
  1034. //Check if we should autoscale y-axis
  1035. if( !$this->yscale->IsSpecified() && count($this->plots)>0 ) {
  1036. list($min,$max) = $this->GetPlotsYMinMax($this->plots);
  1037. $this->yscale->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor);
  1038. }
  1039. elseif( $this->yscale->IsSpecified() && $this->yscale->auto_ticks ) {
  1040. // If the user has specified a min/max value for the scale we still use the
  1041. // autoscaling to get a suitable tick distance. This might adjust the specified
  1042. // min max values so they end up on a tick mark.
  1043. $min = $this->yscale->scale[0];
  1044. $max = $this->yscale->scale[1];
  1045. $this->yscale->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor);
  1046. }
  1047. if( $this->y2scale != null) {
  1048. if( !$this->y2scale->IsSpecified() && count($this->y2plots)>0 ) {
  1049. list($min,$max) = $this->GetPlotsYMinMax($this->y2plots);
  1050. $this->y2scale->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor);
  1051. }
  1052. elseif( $this->y2scale->IsSpecified() && $this->y2scale->auto_ticks ) {
  1053. // If the user has specified a min/max value for the scale we still use the
  1054. // autoscaling to get a suitable tick distance. This might adjust the specified
  1055. // min max values so they end up on a tick mark.
  1056. $min = $this->y2scale->scale[0];
  1057. $max = $this->y2scale->scale[1];
  1058. $this->y2scale->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor);
  1059. }
  1060. }
  1061. //Check if we should autoscale x-axis
  1062. if( !$this->xscale->IsSpecified() ) {
  1063. if( substr($this->axtype,0,4) == "text" ) {
  1064. $max=0;
  1065. foreach( $this->plots as $p ) {
  1066. $max=max($max,$p->numpoints-1);
  1067. }
  1068. $min=0;
  1069. if( $this->y2axis != null ) {
  1070. foreach( $this->y2plots as $p ) {
  1071. $max=max($max,$p->numpoints-1);
  1072. }
  1073. }
  1074. $this->xscale->Update($this->img,$min,$max);
  1075. $this->xscale->ticks->Set($this->xaxis->tick_step,1);
  1076. $this->xscale->ticks->SupressMinorTickMarks();
  1077. }
  1078. else {
  1079. list($min,$ymin) = $this->plots[0]->Min();
  1080. list($max,$ymax) = $this->plots[0]->Max();
  1081. foreach( $this->plots as $p ) {
  1082. list($xmin,$ymin) = $p->Min();
  1083. list($xmax,$ymax) = $p->Max();
  1084. $min = Min($xmin,$min);
  1085. $max = Max($xmax,$max);
  1086. }
  1087. if( $this->y2axis != null ) {
  1088. foreach( $this->y2plots as $p ) {
  1089. list($xmin,$ymin) = $p->Min();
  1090. list($xmax,$ymax) = $p->Max();
  1091. $min = Min($xmin,$min);
  1092. $max = Max($xmax,$max);
  1093. }
  1094. }
  1095. $this->xscale->AutoScale($this->img,$min,$max,$this->img->plotwidth/$this->xtick_factor);
  1096. }
  1097. //Adjust position of y-axis and y2-axis to minimum/maximum of x-scale
  1098. if( !is_numeric($this->yaxis->pos) && !is_string($this->yaxis->pos) )
  1099. $this->yaxis->SetPos($this->xscale->GetMinVal());
  1100. if( $this->y2axis != null ) {
  1101. if( !is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos) )
  1102. $this->y2axis->SetPos($this->xscale->GetMaxVal());
  1103. $this->y2axis->SetTitleSide(SIDE_RIGHT);
  1104. }
  1105. }
  1106. // If we have a negative values and x-axis position is at 0
  1107. // we need to supress the first and possible the last tick since
  1108. // they will be drawn on top of the y-axis (and possible y2 axis)
  1109. // The test below might seem strange the reasone being that if
  1110. // the user hasn't specified a value for position this will not
  1111. // be set until we do the stroke for the axis so as of now it
  1112. // is undefined.
  1113. // For X-text scale we ignore all this since the tick are usually
  1114. // much further in and not close to the Y-axis. Hence the test
  1115. // for 'text'
  1116. if( ($this->yaxis->pos==$this->xscale->GetMinVal() ||
  1117. (is_string($this->yaxis->pos) && $this->yaxis->pos=='min')) &&
  1118. !is_numeric($this->xaxis->pos) && $this->yscale->GetMinVal() < 0 &&
  1119. substr($this->axtype,0,4) != 'text' && $this->xaxis->pos!="min" ) {
  1120. //$this->yscale->ticks->SupressZeroLabel(false);
  1121. $this->xscale->ticks->SupressFirst();
  1122. if( $this->y2axis != null ) {
  1123. $this->xscale->ticks->SupressLast();
  1124. }
  1125. }
  1126. elseif( !is_numeric($this->yaxis->pos) && $this->yaxis->pos=='max' ) {
  1127. $this->xscale->ticks->SupressLast();
  1128. }
  1129. if( !$_csim ) {
  1130. $this->StrokePlotArea();
  1131. $this->StrokeAxis();
  1132. }
  1133. // Stroke bands
  1134. if( $this->bands != null && !$_csim)
  1135. for($i=0; $i<count($this->bands); ++$i) {
  1136. // Stroke all bands that asks to be in the background
  1137. if( $this->bands[$i]->depth == DEPTH_BACK )
  1138. $this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale);
  1139. }
  1140. if( $this->grid_depth == DEPTH_BACK && !$_csim) {
  1141. $this->ygrid->Stroke();
  1142. $this->xgrid->Stroke();
  1143. }
  1144. // Stroke Y2-axis
  1145. if( $this->y2axis != null && !$_csim) {
  1146. $this->y2axis->Stroke($this->xscale);
  1147. $this->y2grid->Stroke();
  1148. }
  1149. $oldoff=$this->xscale->off;
  1150. if(substr($this->axtype,0,4)=="text") {
  1151. $this->xscale->off +=
  1152. ceil($this->xscale->scale_factor*$this->text_scale_off*$this->xscale->ticks->minor_step);
  1153. }
  1154. // Stroke all plots for Y1 axis
  1155. for($i=0; $i < count($this->plots); ++$i) {
  1156. $this->plots[$i]->Stroke($this->img,$this->xscale,$this->yscale);
  1157. $this->plots[$i]->StrokeMargin($this->img);
  1158. }
  1159. // Stroke all plots for Y2 axis
  1160. if( $this->y2scale != null )
  1161. for($i=0; $i< count($this->y2plots); ++$i ) {
  1162. $this->y2plots[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
  1163. }
  1164. $this->xscale->off=$oldoff;
  1165. if( $this->grid_depth == DEPTH_FRONT && !$_csim ) {
  1166. $this->ygrid->Stroke();
  1167. $this->xgrid->Stroke();
  1168. }
  1169. // Stroke bands
  1170. if( $this->bands!= null )
  1171. for($i=0; $i<count($this->bands); ++$i) {
  1172. // Stroke all bands that asks to be in the foreground
  1173. if( $this->bands[$i]->depth == DEPTH_FRONT )
  1174. $this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale);
  1175. }
  1176. // Stroke any lines added
  1177. if( $this->lines != null ) {
  1178. for($i=0; $i<count($this->lines); ++$i) {
  1179. $this->lines[$i]->Stroke($this->img,$this->xscale,$this->yscale);
  1180. }
  1181. }
  1182. // Finally draw the axis again since some plots may have nagged
  1183. // the axis in the edges.
  1184. if( !$_csim )
  1185. $this->StrokeAxis();
  1186. if( $this->y2scale != null && !$_csim )
  1187. $this->y2axis->Stroke($this->xscale);
  1188. if( !$_csim ) {
  1189. $this->StrokePlotBox();
  1190. $this->footer->Stroke($this->img);
  1191. }
  1192. if( !$_csim ) {
  1193. // The titles and legends never gets rotated so make sure
  1194. // that the angle is 0 before stroking them
  1195. $aa = $this->img->SetAngle(0);
  1196. $this->StrokeTitles();
  1197. }
  1198. $this->legend->Stroke($this->img);
  1199. if( !$_csim ) {
  1200. $this->StrokeTexts();
  1201. $this->img->SetAngle($aa);
  1202. // Draw an outline around the image map
  1203. if(JPG_DEBUG)
  1204. $this->DisplayClientSideaImageMapAreas();
  1205. // Adjust the appearance of the image
  1206. $this->AdjustSaturationBrightnessContrast();
  1207. // If the filename is given as the special "__handle"
  1208. // then the image handler is returned and the image is NOT
  1209. // streamed back
  1210. if( $aStrokeFileName == _IMG_HANDLER ) {
  1211. return $this->img->img;
  1212. }
  1213. else {
  1214. // Finally stream the generated picture
  1215. $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
  1216. $aStrokeFileName);
  1217. }
  1218. }
  1219. }
  1220. //---------------
  1221. // PRIVATE METHODS
  1222. function StrokeAxis() {
  1223. // Stroke axis
  1224. if( $this->iAxisStyle != AXSTYLE_SIMPLE ) {
  1225. switch( $this->iAxisStyle ) {
  1226. case AXSTYLE_BOXIN :
  1227. $toppos = SIDE_DOWN;
  1228. $bottompos = SIDE_UP;
  1229. $leftpos = SIDE_RIGHT;
  1230. $rightpos = SIDE_LEFT;
  1231. break;
  1232. case AXSTYLE_BOXOUT :
  1233. $toppos = SIDE_UP;
  1234. $bottompos = SIDE_DOWN;
  1235. $leftpos = SIDE_LEFT;
  1236. $rightpos = SIDE_RIGHT;
  1237. break;
  1238. default:
  1239. JpGRaphError::Raise('Unknown AxisStyle() : '.$this->iAxisStyle);
  1240. break;
  1241. }
  1242. $this->xaxis->SetPos('min');
  1243. // By default we hide the first label so it doesn't cross the
  1244. // Y-axis in case the positon hasn't been set by the user.
  1245. // However, if we use a box we always want the first value
  1246. // displayed so we make sure it will be displayed.
  1247. $this->xscale->ticks->SupressFirst(false);
  1248. $this->xaxis->SetLabelSide(SIDE_DOWN);
  1249. $this->xaxis->scale->ticks->SetSide($bottompos);
  1250. $this->xaxis->Stroke($this->yscale);
  1251. // To avoid side effects we work on a new copy
  1252. $maxis = $this->xaxis;
  1253. $maxis->SetPos('max');
  1254. $maxis->SetLabelSide(SIDE_UP);
  1255. $maxis->SetLabelMargin(7);
  1256. $this->xaxis->scale->ticks->SetSide($toppos);
  1257. $maxis->Stroke($this->yscale);
  1258. $this->yaxis->SetPos('min');
  1259. $this->yaxis->SetLabelMargin(10);
  1260. $this->yaxis->SetLabelSide(SIDE_LEFT);
  1261. $this->yaxis->scale->ticks->SetSide($leftpos);
  1262. $this->yaxis->Stroke($this->xscale);
  1263. $myaxis = $this->yaxis;
  1264. $myaxis->SetPos('max');
  1265. $myaxis->SetLabelMargin(10);
  1266. $myaxis->SetLabelSide(SIDE_RIGHT);
  1267. $myaxis->scale->ticks->SetSide($rightpos);
  1268. $myaxis->Stroke($this->xscale);
  1269. }
  1270. else {
  1271. $this->xaxis->Stroke($this->yscale);
  1272. $this->yaxis->Stroke($this->xscale);
  1273. }
  1274. }
  1275. // Private helper function for backgound image
  1276. function LoadBkgImage($aImgFormat="png",$aBright=0,$aContr=0) {
  1277. if( $aImgFormat == "jpg" )
  1278. $f = "imagecreatefromjpeg";
  1279. else
  1280. $f = "imagecreatefrom".$aImgFormat;
  1281. $imgtag = $aImgFormat;
  1282. if( $aImgFormat == "jpeg" )
  1283. $imgtag = "jpg";
  1284. if( !strstr($this->background_image,$imgtag) && strstr($this->background_image,".") ) {
  1285. $t = " Background image seems to be of different type (has different file extension)".
  1286. " than specified imagetype. <br>Specified: '".
  1287. $aImgFormat."'<br>File: '".$this->background_image."'";
  1288. JpGraphError::Raise($t);
  1289. }
  1290. $img = $f($this->background_image);
  1291. if( !$img ) {
  1292. JpGraphError::Raise(" Can't read background image: '".$this->background_image."'");
  1293. }
  1294. return $img;
  1295. }
  1296. function StrokeFrameBackground() {
  1297. $bkgimg = $this->LoadBkgIma

Large files files are truncated, but you can click here to view the full file