source: trunk/client/modules/Elezioni/grafici/jpgraph.php@ 113

Last change on this file since 113 was 2, checked in by root, 15 years ago

importo il progetto

File size: 162.2 KB
Line 
1<?php
2//=======================================================================
3// File: JPGRAPH.PHP
4// Description: PHP Graph Plotting library. Base module.
5// Created: 2001-01-08
6// Ver: $Id: jpgraph.php 1091 2009-01-18 22:57:40Z ljp $
7//
8// Copyright (c) Aditus Consulting. All rights reserved.
9//========================================================================
10
11require_once('jpg-config.inc.php');
12require_once('jpgraph_gradient.php');
13require_once('jpgraph_errhandler.inc.php');
14require_once('jpgraph_ttf.inc.php');
15require_once('jpgraph_rgb.inc.php');
16require_once('jpgraph_text.inc.php');
17require_once('jpgraph_legend.inc.php');
18
19// Version info
20define('JPG_VERSION','2.3.5-dev');
21
22// Minimum required PHP version
23define('MIN_PHPVERSION','5.1.0');
24
25// Should the image be a truecolor image?
26define('USE_TRUECOLOR',true);
27
28//------------------------------------------------------------------------
29// Automatic settings of path for cache and font directory
30// if they have not been previously specified
31//------------------------------------------------------------------------
32if(USE_CACHE) {
33 if (!defined('CACHE_DIR')) {
34 if ( strstr( PHP_OS, 'WIN') ) {
35 if( empty($_SERVER['TEMP']) ) {
36 $t = new ErrMsgText();
37 $msg = $t->Get(11,$file,$lineno);
38 die($msg);
39 }
40 else {
41 define('CACHE_DIR', $_SERVER['TEMP'] . '/');
42 }
43 } else {
44 define('CACHE_DIR','/tmp/jpgraph_cache/');
45 }
46 }
47}
48elseif( !defined('CACHE_DIR') ) {
49 define('CACHE_DIR', '');
50}
51
52if (!defined('TTF_DIR')) {
53 if (strstr( PHP_OS, 'WIN') ) {
54 $sroot = getenv('SystemRoot');
55 if( empty($sroot) ) {
56 $t = new ErrMsgText();
57 $msg = $t->Get(12,$file,$lineno);
58 die($msg);
59 }
60 else {
61 define('TTF_DIR', $sroot.'/fonts/');
62 }
63 } else {
64 define('TTF_DIR','/usr/share/fonts/truetype/');
65 }
66}
67
68if (!defined('MBTTF_DIR')) {
69 if (strstr( PHP_OS, 'WIN') ) {
70 $sroot = getenv('SystemRoot');
71 if( empty($sroot) ) {
72 $t = new ErrMsgText();
73 $msg = $t->Get(12,$file,$lineno);
74 die($msg);
75 }
76 else {
77 define('TTF_DIR', $sroot.'/fonts/');
78 }
79 } else {
80 define('MBTTF_DIR','/usr/share/fonts/ja/TrueType/');
81 }
82}
83
84//------------------------------------------------------------------
85// Constants which are used as parameters for the method calls
86//------------------------------------------------------------------
87
88
89// Tick density
90define("TICKD_DENSE",1);
91define("TICKD_NORMAL",2);
92define("TICKD_SPARSE",3);
93define("TICKD_VERYSPARSE",4);
94
95// Side for ticks and labels.
96define("SIDE_LEFT",-1);
97define("SIDE_RIGHT",1);
98define("SIDE_DOWN",-1);
99define("SIDE_BOTTOM",-1);
100define("SIDE_UP",1);
101define("SIDE_TOP",1);
102
103// Legend type stacked vertical or horizontal
104define("LEGEND_VERT",0);
105define("LEGEND_HOR",1);
106
107// Mark types for plot marks
108define("MARK_SQUARE",1);
109define("MARK_UTRIANGLE",2);
110define("MARK_DTRIANGLE",3);
111define("MARK_DIAMOND",4);
112define("MARK_CIRCLE",5);
113define("MARK_FILLEDCIRCLE",6);
114define("MARK_CROSS",7);
115define("MARK_STAR",8);
116define("MARK_X",9);
117define("MARK_LEFTTRIANGLE",10);
118define("MARK_RIGHTTRIANGLE",11);
119define("MARK_FLASH",12);
120define("MARK_IMG",13);
121define("MARK_FLAG1",14);
122define("MARK_FLAG2",15);
123define("MARK_FLAG3",16);
124define("MARK_FLAG4",17);
125
126// Builtin images
127define("MARK_IMG_PUSHPIN",50);
128define("MARK_IMG_SPUSHPIN",50);
129define("MARK_IMG_LPUSHPIN",51);
130define("MARK_IMG_DIAMOND",52);
131define("MARK_IMG_SQUARE",53);
132define("MARK_IMG_STAR",54);
133define("MARK_IMG_BALL",55);
134define("MARK_IMG_SBALL",55);
135define("MARK_IMG_MBALL",56);
136define("MARK_IMG_LBALL",57);
137define("MARK_IMG_BEVEL",58);
138
139// Inline defines
140define("INLINE_YES",1);
141define("INLINE_NO",0);
142
143// Format for background images
144define("BGIMG_FILLPLOT",1);
145define("BGIMG_FILLFRAME",2);
146define("BGIMG_COPY",3);
147define("BGIMG_CENTER",4);
148define("BGIMG_FREE",5);
149
150// Depth of objects
151define("DEPTH_BACK",0);
152define("DEPTH_FRONT",1);
153
154// Direction
155define("VERTICAL",1);
156define("HORIZONTAL",0);
157
158
159// Axis styles for scientific style axis
160define('AXSTYLE_SIMPLE',1);
161define('AXSTYLE_BOXIN',2);
162define('AXSTYLE_BOXOUT',3);
163define('AXSTYLE_YBOXIN',4);
164define('AXSTYLE_YBOXOUT',5);
165
166// Style for title backgrounds
167define('TITLEBKG_STYLE1',1);
168define('TITLEBKG_STYLE2',2);
169define('TITLEBKG_STYLE3',3);
170define('TITLEBKG_FRAME_NONE',0);
171define('TITLEBKG_FRAME_FULL',1);
172define('TITLEBKG_FRAME_BOTTOM',2);
173define('TITLEBKG_FRAME_BEVEL',3);
174define('TITLEBKG_FILLSTYLE_HSTRIPED',1);
175define('TITLEBKG_FILLSTYLE_VSTRIPED',2);
176define('TITLEBKG_FILLSTYLE_SOLID',3);
177
178// Style for background gradient fills
179define('BGRAD_FRAME',1);
180define('BGRAD_MARGIN',2);
181define('BGRAD_PLOT',3);
182
183// Width of tab titles
184define('TABTITLE_WIDTHFIT',0);
185define('TABTITLE_WIDTHFULL',-1);
186
187// Defines for 3D skew directions
188define('SKEW3D_UP',0);
189define('SKEW3D_DOWN',1);
190define('SKEW3D_LEFT',2);
191define('SKEW3D_RIGHT',3);
192
193// Line styles
194define('LINESTYLE_SOLID',1);
195define('LINESTYLE_DOTTED',2);
196define('LINESTYLE_DASHED',3);
197define('LINESTYLE_LONGDASH',4);
198
199// For internal use only
200define("_JPG_DEBUG",false);
201define("_FORCE_IMGTOFILE",false);
202define("_FORCE_IMGDIR",'/tmp/jpgimg/');
203
204require_once('gd_image.inc.php');
205
206function CheckPHPVersion($aMinVersion)
207{
208 list($majorC, $minorC, $editC) = split('[/.-]', PHP_VERSION);
209 list($majorR, $minorR, $editR) = split('[/.-]', $aMinVersion);
210
211 if ($majorC != $majorR) return false;
212 if ($majorC < $majorR) return false;
213 // same major - check ninor
214 if ($minorC > $minorR) return true;
215 if ($minorC < $minorR) return false;
216 // and same minor
217 if ($editC >= $editR) return true;
218 return true;
219}
220
221//
222// Make sure PHP version is high enough
223//
224if( !CheckPHPVersion(MIN_PHPVERSION) ) {
225 JpGraphError::RaiseL(13,PHP_VERSION,MIN_PHPVERSION);
226 die();
227}
228
229
230//
231// Make GD sanity check
232//
233if( !function_exists("imagetypes") || !function_exists('imagecreatefromstring') ) {
234 JpGraphError::RaiseL(25001);
235//("This PHP installation is not configured with the GD library. Please recompile PHP with GD support to run JpGraph. (Neither function imagetypes() nor imagecreatefromstring() does exist)");
236}
237
238//
239// Setup PHP error handler
240//
241function _phpErrorHandler($errno,$errmsg,$filename, $linenum, $vars) {
242 // Respect current error level
243 if( $errno & error_reporting() ) {
244 JpGraphError::RaiseL(25003,basename($filename),$linenum,$errmsg);
245 }
246}
247
248if( INSTALL_PHP_ERR_HANDLER ) {
249 set_error_handler("_phpErrorHandler");
250}
251
252//
253//Check if there were any warnings, perhaps some wrong includes by the
254//user
255//
256if( isset($GLOBALS['php_errormsg']) && CATCH_PHPERRMSG &&
257 !preg_match('/|Deprecated|/i', $GLOBALS['php_errormsg']) ) {
258 JpGraphError::RaiseL(25004,$GLOBALS['php_errormsg']);
259}
260
261
262// Useful mathematical function
263function sign($a) {return $a >= 0 ? 1 : -1;}
264
265// Utility function to generate an image name based on the filename we
266// are running from and assuming we use auto detection of graphic format
267// (top level), i.e it is safe to call this function
268// from a script that uses JpGraph
269function GenImgName() {
270 // Determine what format we should use when we save the images
271 $supported = imagetypes();
272 if( $supported & IMG_PNG ) $img_format="png";
273 elseif( $supported & IMG_GIF ) $img_format="gif";
274 elseif( $supported & IMG_JPG ) $img_format="jpeg";
275 elseif( $supported & IMG_WBMP ) $img_format="wbmp";
276 elseif( $supported & IMG_XPM ) $img_format="xpm";
277
278
279 if( !isset($_SERVER['PHP_SELF']) )
280 JpGraphError::RaiseL(25005);
281//(" Can't access PHP_SELF, PHP global variable. You can't run PHP from command line if you want to use the 'auto' naming of cache or image files.");
282 $fname = basename($_SERVER['PHP_SELF']);
283 if( !empty($_SERVER['QUERY_STRING']) ) {
284 $q = @$_SERVER['QUERY_STRING'];
285 $fname .= '_'.preg_replace("/\W/", "_", $q).'.'.$img_format;
286 }
287 else {
288 $fname = substr($fname,0,strlen($fname)-4).'.'.$img_format;
289 }
290 return $fname;
291}
292
293
294//===================================================
295// CLASS JpgTimer
296// Description: General timing utility class to handle
297// time measurement of generating graphs. Multiple
298// timers can be started.
299//===================================================
300class JpgTimer {
301 private $start, $idx;
302//---------------
303// CONSTRUCTOR
304 function JpgTimer() {
305 $this->idx=0;
306 }
307
308//---------------
309// PUBLIC METHODS
310
311 // Push a new timer start on stack
312 function Push() {
313 list($ms,$s)=explode(" ",microtime());
314 $this->start[$this->idx++]=floor($ms*1000) + 1000*$s;
315 }
316
317 // Pop the latest timer start and return the diff with the
318 // current time
319 function Pop() {
320 assert($this->idx>0);
321 list($ms,$s)=explode(" ",microtime());
322 $etime=floor($ms*1000) + (1000*$s);
323 $this->idx--;
324 return $etime-$this->start[$this->idx];
325 }
326} // Class
327
328$gJpgBrandTiming = BRAND_TIMING;
329//===================================================
330// CLASS DateLocale
331// Description: Hold localized text used in dates
332//===================================================
333class DateLocale {
334
335 public $iLocale = 'C'; // environmental locale be used by default
336 private $iDayAbb = null, $iShortDay = null, $iShortMonth = null, $iMonthName = null;
337
338//---------------
339// CONSTRUCTOR
340 function DateLocale() {
341 settype($this->iDayAbb, 'array');
342 settype($this->iShortDay, 'array');
343 settype($this->iShortMonth, 'array');
344 settype($this->iMonthName, 'array');
345
346
347 $this->Set('C');
348 }
349
350//---------------
351// PUBLIC METHODS
352 function Set($aLocale) {
353 if ( in_array($aLocale, array_keys($this->iDayAbb)) ){
354 $this->iLocale = $aLocale;
355 return TRUE; // already cached nothing else to do!
356 }
357
358 $pLocale = setlocale(LC_TIME, 0); // get current locale for LC_TIME
359
360 if (is_array($aLocale)) {
361 foreach ($aLocale as $loc) {
362 $res = @setlocale(LC_TIME, $loc);
363 if ( $res ) {
364 $aLocale = $loc;
365 break;
366 }
367 }
368 }
369 else {
370 $res = @setlocale(LC_TIME, $aLocale);
371 }
372
373 if ( ! $res ){
374 JpGraphError::RaiseL(25007,$aLocale);
375//("You are trying to use the locale ($aLocale) which your PHP installation does not support. Hint: Use '' to indicate the default locale for this geographic region.");
376 return FALSE;
377 }
378
379 $this->iLocale = $aLocale;
380 for ( $i = 0, $ofs = 0 - strftime('%w'); $i < 7; $i++, $ofs++ ){
381 $day = strftime('%a', strtotime("$ofs day"));
382 $day[0] = strtoupper($day[0]);
383 $this->iDayAbb[$aLocale][]= $day[0];
384 $this->iShortDay[$aLocale][]= $day;
385 }
386
387 for($i=1; $i<=12; ++$i) {
388 list($short ,$full) = explode('|', strftime("%b|%B",strtotime("2001-$i-01")));
389 $this->iShortMonth[$aLocale][] = ucfirst($short);
390 $this->iMonthName [$aLocale][] = ucfirst($full);
391 }
392
393 setlocale(LC_TIME, $pLocale);
394
395 return TRUE;
396 }
397
398
399 function GetDayAbb() {
400 return $this->iDayAbb[$this->iLocale];
401 }
402
403 function GetShortDay() {
404 return $this->iShortDay[$this->iLocale];
405 }
406
407 function GetShortMonth() {
408 return $this->iShortMonth[$this->iLocale];
409 }
410
411 function GetShortMonthName($aNbr) {
412 return $this->iShortMonth[$this->iLocale][$aNbr];
413 }
414
415 function GetLongMonthName($aNbr) {
416 return $this->iMonthName[$this->iLocale][$aNbr];
417 }
418
419 function GetMonth() {
420 return $this->iMonthName[$this->iLocale];
421 }
422}
423
424$gDateLocale = new DateLocale();
425$gJpgDateLocale = new DateLocale();
426
427//=======================================================
428// CLASS Footer
429// Description: Encapsulates the footer line in the Graph
430//=======================================================
431class Footer {
432 public $iLeftMargin = 3, $iRightMargin = 3, $iBottomMargin = 3 ;
433 public $left,$center,$right;
434
435 function Footer() {
436 $this->left = new Text();
437 $this->left->ParagraphAlign('left');
438 $this->center = new Text();
439 $this->center->ParagraphAlign('center');
440 $this->right = new Text();
441 $this->right->ParagraphAlign('right');
442 }
443
444 function SetMargin($aLeft=3,$aRight=3,$aBottom=3) {
445 $this->iLeftMargin = $aLeft;
446 $this->iRightMargin = $aRight;
447 $this->iBottomMargin = $aBottom;
448 }
449
450 function Stroke($aImg) {
451 $y = $aImg->height - $this->iBottomMargin;
452 $x = $this->iLeftMargin;
453 $this->left->Align('left','bottom');
454 $this->left->Stroke($aImg,$x,$y);
455
456 $x = ($aImg->width - $this->iLeftMargin - $this->iRightMargin)/2;
457 $this->center->Align('center','bottom');
458 $this->center->Stroke($aImg,$x,$y);
459
460 $x = $aImg->width - $this->iRightMargin;
461 $this->right->Align('right','bottom');
462 $this->right->Stroke($aImg,$x,$y);
463 }
464}
465
466
467//===================================================
468// CLASS Graph
469// Description: Main class to handle graphs
470//===================================================
471class Graph {
472 public $cache=null; // Cache object (singleton)
473 public $img=null; // Img object (singleton)
474 public $plots=array(); // Array of all plot object in the graph (for Y 1 axis)
475 public $y2plots=array(); // Array of all plot object in the graph (for Y 2 axis)
476 public $ynplots=array();
477 public $xscale=null; // X Scale object (could be instance of LinearScale or LogScale
478 public $yscale=null,$y2scale=null, $ynscale=array();
479 public $iIcons = array(); // Array of Icons to add to
480 public $cache_name; // File name to be used for the current graph in the cache directory
481 public $xgrid=null; // X Grid object (linear or logarithmic)
482 public $ygrid=null,$y2grid=null; //dito for Y
483 public $doframe=true,$frame_color=array(0,0,0), $frame_weight=1; // Frame around graph
484 public $boxed=false, $box_color=array(0,0,0), $box_weight=1; // Box around plot area
485 public $doshadow=false,$shadow_width=4,$shadow_color=array(102,102,102); // Shadow for graph
486 public $xaxis=null; // X-axis (instane of Axis class)
487 public $yaxis=null, $y2axis=null, $ynaxis=array(); // Y axis (instance of Axis class)
488 public $margin_color=array(200,200,200); // Margin color of graph
489 public $plotarea_color=array(255,255,255); // Plot area color
490 public $title,$subtitle,$subsubtitle; // Title and subtitle(s) text object
491 public $axtype="linlin"; // Type of axis
492 public $xtick_factor,$ytick_factor; // Factor to determine the maximum number of ticks depending on the plot width
493 public $texts=null, $y2texts=null; // Text object to ge shown in the graph
494 public $lines=null, $y2lines=null;
495 public $bands=null, $y2bands=null;
496 public $text_scale_off=0, $text_scale_abscenteroff=-1; // Text scale in fractions and for centering bars
497 public $background_image="",$background_image_type=-1,$background_image_format="png";
498 public $background_image_bright=0,$background_image_contr=0,$background_image_sat=0;
499 public $background_image_xpos=0,$background_image_ypos=0;
500 public $image_bright=0, $image_contr=0, $image_sat=0;
501 public $inline;
502 public $showcsim=0,$csimcolor="red";//debug stuff, draw the csim boundaris on the image if <>0
503 public $grid_depth=DEPTH_BACK; // Draw grid under all plots as default
504 public $iAxisStyle = AXSTYLE_SIMPLE;
505 public $iCSIMdisplay=false,$iHasStroked = false;
506 public $footer;
507 public $csimcachename = '', $csimcachetimeout = 0, $iCSIMImgAlt='';
508 public $iDoClipping = false;
509 public $y2orderback=true;
510 public $tabtitle;
511 public $bkg_gradtype=-1,$bkg_gradstyle=BGRAD_MARGIN;
512 public $bkg_gradfrom='navy', $bkg_gradto='silver';
513 public $titlebackground = false;
514 public $titlebackground_color = 'lightblue',
515 $titlebackground_style = 1,
516 $titlebackground_framecolor = 'blue',
517 $titlebackground_framestyle = 2,
518 $titlebackground_frameweight = 1,
519 $titlebackground_bevelheight = 3 ;
520 public $titlebkg_fillstyle=TITLEBKG_FILLSTYLE_SOLID;
521 public $titlebkg_scolor1='black',$titlebkg_scolor2='white';
522 public $framebevel = false, $framebeveldepth = 2 ;
523 public $framebevelborder = false, $framebevelbordercolor='black';
524 public $framebevelcolor1='white@0.4', $framebevelcolor2='black@0.4';
525 public $background_image_mix=100;
526 public $background_cflag = '';
527 public $background_cflag_type = BGIMG_FILLPLOT;
528 public $background_cflag_mix = 100;
529 public $iImgTrans=false,
530 $iImgTransHorizon = 100,$iImgTransSkewDist=150,
531 $iImgTransDirection = 1, $iImgTransMinSize = true,
532 $iImgTransFillColor='white',$iImgTransHighQ=false,
533 $iImgTransBorder=false,$iImgTransHorizonPos=0.5;
534 public $legend;
535 protected $iYAxisDeltaPos=50;
536 protected $iIconDepth=DEPTH_BACK;
537 protected $iAxisLblBgType = 0,
538 $iXAxisLblBgFillColor = 'lightgray', $iXAxisLblBgColor = 'black',
539 $iYAxisLblBgFillColor = 'lightgray', $iYAxisLblBgColor = 'black';
540 protected $iTables=NULL;
541
542//---------------
543// CONSTRUCTOR
544
545 // aWIdth Width in pixels of image
546 // aHeight Height in pixels of image
547 // aCachedName Name for image file in cache directory
548 // aTimeOut Timeout in minutes for image in cache
549 // aInline If true the image is streamed back in the call to Stroke()
550 // If false the image is just created in the cache
551 function Graph($aWidth=300,$aHeight=200,$aCachedName="",$aTimeOut=0,$aInline=true) {
552 GLOBAL $gJpgBrandTiming;
553 // If timing is used create a new timing object
554 if( $gJpgBrandTiming ) {
555 global $tim;
556 $tim = new JpgTimer();
557 $tim->Push();
558 }
559
560 if( !is_numeric($aWidth) || !is_numeric($aHeight) ) {
561 JpGraphError::RaiseL(25008);//('Image width/height argument in Graph::Graph() must be numeric');
562 }
563
564 // Automatically generate the image file name based on the name of the script that
565 // generates the graph
566 if( $aCachedName=="auto" )
567 $aCachedName=GenImgName();
568
569 // Should the image be streamed back to the browser or only to the cache?
570 $this->inline=$aInline;
571
572 $this->img = new RotImage($aWidth,$aHeight);
573
574 $this->cache = new ImgStreamCache($this->img);
575 $this->cache->SetTimeOut($aTimeOut);
576
577 $this->title = new Text();
578 $this->title->ParagraphAlign('center');
579 $this->title->SetFont(FF_FONT2,FS_BOLD);
580 $this->title->SetMargin(3);
581 $this->title->SetAlign('center');
582
583 $this->subtitle = new Text();
584 $this->subtitle->ParagraphAlign('center');
585 $this->subtitle->SetMargin(2);
586 $this->subtitle->SetAlign('center');
587
588 $this->subsubtitle = new Text();
589 $this->subsubtitle->ParagraphAlign('center');
590 $this->subsubtitle->SetMargin(2);
591 $this->subsubtitle->SetAlign('center');
592
593 $this->legend = new Legend();
594 $this->footer = new Footer();
595
596 // Window doesn't like '?' in the file name so replace it with an '_'
597 $aCachedName = str_replace("?","_",$aCachedName);
598
599 // If the cached version exist just read it directly from the
600 // cache, stream it back to browser and exit
601 if( $aCachedName!="" && READ_CACHE && $aInline )
602 if( $this->cache->GetAndStream($aCachedName) ) {
603 exit();
604 }
605
606 $this->cache_name = $aCachedName;
607 $this->SetTickDensity(); // Normal density
608
609 $this->tabtitle = new GraphTabTitle();
610 }
611//---------------
612// PUBLIC METHODS
613
614 // Enable final image perspective transformation
615 function Set3DPerspective($aDir=1,$aHorizon=100,$aSkewDist=120,$aQuality=false,$aFillColor='#FFFFFF',$aBorder=false,$aMinSize=true,$aHorizonPos=0.5) {
616 $this->iImgTrans = true;
617 $this->iImgTransHorizon = $aHorizon;
618 $this->iImgTransSkewDist= $aSkewDist;
619 $this->iImgTransDirection = $aDir;
620 $this->iImgTransMinSize = $aMinSize;
621 $this->iImgTransFillColor=$aFillColor;
622 $this->iImgTransHighQ=$aQuality;
623 $this->iImgTransBorder=$aBorder;
624 $this->iImgTransHorizonPos=$aHorizonPos;
625 }
626
627 function SetUserFont($aNormal,$aBold='',$aItalic='',$aBoldIt='') {
628 $this->img->ttf->SetUserFont($aNormal,$aBold,$aItalic,$aBoldIt);
629 }
630
631 function SetUserFont1($aNormal,$aBold='',$aItalic='',$aBoldIt='') {
632 $this->img->ttf->SetUserFont1($aNormal,$aBold,$aItalic,$aBoldIt);
633 }
634
635 function SetUserFont2($aNormal,$aBold='',$aItalic='',$aBoldIt='') {
636 $this->img->ttf->SetUserFont2($aNormal,$aBold,$aItalic,$aBoldIt);
637 }
638
639 function SetUserFont3($aNormal,$aBold='',$aItalic='',$aBoldIt='') {
640 $this->img->ttf->SetUserFont3($aNormal,$aBold,$aItalic,$aBoldIt);
641 }
642
643 // Set Image format and optional quality
644 function SetImgFormat($aFormat,$aQuality=75) {
645 $this->img->SetImgFormat($aFormat,$aQuality);
646 }
647
648 // Should the grid be in front or back of the plot?
649 function SetGridDepth($aDepth) {
650 $this->grid_depth=$aDepth;
651 }
652
653 function SetIconDepth($aDepth) {
654 $this->iIconDepth=$aDepth;
655 }
656
657 // Specify graph angle 0-360 degrees.
658 function SetAngle($aAngle) {
659 $this->img->SetAngle($aAngle);
660 }
661
662 function SetAlphaBlending($aFlg=true) {
663 $this->img->SetAlphaBlending($aFlg);
664 }
665
666 // Shortcut to image margin
667 function SetMargin($lm,$rm,$tm,$bm) {
668 $this->img->SetMargin($lm,$rm,$tm,$bm);
669 }
670
671 function SetY2OrderBack($aBack=true) {
672 $this->y2orderback = $aBack;
673 }
674
675 // Rotate the graph 90 degrees and set the margin
676 // when we have done a 90 degree rotation
677 function Set90AndMargin($lm=0,$rm=0,$tm=0,$bm=0) {
678 $lm = $lm ==0 ? floor(0.2 * $this->img->width) : $lm ;
679 $rm = $rm ==0 ? floor(0.1 * $this->img->width) : $rm ;
680 $tm = $tm ==0 ? floor(0.2 * $this->img->height) : $tm ;
681 $bm = $bm ==0 ? floor(0.1 * $this->img->height) : $bm ;
682
683 $adj = ($this->img->height - $this->img->width)/2;
684 $this->img->SetMargin($tm-$adj,$bm-$adj,$rm+$adj,$lm+$adj);
685 $this->img->SetCenter(floor($this->img->width/2),floor($this->img->height/2));
686 $this->SetAngle(90);
687 if( empty($this->yaxis) || empty($this->xaxis) ) {
688 JpgraphError::RaiseL(25009);//('You must specify what scale to use with a call to Graph::SetScale()');
689 }
690 $this->xaxis->SetLabelAlign('right','center');
691 $this->yaxis->SetLabelAlign('center','bottom');
692 }
693
694 function SetClipping($aFlg=true) {
695 $this->iDoClipping = $aFlg ;
696 }
697
698 // Add a plot object to the graph
699 function Add($aPlot) {
700 if( $aPlot == null )
701 JpGraphError::RaiseL(25010);//("Graph::Add() You tried to add a null plot to the graph.");
702 if( is_array($aPlot) && count($aPlot) > 0 )
703 $cl = $aPlot[0];
704 else
705 $cl = $aPlot;
706
707 if( $cl instanceof Text )
708 $this->AddText($aPlot);
709 elseif( $cl instanceof PlotLine )
710 $this->AddLine($aPlot);
711 elseif( class_exists('PlotBand',false) && ($cl instanceof PlotBand) )
712 $this->AddBand($aPlot);
713 elseif( class_exists('IconPlot',false) && ($cl instanceof IconPlot) )
714 $this->AddIcon($aPlot);
715 elseif( class_exists('GTextTable',false) && ($cl instanceof GTextTable) )
716 $this->AddTable($aPlot);
717 else
718 $this->plots[] = $aPlot;
719 }
720
721 function AddTable($aTable) {
722 if( is_array($aTable) ) {
723 for($i=0; $i < count($aTable); ++$i )
724 $this->iTables[]=$aTable[$i];
725 }
726 else {
727 $this->iTables[] = $aTable ;
728 }
729 }
730
731 function AddIcon($aIcon) {
732 if( is_array($aIcon) ) {
733 for($i=0; $i < count($aIcon); ++$i )
734 $this->iIcons[]=$aIcon[$i];
735 }
736 else {
737 $this->iIcons[] = $aIcon ;
738 }
739 }
740
741 // Add plot to second Y-scale
742 function AddY2($aPlot) {
743 if( $aPlot == null )
744 JpGraphError::RaiseL(25011);//("Graph::AddY2() You tried to add a null plot to the graph.");
745
746 if( is_array($aPlot) && count($aPlot) > 0 )
747 $cl = $aPlot[0];
748 else
749 $cl = $aPlot;
750
751 if( $cl instanceof Text )
752 $this->AddText($aPlot,true);
753 elseif( $cl instanceof PlotLine )
754 $this->AddLine($aPlot,true);
755 elseif( class_exists('PlotBand',false) && ($cl instanceof PlotBand) )
756 $this->AddBand($aPlot,true);
757 else
758 $this->y2plots[] = $aPlot;
759 }
760
761 // Add plot to the extra Y-axises
762 function AddY($aN,$aPlot) {
763
764 if( $aPlot == null )
765 JpGraphError::RaiseL(25012);//("Graph::AddYN() You tried to add a null plot to the graph.");
766
767 if( is_array($aPlot) && count($aPlot) > 0 )
768 $cl = $aPlot[0];
769 else
770 $cl = $aPlot;
771
772 if( ($cl instanceof Text) || ($cl instanceof PlotLine) ||
773 (class_exists('PlotBand',false) && ($cl instanceof PlotBand)) )
774 JpGraph::RaiseL(25013);//('You can only add standard plots to multiple Y-axis');
775 else
776 $this->ynplots[$aN][] = $aPlot;
777 }
778
779 // Add text object to the graph
780 function AddText($aTxt,$aToY2=false) {
781 if( $aTxt == null )
782 JpGraphError::RaiseL(25014);//("Graph::AddText() You tried to add a null text to the graph.");
783 if( $aToY2 ) {
784 if( is_array($aTxt) ) {
785 for($i=0; $i < count($aTxt); ++$i )
786 $this->y2texts[]=$aTxt[$i];
787 }
788 else
789 $this->y2texts[] = $aTxt;
790 }
791 else {
792 if( is_array($aTxt) ) {
793 for($i=0; $i < count($aTxt); ++$i )
794 $this->texts[]=$aTxt[$i];
795 }
796 else
797 $this->texts[] = $aTxt;
798 }
799 }
800
801 // Add a line object (class PlotLine) to the graph
802 function AddLine($aLine,$aToY2=false) {
803 if( $aLine == null )
804 JpGraphError::RaiseL(25015);//("Graph::AddLine() You tried to add a null line to the graph.");
805
806 if( $aToY2 ) {
807 if( is_array($aLine) ) {
808 for($i=0; $i < count($aLine); ++$i )
809 $this->y2lines[]=$aLine[$i];
810 }
811 else
812 $this->y2lines[] = $aLine;
813 }
814 else {
815 if( is_array($aLine) ) {
816 for($i=0; $i<count($aLine); ++$i )
817 $this->lines[]=$aLine[$i];
818 }
819 else
820 $this->lines[] = $aLine;
821 }
822 }
823
824 // Add vertical or horizontal band
825 function AddBand($aBand,$aToY2=false) {
826 if( $aBand == null )
827 JpGraphError::RaiseL(25016);//(" Graph::AddBand() You tried to add a null band to the graph.");
828
829 if( $aToY2 ) {
830 if( is_array($aBand) ) {
831 for($i=0; $i < count($aBand); ++$i )
832 $this->y2bands[] = $aBand[$i];
833 }
834 else
835 $this->y2bands[] = $aBand;
836 }
837 else {
838 if( is_array($aBand) ) {
839 for($i=0; $i < count($aBand); ++$i )
840 $this->bands[] = $aBand[$i];
841 }
842 else
843 $this->bands[] = $aBand;
844 }
845 }
846
847 function SetBackgroundGradient($aFrom='navy',$aTo='silver',$aGradType=2,$aStyle=BGRAD_FRAME) {
848 $this->bkg_gradtype=$aGradType;
849 $this->bkg_gradstyle=$aStyle;
850 $this->bkg_gradfrom = $aFrom;
851 $this->bkg_gradto = $aTo;
852 }
853
854 // Set a country flag in the background
855 function SetBackgroundCFlag($aName,$aBgType=BGIMG_FILLPLOT,$aMix=100) {
856 $this->background_cflag = $aName;
857 $this->background_cflag_type = $aBgType;
858 $this->background_cflag_mix = $aMix;
859 }
860
861 // Alias for the above method
862 function SetBackgroundCountryFlag($aName,$aBgType=BGIMG_FILLPLOT,$aMix=100) {
863 $this->background_cflag = $aName;
864 $this->background_cflag_type = $aBgType;
865 $this->background_cflag_mix = $aMix;
866 }
867
868
869 // Specify a background image
870 function SetBackgroundImage($aFileName,$aBgType=BGIMG_FILLPLOT,$aImgFormat="auto") {
871
872 if( !USE_TRUECOLOR ) {
873 JpGraphError::RaiseL(25017);//("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.");
874 }
875
876 // Get extension to determine image type
877 if( $aImgFormat == "auto" ) {
878 $e = explode('.',$aFileName);
879 if( !$e ) {
880 JpGraphError::RaiseL(25018,$aFileName);//('Incorrect file name for Graph::SetBackgroundImage() : '.$aFileName.' Must have a valid image extension (jpg,gif,png) when using autodetection of image type');
881 }
882
883 $valid_formats = array('png', 'jpg', 'gif');
884 $aImgFormat = strtolower($e[count($e)-1]);
885 if ($aImgFormat == 'jpeg') {
886 $aImgFormat = 'jpg';
887 }
888 elseif (!in_array($aImgFormat, $valid_formats) ) {
889 JpGraphError::RaiseL(25019,$aImgFormat);//('Unknown file extension ($aImgFormat) in Graph::SetBackgroundImage() for filename: '.$aFileName);
890 }
891 }
892
893 $this->background_image = $aFileName;
894 $this->background_image_type=$aBgType;
895 $this->background_image_format=$aImgFormat;
896 }
897
898 function SetBackgroundImageMix($aMix) {
899 $this->background_image_mix = $aMix ;
900 }
901
902 // Adjust background image position
903 function SetBackgroundImagePos($aXpos,$aYpos) {
904 $this->background_image_xpos = $aXpos ;
905 $this->background_image_ypos = $aYpos ;
906 }
907
908 // Specify axis style (boxed or single)
909 function SetAxisStyle($aStyle) {
910 $this->iAxisStyle = $aStyle ;
911 }
912
913 // Set a frame around the plot area
914 function SetBox($aDrawPlotFrame=true,$aPlotFrameColor=array(0,0,0),$aPlotFrameWeight=1) {
915 $this->boxed = $aDrawPlotFrame;
916 $this->box_weight = $aPlotFrameWeight;
917 $this->box_color = $aPlotFrameColor;
918 }
919
920 // Specify color for the plotarea (not the margins)
921 function SetColor($aColor) {
922 $this->plotarea_color=$aColor;
923 }
924
925 // Specify color for the margins (all areas outside the plotarea)
926 function SetMarginColor($aColor) {
927 $this->margin_color=$aColor;
928 }
929
930 // Set a frame around the entire image
931 function SetFrame($aDrawImgFrame=true,$aImgFrameColor=array(0,0,0),$aImgFrameWeight=1) {
932 $this->doframe = $aDrawImgFrame;
933 $this->frame_color = $aImgFrameColor;
934 $this->frame_weight = $aImgFrameWeight;
935 }
936
937 function SetFrameBevel($aDepth=3,$aBorder=false,$aBorderColor='black',$aColor1='white@0.4',$aColor2='darkgray@0.4',$aFlg=true) {
938 $this->framebevel = $aFlg ;
939 $this->framebeveldepth = $aDepth ;
940 $this->framebevelborder = $aBorder ;
941 $this->framebevelbordercolor = $aBorderColor ;
942 $this->framebevelcolor1 = $aColor1 ;
943 $this->framebevelcolor2 = $aColor2 ;
944
945 $this->doshadow = false ;
946 }
947
948 // Set the shadow around the whole image
949 function SetShadow($aShowShadow=true,$aShadowWidth=5,$aShadowColor=array(102,102,102)) {
950 $this->doshadow = $aShowShadow;
951 $this->shadow_color = $aShadowColor;
952 $this->shadow_width = $aShadowWidth;
953 $this->footer->iBottomMargin += $aShadowWidth;
954 $this->footer->iRightMargin += $aShadowWidth;
955 }
956
957 // Specify x,y scale. Note that if you manually specify the scale
958 // you must also specify the tick distance with a call to Ticks::Set()
959 function SetScale($aAxisType,$aYMin=1,$aYMax=1,$aXMin=1,$aXMax=1) {
960 $this->axtype = $aAxisType;
961
962 if( $aYMax < $aYMin || $aXMax < $aXMin )
963 JpGraphError::RaiseL(25020);//('Graph::SetScale(): Specified Max value must be larger than the specified Min value.');
964
965 $yt=substr($aAxisType,-3,3);
966 if( $yt=="lin" )
967 $this->yscale = new LinearScale($aYMin,$aYMax);
968 elseif( $yt == "int" ) {
969 $this->yscale = new LinearScale($aYMin,$aYMax);
970 $this->yscale->SetIntScale();
971 }
972 elseif( $yt=="log" )
973 $this->yscale = new LogScale($aYMin,$aYMax);
974 else
975 JpGraphError::RaiseL(25021,$aAxisType);//("Unknown scale specification for Y-scale. ($aAxisType)");
976
977 $xt=substr($aAxisType,0,3);
978 if( $xt == "lin" || $xt == "tex" ) {
979 $this->xscale = new LinearScale($aXMin,$aXMax,"x");
980 $this->xscale->textscale = ($xt == "tex");
981 }
982 elseif( $xt == "int" ) {
983 $this->xscale = new LinearScale($aXMin,$aXMax,"x");
984 $this->xscale->SetIntScale();
985 }
986 elseif( $xt == "dat" ) {
987 $this->xscale = new DateScale($aXMin,$aXMax,"x");
988 }
989 elseif( $xt == "log" )
990 $this->xscale = new LogScale($aXMin,$aXMax,"x");
991 else
992 JpGraphError::RaiseL(25022,$aAxisType);//(" Unknown scale specification for X-scale. ($aAxisType)");
993
994 $this->xaxis = new Axis($this->img,$this->xscale);
995 $this->yaxis = new Axis($this->img,$this->yscale);
996 $this->xgrid = new Grid($this->xaxis);
997 $this->ygrid = new Grid($this->yaxis);
998 $this->ygrid->Show();
999 }
1000
1001 // Specify secondary Y scale
1002 function SetY2Scale($aAxisType="lin",$aY2Min=1,$aY2Max=1) {
1003 if( $aAxisType=="lin" )
1004 $this->y2scale = new LinearScale($aY2Min,$aY2Max);
1005 elseif( $aAxisType == "int" ) {
1006 $this->y2scale = new LinearScale($aY2Min,$aY2Max);
1007 $this->y2scale->SetIntScale();
1008 }
1009 elseif( $aAxisType=="log" ) {
1010 $this->y2scale = new LogScale($aY2Min,$aY2Max);
1011 }
1012 else JpGraphError::RaiseL(25023,$aAxisType);//("JpGraph: Unsupported Y2 axis type: $aAxisType\nMust be one of (lin,log,int)");
1013
1014 $this->y2axis = new Axis($this->img,$this->y2scale);
1015 $this->y2axis->scale->ticks->SetDirection(SIDE_LEFT);
1016 $this->y2axis->SetLabelSide(SIDE_RIGHT);
1017 $this->y2axis->SetPos('max');
1018 $this->y2axis->SetTitleSide(SIDE_RIGHT);
1019
1020 // Deafult position is the max x-value
1021 $this->y2grid = new Grid($this->y2axis);
1022 }
1023
1024 // Set the delta position (in pixels) between the multiple Y-axis
1025 function SetYDeltaDist($aDist) {
1026 $this->iYAxisDeltaPos = $aDist;
1027 }
1028
1029 // Specify secondary Y scale
1030 function SetYScale($aN,$aAxisType="lin",$aYMin=1,$aYMax=1) {
1031
1032 if( $aAxisType=="lin" )
1033 $this->ynscale[$aN] = new LinearScale($aYMin,$aYMax);
1034 elseif( $aAxisType == "int" ) {
1035 $this->ynscale[$aN] = new LinearScale($aYMin,$aYMax);
1036 $this->ynscale[$aN]->SetIntScale();
1037 }
1038 elseif( $aAxisType=="log" ) {
1039 $this->ynscale[$aN] = new LogScale($aYMin,$aYMax);
1040 }
1041 else JpGraphError::RaiseL(25024,$aAxisType);//("JpGraph: Unsupported Y axis type: $aAxisType\nMust be one of (lin,log,int)");
1042
1043 $this->ynaxis[$aN] = new Axis($this->img,$this->ynscale[$aN]);
1044 $this->ynaxis[$aN]->scale->ticks->SetDirection(SIDE_LEFT);
1045 $this->ynaxis[$aN]->SetLabelSide(SIDE_RIGHT);
1046 }
1047
1048 // Specify density of ticks when autoscaling 'normal', 'dense', 'sparse', 'verysparse'
1049 // The dividing factor have been determined heuristically according to my aesthetic
1050 // sense (or lack off) y.m.m.v !
1051 function SetTickDensity($aYDensity=TICKD_NORMAL,$aXDensity=TICKD_NORMAL) {
1052 $this->xtick_factor=30;
1053 $this->ytick_factor=25;
1054 switch( $aYDensity ) {
1055 case TICKD_DENSE:
1056 $this->ytick_factor=12;
1057 break;
1058 case TICKD_NORMAL:
1059 $this->ytick_factor=25;
1060 break;
1061 case TICKD_SPARSE:
1062 $this->ytick_factor=40;
1063 break;
1064 case TICKD_VERYSPARSE:
1065 $this->ytick_factor=100;
1066 break;
1067 default:
1068 JpGraphError::RaiseL(25025,$densy);//("JpGraph: Unsupported Tick density: $densy");
1069 }
1070 switch( $aXDensity ) {
1071 case TICKD_DENSE:
1072 $this->xtick_factor=15;
1073 break;
1074 case TICKD_NORMAL:
1075 $this->xtick_factor=30;
1076 break;
1077 case TICKD_SPARSE:
1078 $this->xtick_factor=45;
1079 break;
1080 case TICKD_VERYSPARSE:
1081 $this->xtick_factor=60;
1082 break;
1083 default:
1084 JpGraphError::RaiseL(25025,$densx);//("JpGraph: Unsupported Tick density: $densx");
1085 }
1086 }
1087
1088
1089 // Get a string of all image map areas
1090 function GetCSIMareas() {
1091 if( !$this->iHasStroked )
1092 $this->Stroke(_CSIM_SPECIALFILE);
1093
1094 $csim = $this->title->GetCSIMAreas();
1095 $csim .= $this->subtitle->GetCSIMAreas();
1096 $csim .= $this->subsubtitle->GetCSIMAreas();
1097 $csim .= $this->legend->GetCSIMAreas();
1098
1099 if( $this->y2axis != NULL ) {
1100 $csim .= $this->y2axis->title->GetCSIMAreas();
1101 }
1102
1103 if( $this->texts != null ) {
1104 $n = count($this->texts);
1105 for($i=0; $i < $n; ++$i ) {
1106 $csim .= $this->texts[$i]->GetCSIMAreas();
1107 }
1108 }
1109
1110 if( $this->y2texts != null && $this->y2scale != null ) {
1111 $n = count($this->y2texts);
1112 for($i=0; $i < $n; ++$i ) {
1113 $csim .= $this->y2texts[$i]->GetCSIMAreas();
1114 }
1115 }
1116
1117 if( $this->yaxis != null && $this->xaxis != null ) {
1118 $csim .= $this->yaxis->title->GetCSIMAreas();
1119 $csim .= $this->xaxis->title->GetCSIMAreas();
1120 }
1121
1122 $n = count($this->plots);
1123 for( $i=0; $i < $n; ++$i )
1124 $csim .= $this->plots[$i]->GetCSIMareas();
1125
1126 $n = count($this->y2plots);
1127 for( $i=0; $i < $n; ++$i )
1128 $csim .= $this->y2plots[$i]->GetCSIMareas();
1129
1130 $n = count($this->ynaxis);
1131 for( $i=0; $i < $n; ++$i ) {
1132 $m = count($this->ynplots[$i]);
1133 for($j=0; $j < $m; ++$j ) {
1134 $csim .= $this->ynplots[$i][$j]->GetCSIMareas();
1135 }
1136 }
1137
1138 $n = count($this->iTables);
1139 for( $i=0; $i < $n; ++$i ) {
1140 $csim .= $this->iTables[$i]->GetCSIMareas();
1141 }
1142
1143 return $csim;
1144 }
1145
1146 // Get a complete <MAP>..</MAP> tag for the final image map
1147 function GetHTMLImageMap($aMapName) {
1148 $im = "<map name=\"$aMapName\" id=\"$aMapName\" >\n";
1149 $im .= $this->GetCSIMareas();
1150 $im .= "</map>";
1151 return $im;
1152 }
1153
1154 function CheckCSIMCache($aCacheName,$aTimeOut=60) {
1155 global $_SERVER;
1156
1157 if( $aCacheName=='auto' )
1158 $aCacheName=basename($_SERVER['PHP_SELF']);
1159
1160 $urlarg = $this->GetURLArguments();
1161 $this->csimcachename = CSIMCACHE_DIR.$aCacheName.$urlarg;
1162 $this->csimcachetimeout = $aTimeOut;
1163
1164 // First determine if we need to check for a cached version
1165 // This differs from the standard cache in the sense that the
1166 // image and CSIM map HTML file is written relative to the directory
1167 // the script executes in and not the specified cache directory.
1168 // The reason for this is that the cache directory is not necessarily
1169 // accessible from the HTTP server.
1170 if( $this->csimcachename != '' ) {
1171 $dir = dirname($this->csimcachename);
1172 $base = basename($this->csimcachename);
1173 $base = strtok($base,'.');
1174 $suffix = strtok('.');
1175 $basecsim = $dir.'/'.$base.'?'.$urlarg.'_csim_.html';
1176 $baseimg = $dir.'/'.$base.'?'.$urlarg.'.'.$this->img->img_format;
1177
1178 $timedout=false;
1179 // Does it exist at all ?
1180
1181 if( file_exists($basecsim) && file_exists($baseimg) ) {
1182 // Check that it hasn't timed out
1183 $diff=time()-filemtime($basecsim);
1184 if( $this->csimcachetimeout>0 && ($diff > $this->csimcachetimeout*60) ) {
1185 $timedout=true;
1186 @unlink($basecsim);
1187 @unlink($baseimg);
1188 }
1189 else {
1190 if ($fh = @fopen($basecsim, "r")) {
1191 fpassthru($fh);
1192 return true;
1193 }
1194 else
1195 JpGraphError::RaiseL(25027,$basecsim);//(" Can't open cached CSIM \"$basecsim\" for reading.");
1196 }
1197 }
1198 }
1199 return false;
1200 }
1201
1202 // Build the argument string to be used with the csim images
1203 function GetURLArguments() {
1204
1205 // This is a JPGRAPH internal defined that prevents
1206 // us from recursively coming here again
1207 $urlarg = _CSIM_DISPLAY.'=1';
1208
1209 // Now reconstruct any user URL argument
1210 reset($_GET);
1211 while( list($key,$value) = each($_GET) ) {
1212 if( is_array($value) ) {
1213 foreach ( $value as $k => $v ) {
1214 $urlarg .= '&amp;'.$key.'%5B'.$k.'%5D='.urlencode($v);
1215 }
1216 }
1217 else {
1218 $urlarg .= '&amp;'.$key.'='.urlencode($value);
1219 }
1220 }
1221
1222 // It's not ideal to convert POST argument to GET arguments
1223 // but there is little else we can do. One idea for the
1224 // future might be recreate the POST header in case.
1225 reset($_POST);
1226 while( list($key,$value) = each($_POST) ) {
1227 if( is_array($value) ) {
1228 foreach ( $value as $k => $v ) {
1229 $urlarg .= '&amp;'.$key.'%5B'.$k.'%5D='.urlencode($v);
1230 }
1231 }
1232 else {
1233 $urlarg .= '&amp;'.$key.'='.urlencode($value);
1234 }
1235 }
1236
1237 return $urlarg;
1238 }
1239
1240 function SetCSIMImgAlt($aAlt) {
1241 $this->iCSIMImgAlt = $aAlt;
1242 }
1243
1244 function StrokeCSIM($aScriptName='auto',$aCSIMName='',$aBorder=0) {
1245 if( $aCSIMName=='' ) {
1246 // create a random map name
1247 srand ((double) microtime() * 1000000);
1248 $r = rand(0,100000);
1249 $aCSIMName='__mapname'.$r.'__';
1250 }
1251
1252 if( $aScriptName=='auto' )
1253 $aScriptName=basename($_SERVER['PHP_SELF']);
1254
1255 $urlarg = $this->GetURLArguments();
1256
1257 if( empty($_GET[_CSIM_DISPLAY]) ) {
1258 // First determine if we need to check for a cached version
1259 // This differs from the standard cache in the sense that the
1260 // image and CSIM map HTML file is written relative to the directory
1261 // the script executes in and not the specified cache directory.
1262 // The reason for this is that the cache directory is not necessarily
1263 // accessible from the HTTP server.
1264 if( $this->csimcachename != '' ) {
1265 $dir = dirname($this->csimcachename);
1266 $base = basename($this->csimcachename);
1267 $base = strtok($base,'.');
1268 $suffix = strtok('.');
1269 $basecsim = $dir.'/'.$base.'?'.$urlarg.'_csim_.html';
1270 $baseimg = $base.'?'.$urlarg.'.'.$this->img->img_format;
1271
1272 // Check that apache can write to directory specified
1273
1274 if( file_exists($dir) && !is_writeable($dir) ) {
1275 JpgraphError::RaiseL(25028,$dir);//('Apache/PHP does not have permission to write to the CSIM cache directory ('.$dir.'). Check permissions.');
1276 }
1277
1278 // Make sure directory exists
1279 $this->cache->MakeDirs($dir);
1280
1281 // Write the image file
1282 $this->Stroke(CSIMCACHE_DIR.$baseimg);
1283
1284 // Construct wrapper HTML and write to file and send it back to browser
1285
1286 // In the src URL we must replace the '?' with its encoding to prevent the arguments
1287 // to be converted to real arguments.
1288 $tmp = str_replace('?','%3f',$baseimg);
1289 $htmlwrap = $this->GetHTMLImageMap($aCSIMName)."\n".
1290 '<img src="'.CSIMCACHE_HTTP_DIR.$tmp.'" ismap="ismap" usemap="#'.$aCSIMName.'" border="'.$aBorder.'" width="'.$this->img->width.'" height="'.$this->img->height."\" alt=\"".$this->iCSIMImgAlt."\" />\n";
1291
1292 if($fh = @fopen($basecsim,'w') ) {
1293 fwrite($fh,$htmlwrap);
1294 fclose($fh);
1295 echo $htmlwrap;
1296 }
1297 else
1298 JpGraphError::RaiseL(25029,$basecsim);//(" Can't write CSIM \"$basecsim\" for writing. Check free space and permissions.");
1299 }
1300 else {
1301
1302 if( $aScriptName=='' ) {
1303 JpGraphError::RaiseL(25030);//('Missing script name in call to StrokeCSIM(). You must specify the name of the actual image script as the first parameter to StrokeCSIM().');
1304 }
1305 echo $this->GetHTMLImageMap($aCSIMName);
1306 echo "<img src=\"".$aScriptName.'?'.$urlarg."\" ismap=\"ismap\" usemap=\"#".$aCSIMName.'" border="'.$aBorder.'" width="'.$this->img->width.'" height="'.$this->img->height."\" alt=\"".$this->iCSIMImgAlt."\" />\n";
1307 }
1308 }
1309 else {
1310 $this->Stroke();
1311 }
1312 }
1313
1314 function GetTextsYMinMax($aY2=false) {
1315 if( $aY2 )
1316 $txts = $this->y2texts;
1317 else
1318 $txts = $this->texts;
1319 $n = count($txts);
1320 $min=null;
1321 $max=null;
1322 for( $i=0; $i < $n; ++$i ) {
1323 if( $txts[$i]->iScalePosY !== null &&
1324 $txts[$i]->iScalePosX !== null ) {
1325 if( $min === null ) {
1326 $min = $max = $txts[$i]->iScalePosY ;
1327 }
1328 else {
1329 $min = min($min,$txts[$i]->iScalePosY);
1330 $max = max($max,$txts[$i]->iScalePosY);
1331 }
1332 }
1333 }
1334 if( $min !== null ) {
1335 return array($min,$max);
1336 }
1337 else
1338 return null;
1339 }
1340
1341 function GetTextsXMinMax($aY2=false) {
1342 if( $aY2 )
1343 $txts = $this->y2texts;
1344 else
1345 $txts = $this->texts;
1346 $n = count($txts);
1347 $min=null;
1348 $max=null;
1349 for( $i=0; $i < $n; ++$i ) {
1350 if( $txts[$i]->iScalePosY !== null &&
1351 $txts[$i]->iScalePosX !== null ) {
1352 if( $min === null ) {
1353 $min = $max = $txts[$i]->iScalePosX ;
1354 }
1355 else {
1356 $min = min($min,$txts[$i]->iScalePosX);
1357 $max = max($max,$txts[$i]->iScalePosX);
1358 }
1359 }
1360 }
1361 if( $min !== null ) {
1362 return array($min,$max);
1363 }
1364 else
1365 return null;
1366 }
1367
1368 function GetXMinMax() {
1369 list($min,$ymin) = $this->plots[0]->Min();
1370 list($max,$ymax) = $this->plots[0]->Max();
1371 foreach( $this->plots as $p ) {
1372 list($xmin,$ymin) = $p->Min();
1373 list($xmax,$ymax) = $p->Max();
1374 $min = Min($xmin,$min);
1375 $max = Max($xmax,$max);
1376 }
1377
1378 if( $this->y2axis != null ) {
1379 foreach( $this->y2plots as $p ) {
1380 list($xmin,$ymin) = $p->Min();
1381 list($xmax,$ymax) = $p->Max();
1382 $min = Min($xmin,$min);
1383 $max = Max($xmax,$max);
1384 }
1385 }
1386
1387 $n = count($this->ynaxis);
1388 for( $i=0; $i < $n; ++$i ) {
1389 if( $this->ynaxis[$i] != null) {
1390 foreach( $this->ynplots[$i] as $p ) {
1391 list($xmin,$ymin) = $p->Min();
1392 list($xmax,$ymax) = $p->Max();
1393 $min = Min($xmin,$min);
1394 $max = Max($xmax,$max);
1395 }
1396 }
1397 }
1398 return array($min,$max);
1399 }
1400
1401 function AdjustMarginsForTitles() {
1402 $totrequired =
1403 ($this->title->t != '' ?
1404 $this->title->GetTextHeight($this->img) + $this->title->margin + 5 : 0 ) +
1405 ($this->subtitle->t != '' ?
1406 $this->subtitle->GetTextHeight($this->img) + $this->subtitle->margin + 5 : 0 ) +
1407 ($this->subsubtitle->t != '' ?
1408 $this->subsubtitle->GetTextHeight($this->img) + $this->subsubtitle->margin + 5 : 0 ) ;
1409
1410
1411 $btotrequired = 0;
1412 if($this->xaxis != null && !$this->xaxis->hide && !$this->xaxis->hide_labels ) {
1413 // Minimum bottom margin
1414 if( $this->xaxis->title->t != '' ) {
1415 if( $this->img->a == 90 )
1416 $btotrequired = $this->yaxis->title->GetTextHeight($this->img) + 5 ;
1417 else
1418 $btotrequired = $this->xaxis->title->GetTextHeight($this->img) + 5 ;
1419 }
1420 else
1421 $btotrequired = 0;
1422
1423 if( $this->img->a == 90 ) {
1424 $this->img->SetFont($this->yaxis->font_family,$this->yaxis->font_style,
1425 $this->yaxis->font_size);
1426 $lh = $this->img->GetTextHeight('Mg',$this->yaxis->label_angle);
1427 }
1428 else {
1429 $this->img->SetFont($this->xaxis->font_family,$this->xaxis->font_style,
1430 $this->xaxis->font_size);
1431 $lh = $this->img->GetTextHeight('Mg',$this->xaxis->label_angle);
1432 }
1433
1434 $btotrequired += $lh + 5;
1435 }
1436
1437 if( $this->img->a == 90 ) {
1438 // DO Nothing. It gets too messy to do this properly for 90 deg...
1439 }
1440 else{
1441 if( $this->img->top_margin < $totrequired ) {
1442 $this->SetMargin($this->img->left_margin,$this->img->right_margin,
1443 $totrequired,$this->img->bottom_margin);
1444 }
1445 if( $this->img->bottom_margin < $btotrequired ) {
1446 $this->SetMargin($this->img->left_margin,$this->img->right_margin,
1447 $this->img->top_margin,$btotrequired);
1448 }
1449 }
1450 }
1451
1452 // Stroke the graph
1453 // $aStrokeFileName If != "" the image will be written to this file and NOT
1454 // streamed back to the browser
1455 function Stroke($aStrokeFileName="") {
1456
1457 // Fist make a sanity check that user has specified a scale
1458 if( empty($this->yscale) ) {
1459 JpGraphError::RaiseL(25031);//('You must specify what scale to use with a call to Graph::SetScale().');
1460 }
1461
1462 // Start by adjusting the margin so that potential titles will fit.
1463 $this->AdjustMarginsForTitles();
1464
1465 // Setup scale constants
1466 if( $this->yscale ) $this->yscale->InitConstants($this->img);
1467 if( $this->xscale ) $this->xscale->InitConstants($this->img);
1468 if( $this->y2scale ) $this->y2scale->InitConstants($this->img);
1469
1470 $n=count($this->ynscale);
1471 for($i=0; $i < $n; ++$i) {
1472 if( $this->ynscale[$i] ) $this->ynscale[$i]->InitConstants($this->img);
1473 }
1474
1475 // If the filename is the predefined value = '_csim_special_'
1476 // we assume that the call to stroke only needs to do enough
1477 // to correctly generate the CSIM maps.
1478 // We use this variable to skip things we don't strictly need
1479 // to do to generate the image map to improve performance
1480 // a best we can. Therefor you will see a lot of tests !$_csim in the
1481 // code below.
1482 $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
1483
1484 // We need to know if we have stroked the plot in the
1485 // GetCSIMareas. Otherwise the CSIM hasn't been generated
1486 // and in the case of GetCSIM called before stroke to generate
1487 // CSIM without storing an image to disk GetCSIM must call Stroke.
1488 $this->iHasStroked = true;
1489
1490 // Do any pre-stroke adjustment that is needed by the different plot types
1491 // (i.e bar plots want's to add an offset to the x-labels etc)
1492 for($i=0; $i < count($this->plots) ; ++$i ) {
1493 $this->plots[$i]->PreStrokeAdjust($this);
1494 $this->plots[$i]->DoLegend($this);
1495 }
1496
1497 // Any plots on the second Y scale?
1498 if( $this->y2scale != null ) {
1499 for($i=0; $i<count($this->y2plots) ; ++$i ) {
1500 $this->y2plots[$i]->PreStrokeAdjust($this);
1501 $this->y2plots[$i]->DoLegend($this);
1502 }
1503 }
1504
1505 // Any plots on the extra Y axises?
1506 $n = count($this->ynaxis);
1507 for($i=0; $i<$n ; ++$i ) {
1508 if( $this->ynplots == null || $this->ynplots[$i] == null) {
1509 JpGraphError::RaiseL(25032,$i);//("No plots for Y-axis nbr:$i");
1510 }
1511 $m = count($this->ynplots[$i]);
1512 for($j=0; $j < $m; ++$j ) {
1513 $this->ynplots[$i][$j]->PreStrokeAdjust($this);
1514 $this->ynplots[$i][$j]->DoLegend($this);
1515 }
1516 }
1517
1518 // Bail out if any of the Y-axis not been specified and
1519 // has no plots. (This means it is impossible to do autoscaling and
1520 // no other scale was given so we can't possible draw anything). If you use manual
1521 // scaling you also have to supply the tick steps as well.
1522 if( (!$this->yscale->IsSpecified() && count($this->plots)==0) ||
1523 ($this->y2scale!=null && !$this->y2scale->IsSpecified() && count($this->y2plots)==0) ) {
1524 //$e = "n=".count($this->y2plots)."\n";
1525 // $e = "Can't draw unspecified Y-scale.<br>\nYou have either:<br>\n";
1526 // $e .= "1. Specified an Y axis for autoscaling but have not supplied any plots<br>\n";
1527 // $e .= "2. Specified a scale manually but have forgot to specify the tick steps";
1528 JpGraphError::RaiseL(25026);
1529 }
1530
1531 // Bail out if no plots and no specified X-scale
1532 if( (!$this->xscale->IsSpecified() && count($this->plots)==0 && count($this->y2plots)==0) )
1533 JpGraphError::RaiseL(25034);//("<strong>JpGraph: Can't draw unspecified X-scale.</strong><br>No plots.<br>");
1534
1535 //Check if we should autoscale y-axis
1536 if( !$this->yscale->IsSpecified() && count($this->plots)>0 ) {
1537 list($min,$max) = $this->GetPlotsYMinMax($this->plots);
1538 $lres = $this->GetLinesYMinMax($this->lines);
1539 if( is_array($lres) ) {
1540 list($linmin,$linmax) = $lres ;
1541 $min = min($min,$linmin);
1542 $max = max($max,$linmax);
1543 }
1544 $tres = $this->GetTextsYMinMax();
1545 if( is_array($tres) ) {
1546 list($tmin,$tmax) = $tres ;
1547 $min = min($min,$tmin);
1548 $max = max($max,$tmax);
1549 }
1550 $this->yscale->AutoScale($this->img,$min,$max,
1551 $this->img->plotheight/$this->ytick_factor);
1552 }
1553 elseif( $this->yscale->IsSpecified() &&
1554 ( $this->yscale->auto_ticks || !$this->yscale->ticks->IsSpecified()) ) {
1555 // The tick calculation will use the user suplied min/max values to determine
1556 // the ticks. If auto_ticks is false the exact user specifed min and max
1557 // values will be used for the scale.
1558 // If auto_ticks is true then the scale might be slightly adjusted
1559 // so that the min and max values falls on an even major step.
1560 $min = $this->yscale->scale[0];
1561 $max = $this->yscale->scale[1];
1562 $this->yscale->AutoScale($this->img,$min,$max,
1563 $this->img->plotheight/$this->ytick_factor,
1564 $this->yscale->auto_ticks);
1565
1566 // Now make sure we show enough precision to accurate display the
1567 // labels. If this is not done then the user might end up with
1568 // a scale that might actually start with, say 13.5, butdue to rounding
1569 // the scale label will ony show 14.
1570 if( abs(floor($min)-$min) > 0 ) {
1571
1572 // If the user has set a format then we bail out
1573 if( $this->yscale->ticks->label_formatstr == '' && $this->yscale->ticks->label_dateformatstr == '' ) {
1574 $this->yscale->ticks->precision = abs( floor(log10( abs(floor($min)-$min))) )+1;
1575 }
1576 }
1577 }
1578
1579 if( $this->y2scale != null) {
1580 if( !$this->y2scale->IsSpecified() && count($this->y2plots)>0 ) {
1581 list($min,$max) = $this->GetPlotsYMinMax($this->y2plots);
1582
1583 $lres = $this->GetLinesYMinMax($this->y2lines);
1584 if( is_array($lres) ) {
1585 list($linmin,$linmax) = $lres ;
1586 $min = min($min,$linmin);
1587 $max = max($max,$linmax);
1588 }
1589 $tres = $this->GetTextsYMinMax(true);
1590 if( is_array($tres) ) {
1591 list($tmin,$tmax) = $tres ;
1592 $min = min($min,$tmin);
1593 $max = max($max,$tmax);
1594 }
1595 $this->y2scale->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor);
1596 }
1597 elseif( $this->y2scale->IsSpecified() &&
1598 ( $this->y2scale->auto_ticks || !$this->y2scale->ticks->IsSpecified()) ) {
1599 // The tick calculation will use the user suplied min/max values to determine
1600 // the ticks. If auto_ticks is false the exact user specifed min and max
1601 // values will be used for the scale.
1602 // If auto_ticks is true then the scale might be slightly adjusted
1603 // so that the min and max values falls on an even major step.
1604 $min = $this->y2scale->scale[0];
1605 $max = $this->y2scale->scale[1];
1606 $this->y2scale->AutoScale($this->img,$min,$max,
1607 $this->img->plotheight/$this->ytick_factor,
1608 $this->y2scale->auto_ticks);
1609
1610 // Now make sure we show enough precision to accurate display the
1611 // labels. If this is not done then the user might end up with
1612 // a scale that might actually start with, say 13.5, butdue to rounding
1613 // the scale label will ony show 14.
1614 if( abs(floor($min)-$min) > 0 ) {
1615
1616 // If the user has set a format then we bail out
1617 if( $this->y2scale->ticks->label_formatstr == '' && $this->y2scale->ticks->label_dateformatstr == '' ) {
1618 $this->y2scale->ticks->precision = abs( floor(log10( abs(floor($min)-$min))) )+1;
1619 }
1620 }
1621
1622 }
1623 }
1624
1625 //
1626 // Autoscale the extra Y-axises
1627 //
1628 $n = count($this->ynaxis);
1629 for( $i=0; $i < $n; ++$i ) {
1630 if( $this->ynscale[$i] != null) {
1631 if( !$this->ynscale[$i]->IsSpecified() && count($this->ynplots[$i])>0 ) {
1632 list($min,$max) = $this->GetPlotsYMinMax($this->ynplots[$i]);
1633 $this->ynscale[$i]->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor);
1634 }
1635 elseif( $this->ynscale[$i]->IsSpecified() &&
1636 ( $this->ynscale[$i]->auto_ticks || !$this->ynscale[$i]->ticks->IsSpecified()) ) {
1637 // The tick calculation will use the user suplied min/max values to determine
1638 // the ticks. If auto_ticks is false the exact user specifed min and max
1639 // values will be used for the scale.
1640 // If auto_ticks is true then the scale might be slightly adjusted
1641 // so that the min and max values falls on an even major step.
1642 $min = $this->ynscale[$i]->scale[0];
1643 $max = $this->ynscale[$i]->scale[1];
1644 $this->ynscale[$i]->AutoScale($this->img,$min,$max,
1645 $this->img->plotheight/$this->ytick_factor,
1646 $this->ynscale[$i]->auto_ticks);
1647
1648 // Now make sure we show enough precision to accurate display the
1649 // labels. If this is not done then the user might end up with
1650 // a scale that might actually start with, say 13.5, butdue to rounding
1651 // the scale label will ony show 14.
1652 if( abs(floor($min)-$min) > 0 ) {
1653
1654 // If the user has set a format then we bail out
1655 if( $this->ynscale[$i]->ticks->label_formatstr == '' && $this->ynscale[$i]->ticks->label_dateformatstr == '' ) {
1656 $this->ynscale[$i]->ticks->precision = abs( floor(log10( abs(floor($min)-$min))) )+1;
1657 }
1658 }
1659
1660 }
1661 }
1662 }
1663
1664 //Check if we should autoscale x-axis
1665 if( !$this->xscale->IsSpecified() ) {
1666 if( substr($this->axtype,0,4) == "text" ) {
1667 $max=0;
1668 $n = count($this->plots);
1669 for($i=0; $i < $n; ++$i ) {
1670 $p = $this->plots[$i];
1671 // We need some unfortunate sub class knowledge here in order
1672 // to increase number of data points in case it is a line plot
1673 // which has the barcenter set. If not it could mean that the
1674 // last point of the data is outside the scale since the barcenter
1675 // settings means that we will shift the entire plot half a tick step
1676 // to the right in oder to align with the center of the bars.
1677 if( class_exists('BarPlot',false) ) {
1678 $cl = strtolower(get_class($p));
1679 if( (class_exists('BarPlot',false) && ($p instanceof BarPlot)) ||
1680 empty($p->barcenter) )
1681 $max=max($max,$p->numpoints-1);
1682 else {
1683 $max=max($max,$p->numpoints);
1684 }
1685 }
1686 else {
1687 if( empty($p->barcenter) ) {
1688 $max=max($max,$p->numpoints-1);
1689 }
1690 else {
1691 $max=max($max,$p->numpoints);
1692 }
1693 }
1694 }
1695 $min=0;
1696 if( $this->y2axis != null ) {
1697 foreach( $this->y2plots as $p ) {
1698 $max=max($max,$p->numpoints-1);
1699 }
1700 }
1701 $n = count($this->ynaxis);
1702 for( $i=0; $i < $n; ++$i ) {
1703 if( $this->ynaxis[$i] != null) {
1704 foreach( $this->ynplots[$i] as $p ) {
1705 $max=max($max,$p->numpoints-1);
1706 }
1707 }
1708 }
1709
1710 $this->xscale->Update($this->img,$min,$max);
1711 $this->xscale->ticks->Set($this->xaxis->tick_step,1);
1712 $this->xscale->ticks->SupressMinorTickMarks();
1713 }
1714 else {
1715 list($min,$max) = $this->GetXMinMax();
1716
1717 $lres = $this->GetLinesXMinMax($this->lines);
1718 if( $lres ) {
1719 list($linmin,$linmax) = $lres ;
1720 $min = min($min,$linmin);
1721 $max = max($max,$linmax);
1722 }
1723
1724 $lres = $this->GetLinesXMinMax($this->y2lines);
1725 if( $lres ) {
1726 list($linmin,$linmax) = $lres ;
1727 $min = min($min,$linmin);
1728 $max = max($max,$linmax);
1729 }
1730
1731 $tres = $this->GetTextsXMinMax();
1732 if( $tres ) {
1733 list($tmin,$tmax) = $tres ;
1734 $min = min($min,$tmin);
1735 $max = max($max,$tmax);
1736 }
1737
1738 $tres = $this->GetTextsXMinMax(true);
1739 if( $tres ) {
1740 list($tmin,$tmax) = $tres ;
1741 $min = min($min,$tmin);
1742 $max = max($max,$tmax);
1743 }
1744
1745 $this->xscale->AutoScale($this->img,$min,$max,round($this->img->plotwidth/$this->xtick_factor));
1746 }
1747
1748 //Adjust position of y-axis and y2-axis to minimum/maximum of x-scale
1749 if( !is_numeric($this->yaxis->pos) && !is_string($this->yaxis->pos) )
1750 $this->yaxis->SetPos($this->xscale->GetMinVal());
1751 if( $this->y2axis != null ) {
1752 if( !is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos) )
1753 $this->y2axis->SetPos($this->xscale->GetMaxVal());
1754 $this->y2axis->SetTitleSide(SIDE_RIGHT);
1755 }
1756 $n = count($this->ynaxis);
1757 $nY2adj = $this->y2axis != null ? $this->iYAxisDeltaPos : 0;
1758 for( $i=0; $i < $n; ++$i ) {
1759 if( $this->ynaxis[$i] != null ) {
1760 if( !is_numeric($this->ynaxis[$i]->pos) && !is_string($this->ynaxis[$i]->pos) ) {
1761 $this->ynaxis[$i]->SetPos($this->xscale->GetMaxVal());
1762 $this->ynaxis[$i]->SetPosAbsDelta($i*$this->iYAxisDeltaPos + $nY2adj);
1763 }
1764 $this->ynaxis[$i]->SetTitleSide(SIDE_RIGHT);
1765 }
1766 }
1767
1768 }
1769 elseif( $this->xscale->IsSpecified() &&
1770 ( $this->xscale->auto_ticks || !$this->xscale->ticks->IsSpecified()) ) {
1771 // The tick calculation will use the user suplied min/max values to determine
1772 // the ticks. If auto_ticks is false the exact user specifed min and max
1773 // values will be used for the scale.
1774 // If auto_ticks is true then the scale might be slightly adjusted
1775 // so that the min and max values falls on an even major step.
1776 $min = $this->xscale->scale[0];
1777 $max = $this->xscale->scale[1];
1778 $this->xscale->AutoScale($this->img,$min,$max,
1779 round($this->img->plotwidth/$this->xtick_factor),
1780 false);
1781
1782 // Now make sure we show enough precision to accurate display the
1783 // labels. If this is not done then the user might end up with
1784 // a scale that might actually start with, say 13.5, butdue to rounding
1785 // the scale label will ony show 14.
1786 if( abs(floor($min)-$min) > 0 ) {
1787
1788 // If the user has set a format then we bail out
1789 if( $this->xscale->ticks->label_formatstr == '' && $this->xscale->ticks->label_dateformatstr == '' ) {
1790 $this->xscale->ticks->precision = abs( floor(log10( abs(floor($min)-$min))) )+1;
1791 }
1792 }
1793
1794
1795 if( $this->y2axis != null ) {
1796 if( !is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos) )
1797 $this->y2axis->SetPos($this->xscale->GetMaxVal());
1798 $this->y2axis->SetTitleSide(SIDE_RIGHT);
1799 }
1800
1801 }
1802
1803 // If we have a negative values and x-axis position is at 0
1804 // we need to supress the first and possible the last tick since
1805 // they will be drawn on top of the y-axis (and possible y2 axis)
1806 // The test below might seem strange the reasone being that if
1807 // the user hasn't specified a value for position this will not
1808 // be set until we do the stroke for the axis so as of now it
1809 // is undefined.
1810 // For X-text scale we ignore all this since the tick are usually
1811 // much further in and not close to the Y-axis. Hence the test
1812 // for 'text'
1813
1814 if( ($this->yaxis->pos==$this->xscale->GetMinVal() ||
1815 (is_string($this->yaxis->pos) && $this->yaxis->pos=='min')) &&
1816 !is_numeric($this->xaxis->pos) && $this->yscale->GetMinVal() < 0 &&
1817 substr($this->axtype,0,4) != 'text' && $this->xaxis->pos!="min" ) {
1818
1819 //$this->yscale->ticks->SupressZeroLabel(false);
1820 $this->xscale->ticks->SupressFirst();
1821 if( $this->y2axis != null ) {
1822 $this->xscale->ticks->SupressLast();
1823 }
1824 }
1825 elseif( !is_numeric($this->yaxis->pos) && $this->yaxis->pos=='max' ) {
1826 $this->xscale->ticks->SupressLast();
1827 }
1828
1829
1830 if( !$_csim ) {
1831 $this->StrokePlotArea();
1832 if( $this->iIconDepth == DEPTH_BACK ) {
1833 $this->StrokeIcons();
1834 }
1835 }
1836 $this->StrokeAxis(false);
1837
1838 // Stroke bands
1839 if( $this->bands != null && !$_csim)
1840 for($i=0; $i < count($this->bands); ++$i) {
1841 // Stroke all bands that asks to be in the background
1842 if( $this->bands[$i]->depth == DEPTH_BACK )
1843 $this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale);
1844 }
1845
1846 if( $this->y2bands != null && $this->y2scale != null && !$_csim )
1847 for($i=0; $i < count($this->y2bands); ++$i) {
1848 // Stroke all bands that asks to be in the foreground
1849 if( $this->y2bands[$i]->depth == DEPTH_BACK )
1850 $this->y2bands[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
1851 }
1852
1853
1854 if( $this->grid_depth == DEPTH_BACK && !$_csim) {
1855 $this->ygrid->Stroke();
1856 $this->xgrid->Stroke();
1857 }
1858
1859 // Stroke Y2-axis
1860 if( $this->y2axis != null && !$_csim) {
1861 $this->y2axis->Stroke($this->xscale);
1862 $this->y2grid->Stroke();
1863 }
1864
1865 // Stroke yn-axis
1866 $n = count($this->ynaxis);
1867 for( $i=0; $i < $n; ++$i ) {
1868 $this->ynaxis[$i]->Stroke($this->xscale);
1869 }
1870
1871 $oldoff=$this->xscale->off;
1872 if(substr($this->axtype,0,4)=="text") {
1873 if( $this->text_scale_abscenteroff > -1 ) {
1874 // For a text scale the scale factor is the number of pixel per step.
1875 // Hence we can use the scale factor as a substitute for number of pixels
1876 // per major scale step and use that in order to adjust the offset so that
1877 // an object of width "abscenteroff" becomes centered.
1878 $this->xscale->off += round($this->xscale->scale_factor/2)-round($this->text_scale_abscenteroff/2);
1879 }
1880 else {
1881 $this->xscale->off +=
1882 ceil($this->xscale->scale_factor*$this->text_scale_off*$this->xscale->ticks->minor_step);
1883 }
1884 }
1885
1886 if( $this->iDoClipping ) {
1887 $oldimage = $this->img->CloneCanvasH();
1888 }
1889
1890 if( ! $this->y2orderback ) {
1891 // Stroke all plots for Y1 axis
1892 for($i=0; $i < count($this->plots); ++$i) {
1893 $this->plots[$i]->Stroke($this->img,$this->xscale,$this->yscale);
1894 $this->plots[$i]->StrokeMargin($this->img);
1895 }
1896 }
1897
1898 // Stroke all plots for Y2 axis
1899 if( $this->y2scale != null )
1900 for($i=0; $i< count($this->y2plots); ++$i ) {
1901 $this->y2plots[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
1902 }
1903
1904 if( $this->y2orderback ) {
1905 // Stroke all plots for Y1 axis
1906 for($i=0; $i < count($this->plots); ++$i) {
1907 $this->plots[$i]->Stroke($this->img,$this->xscale,$this->yscale);
1908 $this->plots[$i]->StrokeMargin($this->img);
1909 }
1910 }
1911
1912 $n = count($this->ynaxis);
1913 for( $i=0; $i < $n; ++$i ) {
1914 $m = count($this->ynplots[$i]);
1915 for( $j=0; $j < $m; ++$j ) {
1916 $this->ynplots[$i][$j]->Stroke($this->img,$this->xscale,$this->ynscale[$i]);
1917 $this->ynplots[$i][$j]->StrokeMargin($this->img);
1918 }
1919 }
1920
1921 if( $this->iIconDepth == DEPTH_FRONT) {
1922 $this->StrokeIcons();
1923 }
1924
1925 if( $this->iDoClipping ) {
1926 // Clipping only supports graphs at 0 and 90 degrees
1927 if( $this->img->a == 0 ) {
1928 $this->img->CopyCanvasH($oldimage,$this->img->img,
1929 $this->img->left_margin,$this->img->top_margin,
1930 $this->img->left_margin,$this->img->top_margin,
1931 $this->img->plotwidth+1,$this->img->plotheight);
1932 }
1933 elseif( $this->img->a == 90 ) {
1934 $adj = ($this->img->height - $this->img->width)/2;
1935 $this->img->CopyCanvasH($oldimage,$this->img->img,
1936 $this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
1937 $this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
1938 $this->img->plotheight+1,$this->img->plotwidth);
1939 }
1940 else {
1941 JpGraphError::RaiseL(25035,$this->img->a);//('You have enabled clipping. Cliping is only supported for graphs at 0 or 90 degrees rotation. Please adjust you current angle (='.$this->img->a.' degrees) or disable clipping.');
1942 }
1943 $this->img->Destroy();
1944 $this->img->SetCanvasH($oldimage);
1945 }
1946
1947 $this->xscale->off=$oldoff;
1948
1949 if( $this->grid_depth == DEPTH_FRONT && !$_csim ) {
1950 $this->ygrid->Stroke();
1951 $this->xgrid->Stroke();
1952 }
1953
1954 // Stroke bands
1955 if( $this->bands!= null )
1956 for($i=0; $i < count($this->bands); ++$i) {
1957 // Stroke all bands that asks to be in the foreground
1958 if( $this->bands[$i]->depth == DEPTH_FRONT )
1959 $this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale);
1960 }
1961
1962 if( $this->y2bands!= null && $this->y2scale != null )
1963 for($i=0; $i < count($this->y2bands); ++$i) {
1964 // Stroke all bands that asks to be in the foreground
1965 if( $this->y2bands[$i]->depth == DEPTH_FRONT )
1966 $this->y2bands[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
1967 }
1968
1969
1970 // Stroke any lines added
1971 if( $this->lines != null ) {
1972 for($i=0; $i < count($this->lines); ++$i) {
1973 $this->lines[$i]->Stroke($this->img,$this->xscale,$this->yscale);
1974 $this->lines[$i]->DoLegend($this);
1975 }
1976 }
1977
1978 if( $this->y2lines != null && $this->y2scale != null ) {
1979 for($i=0; $i < count($this->y2lines); ++$i) {
1980 $this->y2lines[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
1981 $this->y2lines[$i]->DoLegend($this);
1982 }
1983 }
1984
1985 // Finally draw the axis again since some plots may have nagged
1986 // the axis in the edges.
1987 if( !$_csim ) {
1988 $this->StrokeAxis();
1989 }
1990
1991 if( $this->y2scale != null && !$_csim )
1992 $this->y2axis->Stroke($this->xscale,false);
1993
1994 if( !$_csim ) {
1995 $this->StrokePlotBox();
1996 }
1997
1998 // The titles and legends never gets rotated so make sure
1999 // that the angle is 0 before stroking them
2000 $aa = $this->img->SetAngle(0);
2001 $this->StrokeTitles();
2002 $this->footer->Stroke($this->img);
2003 $this->legend->Stroke($this->img);
2004 $this->img->SetAngle($aa);
2005 $this->StrokeTexts();
2006 $this->StrokeTables();
2007
2008 if( !$_csim ) {
2009
2010 $this->img->SetAngle($aa);
2011
2012 // Draw an outline around the image map
2013 if(_JPG_DEBUG) {
2014 $this->DisplayClientSideaImageMapAreas();
2015 }
2016
2017 // Should we do any final image transformation
2018 if( $this->iImgTrans ) {
2019 if( !class_exists('ImgTrans',false) ) {
2020 require_once('jpgraph_imgtrans.php');
2021 //JpGraphError::Raise('In order to use image transformation you must include the file jpgraph_imgtrans.php in your script.');
2022 }
2023
2024 $tform = new ImgTrans($this->img->img);
2025 $this->img->img = $tform->Skew3D($this->iImgTransHorizon,$this->iImgTransSkewDist,
2026 $this->iImgTransDirection,$this->iImgTransHighQ,
2027 $this->iImgTransMinSize,$this->iImgTransFillColor,
2028 $this->iImgTransBorder);
2029 }
2030
2031 // If the filename is given as the special "__handle"
2032 // then the image handler is returned and the image is NOT
2033 // streamed back
2034 if( $aStrokeFileName == _IMG_HANDLER ) {
2035 return $this->img->img;
2036 }
2037 else {
2038 // Finally stream the generated picture
2039 $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,$aStrokeFileName);
2040 }
2041 }
2042 }
2043
2044 function SetAxisLabelBackground($aType,$aXFColor='lightgray',$aXColor='black',$aYFColor='lightgray',$aYColor='black') {
2045 $this->iAxisLblBgType = $aType;
2046 $this->iXAxisLblBgFillColor = $aXFColor;
2047 $this->iXAxisLblBgColor = $aXColor;
2048 $this->iYAxisLblBgFillColor = $aYFColor;
2049 $this->iYAxisLblBgColor = $aYColor;
2050 }
2051
2052//---------------
2053// PRIVATE METHODS
2054
2055 function StrokeAxisLabelBackground() {
2056 // Types
2057 // 0 = No background
2058 // 1 = Only X-labels, length of axis
2059 // 2 = Only Y-labels, length of axis
2060 // 3 = As 1 but extends to width of graph
2061 // 4 = As 2 but extends to height of graph
2062 // 5 = Combination of 3 & 4
2063 // 6 = Combination of 1 & 2
2064
2065 $t = $this->iAxisLblBgType ;
2066 if( $t < 1 ) return;
2067 // Stroke optional X-axis label background color
2068 if( $t == 1 || $t == 3 || $t == 5 || $t == 6 ) {
2069 $this->img->PushColor($this->iXAxisLblBgFillColor);
2070 if( $t == 1 || $t == 6 ) {
2071 $xl = $this->img->left_margin;
2072 $yu = $this->img->height - $this->img->bottom_margin + 1;
2073 $xr = $this->img->width - $this->img->right_margin ;
2074 $yl = $this->img->height-1-$this->frame_weight;
2075 }
2076 else { // t==3 || t==5
2077 $xl = $this->frame_weight;
2078 $yu = $this->img->height - $this->img->bottom_margin + 1;
2079 $xr = $this->img->width - 1 - $this->frame_weight;
2080 $yl = $this->img->height-1-$this->frame_weight;
2081 }
2082
2083 $this->img->FilledRectangle($xl,$yu,$xr,$yl);
2084 $this->img->PopColor();
2085
2086 // Check if we should add the vertical lines at left and right edge
2087 if( $this->iXAxisLblBgColor !== '' ) {
2088 // Hardcode to one pixel wide
2089 $this->img->SetLineWeight(1);
2090 $this->img->PushColor($this->iXAxisLblBgColor);
2091 if( $t == 1 || $t == 6 ) {
2092 $this->img->Line($xl,$yu,$xl,$yl);
2093 $this->img->Line($xr,$yu,$xr,$yl);
2094 }
2095 else {
2096 $xl = $this->img->width - $this->img->right_margin ;
2097 $this->img->Line($xl,$yu-1,$xr,$yu-1);
2098 }
2099 $this->img->PopColor();
2100 }
2101 }
2102
2103 if( $t == 2 || $t == 4 || $t == 5 || $t == 6 ) {
2104 $this->img->PushColor($this->iYAxisLblBgFillColor);
2105 if( $t == 2 || $t == 6 ) {
2106 $xl = $this->frame_weight;
2107 $yu = $this->frame_weight+$this->img->top_margin;
2108 $xr = $this->img->left_margin - 1;
2109 $yl = $this->img->height - $this->img->bottom_margin + 1;
2110 }
2111 else {
2112 $xl = $this->frame_weight;
2113 $yu = $this->frame_weight;
2114 $xr = $this->img->left_margin - 1;
2115 $yl = $this->img->height-1-$this->frame_weight;
2116 }
2117
2118 $this->img->FilledRectangle($xl,$yu,$xr,$yl);
2119 $this->img->PopColor();
2120
2121 // Check if we should add the vertical lines at left and right edge
2122 if( $this->iXAxisLblBgColor !== '' ) {
2123 $this->img->PushColor($this->iXAxisLblBgColor);
2124 if( $t == 2 || $t == 6 ) {
2125 $this->img->Line($xl,$yu-1,$xr,$yu-1);
2126 $this->img->Line($xl,$yl-1,$xr,$yl-1);
2127 }
2128 else {
2129 $this->img->Line($xr+1,$yu,$xr+1,$this->img->top_margin);
2130 }
2131 $this->img->PopColor();
2132 }
2133
2134 }
2135 }
2136
2137 function StrokeAxis($aStrokeLabels=true) {
2138
2139 if( $aStrokeLabels ) {
2140 $this->StrokeAxisLabelBackground();
2141 }
2142
2143 // Stroke axis
2144 if( $this->iAxisStyle != AXSTYLE_SIMPLE ) {
2145 switch( $this->iAxisStyle ) {
2146 case AXSTYLE_BOXIN :
2147 $toppos = SIDE_DOWN;
2148 $bottompos = SIDE_UP;
2149 $leftpos = SIDE_RIGHT;
2150 $rightpos = SIDE_LEFT;
2151 break;
2152 case AXSTYLE_BOXOUT :
2153 $toppos = SIDE_UP;
2154 $bottompos = SIDE_DOWN;
2155 $leftpos = SIDE_LEFT;
2156 $rightpos = SIDE_RIGHT;
2157 break;
2158 case AXSTYLE_YBOXIN:
2159 $toppos = FALSE;
2160 $bottompos = SIDE_UP;
2161 $leftpos = SIDE_RIGHT;
2162 $rightpos = SIDE_LEFT;
2163 break;
2164 case AXSTYLE_YBOXOUT:
2165 $toppos = FALSE;
2166 $bottompos = SIDE_DOWN;
2167 $leftpos = SIDE_LEFT;
2168 $rightpos = SIDE_RIGHT;
2169 break;
2170 default:
2171 JpGRaphError::RaiseL(25036,$this->iAxisStyle); //('Unknown AxisStyle() : '.$this->iAxisStyle);
2172 break;
2173 }
2174
2175 // By default we hide the first label so it doesn't cross the
2176 // Y-axis in case the positon hasn't been set by the user.
2177 // However, if we use a box we always want the first value
2178 // displayed so we make sure it will be displayed.
2179 $this->xscale->ticks->SupressFirst(false);
2180
2181 // Now draw the bottom X-axis
2182 $this->xaxis->SetPos('min');
2183 $this->xaxis->SetLabelSide(SIDE_DOWN);
2184 $this->xaxis->scale->ticks->SetSide($bottompos);
2185 $this->xaxis->Stroke($this->yscale,$aStrokeLabels);
2186
2187 if( $toppos !== FALSE ) {
2188 // We also want a top X-axis
2189 $this->xaxis = $this->xaxis;
2190 $this->xaxis->SetPos('max');
2191 $this->xaxis->SetLabelSide(SIDE_UP);
2192 // No title for the top X-axis
2193 if( $aStrokeLabels ) {
2194 $this->xaxis->title->Set('');
2195 }
2196 $this->xaxis->scale->ticks->SetSide($toppos);
2197 $this->xaxis->Stroke($this->yscale,$aStrokeLabels);
2198 }
2199
2200 // Stroke the left Y-axis
2201 $this->yaxis->SetPos('min');
2202 $this->yaxis->SetLabelSide(SIDE_LEFT);
2203 $this->yaxis->scale->ticks->SetSide($leftpos);
2204 $this->yaxis->Stroke($this->xscale,$aStrokeLabels);
2205
2206 // Stroke the right Y-axis
2207 $this->yaxis->SetPos('max');
2208 // No title for the right side
2209 if( $aStrokeLabels ) {
2210 $this->yaxis->title->Set('');
2211 }
2212 $this->yaxis->SetLabelSide(SIDE_RIGHT);
2213 $this->yaxis->scale->ticks->SetSide($rightpos);
2214 $this->yaxis->Stroke($this->xscale,$aStrokeLabels);
2215 }
2216 else {
2217 $this->xaxis->Stroke($this->yscale,$aStrokeLabels);
2218 $this->yaxis->Stroke($this->xscale,$aStrokeLabels);
2219 }
2220 }
2221
2222
2223 // Private helper function for backgound image
2224 static function LoadBkgImage($aImgFormat='',$aFile='',$aImgStr='') {
2225 if( $aImgStr != '' ) {
2226 return Image::CreateFromString($aImgStr);
2227 }
2228
2229 // Remove case sensitivity and setup appropriate function to create image
2230 // Get file extension. This should be the LAST '.' separated part of the filename
2231 $e = explode('.',$aFile);
2232 $ext = strtolower($e[count($e)-1]);
2233 if ($ext == "jpeg") {
2234 $ext = "jpg";
2235 }
2236
2237 if( trim($ext) == '' )
2238 $ext = 'png'; // Assume PNG if no extension specified
2239
2240 if( $aImgFormat == '' )
2241 $imgtag = $ext;
2242 else
2243 $imgtag = $aImgFormat;
2244
2245 $supported = imagetypes();
2246 if( ( $ext == 'jpg' && !($supported & IMG_JPG) ) ||
2247 ( $ext == 'gif' && !($supported & IMG_GIF) ) ||
2248 ( $ext == 'png' && !($supported & IMG_PNG) ) ||
2249 ( $ext == 'bmp' && !($supported & IMG_WBMP) ) ||
2250 ( $ext == 'xpm' && !($supported & IMG_XPM) ) ) {
2251
2252 JpGraphError::RaiseL(25037,$aFile);//('The image format of your background image ('.$aFile.') is not supported in your system configuration. ');
2253 }
2254
2255
2256 if( $imgtag == "jpg" || $imgtag == "jpeg")
2257 {
2258 $f = "imagecreatefromjpeg";
2259 $imgtag = "jpg";
2260 }
2261 else
2262 {
2263 $f = "imagecreatefrom".$imgtag;
2264 }
2265
2266 // Compare specified image type and file extension
2267 if( $imgtag != $ext ) {
2268 //$t = "Background image seems to be of different type (has different file extension) than specified imagetype. Specified: '".$aImgFormat."'File: '".$aFile."'";
2269 JpGraphError::RaiseL(25038, $aImgFormat, $aFile);
2270 }
2271
2272 $img = @$f($aFile);
2273 if( !$img ) {
2274 JpGraphError::RaiseL(25039,$aFile);//(" Can't read background image: '".$aFile."'");
2275 }
2276 return $img;
2277 }
2278
2279 function StrokeBackgroundGrad() {
2280 if( $this->bkg_gradtype < 0 )
2281 return;
2282 $grad = new Gradient($this->img);
2283 if( $this->bkg_gradstyle == BGRAD_PLOT ) {
2284 $xl = $this->img->left_margin;
2285 $yt = $this->img->top_margin;
2286 $xr = $xl + $this->img->plotwidth+1 ;
2287 $yb = $yt + $this->img->plotheight ;
2288 $grad->FilledRectangle($xl,$yt,$xr,$yb,$this->bkg_gradfrom,$this->bkg_gradto,$this->bkg_gradtype);
2289 }
2290 else {
2291 $xl = 0;
2292 $yt = 0;
2293 $xr = $xl + $this->img->width - 1;
2294 $yb = $yt + $this->img->height ;
2295 if( $this->doshadow ) {
2296 $xr -= $this->shadow_width;
2297 $yb -= $this->shadow_width;
2298 }
2299 if( $this->doframe ) {
2300 $yt += $this->frame_weight;
2301 $yb -= $this->frame_weight;
2302 $xl += $this->frame_weight;
2303 $xr -= $this->frame_weight;
2304 }
2305 $aa = $this->img->SetAngle(0);
2306 $grad->FilledRectangle($xl,$yt,$xr,$yb,$this->bkg_gradfrom,$this->bkg_gradto,$this->bkg_gradtype);
2307 $aa = $this->img->SetAngle($aa);
2308 }
2309 }
2310
2311 function StrokeFrameBackground() {
2312 if( $this->background_image != "" && $this->background_cflag != "" ) {
2313 JpGraphError::RaiseL(25040);//('It is not possible to specify both a background image and a background country flag.');
2314 }
2315 if( $this->background_image != "" ) {
2316 $bkgimg = $this->LoadBkgImage($this->background_image_format,$this->background_image);
2317 }
2318 elseif( $this->background_cflag != "" ) {
2319 if( ! class_exists('FlagImages',false) ) {
2320 JpGraphError::RaiseL(25041);//('In order to use Country flags as backgrounds you must include the "jpgraph_flags.php" file.');
2321 }
2322 $fobj = new FlagImages(FLAGSIZE4);
2323 $dummy='';
2324 $bkgimg = $fobj->GetImgByName($this->background_cflag,$dummy);
2325 $this->background_image_mix = $this->background_cflag_mix;
2326 $this->background_image_type = $this->background_cflag_type;
2327 }
2328 else {
2329 return ;
2330 }
2331
2332 $bw = ImageSX($bkgimg);
2333 $bh = ImageSY($bkgimg);
2334
2335 // No matter what the angle is we always stroke the image and frame
2336 // assuming it is 0 degree
2337 $aa = $this->img->SetAngle(0);
2338
2339 switch( $this->background_image_type ) {
2340 case BGIMG_FILLPLOT: // Resize to just fill the plotarea
2341 $this->FillMarginArea();
2342 $this->StrokeFrame();
2343 // Special case to hande 90 degree rotated graph corectly
2344 if( $aa == 90 ) {
2345 $this->img->SetAngle(90);
2346 $this->FillPlotArea();
2347 $aa = $this->img->SetAngle(0);
2348 $adj = ($this->img->height - $this->img->width)/2;
2349 $this->img->CopyMerge($bkgimg,
2350 $this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
2351 0,0,
2352 $this->img->plotheight+1,$this->img->plotwidth,
2353 $bw,$bh,$this->background_image_mix);
2354
2355 }
2356 else {
2357 $this->FillPlotArea();
2358 $this->img->CopyMerge($bkgimg,
2359 $this->img->left_margin,$this->img->top_margin,
2360 0,0,$this->img->plotwidth+1,$this->img->plotheight,
2361 $bw,$bh,$this->background_image_mix);
2362 }
2363 break;
2364 case BGIMG_FILLFRAME: // Fill the whole area from upper left corner, resize to just fit
2365 $hadj=0; $vadj=0;
2366 if( $this->doshadow ) {
2367 $hadj = $this->shadow_width;
2368 $vadj = $this->shadow_width;
2369 }
2370 $this->FillMarginArea();
2371 $this->FillPlotArea();
2372 $this->img->CopyMerge($bkgimg,0,0,0,0,$this->img->width-$hadj,$this->img->height-$vadj,
2373 $bw,$bh,$this->background_image_mix);
2374 $this->StrokeFrame();
2375 break;
2376 case BGIMG_COPY: // Just copy the image from left corner, no resizing
2377 $this->FillMarginArea();
2378 $this->FillPlotArea();
2379 $this->img->CopyMerge($bkgimg,0,0,0,0,$bw,$bh,
2380 $bw,$bh,$this->background_image_mix);
2381 $this->StrokeFrame();
2382 break;
2383 case BGIMG_CENTER: // Center original image in the plot area
2384 $this->FillMarginArea();
2385 $this->FillPlotArea();
2386 $centerx = round($this->img->plotwidth/2+$this->img->left_margin-$bw/2);
2387 $centery = round($this->img->plotheight/2+$this->img->top_margin-$bh/2);
2388 $this->img->CopyMerge($bkgimg,$centerx,$centery,0,0,$bw,$bh,
2389 $bw,$bh,$this->background_image_mix);
2390 $this->StrokeFrame();
2391 break;
2392 case BGIMG_FREE: // Just copy the image to the specified location
2393 $this->img->CopyMerge($bkgimg,
2394 $this->background_image_xpos,$this->background_image_ypos,
2395 0,0,$bw,$bh,$bw,$bh,$this->background_image_mix);
2396 $this->StrokeFrame(); // New
2397 break;
2398 default:
2399 JpGraphError::RaiseL(25042);//(" Unknown background image layout");
2400 }
2401 $this->img->SetAngle($aa);
2402 }
2403
2404 // Private
2405 // Draw a frame around the image
2406 function StrokeFrame() {
2407 if( !$this->doframe ) return;
2408
2409 if( $this->background_image_type <= 1 &&
2410 ($this->bkg_gradtype < 0 || ($this->bkg_gradtype > 0 && $this->bkg_gradstyle==BGRAD_PLOT)) ) {
2411 $c = $this->margin_color;
2412 }
2413 else {
2414 $c = false;
2415 }
2416
2417 if( $this->doshadow ) {
2418 $this->img->SetColor($this->frame_color);
2419 $this->img->ShadowRectangle(0,0,$this->img->width,$this->img->height,
2420 $c,$this->shadow_width,$this->shadow_color);
2421 }
2422 elseif( $this->framebevel ) {
2423 if( $c ) {
2424 $this->img->SetColor($this->margin_color);
2425 $this->img->FilledRectangle(0,0,$this->img->width-1,$this->img->height-1);
2426 }
2427 $this->img->Bevel(1,1,$this->img->width-2,$this->img->height-2,
2428 $this->framebeveldepth,
2429 $this->framebevelcolor1,$this->framebevelcolor2);
2430 if( $this->framebevelborder ) {
2431 $this->img->SetColor($this->framebevelbordercolor);
2432 $this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
2433 }
2434 }
2435 else {
2436 $this->img->SetLineWeight($this->frame_weight);
2437 if( $c ) {
2438 $this->img->SetColor($this->margin_color);
2439 $this->img->FilledRectangle(0,0,$this->img->width-1,$this->img->height-1);
2440 }
2441 $this->img->SetColor($this->frame_color);
2442 $this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
2443 }
2444 }
2445
2446 function FillMarginArea() {
2447 $hadj=0; $vadj=0;
2448 if( $this->doshadow ) {
2449 $hadj = $this->shadow_width;
2450 $vadj = $this->shadow_width;
2451 }
2452
2453 $this->img->SetColor($this->margin_color);
2454// $this->img->FilledRectangle(0,0,$this->img->width-1-$hadj,$this->img->height-1-$vadj);
2455
2456 $this->img->FilledRectangle(0,0,$this->img->width-1-$hadj,$this->img->top_margin);
2457 $this->img->FilledRectangle(0,$this->img->top_margin,$this->img->left_margin,$this->img->height-1-$hadj);
2458 $this->img->FilledRectangle($this->img->left_margin+1,
2459 $this->img->height-$this->img->bottom_margin,
2460 $this->img->width-1-$hadj,
2461 $this->img->height-1-$hadj);
2462 $this->img->FilledRectangle($this->img->width-$this->img->right_margin,
2463 $this->img->top_margin+1,
2464 $this->img->width-1-$hadj,
2465 $this->img->height-$this->img->bottom_margin-1);
2466 }
2467
2468 function FillPlotArea() {
2469 $this->img->PushColor($this->plotarea_color);
2470 $this->img->FilledRectangle($this->img->left_margin,
2471 $this->img->top_margin,
2472 $this->img->width-$this->img->right_margin,
2473 $this->img->height-$this->img->bottom_margin);
2474 $this->img->PopColor();
2475 }
2476
2477 // Stroke the plot area with either a solid color or a background image
2478 function StrokePlotArea() {
2479 // Note: To be consistent we really should take a possible shadow
2480 // into account. However, that causes some problem for the LinearScale class
2481 // since in the current design it does not have any links to class Graph which
2482 // means it has no way of compensating for the adjusted plotarea in case of a
2483 // shadow. So, until I redesign LinearScale we can't compensate for this.
2484 // So just set the two adjustment parameters to zero for now.
2485 $boxadj = 0; //$this->doframe ? $this->frame_weight : 0 ;
2486 $adj = 0; //$this->doshadow ? $this->shadow_width : 0 ;
2487
2488 if( $this->background_image != "" || $this->background_cflag != "" ) {
2489 $this->StrokeFrameBackground();
2490 }
2491 else {
2492 $aa = $this->img->SetAngle(0);
2493 $this->StrokeFrame();
2494 $aa = $this->img->SetAngle($aa);
2495 $this->StrokeBackgroundGrad();
2496 if( $this->bkg_gradtype < 0 ||
2497 ($this->bkg_gradtype > 0 && $this->bkg_gradstyle==BGRAD_MARGIN) ) {
2498 $this->FillPlotArea();
2499 }
2500 }
2501 }
2502
2503 function StrokeIcons() {
2504 $n = count($this->iIcons);
2505 for( $i=0; $i < $n; ++$i ) {
2506 $this->iIcons[$i]->StrokeWithScale($this->img,$this->xscale,$this->yscale);
2507 }
2508 }
2509
2510 function StrokePlotBox() {
2511 // Should we draw a box around the plot area?
2512 if( $this->boxed ) {
2513 $this->img->SetLineWeight(1);
2514 $this->img->SetLineStyle('solid');
2515 $this->img->SetColor($this->box_color);
2516 for($i=0; $i < $this->box_weight; ++$i ) {
2517 $this->img->Rectangle(
2518 $this->img->left_margin-$i,$this->img->top_margin-$i,
2519 $this->img->width-$this->img->right_margin+$i,
2520 $this->img->height-$this->img->bottom_margin+$i);
2521 }
2522 }
2523 }
2524
2525 function SetTitleBackgroundFillStyle($aStyle,$aColor1='black',$aColor2='white') {
2526 $this->titlebkg_fillstyle = $aStyle;
2527 $this->titlebkg_scolor1 = $aColor1;
2528 $this->titlebkg_scolor2 = $aColor2;
2529 }
2530
2531 function SetTitleBackground($aBackColor='gray', $aStyle=TITLEBKG_STYLE1, $aFrameStyle=TITLEBKG_FRAME_NONE, $aFrameColor='black', $aFrameWeight=1, $aBevelHeight=3, $aEnable=true) {
2532 $this->titlebackground = $aEnable;
2533 $this->titlebackground_color = $aBackColor;
2534 $this->titlebackground_style = $aStyle;
2535 $this->titlebackground_framecolor = $aFrameColor;
2536 $this->titlebackground_framestyle = $aFrameStyle;
2537 $this->titlebackground_frameweight = $aFrameWeight;
2538 $this->titlebackground_bevelheight = $aBevelHeight ;
2539 }
2540
2541
2542 function StrokeTitles() {
2543
2544 $margin=3;
2545
2546 if( $this->titlebackground ) {
2547
2548 // Find out height
2549 $this->title->margin += 2 ;
2550 $h = $this->title->GetTextHeight($this->img)+$this->title->margin+$margin;
2551 if( $this->subtitle->t != "" && !$this->subtitle->hide ) {
2552 $h += $this->subtitle->GetTextHeight($this->img)+$margin+
2553 $this->subtitle->margin;
2554 $h += 2;
2555 }
2556 if( $this->subsubtitle->t != "" && !$this->subsubtitle->hide ) {
2557 $h += $this->subsubtitle->GetTextHeight($this->img)+$margin+
2558 $this->subsubtitle->margin;
2559 $h += 2;
2560 }
2561 $this->img->PushColor($this->titlebackground_color);
2562 if( $this->titlebackground_style === TITLEBKG_STYLE1 ) {
2563 // Inside the frame
2564 if( $this->framebevel ) {
2565 $x1 = $y1 = $this->framebeveldepth + 1 ;
2566 $x2 = $this->img->width - $this->framebeveldepth - 2 ;
2567 $this->title->margin += $this->framebeveldepth + 1 ;
2568 $h += $y1 ;
2569 $h += 2;
2570 }
2571 else {
2572 $x1 = $y1 = $this->frame_weight;
2573 $x2 = $this->img->width - 2*$x1;
2574 }
2575 }
2576 elseif( $this->titlebackground_style === TITLEBKG_STYLE2 ) {
2577 // Cover the frame as well
2578 $x1 = $y1 = 0;
2579 $x2 = $this->img->width - 1 ;
2580 }
2581 elseif( $this->titlebackground_style === TITLEBKG_STYLE3 ) {
2582 // Cover the frame as well (the difference is that
2583 // for style==3 a bevel frame border is on top
2584 // of the title background)
2585 $x1 = $y1 = 0;
2586 $x2 = $this->img->width - 1 ;
2587 $h += $this->framebeveldepth ;
2588 $this->title->margin += $this->framebeveldepth ;
2589 }
2590 else {
2591 JpGraphError::RaiseL(25043);//('Unknown title background style.');
2592 }
2593
2594 if( $this->titlebackground_framestyle === 3 ) {
2595 $h += $this->titlebackground_bevelheight*2 + 1 ;
2596 $this->title->margin += $this->titlebackground_bevelheight ;
2597 }
2598
2599 if( $this->doshadow ) {
2600 $x2 -= $this->shadow_width ;
2601 }
2602
2603 $indent=0;
2604 if( $this->titlebackground_framestyle == TITLEBKG_FRAME_BEVEL ) {
2605 $ind = $this->titlebackground_bevelheight;
2606 }
2607
2608 if( $this->titlebkg_fillstyle==TITLEBKG_FILLSTYLE_HSTRIPED ) {
2609 $this->img->FilledRectangle2($x1+$ind,$y1+$ind,$x2-$ind,$h-$ind,
2610 $this->titlebkg_scolor1,
2611 $this->titlebkg_scolor2);
2612 }
2613 elseif( $this->titlebkg_fillstyle==TITLEBKG_FILLSTYLE_VSTRIPED ) {
2614 $this->img->FilledRectangle2($x1+$ind,$y1+$ind,$x2-$ind,$h-$ind,
2615 $this->titlebkg_scolor1,
2616 $this->titlebkg_scolor2,2);
2617 }
2618 else {
2619 // Solid fill
2620 $this->img->FilledRectangle($x1,$y1,$x2,$h);
2621 }
2622 $this->img->PopColor();
2623
2624 $this->img->PushColor($this->titlebackground_framecolor);
2625 $this->img->SetLineWeight($this->titlebackground_frameweight);
2626 if( $this->titlebackground_framestyle == TITLEBKG_FRAME_FULL ) {
2627 // Frame background
2628 $this->img->Rectangle($x1,$y1,$x2,$h);
2629 }
2630 elseif( $this->titlebackground_framestyle == TITLEBKG_FRAME_BOTTOM ) {
2631 // Bottom line only
2632 $this->img->Line($x1,$h,$x2,$h);
2633 }
2634 elseif( $this->titlebackground_framestyle == TITLEBKG_FRAME_BEVEL ) {
2635 $this->img->Bevel($x1,$y1,$x2,$h,$this->titlebackground_bevelheight);
2636 }
2637 $this->img->PopColor();
2638
2639 // This is clumsy. But we neeed to stroke the whole graph frame if it is
2640 // set to bevel to get the bevel shading on top of the text background
2641 if( $this->framebevel && $this->doframe &&
2642 $this->titlebackground_style === 3 ) {
2643 $this->img->Bevel(1,1,$this->img->width-2,$this->img->height-2,
2644 $this->framebeveldepth,
2645 $this->framebevelcolor1,$this->framebevelcolor2);
2646 if( $this->framebevelborder ) {
2647 $this->img->SetColor($this->framebevelbordercolor);
2648 $this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
2649 }
2650 }
2651 }
2652
2653 // Stroke title
2654 $y = $this->title->margin;
2655 if( $this->title->halign == 'center' )
2656 $this->title->Center(0,$this->img->width,$y);
2657 elseif( $this->title->halign == 'left' ) {
2658 $this->title->SetPos($this->title->margin+2,$y);
2659 }
2660 elseif( $this->title->halign == 'right' ) {
2661 $indent = 0;
2662 if( $this->doshadow )
2663 $indent = $this->shadow_width+2;
2664 $this->title->SetPos($this->img->width-$this->title->margin-$indent,$y,'right');
2665 }
2666 $this->title->Stroke($this->img);
2667
2668 // ... and subtitle
2669 $y += $this->title->GetTextHeight($this->img) + $margin + $this->subtitle->margin;
2670 if( $this->subtitle->halign == 'center' )
2671 $this->subtitle->Center(0,$this->img->width,$y);
2672 elseif( $this->subtitle->halign == 'left' ) {
2673 $this->subtitle->SetPos($this->subtitle->margin+2,$y);
2674 }
2675 elseif( $this->subtitle->halign == 'right' ) {
2676 $indent = 0;
2677 if( $this->doshadow )
2678 $indent = $this->shadow_width+2;
2679 $this->subtitle->SetPos($this->img->width-$this->subtitle->margin-$indent,$y,'right');
2680 }
2681 $this->subtitle->Stroke($this->img);
2682
2683 // ... and subsubtitle
2684 $y += $this->subtitle->GetTextHeight($this->img) + $margin + $this->subsubtitle->margin;
2685 if( $this->subsubtitle->halign == 'center' )
2686 $this->subsubtitle->Center(0,$this->img->width,$y);
2687 elseif( $this->subsubtitle->halign == 'left' ) {
2688 $this->subsubtitle->SetPos($this->subsubtitle->margin+2,$y);
2689 }
2690 elseif( $this->subsubtitle->halign == 'right' ) {
2691 $indent = 0;
2692 if( $this->doshadow )
2693 $indent = $this->shadow_width+2;
2694 $this->subsubtitle->SetPos($this->img->width-$this->subsubtitle->margin-$indent,$y,'right');
2695 }
2696 $this->subsubtitle->Stroke($this->img);
2697
2698 // ... and fancy title
2699 $this->tabtitle->Stroke($this->img);
2700
2701 }
2702
2703 function StrokeTexts() {
2704 // Stroke any user added text objects
2705 if( $this->texts != null ) {
2706 for($i=0; $i < count($this->texts); ++$i) {
2707 $this->texts[$i]->StrokeWithScale($this->img,$this->xscale,$this->yscale);
2708 }
2709 }
2710
2711 if( $this->y2texts != null && $this->y2scale != null ) {
2712 for($i=0; $i < count($this->y2texts); ++$i) {
2713 $this->y2texts[$i]->StrokeWithScale($this->img,$this->xscale,$this->y2scale);
2714 }
2715 }
2716
2717 }
2718
2719 function StrokeTables() {
2720 if( $this->iTables != null ) {
2721 $n = count($this->iTables);
2722 for( $i=0; $i < $n; ++$i ) {
2723 $this->iTables[$i]->StrokeWithScale($this->img,$this->xscale,$this->yscale);
2724 }
2725 }
2726 }
2727
2728 function DisplayClientSideaImageMapAreas() {
2729 // Debug stuff - display the outline of the image map areas
2730 $csim='';
2731 foreach ($this->plots as $p) {
2732 $csim.= $p->GetCSIMareas();
2733 }
2734 $csim .= $this->legend->GetCSIMareas();
2735 if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) {
2736 $this->img->SetColor($this->csimcolor);
2737 $n = count($coords[0]);
2738 for ($i=0; $i < $n; $i++) {
2739 if ($coords[1][$i]=="poly") {
2740 preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts);
2741 $this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]);
2742 $m = count($pts[0]);
2743 for ($j=0; $j < $m; $j++) {
2744 $this->img->LineTo($pts[1][$j],$pts[2][$j]);
2745 }
2746 } else if ($coords[1][$i]=="rect") {
2747 $pts = preg_split('/,/', $coords[2][$i]);
2748 $this->img->SetStartPoint($pts[0],$pts[1]);
2749 $this->img->LineTo($pts[2],$pts[1]);
2750 $this->img->LineTo($pts[2],$pts[3]);
2751 $this->img->LineTo($pts[0],$pts[3]);
2752 $this->img->LineTo($pts[0],$pts[1]);
2753 }
2754 }
2755 }
2756 }
2757
2758 // Text scale offset in world coordinates
2759 function SetTextScaleOff($aOff) {
2760 $this->text_scale_off = $aOff;
2761 $this->xscale->text_scale_off = $aOff;
2762 }
2763
2764 // Text width of bar to be centered in absolute pixels
2765 function SetTextScaleAbsCenterOff($aOff) {
2766 $this->text_scale_abscenteroff = $aOff;
2767 }
2768
2769 // Get Y min and max values for added lines
2770 function GetLinesYMinMax( $aLines ) {
2771 $n = count($aLines);
2772 if( $n == 0 ) return false;
2773 $min = $aLines[0]->scaleposition ;
2774 $max = $min ;
2775 $flg = false;
2776 for( $i=0; $i < $n; ++$i ) {
2777 if( $aLines[$i]->direction == HORIZONTAL ) {
2778 $flg = true ;
2779 $v = $aLines[$i]->scaleposition ;
2780 if( $min > $v ) $min = $v ;
2781 if( $max < $v ) $max = $v ;
2782 }
2783 }
2784 return $flg ? array($min,$max) : false ;
2785 }
2786
2787 // Get X min and max values for added lines
2788 function GetLinesXMinMax( $aLines ) {
2789 $n = count($aLines);
2790 if( $n == 0 ) return false ;
2791 $min = $aLines[0]->scaleposition ;
2792 $max = $min ;
2793 $flg = false;
2794 for( $i=0; $i < $n; ++$i ) {
2795 if( $aLines[$i]->direction == VERTICAL ) {
2796 $flg = true ;
2797 $v = $aLines[$i]->scaleposition ;
2798 if( $min > $v ) $min = $v ;
2799 if( $max < $v ) $max = $v ;
2800 }
2801 }
2802 return $flg ? array($min,$max) : false ;
2803 }
2804
2805 // Get min and max values for all included plots
2806 function GetPlotsYMinMax($aPlots) {
2807 $n = count($aPlots);
2808 $i=0;
2809 do {
2810 list($xmax,$max) = $aPlots[$i]->Max();
2811 } while( ++$i < $n && !is_numeric($max) );
2812
2813 $i=0;
2814 do {
2815 list($xmin,$min) = $aPlots[$i]->Min();
2816 } while( ++$i < $n && !is_numeric($min) );
2817
2818 if( !is_numeric($min) || !is_numeric($max) ) {
2819 JpGraphError::RaiseL(25044);//('Cannot use autoscaling since it is impossible to determine a valid min/max value of the Y-axis (only null values).');
2820 }
2821
2822 for($i=0; $i < $n; ++$i ) {
2823 list($xmax,$ymax)=$aPlots[$i]->Max();
2824 list($xmin,$ymin)=$aPlots[$i]->Min();
2825 if (is_numeric($ymax)) $max=max($max,$ymax);
2826 if (is_numeric($ymin)) $min=min($min,$ymin);
2827 }
2828 if( $min == '' ) $min = 0;
2829 if( $max == '' ) $max = 0;
2830 if( $min == 0 && $max == 0 ) {
2831 // Special case if all values are 0
2832 $min=0;$max=1;
2833 }
2834 return array($min,$max);
2835 }
2836
2837} // Class
2838
2839//===================================================
2840// CLASS LineProperty
2841// Description: Holds properties for a line
2842//===================================================
2843class LineProperty {
2844 public $iWeight=1, $iColor="black",$iStyle="solid",$iShow=true;
2845
2846//---------------
2847// PUBLIC METHODS
2848 function SetColor($aColor) {
2849 $this->iColor = $aColor;
2850 }
2851
2852 function SetWeight($aWeight) {
2853 $this->iWeight = $aWeight;
2854 }
2855
2856 function SetStyle($aStyle) {
2857 $this->iStyle = $aStyle;
2858 }
2859
2860 function Show($aShow=true) {
2861 $this->iShow=$aShow;
2862 }
2863
2864 function Stroke($aImg,$aX1,$aY1,$aX2,$aY2) {
2865 if( $this->iShow ) {
2866 $aImg->PushColor($this->iColor);
2867 $oldls = $aImg->line_style;
2868 $oldlw = $aImg->line_weight;
2869 $aImg->SetLineWeight($this->iWeight);
2870 $aImg->SetLineStyle($this->iStyle);
2871 $aImg->StyleLine($aX1,$aY1,$aX2,$aY2);
2872 $aImg->PopColor($this->iColor);
2873 $aImg->line_style = $oldls;
2874 $aImg->line_weight = $oldlw;
2875
2876 }
2877 }
2878}
2879
2880class GraphTabTitle extends Text{
2881 private $corner = 6 , $posx = 7, $posy = 4;
2882 private $fillcolor='lightyellow',$bordercolor='black';
2883 private $align = 'left', $width=TABTITLE_WIDTHFIT;
2884 function GraphTabTitle() {
2885 $this->t = '';
2886 $this->font_style = FS_BOLD;
2887 $this->hide = true;
2888 $this->color = 'darkred';
2889 }
2890
2891 function SetColor($aTxtColor,$aFillColor='lightyellow',$aBorderColor='black') {
2892 $this->color = $aTxtColor;
2893 $this->fillcolor = $aFillColor;
2894 $this->bordercolor = $aBorderColor;
2895 }
2896
2897 function SetFillColor($aFillColor) {
2898 $this->fillcolor = $aFillColor;
2899 }
2900
2901 function SetTabAlign($aAlign) {
2902 $this->align = $aAlign;
2903 }
2904
2905 function SetWidth($aWidth) {
2906 $this->width = $aWidth ;
2907 }
2908
2909 function Set($t) {
2910 $this->t = $t;
2911 $this->hide = false;
2912 }
2913
2914 function SetCorner($aD) {
2915 $this->corner = $aD ;
2916 }
2917
2918 function Stroke($aImg,$aDummy1=null,$aDummy2=null) {
2919 if( $this->hide )
2920 return;
2921 $this->boxed = false;
2922 $w = $this->GetWidth($aImg) + 2*$this->posx;
2923 $h = $this->GetTextHeight($aImg) + 2*$this->posy;
2924
2925 $x = $aImg->left_margin;
2926 $y = $aImg->top_margin;
2927
2928 if( $this->width === TABTITLE_WIDTHFIT ) {
2929 if( $this->align == 'left' ) {
2930 $p = array($x, $y,
2931 $x, $y-$h+$this->corner,
2932 $x + $this->corner,$y-$h,
2933 $x + $w - $this->corner, $y-$h,
2934 $x + $w, $y-$h+$this->corner,
2935 $x + $w, $y);
2936 }
2937 elseif( $this->align == 'center' ) {
2938 $x += round($aImg->plotwidth/2) - round($w/2);
2939 $p = array($x, $y,
2940 $x, $y-$h+$this->corner,
2941 $x + $this->corner, $y-$h,
2942 $x + $w - $this->corner, $y-$h,
2943 $x + $w, $y-$h+$this->corner,
2944 $x + $w, $y);
2945 }
2946 else {
2947 $x += $aImg->plotwidth -$w;
2948 $p = array($x, $y,
2949 $x, $y-$h+$this->corner,
2950 $x + $this->corner,$y-$h,
2951 $x + $w - $this->corner, $y-$h,
2952 $x + $w, $y-$h+$this->corner,
2953 $x + $w, $y);
2954 }
2955 }
2956 else {
2957 if( $this->width === TABTITLE_WIDTHFULL )
2958 $w = $aImg->plotwidth ;
2959 else
2960 $w = $this->width ;
2961
2962 // Make the tab fit the width of the plot area
2963 $p = array($x, $y,
2964 $x, $y-$h+$this->corner,
2965 $x + $this->corner,$y-$h,
2966 $x + $w - $this->corner, $y-$h,
2967 $x + $w, $y-$h+$this->corner,
2968 $x + $w, $y);
2969
2970 }
2971 if( $this->halign == 'left' ) {
2972 $aImg->SetTextAlign('left','bottom');
2973 $x += $this->posx;
2974 $y -= $this->posy;
2975 }
2976 elseif( $this->halign == 'center' ) {
2977 $aImg->SetTextAlign('center','bottom');
2978 $x += $w/2;
2979 $y -= $this->posy;
2980 }
2981 else {
2982 $aImg->SetTextAlign('right','bottom');
2983 $x += $w - $this->posx;
2984 $y -= $this->posy;
2985 }
2986
2987 $aImg->SetColor($this->fillcolor);
2988 $aImg->FilledPolygon($p);
2989
2990 $aImg->SetColor($this->bordercolor);
2991 $aImg->Polygon($p,true);
2992
2993 $aImg->SetColor($this->color);
2994 $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
2995 $aImg->StrokeText($x,$y,$this->t,0,'center');
2996 }
2997
2998}
2999
3000//===================================================
3001// CLASS SuperScriptText
3002// Description: Format a superscript text
3003//===================================================
3004class SuperScriptText extends Text {
3005 private $iSuper="";
3006 private $sfont_family="",$sfont_style="",$sfont_size=8;
3007 private $iSuperMargin=2,$iVertOverlap=4,$iSuperScale=0.65;
3008 private $iSDir=0;
3009 private $iSimple=false;
3010
3011 function SuperScriptText($aTxt="",$aSuper="",$aXAbsPos=0,$aYAbsPos=0) {
3012 parent::Text($aTxt,$aXAbsPos,$aYAbsPos);
3013 $this->iSuper = $aSuper;
3014 }
3015
3016 function FromReal($aVal,$aPrecision=2) {
3017 // Convert a floating point number to scientific notation
3018 $neg=1.0;
3019 if( $aVal < 0 ) {
3020 $neg = -1.0;
3021 $aVal = -$aVal;
3022 }
3023
3024 $l = floor(log10($aVal));
3025 $a = sprintf("%0.".$aPrecision."f",round($aVal / pow(10,$l),$aPrecision));
3026 $a *= $neg;
3027 if( $this->iSimple && ($a == 1 || $a==-1) ) $a = '';
3028
3029 if( $a != '' )
3030 $this->t = $a.' * 10';
3031 else {
3032 if( $neg == 1 )
3033 $this->t = '10';
3034 else
3035 $this->t = '-10';
3036 }
3037 $this->iSuper = $l;
3038 }
3039
3040 function Set($aTxt,$aSuper="") {
3041 $this->t = $aTxt;
3042 $this->iSuper = $aSuper;
3043 }
3044
3045 function SetSuperFont($aFontFam,$aFontStyle=FS_NORMAL,$aFontSize=8) {
3046 $this->sfont_family = $aFontFam;
3047 $this->sfont_style = $aFontStyle;
3048 $this->sfont_size = $aFontSize;
3049 }
3050
3051 // Total width of text
3052 function GetWidth($aImg) {
3053 $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
3054 $w = $aImg->GetTextWidth($this->t);
3055 $aImg->SetFont($this->sfont_family,$this->sfont_style,$this->sfont_size);
3056 $w += $aImg->GetTextWidth($this->iSuper);
3057 $w += $this->iSuperMargin;
3058 return $w;
3059 }
3060
3061 // Hight of font (approximate the height of the text)
3062 function GetFontHeight($aImg) {
3063 $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
3064 $h = $aImg->GetFontHeight();
3065 $aImg->SetFont($this->sfont_family,$this->sfont_style,$this->sfont_size);
3066 $h += $aImg->GetFontHeight();
3067 return $h;
3068 }
3069
3070 // Hight of text
3071 function GetTextHeight($aImg) {
3072 $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
3073 $h = $aImg->GetTextHeight($this->t);
3074 $aImg->SetFont($this->sfont_family,$this->sfont_style,$this->sfont_size);
3075 $h += $aImg->GetTextHeight($this->iSuper);
3076 return $h;
3077 }
3078
3079 function Stroke($aImg,$ax=-1,$ay=-1) {
3080
3081 // To position the super script correctly we need different
3082 // cases to handle the alignmewnt specified since that will
3083 // determine how we can interpret the x,y coordinates
3084
3085 $w = parent::GetWidth($aImg);
3086 $h = parent::GetTextHeight($aImg);
3087 switch( $this->valign ) {
3088 case 'top':
3089 $sy = $this->y;
3090 break;
3091 case 'center':
3092 $sy = $this->y - $h/2;
3093 break;
3094 case 'bottom':
3095 $sy = $this->y - $h;
3096 break;
3097 default:
3098 JpGraphError::RaiseL(25052);//('PANIC: Internal error in SuperScript::Stroke(). Unknown vertical alignment for text');
3099 break;
3100 }
3101
3102 switch( $this->halign ) {
3103 case 'left':
3104 $sx = $this->x + $w;
3105 break;
3106 case 'center':
3107 $sx = $this->x + $w/2;
3108 break;
3109 case 'right':
3110 $sx = $this->x;
3111 break;
3112 default:
3113 JpGraphError::RaiseL(25053);//('PANIC: Internal error in SuperScript::Stroke(). Unknown horizontal alignment for text');
3114 break;
3115 }
3116
3117 $sx += $this->iSuperMargin;
3118 $sy += $this->iVertOverlap;
3119
3120 // Should we automatically determine the font or
3121 // has the user specified it explicetly?
3122 if( $this->sfont_family == "" ) {
3123 if( $this->font_family <= FF_FONT2 ) {
3124 if( $this->font_family == FF_FONT0 ) {
3125 $sff = FF_FONT0;
3126 }
3127 elseif( $this->font_family == FF_FONT1 ) {
3128 if( $this->font_style == FS_NORMAL )
3129 $sff = FF_FONT0;
3130 else
3131 $sff = FF_FONT1;
3132 }
3133 else {
3134 $sff = FF_FONT1;
3135 }
3136 $sfs = $this->font_style;
3137 $sfz = $this->font_size;
3138 }
3139 else {
3140 // TTF fonts
3141 $sff = $this->font_family;
3142 $sfs = $this->font_style;
3143 $sfz = floor($this->font_size*$this->iSuperScale);
3144 if( $sfz < 8 ) $sfz = 8;
3145 }
3146 $this->sfont_family = $sff;
3147 $this->sfont_style = $sfs;
3148 $this->sfont_size = $sfz;
3149 }
3150 else {
3151 $sff = $this->sfont_family;
3152 $sfs = $this->sfont_style;
3153 $sfz = $this->sfont_size;
3154 }
3155
3156 parent::Stroke($aImg,$ax,$ay);
3157
3158
3159 // For the builtin fonts we need to reduce the margins
3160 // since the bounding bx reported for the builtin fonts
3161 // are much larger than for the TTF fonts.
3162 if( $sff <= FF_FONT2 ) {
3163 $sx -= 2;
3164 $sy += 3;
3165 }
3166
3167 $aImg->SetTextAlign('left','bottom');
3168 $aImg->SetFont($sff,$sfs,$sfz);
3169 $aImg->PushColor($this->color);
3170 $aImg->StrokeText($sx,$sy,$this->iSuper,$this->iSDir,'left');
3171 $aImg->PopColor();
3172 }
3173}
3174
3175
3176//===================================================
3177// CLASS Grid
3178// Description: responsible for drawing grid lines in graph
3179//===================================================
3180class Grid {
3181 protected $img;
3182 protected $scale;
3183 protected $grid_color='#DDDDDD',$grid_mincolor='#DDDDDD';
3184 protected $type="solid";
3185 protected $show=false, $showMinor=false,$weight=1;
3186 protected $fill=false,$fillcolor=array('#EFEFEF','#BBCCFF');
3187//---------------
3188// CONSTRUCTOR
3189 function Grid($aAxis) {
3190 $this->scale = $aAxis->scale;
3191 $this->img = $aAxis->img;
3192 }
3193//---------------
3194// PUBLIC METHODS
3195 function SetColor($aMajColor,$aMinColor=false) {
3196 $this->grid_color=$aMajColor;
3197 if( $aMinColor === false )
3198 $aMinColor = $aMajColor ;
3199 $this->grid_mincolor = $aMinColor;
3200 }
3201
3202 function SetWeight($aWeight) {
3203 $this->weight=$aWeight;
3204 }
3205
3206 // Specify if grid should be dashed, dotted or solid
3207 function SetLineStyle($aType) {
3208 $this->type = $aType;
3209 }
3210
3211 // Decide if both major and minor grid should be displayed
3212 function Show($aShowMajor=true,$aShowMinor=false) {
3213 $this->show=$aShowMajor;
3214 $this->showMinor=$aShowMinor;
3215 }
3216
3217 function SetFill($aFlg=true,$aColor1='lightgray',$aColor2='lightblue') {
3218 $this->fill = $aFlg;
3219 $this->fillcolor = array( $aColor1, $aColor2 );
3220 }
3221
3222 // Display the grid
3223 function Stroke() {
3224 if( $this->showMinor && !$this->scale->textscale ) {
3225 $tmp = $this->grid_color;
3226 $this->grid_color = $this->grid_mincolor;
3227 $this->DoStroke($this->scale->ticks->ticks_pos);
3228
3229 $this->grid_color = $tmp;
3230 $this->DoStroke($this->scale->ticks->maj_ticks_pos);
3231 }
3232 else {
3233 $this->DoStroke($this->scale->ticks->maj_ticks_pos);
3234 }
3235 }
3236
3237//--------------
3238// Private methods
3239 // Draw the grid
3240 function DoStroke($aTicksPos) {
3241 if( !$this->show )
3242 return;
3243 $nbrgrids = count($aTicksPos);
3244
3245 if( $this->scale->type=="y" ) {
3246 $xl=$this->img->left_margin;
3247 $xr=$this->img->width-$this->img->right_margin;
3248
3249 if( $this->fill ) {
3250 // Draw filled areas
3251 $y2 = $aTicksPos[0];
3252 $i=1;
3253 while( $i < $nbrgrids ) {
3254 $y1 = $y2;
3255 $y2 = $aTicksPos[$i++];
3256 $this->img->SetColor($this->fillcolor[$i & 1]);
3257 $this->img->FilledRectangle($xl,$y1,$xr,$y2);
3258 }
3259 }
3260
3261 $this->img->SetColor($this->grid_color);
3262 $this->img->SetLineWeight($this->weight);
3263
3264 // Draw grid lines
3265 switch( $this->type ) {
3266 case "solid": $style = LINESTYLE_SOLID; break;
3267 case "dotted": $style = LINESTYLE_DOTTED; break;
3268 case "dashed": $style = LINESTYLE_DASHED; break;
3269 case "longdashed": $style = LINESTYLE_LONGDASH; break;
3270 default:
3271 $style = LINESTYLE_SOLID; break;
3272 }
3273
3274 for($i=0; $i < $nbrgrids; ++$i) {
3275 $y=$aTicksPos[$i];
3276 $this->img->StyleLine($xl,$y,$xr,$y,$style);
3277 }
3278 }
3279 elseif( $this->scale->type=="x" ) {
3280 $yu=$this->img->top_margin;
3281 $yl=$this->img->height-$this->img->bottom_margin;
3282 $limit=$this->img->width-$this->img->right_margin;
3283
3284 if( $this->fill ) {
3285 // Draw filled areas
3286 $x2 = $aTicksPos[0];
3287 $i=1;
3288 while( $i < $nbrgrids ) {
3289 $x1 = $x2;
3290 $x2 = min($aTicksPos[$i++],$limit) ;
3291 $this->img->SetColor($this->fillcolor[$i & 1]);
3292 $this->img->FilledRectangle($x1,$yu,$x2,$yl);
3293 }
3294 }
3295
3296 $this->img->SetColor($this->grid_color);
3297 $this->img->SetLineWeight($this->weight);
3298
3299 // We must also test for limit since we might have
3300 // an offset and the number of ticks is calculated with
3301 // assumption offset==0 so we might end up drawing one
3302 // to many gridlines
3303 $i=0;
3304 $x=$aTicksPos[$i];
3305 while( $i<count($aTicksPos) && ($x=$aTicksPos[$i]) <= $limit ) {
3306 if( $this->type == "solid" )
3307 $this->img->Line($x,$yl,$x,$yu);
3308 elseif( $this->type == "dotted" )
3309 $this->img->DashedLine($x,$yl,$x,$yu,1,6);
3310 elseif( $this->type == "dashed" )
3311 $this->img->DashedLine($x,$yl,$x,$yu,2,4);
3312 elseif( $this->type == "longdashed" )
3313 $this->img->DashedLine($x,$yl,$x,$yu,8,6);
3314 ++$i;
3315 }
3316 }
3317 else {
3318 JpGraphError::RaiseL(25054,$this->scale->type);//('Internal error: Unknown grid axis ['.$this->scale->type.']');
3319 }
3320 return true;
3321 }
3322} // Class
3323
3324//===================================================
3325// CLASS Axis
3326// Description: Defines X and Y axis. Notes that at the
3327// moment the code is not really good since the axis on
3328// several occasion must know wheter it's an X or Y axis.
3329// This was a design decision to make the code easier to
3330// follow.
3331//===================================================
3332class AxisPrototype {
3333 public $scale=null;
3334 public $img=null;
3335 public $hide=false,$hide_labels=false;
3336 public $title=null;
3337 public $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12,$label_angle=0;
3338 public $tick_step=1;
3339 public $pos = false;
3340 public $ticks_label = array();
3341
3342 protected $weight=1;
3343 protected $color=array(0,0,0),$label_color=array(0,0,0);
3344 protected $ticks_label_colors=null;
3345 protected $show_first_label=true,$show_last_label=true;
3346 protected $label_step=1; // Used by a text axis to specify what multiple of major steps
3347 // should be labeled.
3348 protected $labelPos=0; // Which side of the axis should the labels be?
3349 protected $title_adjust,$title_margin,$title_side=SIDE_LEFT;
3350 protected $tick_label_margin=7;
3351 protected $label_halign = '',$label_valign = '', $label_para_align='left';
3352 protected $hide_line=false;
3353 protected $iDeltaAbsPos=0;
3354
3355//---------------
3356// CONSTRUCTOR
3357 function Axis($img,$aScale,$color=array(0,0,0)) {
3358 $this->img = $img;
3359 $this->scale = $aScale;
3360 $this->color = $color;
3361 $this->title=new Text("");
3362
3363 if( $aScale->type=="y" ) {
3364 $this->title_margin = 25;
3365 $this->title_adjust="middle";
3366 $this->title->SetOrientation(90);
3367 $this->tick_label_margin=7;
3368 $this->labelPos=SIDE_LEFT;
3369 }
3370 else {
3371 $this->title_margin = 5;
3372 $this->title_adjust="high";
3373 $this->title->SetOrientation(0);
3374 $this->tick_label_margin=7;
3375 $this->labelPos=SIDE_DOWN;
3376 $this->title_side=SIDE_DOWN;
3377 }
3378 }
3379//---------------
3380// PUBLIC METHODS
3381
3382 function SetLabelFormat($aFormStr) {
3383 $this->scale->ticks->SetLabelFormat($aFormStr);
3384 }
3385
3386 function SetLabelFormatString($aFormStr,$aDate=false) {
3387 $this->scale->ticks->SetLabelFormat($aFormStr,$aDate);
3388 }
3389
3390 function SetLabelFormatCallback($aFuncName) {
3391 $this->scale->ticks->SetFormatCallback($aFuncName);
3392 }
3393
3394 function SetLabelAlign($aHAlign,$aVAlign="top",$aParagraphAlign='left') {
3395 $this->label_halign = $aHAlign;
3396 $this->label_valign = $aVAlign;
3397 $this->label_para_align = $aParagraphAlign;
3398 }
3399
3400 // Don't display the first label
3401 function HideFirstTickLabel($aShow=false) {
3402 $this->show_first_label=$aShow;
3403 }
3404
3405 function HideLastTickLabel($aShow=false) {
3406 $this->show_last_label=$aShow;
3407 }
3408
3409 // Manually specify the major and (optional) minor tick position and labels
3410 function SetTickPositions($aMajPos,$aMinPos=NULL,$aLabels=NULL) {
3411 $this->scale->ticks->SetTickPositions($aMajPos,$aMinPos,$aLabels);
3412 }
3413
3414 // Manually specify major tick positions and optional labels
3415 function SetMajTickPositions($aMajPos,$aLabels=NULL) {
3416 $this->scale->ticks->SetTickPositions($aMajPos,NULL,$aLabels);
3417 }
3418
3419 // Hide minor or major tick marks
3420 function HideTicks($aHideMinor=true,$aHideMajor=true) {
3421 $this->scale->ticks->SupressMinorTickMarks($aHideMinor);
3422 $this->scale->ticks->SupressTickMarks($aHideMajor);
3423 }
3424
3425 // Hide zero label
3426 function HideZeroLabel($aFlag=true) {
3427 $this->scale->ticks->SupressZeroLabel();
3428 }
3429
3430 function HideFirstLastLabel() {
3431 // The two first calls to ticks method will supress
3432 // automatically generated scale values. However, that
3433 // will not affect manually specified value, e.g text-scales.
3434 // therefor we also make a kludge here to supress manually
3435 // specified scale labels.
3436 $this->scale->ticks->SupressLast();
3437 $this->scale->ticks->SupressFirst();
3438 $this->show_first_label = false;
3439 $this->show_last_label = false;
3440 }
3441
3442 // Hide the axis
3443 function Hide($aHide=true) {
3444 $this->hide=$aHide;
3445 }
3446
3447 // Hide the actual axis-line, but still print the labels
3448 function HideLine($aHide=true) {
3449 $this->hide_line = $aHide;
3450 }
3451
3452 function HideLabels($aHide=true) {
3453 $this->hide_labels = $aHide;
3454 }
3455
3456
3457 // Weight of axis
3458 function SetWeight($aWeight) {
3459 $this->weight = $aWeight;
3460 }
3461
3462 // Axis color
3463 function SetColor($aColor,$aLabelColor=false) {
3464 $this->color = $aColor;
3465 if( !$aLabelColor ) $this->label_color = $aColor;
3466 else $this->label_color = $aLabelColor;
3467 }
3468
3469 // Title on axis
3470 function SetTitle($aTitle,$aAdjustAlign="high") {
3471 $this->title->Set($aTitle);
3472 $this->title_adjust=$aAdjustAlign;
3473 }
3474
3475 // Specify distance from the axis
3476 function SetTitleMargin($aMargin) {
3477 $this->title_margin=$aMargin;
3478 }
3479
3480 // Which side of the axis should the axis title be?
3481 function SetTitleSide($aSideOfAxis) {
3482 $this->title_side = $aSideOfAxis;
3483 }
3484
3485 // Utility function to set the direction for tick marks
3486 function SetTickDirection($aDir) {
3487 // Will be deprecated from 1.7
3488 if( ERR_DEPRECATED )
3489 JpGraphError::RaiseL(25055);//('Axis::SetTickDirection() is deprecated. Use Axis::SetTickSide() instead');
3490 $this->scale->ticks->SetSide($aDir);
3491 }
3492
3493 function SetTickSide($aDir) {
3494 $this->scale->ticks->SetSide($aDir);
3495 }
3496
3497 // Specify text labels for the ticks. One label for each data point
3498 function SetTickLabels($aLabelArray,$aLabelColorArray=null) {
3499 $this->ticks_label = $aLabelArray;
3500 $this->ticks_label_colors = $aLabelColorArray;
3501 }
3502
3503 // How far from the axis should the labels be drawn
3504 function SetTickLabelMargin($aMargin) {
3505 if( ERR_DEPRECATED )
3506 JpGraphError::RaiseL(25056);//('SetTickLabelMargin() is deprecated. Use Axis::SetLabelMargin() instead.');
3507 $this->tick_label_margin=$aMargin;
3508 }
3509
3510 function SetLabelMargin($aMargin) {
3511 $this->tick_label_margin=$aMargin;
3512 }
3513
3514 // Specify that every $step of the ticks should be displayed starting
3515 // at $start
3516 // DEPRECATED FUNCTION: USE SetTextTickInterval() INSTEAD
3517 function SetTextTicks($step,$start=0) {
3518 JpGraphError::RaiseL(25057);//(" SetTextTicks() is deprecated. Use SetTextTickInterval() instead.");
3519 }
3520
3521 // Specify that every $step of the ticks should be displayed starting
3522 // at $start
3523 function SetTextTickInterval($aStep,$aStart=0) {
3524 $this->scale->ticks->SetTextLabelStart($aStart);
3525 $this->tick_step=$aStep;
3526 }
3527
3528 // Specify that every $step tick mark should have a label
3529 // should be displayed starting
3530 function SetTextLabelInterval($aStep) {
3531 if( $aStep < 1 )
3532 JpGraphError::RaiseL(25058);//(" Text label interval must be specified >= 1.");
3533 $this->label_step=$aStep;
3534 }
3535
3536 // Which side of the axis should the labels be on?
3537 function SetLabelPos($aSidePos) {
3538 // This will be deprecated from 1.7
3539 if( ERR_DEPRECATED )
3540 JpGraphError::RaiseL(25059);//('SetLabelPos() is deprecated. Use Axis::SetLabelSide() instead.');
3541 $this->labelPos=$aSidePos;
3542 }
3543
3544 function SetLabelSide($aSidePos) {
3545 $this->labelPos=$aSidePos;
3546 }
3547
3548 // Set the font
3549 function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
3550 $this->font_family = $aFamily;
3551 $this->font_style = $aStyle;
3552 $this->font_size = $aSize;
3553 }
3554
3555 // Position for axis line on the "other" scale
3556 function SetPos($aPosOnOtherScale) {
3557 $this->pos=$aPosOnOtherScale;
3558 }
3559
3560 // Set the position of the axis to be X-pixels delta to the right
3561 // of the max X-position (used to position the multiple Y-axis)
3562 function SetPosAbsDelta($aDelta) {
3563 $this->iDeltaAbsPos=$aDelta;
3564 }
3565
3566 // Specify the angle for the tick labels
3567 function SetLabelAngle($aAngle) {
3568 $this->label_angle = $aAngle;
3569 }
3570
3571} // Class
3572
3573
3574//===================================================
3575// CLASS Axis
3576// Description: Defines X and Y axis. Notes that at the
3577// moment the code is not really good since the axis on
3578// several occasion must know wheter it's an X or Y axis.
3579// This was a design decision to make the code easier to
3580// follow.
3581//===================================================
3582class Axis extends AxisPrototype {
3583
3584 function Axis($img,$aScale,$color=array(0,0,0)) {
3585 parent::Axis($img,$aScale,$color);
3586 }
3587
3588 // Stroke the axis.
3589 function Stroke($aOtherAxisScale,$aStrokeLabels=true) {
3590 if( $this->hide ) return;
3591 if( is_numeric($this->pos) ) {
3592 $pos=$aOtherAxisScale->Translate($this->pos);
3593 }
3594 else { // Default to minimum of other scale if pos not set
3595 if( ($aOtherAxisScale->GetMinVal() >= 0 && $this->pos==false) || $this->pos=="min" ) {
3596 $pos = $aOtherAxisScale->scale_abs[0];
3597 }
3598 elseif($this->pos == "max") {
3599 $pos = $aOtherAxisScale->scale_abs[1];
3600 }
3601 else { // If negative set x-axis at 0
3602 $this->pos=0;
3603 $pos=$aOtherAxisScale->Translate(0);
3604 }
3605 }
3606 $pos += $this->iDeltaAbsPos;
3607 $this->img->SetLineWeight($this->weight);
3608 $this->img->SetColor($this->color);
3609 $this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
3610 if( $this->scale->type == "x" ) {
3611 if( !$this->hide_line )
3612 $this->img->FilledRectangle($this->img->left_margin,$pos,
3613 $this->img->width-$this->img->right_margin,$pos+$this->weight-1);
3614 if( $this->title_side == SIDE_DOWN ) {
3615 $y = $pos + $this->img->GetFontHeight() + $this->title_margin + $this->title->margin;
3616 $yalign = 'top';
3617 }
3618 else {
3619 $y = $pos - $this->img->GetFontHeight() - $this->title_margin - $this->title->margin;
3620 $yalign = 'bottom';
3621 }
3622
3623 if( $this->title_adjust=='high' )
3624 $this->title->SetPos($this->img->width-$this->img->right_margin,$y,'right',$yalign);
3625 elseif( $this->title_adjust=='middle' || $this->title_adjust=='center' )
3626 $this->title->SetPos(($this->img->width-$this->img->left_margin-$this->img->right_margin)/2+$this->img->left_margin,$y,'center',$yalign);
3627 elseif($this->title_adjust=='low')
3628 $this->title->SetPos($this->img->left_margin,$y,'left',$yalign);
3629 else {
3630 JpGraphError::RaiseL(25060,$this->title_adjust);//('Unknown alignment specified for X-axis title. ('.$this->title_adjust.')');
3631 }
3632 }
3633 elseif( $this->scale->type == "y" ) {
3634 // Add line weight to the height of the axis since
3635 // the x-axis could have a width>1 and we want the axis to fit nicely together.
3636 if( !$this->hide_line )
3637 $this->img->FilledRectangle($pos-$this->weight+1,$this->img->top_margin,
3638 $pos,$this->img->height-$this->img->bottom_margin+$this->weight-1);
3639 $x=$pos ;
3640 if( $this->title_side == SIDE_LEFT ) {
3641 $x -= $this->title_margin;
3642 $x -= $this->title->margin;
3643 $halign="right";
3644 }
3645 else {
3646 $x += $this->title_margin;
3647 $x += $this->title->margin;
3648 $halign="left";
3649 }
3650 // If the user has manually specified an hor. align
3651 // then we override the automatic settings with this
3652 // specifed setting. Since default is 'left' we compare
3653 // with that. (This means a manually set 'left' align
3654 // will have no effect.)
3655 if( $this->title->halign != 'left' )
3656 $halign = $this->title->halign;
3657 if( $this->title_adjust=="high" )
3658 $this->title->SetPos($x,$this->img->top_margin,$halign,"top");
3659 elseif($this->title_adjust=="middle" || $this->title_adjust=="center")
3660 $this->title->SetPos($x,($this->img->height-$this->img->top_margin-$this->img->bottom_margin)/2+$this->img->top_margin,$halign,"center");
3661 elseif($this->title_adjust=="low")
3662 $this->title->SetPos($x,$this->img->height-$this->img->bottom_margin,$halign,"bottom");
3663 else
3664 JpGraphError::RaiseL(25061,$this->title_adjust);//('Unknown alignment specified for Y-axis title. ('.$this->title_adjust.')');
3665
3666 }
3667 $this->scale->ticks->Stroke($this->img,$this->scale,$pos);
3668 if( $aStrokeLabels ) {
3669 if( !$this->hide_labels )
3670 $this->StrokeLabels($pos);
3671 $this->title->Stroke($this->img);
3672 }
3673 }
3674
3675//---------------
3676// PRIVATE METHODS
3677 // Draw all the tick labels on major tick marks
3678 function StrokeLabels($aPos,$aMinor=false,$aAbsLabel=false) {
3679
3680 $this->img->SetColor($this->label_color);
3681 $this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
3682 $yoff=$this->img->GetFontHeight()/2;
3683
3684 // Only draw labels at major tick marks
3685 $nbr = count($this->scale->ticks->maj_ticks_label);
3686
3687 // We have the option to not-display the very first mark
3688 // (Usefull when the first label might interfere with another
3689 // axis.)
3690 $i = $this->show_first_label ? 0 : 1 ;
3691 if( !$this->show_last_label ) --$nbr;
3692 // Now run through all labels making sure we don't overshoot the end
3693 // of the scale.
3694 $ncolor=0;
3695 if( isset($this->ticks_label_colors) )
3696 $ncolor=count($this->ticks_label_colors);
3697 while( $i<$nbr ) {
3698 // $tpos holds the absolute text position for the label
3699 $tpos=$this->scale->ticks->maj_ticklabels_pos[$i];
3700
3701 // Note. the $limit is only used for the x axis since we
3702 // might otherwise overshoot if the scale has been centered
3703 // This is due to us "loosing" the last tick mark if we center.
3704 if( $this->scale->type=="x" && $tpos > $this->img->width-$this->img->right_margin+1 ) {
3705 return;
3706 }
3707 // we only draw every $label_step label
3708 if( ($i % $this->label_step)==0 ) {
3709
3710 // Set specific label color if specified
3711 if( $ncolor > 0 )
3712 $this->img->SetColor($this->ticks_label_colors[$i % $ncolor]);
3713
3714 // If the label has been specified use that and in other case
3715 // just label the mark with the actual scale value
3716 $m=$this->scale->ticks->GetMajor();
3717
3718 // ticks_label has an entry for each data point and is the array
3719 // that holds the labels set by the user. If the user hasn't
3720 // specified any values we use whats in the automatically asigned
3721 // labels in the maj_ticks_label
3722 if( isset($this->ticks_label[$i*$m]) )
3723 $label=$this->ticks_label[$i*$m];
3724 else {
3725 if( $aAbsLabel )
3726 $label=abs($this->scale->ticks->maj_ticks_label[$i]);
3727 else
3728 $label=$this->scale->ticks->maj_ticks_label[$i];
3729 if( $this->scale->textscale && $this->scale->ticks->label_formfunc == '' ) {
3730 ++$label;
3731 }
3732 }
3733
3734 if( $this->scale->type == "x" ) {
3735 if( $this->labelPos == SIDE_DOWN ) {
3736 if( $this->label_angle==0 || $this->label_angle==90 ) {
3737 if( $this->label_halign=='' && $this->label_valign=='')
3738 $this->img->SetTextAlign('center','top');
3739 else
3740 $this->img->SetTextAlign($this->label_halign,$this->label_valign);
3741
3742 }
3743 else {
3744 if( $this->label_halign=='' && $this->label_valign=='')
3745 $this->img->SetTextAlign("right","top");
3746 else
3747 $this->img->SetTextAlign($this->label_halign,$this->label_valign);
3748 }
3749 $this->img->StrokeText($tpos,$aPos+$this->tick_label_margin+1,$label,
3750 $this->label_angle,$this->label_para_align);
3751 }
3752 else {
3753 if( $this->label_angle==0 || $this->label_angle==90 ) {
3754 if( $this->label_halign=='' && $this->label_valign=='')
3755 $this->img->SetTextAlign("center","bottom");
3756 else
3757 $this->img->SetTextAlign($this->label_halign,$this->label_valign);
3758 }
3759 else {
3760 if( $this->label_halign=='' && $this->label_valign=='')
3761 $this->img->SetTextAlign("right","bottom");
3762 else
3763 $this->img->SetTextAlign($this->label_halign,$this->label_valign);
3764 }
3765 $this->img->StrokeText($tpos,$aPos-$this->tick_label_margin-1,$label,
3766 $this->label_angle,$this->label_para_align);
3767 }
3768 }
3769 else {
3770 // scale->type == "y"
3771 //if( $this->label_angle!=0 )
3772 //JpGraphError::Raise(" Labels at an angle are not supported on Y-axis");
3773 if( $this->labelPos == SIDE_LEFT ) { // To the left of y-axis
3774 if( $this->label_halign=='' && $this->label_valign=='')
3775 $this->img->SetTextAlign("right","center");
3776 else
3777 $this->img->SetTextAlign($this->label_halign,$this->label_valign);
3778 $this->img->StrokeText($aPos-$this->tick_label_margin,$tpos,$label,$this->label_angle,$this->label_para_align);
3779 }
3780 else { // To the right of the y-axis
3781 if( $this->label_halign=='' && $this->label_valign=='')
3782 $this->img->SetTextAlign("left","center");
3783 else
3784 $this->img->SetTextAlign($this->label_halign,$this->label_valign);
3785 $this->img->StrokeText($aPos+$this->tick_label_margin,$tpos,$label,$this->label_angle,$this->label_para_align);
3786 }
3787 }
3788 }
3789 ++$i;
3790 }
3791 }
3792
3793}
3794
3795
3796//===================================================
3797// CLASS Ticks
3798// Description: Abstract base class for drawing linear and logarithmic
3799// tick marks on axis
3800//===================================================
3801class Ticks {
3802 public $label_formatstr=''; // C-style format string to use for labels
3803 public $label_formfunc='';
3804 public $label_dateformatstr='';
3805 public $direction=1; // Should ticks be in(=1) the plot area or outside (=-1)
3806 public $supress_last=false,$supress_tickmarks=false,$supress_minor_tickmarks=false;
3807 public $maj_ticks_pos = array(), $maj_ticklabels_pos = array(),
3808 $ticks_pos = array(), $maj_ticks_label = array();
3809 public $precision;
3810
3811 protected $minor_abs_size=3, $major_abs_size=5;
3812 protected $scale;
3813 protected $is_set=false;
3814 protected $supress_zerolabel=false,$supress_first=false;
3815 protected $mincolor="",$majcolor="";
3816 protected $weight=1;
3817 protected $label_usedateformat=FALSE;
3818
3819//---------------
3820// CONSTRUCTOR
3821 function Ticks($aScale) {
3822 $this->scale=$aScale;
3823 $this->precision = -1;
3824 }
3825
3826//---------------
3827// PUBLIC METHODS
3828 // Set format string for automatic labels
3829 function SetLabelFormat($aFormatString,$aDate=FALSE) {
3830 $this->label_formatstr=$aFormatString;
3831 $this->label_usedateformat=$aDate;
3832 }
3833
3834 function SetLabelDateFormat($aFormatString) {
3835 $this->label_dateformatstr=$aFormatString;
3836 }
3837
3838 function SetFormatCallback($aCallbackFuncName) {
3839 $this->label_formfunc = $aCallbackFuncName;
3840 }
3841
3842 // Don't display the first zero label
3843 function SupressZeroLabel($aFlag=true) {
3844 $this->supress_zerolabel=$aFlag;
3845 }
3846
3847 // Don't display minor tick marks
3848 function SupressMinorTickMarks($aHide=true) {
3849 $this->supress_minor_tickmarks=$aHide;
3850 }
3851
3852 // Don't display major tick marks
3853 function SupressTickMarks($aHide=true) {
3854 $this->supress_tickmarks=$aHide;
3855 }
3856
3857 // Hide the first tick mark
3858 function SupressFirst($aHide=true) {
3859 $this->supress_first=$aHide;
3860 }
3861
3862 // Hide the last tick mark
3863 function SupressLast($aHide=true) {
3864 $this->supress_last=$aHide;
3865 }
3866
3867 // Size (in pixels) of minor tick marks
3868 function GetMinTickAbsSize() {
3869 return $this->minor_abs_size;
3870 }
3871
3872 // Size (in pixels) of major tick marks
3873 function GetMajTickAbsSize() {
3874 return $this->major_abs_size;
3875 }
3876
3877 function SetSize($aMajSize,$aMinSize=3) {
3878 $this->major_abs_size = $aMajSize;
3879 $this->minor_abs_size = $aMinSize;
3880 }
3881
3882 // Have the ticks been specified
3883 function IsSpecified() {
3884 return $this->is_set;
3885 }
3886
3887 // Specify number of decimals in automatic labels
3888 // Deprecated from 1.4. Use SetFormatString() instead
3889 function SetPrecision($aPrecision) {
3890 if( ERR_DEPRECATED )
3891 JpGraphError::RaiseL(25063);//('Ticks::SetPrecision() is deprecated. Use Ticks::SetLabelFormat() (or Ticks::SetFormatCallback()) instead');
3892 $this->precision=$aPrecision;
3893 }
3894
3895 function SetSide($aSide) {
3896 $this->direction=$aSide;
3897 }
3898
3899 // Which side of the axis should the ticks be on
3900 function SetDirection($aSide=SIDE_RIGHT) {
3901 $this->direction=$aSide;
3902 }
3903
3904 // Set colors for major and minor tick marks
3905 function SetMarkColor($aMajorColor,$aMinorColor="") {
3906 $this->SetColor($aMajorColor,$aMinorColor);
3907 }
3908
3909 function SetColor($aMajorColor,$aMinorColor="") {
3910 $this->majcolor=$aMajorColor;
3911
3912 // If not specified use same as major
3913 if( $aMinorColor=="" )
3914 $this->mincolor=$aMajorColor;
3915 else
3916 $this->mincolor=$aMinorColor;
3917 }
3918
3919 function SetWeight($aWeight) {
3920 $this->weight=$aWeight;
3921 }
3922
3923} // Class
3924
3925//===================================================
3926// CLASS LinearTicks
3927// Description: Draw linear ticks on axis
3928//===================================================
3929class LinearTicks extends Ticks {
3930 public $minor_step=1, $major_step=2;
3931 public $xlabel_offset=0,$xtick_offset=0;
3932 private $label_offset=0; // What offset should the displayed label have
3933 // i.e should we display 0,1,2 or 1,2,3,4 or 2,3,4 etc
3934 private $text_label_start=0;
3935 private $iManualTickPos = NULL, $iManualMinTickPos = NULL, $iManualTickLabels = NULL;
3936 private $iAdjustForDST = false; // If a date falls within the DST period add one hour to the diaplyed time
3937
3938//---------------
3939// CONSTRUCTOR
3940 function LinearTicks() {
3941 $this->precision = -1;
3942 }
3943
3944//---------------
3945// PUBLIC METHODS
3946
3947
3948 // Return major step size in world coordinates
3949 function GetMajor() {
3950 return $this->major_step;
3951 }
3952
3953 // Return minor step size in world coordinates
3954 function GetMinor() {
3955 return $this->minor_step;
3956 }
3957
3958 // Set Minor and Major ticks (in world coordinates)
3959 function Set($aMajStep,$aMinStep=false) {
3960 if( $aMinStep==false )
3961 $aMinStep=$aMajStep;
3962
3963 if( $aMajStep <= 0 || $aMinStep <= 0 ) {
3964 JpGraphError::RaiseL(25064);
3965//(" Minor or major step size is 0. Check that you haven't got an accidental SetTextTicks(0) in your code. If this is not the case you might have stumbled upon a bug in JpGraph. Please report this and if possible include the data that caused the problem.");
3966 }
3967
3968 $this->major_step=$aMajStep;
3969 $this->minor_step=$aMinStep;
3970 $this->is_set = true;
3971 }
3972
3973 function SetMajTickPositions($aMajPos,$aLabels=NULL) {
3974 $this->SetTickPositions($aMajPos,NULL,$aLabels);
3975 }
3976
3977 function SetTickPositions($aMajPos,$aMinPos=NULL,$aLabels=NULL) {
3978 if( !is_array($aMajPos) || ($aMinPos!==NULL && !is_array($aMinPos)) ) {
3979 JpGraphError::RaiseL(25065);//('Tick positions must be specifued as an array()');
3980 return;
3981 }
3982 $n=count($aMajPos);
3983 if( is_array($aLabels) && (count($aLabels) != $n) ) {
3984 JpGraphError::RaiseL(25066);//('When manually specifying tick positions and labels the number of labels must be the same as the number of specified ticks.');
3985 return;
3986 }
3987 $this->iManualTickPos = $aMajPos;
3988 $this->iManualMinTickPos = $aMinPos;
3989 $this->iManualTickLabels = $aLabels;
3990 }
3991
3992 // Specify all the tick positions manually and possible also the exact labels
3993 function _doManualTickPos($aScale) {
3994 $n=count($this->iManualTickPos);
3995 $m=count($this->iManualMinTickPos);
3996 $doLbl=count($this->iManualTickLabels) > 0;
3997
3998 $this->maj_ticks_pos = array();
3999 $this->maj_ticklabels_pos = array();
4000 $this->ticks_pos = array();
4001
4002 // Now loop through the supplied positions and translate them to screen coordinates
4003 // and store them in the maj_label_positions
4004 $minScale = $aScale->scale[0];
4005 $maxScale = $aScale->scale[1];
4006 $j=0;
4007 for($i=0; $i < $n ; ++$i ) {
4008 // First make sure that the first tick is not lower than the lower scale value
4009 if( !isset($this->iManualTickPos[$i]) ||
4010 $this->iManualTickPos[$i] < $minScale || $this->iManualTickPos[$i] > $maxScale) {
4011 continue;
4012 }
4013
4014
4015 $this->maj_ticks_pos[$j] = $aScale->Translate($this->iManualTickPos[$i]);
4016 $this->maj_ticklabels_pos[$j] = $this->maj_ticks_pos[$j];
4017
4018 // Set the minor tick marks the same as major if not specified
4019 if( $m <= 0 ) {
4020 $this->ticks_pos[$j] = $this->maj_ticks_pos[$j];
4021 }
4022
4023 if( $doLbl ) {
4024 $this->maj_ticks_label[$j] = $this->iManualTickLabels[$i];
4025 }
4026 else {
4027 $this->maj_ticks_label[$j]=$this->_doLabelFormat($this->iManualTickPos[$i],$i,$n);
4028 }
4029 ++$j;
4030 }
4031
4032 // Some sanity check
4033 if( count($this->maj_ticks_pos) < 2 ) {
4034 JpGraphError::RaiseL(25067);//('Your manually specified scale and ticks is not correct. The scale seems to be too small to hold any of the specified tickl marks.');
4035 }
4036
4037 // Setup the minor tick marks
4038 $j=0;
4039 for($i=0; $i < $m; ++$i ) {
4040 if( empty($this->iManualMinTickPos[$i]) ||
4041 $this->iManualMinTickPos[$i] < $minScale || $this->iManualMinTickPos[$i] > $maxScale)
4042 continue;
4043 $this->ticks_pos[$j] = $aScale->Translate($this->iManualMinTickPos[$i]);
4044 ++$j;
4045 }
4046 }
4047
4048 function _doAutoTickPos($aScale) {
4049 $maj_step_abs = $aScale->scale_factor*$this->major_step;
4050 $min_step_abs = $aScale->scale_factor*$this->minor_step;
4051
4052 if( $min_step_abs==0 || $maj_step_abs==0 ) {
4053 JpGraphError::RaiseL(25068);//("A plot has an illegal scale. This could for example be that you are trying to use text autoscaling to draw a line plot with only one point or that the plot area is too small. It could also be that no input data value is numeric (perhaps only '-' or 'x')");
4054 }
4055 // We need to make this an int since comparing it below
4056 // with the result from round() can give wrong result, such that
4057 // (40 < 40) == TRUE !!!
4058 $limit = (int)$aScale->scale_abs[1];
4059
4060 if( $aScale->textscale ) {
4061 // This can only be true for a X-scale (horizontal)
4062 // Define ticks for a text scale. This is slightly different from a
4063 // normal linear type of scale since the position might be adjusted
4064 // and the labels start at on
4065 $label = (float)$aScale->GetMinVal()+$this->text_label_start+$this->label_offset;
4066 $start_abs=$aScale->scale_factor*$this->text_label_start;
4067 $nbrmajticks=round(($aScale->GetMaxVal()-$aScale->GetMinVal()-$this->text_label_start )/$this->major_step)+1;
4068
4069 $x = $aScale->scale_abs[0]+$start_abs+$this->xlabel_offset*$min_step_abs;
4070 for( $i=0; $label <= $aScale->GetMaxVal()+$this->label_offset; ++$i ) {
4071 // Apply format to label
4072 $this->maj_ticks_label[$i]=$this->_doLabelFormat($label,$i,$nbrmajticks);
4073 $label+=$this->major_step;
4074
4075 // The x-position of the tick marks can be different from the labels.
4076 // Note that we record the tick position (not the label) so that the grid
4077 // happen upon tick marks and not labels.
4078 $xtick=$aScale->scale_abs[0]+$start_abs+$this->xtick_offset*$min_step_abs+$i*$maj_step_abs;
4079 $this->maj_ticks_pos[$i]=$xtick;
4080 $this->maj_ticklabels_pos[$i] = round($x);
4081 $x += $maj_step_abs;
4082 }
4083 }
4084 else {
4085 $label = $aScale->GetMinVal();
4086 $abs_pos = $aScale->scale_abs[0];
4087 $j=0; $i=0;
4088 $step = round($maj_step_abs/$min_step_abs);
4089 if( $aScale->type == "x" ) {
4090 // For a normal linear type of scale the major ticks will always be multiples
4091 // of the minor ticks. In order to avoid any rounding issues the major ticks are
4092 // defined as every "step" minor ticks and not calculated separately
4093 $nbrmajticks=round(($aScale->GetMaxVal()-$aScale->GetMinVal()-$this->text_label_start )/$this->major_step)+1;
4094 while( round($abs_pos) <= $limit ) {
4095 $this->ticks_pos[] = round($abs_pos);
4096 $this->ticks_label[] = $label;
4097 if( $step== 0 || $i % $step == 0 && $j < $nbrmajticks ) {
4098 $this->maj_ticks_pos[$j] = round($abs_pos);
4099 $this->maj_ticklabels_pos[$j] = round($abs_pos);
4100 $this->maj_ticks_label[$j]=$this->_doLabelFormat($label,$j,$nbrmajticks);
4101 ++$j;
4102 }
4103 ++$i;
4104 $abs_pos += $min_step_abs;
4105 $label+=$this->minor_step;
4106 }
4107 }
4108 elseif( $aScale->type == "y" ) {
4109 $nbrmajticks=round(($aScale->GetMaxVal()-$aScale->GetMinVal())/$this->major_step)+1;
4110 while( round($abs_pos) >= $limit ) {
4111 $this->ticks_pos[$i] = round($abs_pos);
4112 $this->ticks_label[$i]=$label;
4113 if( $step== 0 || $i % $step == 0 && $j < $nbrmajticks) {
4114 $this->maj_ticks_pos[$j] = round($abs_pos);
4115 $this->maj_ticklabels_pos[$j] = round($abs_pos);
4116 $this->maj_ticks_label[$j]=$this->_doLabelFormat($label,$j,$nbrmajticks);
4117 ++$j;
4118 }
4119 ++$i;
4120 $abs_pos += $min_step_abs;
4121 $label += $this->minor_step;
4122 }
4123 }
4124 }
4125 }
4126
4127 function AdjustForDST($aFlg=true) {
4128 $this->iAdjustForDST = $aFlg;
4129 }
4130
4131
4132 function _doLabelFormat($aVal,$aIdx,$aNbrTicks) {
4133
4134 // If precision hasn't been specified set it to a sensible value
4135 if( $this->precision==-1 ) {
4136 $t = log10($this->minor_step);
4137 if( $t > 0 )
4138 $precision = 0;
4139 else
4140 $precision = -floor($t);
4141 }
4142 else
4143 $precision = $this->precision;
4144
4145 if( $this->label_formfunc != '' ) {
4146 $f=$this->label_formfunc;
4147 $l = call_user_func($f,$aVal);
4148 }
4149 elseif( $this->label_formatstr != '' || $this->label_dateformatstr != '' ) {
4150 if( $this->label_usedateformat ) {
4151 // Adjust the value to take daylight savings into account
4152 if (date("I",$aVal)==1 && $this->iAdjustForDST ) // DST
4153 $aVal+=3600;
4154
4155 $l = date($this->label_formatstr,$aVal);
4156 if( $this->label_formatstr == 'W' ) {
4157 // If we use week formatting then add a single 'w' in front of the
4158 // week number to differentiate it from dates
4159 $l = 'w'.$l;
4160 }
4161 }
4162 else {
4163 if( $this->label_dateformatstr !== '' ) {
4164 // Adjust the value to take daylight savings into account
4165 if (date("I",$aVal)==1 && $this->iAdjustForDST ) // DST
4166 $aVal+=3600;
4167
4168 $l = date($this->label_dateformatstr,$aVal);
4169 if( $this->label_formatstr == 'W' ) {
4170 // If we use week formatting then add a single 'w' in front of the
4171 // week number to differentiate it from dates
4172 $l = 'w'.$l;
4173 }
4174 }
4175 else
4176 $l = sprintf($this->label_formatstr,$aVal);
4177 }
4178 }
4179 else {
4180 $l = sprintf('%01.'.$precision.'f',round($aVal,$precision));
4181 }
4182
4183 if( ($this->supress_zerolabel && $l==0) || ($this->supress_first && $aIdx==0) ||
4184 ($this->supress_last && $aIdx==$aNbrTicks-1) ) {
4185 $l='';
4186 }
4187 return $l;
4188 }
4189
4190 // Stroke ticks on either X or Y axis
4191 function _StrokeTicks($aImg,$aScale,$aPos) {
4192 $hor = $aScale->type == 'x';
4193 $aImg->SetLineWeight($this->weight);
4194
4195 // We need to make this an int since comparing it below
4196 // with the result from round() can give wrong result, such that
4197 // (40 < 40) == TRUE !!!
4198 $limit = (int)$aScale->scale_abs[1];
4199
4200 // A text scale doesn't have any minor ticks
4201 if( !$aScale->textscale ) {
4202 // Stroke minor ticks
4203 $yu = $aPos - $this->direction*$this->GetMinTickAbsSize();
4204 $xr = $aPos + $this->direction*$this->GetMinTickAbsSize();
4205 $n = count($this->ticks_pos);
4206 for($i=0; $i < $n; ++$i ) {
4207 if( !$this->supress_tickmarks && !$this->supress_minor_tickmarks) {
4208 if( $this->mincolor!="" ) $aImg->PushColor($this->mincolor);
4209 if( $hor ) {
4210 //if( $this->ticks_pos[$i] <= $limit )
4211 $aImg->Line($this->ticks_pos[$i],$aPos,$this->ticks_pos[$i],$yu);
4212 }
4213 else {
4214 //if( $this->ticks_pos[$i] >= $limit )
4215 $aImg->Line($aPos,$this->ticks_pos[$i],$xr,$this->ticks_pos[$i]);
4216 }
4217 if( $this->mincolor!="" ) $aImg->PopColor();
4218 }
4219 }
4220 }
4221
4222 // Stroke major ticks
4223 $yu = $aPos - $this->direction*$this->GetMajTickAbsSize();
4224 $xr = $aPos + $this->direction*$this->GetMajTickAbsSize();
4225 $nbrmajticks=round(($aScale->GetMaxVal()-$aScale->GetMinVal()-$this->text_label_start )/$this->major_step)+1;
4226 $n = count($this->maj_ticks_pos);
4227 for($i=0; $i < $n ; ++$i ) {
4228 if(!($this->xtick_offset > 0 && $i==$nbrmajticks-1) && !$this->supress_tickmarks) {
4229 if( $this->majcolor!="" ) $aImg->PushColor($this->majcolor);
4230 if( $hor ) {
4231 //if( $this->maj_ticks_pos[$i] <= $limit )
4232 $aImg->Line($this->maj_ticks_pos[$i],$aPos,$this->maj_ticks_pos[$i],$yu);
4233 }
4234 else {
4235 //if( $this->maj_ticks_pos[$i] >= $limit )
4236 $aImg->Line($aPos,$this->maj_ticks_pos[$i],$xr,$this->maj_ticks_pos[$i]);
4237 }
4238 if( $this->majcolor!="" ) $aImg->PopColor();
4239 }
4240 }
4241
4242 }
4243
4244 // Draw linear ticks
4245 function Stroke($aImg,$aScale,$aPos) {
4246 if( $this->iManualTickPos != NULL )
4247 $this->_doManualTickPos($aScale);
4248 else
4249 $this->_doAutoTickPos($aScale);
4250 $this->_StrokeTicks($aImg,$aScale,$aPos, $aScale->type == 'x' );
4251 }
4252
4253//---------------
4254// PRIVATE METHODS
4255 // Spoecify the offset of the displayed tick mark with the tick "space"
4256 // Legal values for $o is [0,1] used to adjust where the tick marks and label
4257 // should be positioned within the major tick-size
4258 // $lo specifies the label offset and $to specifies the tick offset
4259 // this comes in handy for example in bar graphs where we wont no offset for the
4260 // tick but have the labels displayed halfway under the bars.
4261 function SetXLabelOffset($aLabelOff,$aTickOff=-1) {
4262 $this->xlabel_offset=$aLabelOff;
4263 if( $aTickOff==-1 ) // Same as label offset
4264 $this->xtick_offset=$aLabelOff;
4265 else
4266 $this->xtick_offset=$aTickOff;
4267 if( $aLabelOff>0 )
4268 $this->SupressLast(); // The last tick wont fit
4269 }
4270
4271 // Which tick label should we start with?
4272 function SetTextLabelStart($aTextLabelOff) {
4273 $this->text_label_start=$aTextLabelOff;
4274 }
4275
4276} // Class
4277
4278//===================================================
4279// CLASS LinearScale
4280// Description: Handle linear scaling between screen and world
4281//===================================================
4282class LinearScale {
4283 public $textscale=false; // Just a flag to let the Plot class find out if
4284 // we are a textscale or not. This is a cludge since
4285 // this information is available in Graph::axtype but
4286 // we don't have access to the graph object in the Plots
4287 // stroke method. So we let graph store the status here
4288 // when the linear scale is created. A real cludge...
4289 public $type; // is this x or y scale ?
4290 public $ticks=null; // Store ticks
4291 public $text_scale_off = 0;
4292 public $scale_abs=array(0,0);
4293 public $scale_factor; // Scale factor between world and screen
4294 public $off; // Offset between image edge and plot area
4295 public $scale=array(0,0);
4296 public $name = 'lin';
4297 public $auto_ticks=false; // When using manual scale should the ticks be automatically set?
4298 public $world_abs_size; // Plot area size in pixels (Needed public in jpgraph_radar.php)
4299 public $world_size; // Plot area size in world coordinates
4300 public $intscale=false; // Restrict autoscale to integers
4301 protected $autoscale_min=false; // Forced minimum value, auto determine max
4302 protected $autoscale_max=false; // Forced maximum value, auto determine min
4303 private $gracetop=0,$gracebottom=0;
4304//---------------
4305// CONSTRUCTOR
4306 function LinearScale($aMin=0,$aMax=0,$aType="y") {
4307 assert($aType=="x" || $aType=="y" );
4308 assert($aMin<=$aMax);
4309
4310 $this->type=$aType;
4311 $this->scale=array($aMin,$aMax);
4312 $this->world_size=$aMax-$aMin;
4313 $this->ticks = new LinearTicks();
4314 }
4315
4316//---------------
4317// PUBLIC METHODS
4318 // Check if scale is set or if we should autoscale
4319 // We should do this is either scale or ticks has not been set
4320 function IsSpecified() {
4321 if( $this->GetMinVal()==$this->GetMaxVal() ) { // Scale not set
4322 return false;
4323 }
4324 return true;
4325 }
4326
4327 // Set the minimum data value when the autoscaling is used.
4328 // Usefull if you want a fix minimum (like 0) but have an
4329 // automatic maximum
4330 function SetAutoMin($aMin) {
4331 $this->autoscale_min=$aMin;
4332 }
4333
4334 // Set the minimum data value when the autoscaling is used.
4335 // Usefull if you want a fix minimum (like 0) but have an
4336 // automatic maximum
4337 function SetAutoMax($aMax) {
4338 $this->autoscale_max=$aMax;
4339 }
4340
4341 // If the user manually specifies a scale should the ticks
4342 // still be set automatically?
4343 function SetAutoTicks($aFlag=true) {
4344 $this->auto_ticks = $aFlag;
4345 }
4346
4347 // Specify scale "grace" value (top and bottom)
4348 function SetGrace($aGraceTop,$aGraceBottom=0) {
4349 if( $aGraceTop<0 || $aGraceBottom < 0 )
4350 JpGraphError::RaiseL(25069);//(" Grace must be larger then 0");
4351 $this->gracetop=$aGraceTop;
4352 $this->gracebottom=$aGraceBottom;
4353 }
4354
4355 // Get the minimum value in the scale
4356 function GetMinVal() {
4357 return $this->scale[0];
4358 }
4359
4360 // get maximum value for scale
4361 function GetMaxVal() {
4362 return $this->scale[1];
4363 }
4364
4365 // Specify a new min/max value for sclae
4366 function Update($aImg,$aMin,$aMax) {
4367 $this->scale=array($aMin,$aMax);
4368 $this->world_size=$aMax-$aMin;
4369 $this->InitConstants($aImg);
4370 }
4371
4372 // Translate between world and screen
4373 function Translate($aCoord) {
4374 if( !is_numeric($aCoord) ) {
4375 if( $aCoord != '' && $aCoord != '-' && $aCoord != 'x' )
4376 JpGraphError::RaiseL(25070);//('Your data contains non-numeric values.');
4377 return 0;
4378 }
4379 else {
4380 return $this->off+($aCoord - $this->scale[0]) * $this->scale_factor;
4381 }
4382 }
4383
4384 // Relative translate (don't include offset) usefull when we just want
4385 // to know the relative position (in pixels) on the axis
4386 function RelTranslate($aCoord) {
4387 if( !is_numeric($aCoord) ) {
4388 if( $aCoord != '' && $aCoord != '-' && $aCoord != 'x' )
4389 JpGraphError::RaiseL(25070);//('Your data contains non-numeric values.');
4390 return 0;
4391 }
4392 else {
4393 return ($aCoord - $this->scale[0]) * $this->scale_factor;
4394 }
4395 }
4396
4397 // Restrict autoscaling to only use integers
4398 function SetIntScale($aIntScale=true) {
4399 $this->intscale=$aIntScale;
4400 }
4401
4402 // Calculate an integer autoscale
4403 function IntAutoScale($img,$min,$max,$maxsteps,$majend=true) {
4404 // Make sure limits are integers
4405 $min=floor($min);
4406 $max=ceil($max);
4407 if( abs($min-$max)==0 ) {
4408 --$min; ++$max;
4409 }
4410 $maxsteps = floor($maxsteps);
4411
4412 $gracetop=round(($this->gracetop/100.0)*abs($max-$min));
4413 $gracebottom=round(($this->gracebottom/100.0)*abs($max-$min));
4414 if( is_numeric($this->autoscale_min) ) {
4415 $min = ceil($this->autoscale_min);
4416 if( $min >= $max ) {
4417 JpGraphError::RaiseL(25071);//('You have specified a min value with SetAutoMin() which is larger than the maximum value used for the scale. This is not possible.');
4418 }
4419 }
4420
4421 if( is_numeric($this->autoscale_max) ) {
4422 $max = ceil($this->autoscale_max);
4423 if( $min >= $max ) {
4424 JpGraphError::RaiseL(25072);//('You have specified a max value with SetAutoMax() which is smaller than the miminum value used for the scale. This is not possible.');
4425 }
4426 }
4427
4428 if( abs($min-$max ) == 0 ) {
4429 ++$max;
4430 --$min;
4431 }
4432
4433 $min -= $gracebottom;
4434 $max += $gracetop;
4435
4436 // First get tickmarks as multiples of 1, 10, ...
4437 if( $majend ) {
4438 list($num1steps,$adj1min,$adj1max,$maj1step) =
4439 $this->IntCalcTicks($maxsteps,$min,$max,1);
4440 }
4441 else {
4442 $adj1min = $min;
4443 $adj1max = $max;
4444 list($num1steps,$maj1step) =
4445 $this->IntCalcTicksFreeze($maxsteps,$min,$max,1);
4446 }
4447
4448 if( abs($min-$max) > 2 ) {
4449 // Then get tick marks as 2:s 2, 20, ...
4450 if( $majend ) {
4451 list($num2steps,$adj2min,$adj2max,$maj2step) =
4452 $this->IntCalcTicks($maxsteps,$min,$max,5);
4453 }
4454 else {
4455 $adj2min = $min;
4456 $adj2max = $max;
4457 list($num2steps,$maj2step) =
4458 $this->IntCalcTicksFreeze($maxsteps,$min,$max,5);
4459 }
4460 }
4461 else {
4462 $num2steps = 10000; // Dummy high value so we don't choose this
4463 }
4464
4465 if( abs($min-$max) > 5 ) {
4466 // Then get tickmarks as 5:s 5, 50, 500, ...
4467 if( $majend ) {
4468 list($num5steps,$adj5min,$adj5max,$maj5step) =
4469 $this->IntCalcTicks($maxsteps,$min,$max,2);
4470 }
4471 else {
4472 $adj5min = $min;
4473 $adj5max = $max;
4474 list($num5steps,$maj5step) =
4475 $this->IntCalcTicksFreeze($maxsteps,$min,$max,2);
4476 }
4477 }
4478 else {
4479 $num5steps = 10000; // Dummy high value so we don't choose this
4480 }
4481
4482 // Check to see whichof 1:s, 2:s or 5:s fit better with
4483 // the requested number of major ticks
4484 $match1=abs($num1steps-$maxsteps);
4485 $match2=abs($num2steps-$maxsteps);
4486 if( !empty($maj5step) && $maj5step > 1 )
4487 $match5=abs($num5steps-$maxsteps);
4488 else
4489 $match5=10000; // Dummy high value
4490
4491 // Compare these three values and see which is the closest match
4492 // We use a 0.6 weight to gravitate towards multiple of 5:s
4493 if( $match1 < $match2 ) {
4494 if( $match1 < $match5 )
4495 $r=1;
4496 else
4497 $r=3;
4498 }
4499 else {
4500 if( $match2 < $match5 )
4501 $r=2;
4502 else
4503 $r=3;
4504 }
4505 // Minsteps are always the same as maxsteps for integer scale
4506 switch( $r ) {
4507 case 1:
4508 $this->ticks->Set($maj1step,$maj1step);
4509 $this->Update($img,$adj1min,$adj1max);
4510 break;
4511 case 2:
4512 $this->ticks->Set($maj2step,$maj2step);
4513 $this->Update($img,$adj2min,$adj2max);
4514 break;
4515 case 3:
4516 $this->ticks->Set($maj5step,$maj5step);
4517 $this->Update($img,$adj5min,$adj5max);
4518 break;
4519 default:
4520 JpGraphError::RaiseL(25073,$r);//('Internal error. Integer scale algorithm comparison out of bound (r=$r)');
4521 }
4522 }
4523
4524
4525 // Calculate autoscale. Used if user hasn't given a scale and ticks
4526 // $maxsteps is the maximum number of major tickmarks allowed.
4527 function AutoScale($img,$min,$max,$maxsteps,$majend=true) {
4528 if( $this->intscale ) {
4529 $this->IntAutoScale($img,$min,$max,$maxsteps,$majend);
4530 return;
4531 }
4532 if( abs($min-$max) < 0.00001 ) {
4533 // We need some difference to be able to autoscale
4534 // make it 5% above and 5% below value
4535 if( $min==0 && $max==0 ) { // Special case
4536 $min=-1; $max=1;
4537 }
4538 else {
4539 $delta = (abs($max)+abs($min))*0.005;
4540 $min -= $delta;
4541 $max += $delta;
4542 }
4543 }
4544
4545 $gracetop=($this->gracetop/100.0)*abs($max-$min);
4546 $gracebottom=($this->gracebottom/100.0)*abs($max-$min);
4547 if( is_numeric($this->autoscale_min) ) {
4548 $min = $this->autoscale_min;
4549 if( $min >= $max ) {
4550 JpGraphError::RaiseL(25071);//('You have specified a min value with SetAutoMin() which is larger than the maximum value used for the scale. This is not possible.');
4551 }
4552 if( abs($min-$max ) < 0.00001 )
4553 $max *= 1.2;
4554 }
4555
4556 if( is_numeric($this->autoscale_max) ) {
4557 $max = $this->autoscale_max;
4558 if( $min >= $max ) {
4559 JpGraphError::RaiseL(25072);//('You have specified a max value with SetAutoMax() which is smaller than the miminum value used for the scale. This is not possible.');
4560 }
4561 if( abs($min-$max ) < 0.00001 )
4562 $min *= 0.8;
4563 }
4564
4565 $min -= $gracebottom;
4566 $max += $gracetop;
4567
4568
4569 // First get tickmarks as multiples of 0.1, 1, 10, ...
4570 if( $majend ) {
4571 list($num1steps,$adj1min,$adj1max,$min1step,$maj1step) =
4572 $this->CalcTicks($maxsteps,$min,$max,1,2);
4573 }
4574 else {
4575 $adj1min=$min;
4576 $adj1max=$max;
4577 list($num1steps,$min1step,$maj1step) =
4578 $this->CalcTicksFreeze($maxsteps,$min,$max,1,2,false);
4579 }
4580
4581 // Then get tick marks as 2:s 0.2, 2, 20, ...
4582 if( $majend ) {
4583 list($num2steps,$adj2min,$adj2max,$min2step,$maj2step) =
4584 $this->CalcTicks($maxsteps,$min,$max,5,2);
4585 }
4586 else {
4587 $adj2min=$min;
4588 $adj2max=$max;
4589 list($num2steps,$min2step,$maj2step) =
4590 $this->CalcTicksFreeze($maxsteps,$min,$max,5,2,false);
4591 }
4592
4593 // Then get tickmarks as 5:s 0.05, 0.5, 5, 50, ...
4594 if( $majend ) {
4595 list($num5steps,$adj5min,$adj5max,$min5step,$maj5step) =
4596 $this->CalcTicks($maxsteps,$min,$max,2,5);
4597 }
4598 else {
4599 $adj5min=$min;
4600 $adj5max=$max;
4601 list($num5steps,$min5step,$maj5step) =
4602 $this->CalcTicksFreeze($maxsteps,$min,$max,2,5,false);
4603 }
4604
4605 // Check to see whichof 1:s, 2:s or 5:s fit better with
4606 // the requested number of major ticks
4607 $match1=abs($num1steps-$maxsteps);
4608 $match2=abs($num2steps-$maxsteps);
4609 $match5=abs($num5steps-$maxsteps);
4610 // Compare these three values and see which is the closest match
4611 // We use a 0.8 weight to gravitate towards multiple of 5:s
4612 $r=$this->MatchMin3($match1,$match2,$match5,0.8);
4613 switch( $r ) {
4614 case 1:
4615 $this->Update($img,$adj1min,$adj1max);
4616 $this->ticks->Set($maj1step,$min1step);
4617 break;
4618 case 2:
4619 $this->Update($img,$adj2min,$adj2max);
4620 $this->ticks->Set($maj2step,$min2step);
4621 break;
4622 case 3:
4623 $this->Update($img,$adj5min,$adj5max);
4624 $this->ticks->Set($maj5step,$min5step);
4625 break;
4626 }
4627 }
4628
4629//---------------
4630// PRIVATE METHODS
4631
4632 // This method recalculates all constants that are depending on the
4633 // margins in the image. If the margins in the image are changed
4634 // this method should be called for every scale that is registred with
4635 // that image. Should really be installed as an observer of that image.
4636 function InitConstants($img) {
4637 if( $this->type=="x" ) {
4638 $this->world_abs_size=$img->width - $img->left_margin - $img->right_margin;
4639 $this->off=$img->left_margin;
4640 $this->scale_factor = 0;
4641 if( $this->world_size > 0 )
4642 $this->scale_factor=$this->world_abs_size/($this->world_size*1.0);
4643 }
4644 else { // y scale
4645 $this->world_abs_size=$img->height - $img->top_margin - $img->bottom_margin;
4646 $this->off=$img->top_margin+$this->world_abs_size;
4647 $this->scale_factor = 0;
4648 if( $this->world_size > 0 )
4649 $this->scale_factor=-$this->world_abs_size/($this->world_size*1.0);
4650 }
4651 $size = $this->world_size * $this->scale_factor;
4652 $this->scale_abs=array($this->off,$this->off + $size);
4653 }
4654
4655 // Initialize the conversion constants for this scale
4656 // This tries to pre-calculate as much as possible to speed up the
4657 // actual conversion (with Translate()) later on
4658 // $start =scale start in absolute pixels (for x-scale this is an y-position
4659 // and for an y-scale this is an x-position
4660 // $len =absolute length in pixels of scale
4661 function SetConstants($aStart,$aLen) {
4662 $this->world_abs_size=$aLen;
4663 $this->off=$aStart;
4664
4665 if( $this->world_size<=0 ) {
4666 // This should never ever happen !!
4667 JpGraphError::RaiseL(25074);
4668//("You have unfortunately stumbled upon a bug in JpGraph. It seems like the scale range is ".$this->world_size." [for ".$this->type." scale] <br> Please report Bug #01 to jpgraph@aditus.nu and include the script that gave this error. This problem could potentially be caused by trying to use \"illegal\" values in the input data arrays (like trying to send in strings or only NULL values) which causes the autoscaling to fail.");
4669
4670 }
4671
4672 // scale_factor = number of pixels per world unit
4673 $this->scale_factor=$this->world_abs_size/($this->world_size*1.0);
4674
4675 // scale_abs = start and end points of scale in absolute pixels
4676 $this->scale_abs=array($this->off,$this->off+$this->world_size*$this->scale_factor);
4677 }
4678
4679
4680 // Calculate number of ticks steps with a specific division
4681 // $a is the divisor of 10**x to generate the first maj tick intervall
4682 // $a=1, $b=2 give major ticks with multiple of 10, ...,0.1,1,10,...
4683 // $a=5, $b=2 give major ticks with multiple of 2:s ...,0.2,2,20,...
4684 // $a=2, $b=5 give major ticks with multiple of 5:s ...,0.5,5,50,...
4685 // We return a vector of
4686 // [$numsteps,$adjmin,$adjmax,$minstep,$majstep]
4687 // If $majend==true then the first and last marks on the axis will be major
4688 // labeled tick marks otherwise it will be adjusted to the closest min tick mark
4689 function CalcTicks($maxsteps,$min,$max,$a,$b,$majend=true) {
4690 $diff=$max-$min;
4691 if( $diff==0 )
4692 $ld=0;
4693 else
4694 $ld=floor(log10($diff));
4695
4696 // Gravitate min towards zero if we are close
4697 if( $min>0 && $min < pow(10,$ld) ) $min=0;
4698
4699 //$majstep=pow(10,$ld-1)/$a;
4700 $majstep=pow(10,$ld)/$a;
4701 $minstep=$majstep/$b;
4702
4703 $adjmax=ceil($max/$minstep)*$minstep;
4704 $adjmin=floor($min/$minstep)*$minstep;
4705 $adjdiff = $adjmax-$adjmin;
4706 $numsteps=$adjdiff/$majstep;
4707
4708 while( $numsteps>$maxsteps ) {
4709 $majstep=pow(10,$ld)/$a;
4710 $numsteps=$adjdiff/$majstep;
4711 ++$ld;
4712 }
4713
4714 $minstep=$majstep/$b;
4715 $adjmin=floor($min/$minstep)*$minstep;
4716 $adjdiff = $adjmax-$adjmin;
4717 if( $majend ) {
4718 $adjmin = floor($min/$majstep)*$majstep;
4719 $adjdiff = $adjmax-$adjmin;
4720 $adjmax = ceil($adjdiff/$majstep)*$majstep+$adjmin;
4721 }
4722 else
4723 $adjmax=ceil($max/$minstep)*$minstep;
4724
4725 return array($numsteps,$adjmin,$adjmax,$minstep,$majstep);
4726 }
4727
4728 function CalcTicksFreeze($maxsteps,$min,$max,$a,$b) {
4729 // Same as CalcTicks but don't adjust min/max values
4730 $diff=$max-$min;
4731 if( $diff==0 )
4732 $ld=0;
4733 else
4734 $ld=floor(log10($diff));
4735
4736 //$majstep=pow(10,$ld-1)/$a;
4737 $majstep=pow(10,$ld)/$a;
4738 $minstep=$majstep/$b;
4739 $numsteps=floor($diff/$majstep);
4740
4741 while( $numsteps > $maxsteps ) {
4742 $majstep=pow(10,$ld)/$a;
4743 $numsteps=floor($diff/$majstep);
4744 ++$ld;
4745 }
4746 $minstep=$majstep/$b;
4747 return array($numsteps,$minstep,$majstep);
4748 }
4749
4750
4751 function IntCalcTicks($maxsteps,$min,$max,$a,$majend=true) {
4752 $diff=$max-$min;
4753 if( $diff==0 )
4754 JpGraphError::RaiseL(25075);//('Can\'t automatically determine ticks since min==max.');
4755 else
4756 $ld=floor(log10($diff));
4757
4758 // Gravitate min towards zero if we are close
4759 if( $min>0 && $min < pow(10,$ld) ) $min=0;
4760
4761 if( $ld == 0 ) $ld=1;
4762
4763 if( $a == 1 )
4764 $majstep = 1;
4765 else
4766 $majstep=pow(10,$ld)/$a;
4767 $adjmax=ceil($max/$majstep)*$majstep;
4768
4769 $adjmin=floor($min/$majstep)*$majstep;
4770 $adjdiff = $adjmax-$adjmin;
4771 $numsteps=$adjdiff/$majstep;
4772 while( $numsteps>$maxsteps ) {
4773 $majstep=pow(10,$ld)/$a;
4774 $numsteps=$adjdiff/$majstep;
4775 ++$ld;
4776 }
4777
4778 $adjmin=floor($min/$majstep)*$majstep;
4779 $adjdiff = $adjmax-$adjmin;
4780 if( $majend ) {
4781 $adjmin = floor($min/$majstep)*$majstep;
4782 $adjdiff = $adjmax-$adjmin;
4783 $adjmax = ceil($adjdiff/$majstep)*$majstep+$adjmin;
4784 }
4785 else
4786 $adjmax=ceil($max/$majstep)*$majstep;
4787
4788 return array($numsteps,$adjmin,$adjmax,$majstep);
4789 }
4790
4791
4792 function IntCalcTicksFreeze($maxsteps,$min,$max,$a) {
4793 // Same as IntCalcTick but don't change min/max values
4794 $diff=$max-$min;
4795 if( $diff==0 )
4796 JpGraphError::RaiseL(25075);//('Can\'t automatically determine ticks since min==max.');
4797 else
4798 $ld=floor(log10($diff));
4799
4800 if( $ld == 0 ) $ld=1;
4801
4802 if( $a == 1 )
4803 $majstep = 1;
4804 else
4805 $majstep=pow(10,$ld)/$a;
4806
4807 $numsteps=floor($diff/$majstep);
4808 while( $numsteps > $maxsteps ) {
4809 $majstep=pow(10,$ld)/$a;
4810 $numsteps=floor($diff/$majstep);
4811 ++$ld;
4812 }
4813
4814 return array($numsteps,$majstep);
4815 }
4816
4817
4818
4819 // Determine the minimum of three values witha weight for last value
4820 function MatchMin3($a,$b,$c,$weight) {
4821 if( $a < $b ) {
4822 if( $a < ($c*$weight) )
4823 return 1; // $a smallest
4824 else
4825 return 3; // $c smallest
4826 }
4827 elseif( $b < ($c*$weight) )
4828 return 2; // $b smallest
4829 return 3; // $c smallest
4830 }
4831} // Class
4832
4833
4834//===================================================
4835// CLASS DisplayValue
4836// Description: Used to print data values at data points
4837//===================================================
4838class DisplayValue {
4839 public $margin=5;
4840 public $show=false;
4841 public $valign="",$halign="center";
4842 public $format="%.1f",$negformat="";
4843 private $ff=FF_FONT1,$fs=FS_NORMAL,$fsize=10;
4844 private $iFormCallback='';
4845 private $angle=0;
4846 private $color="navy",$negcolor="";
4847 private $iHideZero=false;
4848
4849 function Show($aFlag=true) {
4850 $this->show=$aFlag;
4851 }
4852
4853 function SetColor($aColor,$aNegcolor="") {
4854 $this->color = $aColor;
4855 $this->negcolor = $aNegcolor;
4856 }
4857
4858 function SetFont($aFontFamily,$aFontStyle=FS_NORMAL,$aFontSize=10) {
4859 $this->ff=$aFontFamily;
4860 $this->fs=$aFontStyle;
4861 $this->fsize=$aFontSize;
4862 }
4863
4864 function ApplyFont($aImg) {
4865 $aImg->SetFont($this->ff,$this->fs,$this->fsize);
4866 }
4867
4868 function SetMargin($aMargin) {
4869 $this->margin = $aMargin;
4870 }
4871
4872 function SetAngle($aAngle) {
4873 $this->angle = $aAngle;
4874 }
4875
4876 function SetAlign($aHAlign,$aVAlign='') {
4877 $this->halign = $aHAlign;
4878 $this->valign = $aVAlign;
4879 }
4880
4881 function SetFormat($aFormat,$aNegFormat="") {
4882 $this->format= $aFormat;
4883 $this->negformat= $aNegFormat;
4884 }
4885
4886 function SetFormatCallback($aFunc) {
4887 $this->iFormCallback = $aFunc;
4888 }
4889
4890 function HideZero($aFlag=true) {
4891 $this->iHideZero=$aFlag;
4892 }
4893
4894 function Stroke($img,$aVal,$x,$y) {
4895
4896 if( $this->show )
4897 {
4898 if( $this->negformat=="" ) $this->negformat=$this->format;
4899 if( $this->negcolor=="" ) $this->negcolor=$this->color;
4900
4901 if( $aVal===NULL || (is_string($aVal) && ($aVal=="" || $aVal=="-" || $aVal=="x" ) ) )
4902 return;
4903
4904 if( is_numeric($aVal) && $aVal==0 && $this->iHideZero ) {
4905 return;
4906 }
4907
4908 // Since the value is used in different cirumstances we need to check what
4909 // kind of formatting we shall use. For example, to display values in a line
4910 // graph we simply display the formatted value, but in the case where the user
4911 // has already specified a text string we don't fo anything.
4912 if( $this->iFormCallback != '' ) {
4913 $f = $this->iFormCallback;
4914 $sval = call_user_func($f,$aVal);
4915 }
4916 elseif( is_numeric($aVal) ) {
4917 if( $aVal >= 0 )
4918 $sval=sprintf($this->format,$aVal);
4919 else
4920 $sval=sprintf($this->negformat,$aVal);
4921 }
4922 else
4923 $sval=$aVal;
4924
4925 $y = $y-sign($aVal)*$this->margin;
4926
4927 $txt = new Text($sval,$x,$y);
4928 $txt->SetFont($this->ff,$this->fs,$this->fsize);
4929 if( $this->valign == "" ) {
4930 if( $aVal >= 0 )
4931 $valign = "bottom";
4932 else
4933 $valign = "top";
4934 }
4935 else
4936 $valign = $this->valign;
4937 $txt->Align($this->halign,$valign);
4938
4939 $txt->SetOrientation($this->angle);
4940 if( $aVal > 0 )
4941 $txt->SetColor($this->color);
4942 else
4943 $txt->SetColor($this->negcolor);
4944 $txt->Stroke($img);
4945 }
4946 }
4947}
4948
4949//===================================================
4950// CLASS Plot
4951// Description: Abstract base class for all concrete plot classes
4952//===================================================
4953class Plot {
4954 public $numpoints=0;
4955 public $value;
4956 public $legend='';
4957 public $coords=array();
4958 public $color="black";
4959 public $hidelegend=false;
4960 public $line_weight=1;
4961 public $csimtargets=array(),$csimwintargets=array(); // Array of targets for CSIM
4962 public $csimareas=""; // Resultant CSIM area tags
4963 public $csimalts=null; // ALT:s for corresponding target
4964 public $legendcsimtarget='',$legendcsimwintarget='';
4965 public $legendcsimalt='';
4966 protected $weight=1;
4967 protected $center=false;
4968//---------------
4969// CONSTRUCTOR
4970 function Plot($aDatay,$aDatax=false) {
4971 $this->numpoints = count($aDatay);
4972 if( $this->numpoints==0 )
4973 JpGraphError::RaiseL(25121);//("Empty input data array specified for plot. Must have at least one data point.");
4974 $this->coords[0]=$aDatay;
4975 if( is_array($aDatax) ) {
4976 $this->coords[1]=$aDatax;
4977 $n = count($aDatax);
4978 for($i=0; $i < $n; ++$i ) {
4979 if( !is_numeric($aDatax[$i]) ) {
4980 JpGraphError::RaiseL(25070);
4981 }
4982 }
4983 }
4984 $this->value = new DisplayValue();
4985 }
4986
4987//---------------
4988// PUBLIC METHODS
4989
4990 // Stroke the plot
4991 // "virtual" function which must be implemented by
4992 // the subclasses
4993 function Stroke($aImg,$aXScale,$aYScale) {
4994 JpGraphError::RaiseL(25122);//("JpGraph: Stroke() must be implemented by concrete subclass to class Plot");
4995 }
4996
4997 function HideLegend($f=true) {
4998 $this->hidelegend = $f;
4999 }
5000
5001 function DoLegend($graph) {
5002 if( !$this->hidelegend )
5003 $this->Legend($graph);
5004 }
5005
5006 function StrokeDataValue($img,$aVal,$x,$y) {
5007 $this->value->Stroke($img,$aVal,$x,$y);
5008 }
5009
5010 // Set href targets for CSIM
5011 function SetCSIMTargets($aTargets,$aAlts='',$aWinTargets='') {
5012 $this->csimtargets=$aTargets;
5013 $this->csimwintargets=$aWinTargets;
5014 $this->csimalts=$aAlts;
5015 }
5016
5017 // Get all created areas
5018 function GetCSIMareas() {
5019 return $this->csimareas;
5020 }
5021
5022 // "Virtual" function which gets called before any scale
5023 // or axis are stroked used to do any plot specific adjustment
5024 function PreStrokeAdjust($aGraph) {
5025 if( substr($aGraph->axtype,0,4) == "text" && (isset($this->coords[1])) )
5026 JpGraphError::RaiseL(25123);//("JpGraph: You can't use a text X-scale with specified X-coords. Use a \"int\" or \"lin\" scale instead.");
5027 return true;
5028 }
5029
5030 // Get minimum values in plot
5031 function Min() {
5032 if( isset($this->coords[1]) )
5033 $x=$this->coords[1];
5034 else
5035 $x="";
5036 if( $x != "" && count($x) > 0 ) {
5037 $xm=min($x);
5038 }
5039 else
5040 $xm=0;
5041 $y=$this->coords[0];
5042 $cnt = count($y);
5043 if( $cnt > 0 ) {
5044 /*
5045 if( ! isset($y[0]) ) {
5046 JpGraphError('The input data array must have consecutive values from position 0 and forward. The given y-array starts with empty values (NULL)');
5047 }
5048 $ym = $y[0];
5049 */
5050 $i=0;
5051 while( $i<$cnt && !is_numeric($ym=$y[$i]) )
5052 $i++;
5053 while( $i < $cnt) {
5054 if( is_numeric($y[$i]) )
5055 $ym=min($ym,$y[$i]);
5056 ++$i;
5057 }
5058 }
5059 else
5060 $ym="";
5061 return array($xm,$ym);
5062 }
5063
5064 // Get maximum value in plot
5065 function Max() {
5066 if( isset($this->coords[1]) )
5067 $x=$this->coords[1];
5068 else
5069 $x="";
5070
5071 if( $x!="" && count($x) > 0 )
5072 $xm=max($x);
5073 else {
5074 $xm = $this->numpoints-1;
5075 }
5076 $y=$this->coords[0];
5077 if( count($y) > 0 ) {
5078 /*
5079 if( !isset($y[0]) ) {
5080 JpGraphError::Raise('The input data array must have consecutive values from position 0 and forward. The given y-array starts with empty values (NULL)');
5081// $y[0] = 0;
5082// Change in 1.5.1 Don't treat this as an error any more. Just silently convert to 0
5083// Change in 1.17 Treat his as an error again !! This is the right way to do !!
5084 }
5085 */
5086 $cnt = count($y);
5087 $i=0;
5088 while( $i<$cnt && !is_numeric($ym=$y[$i]) )
5089 $i++;
5090 while( $i < $cnt ) {
5091 if( is_numeric($y[$i]) )
5092 $ym=max($ym,$y[$i]);
5093 ++$i;
5094 }
5095 }
5096 else
5097 $ym="";
5098 return array($xm,$ym);
5099 }
5100
5101 function SetColor($aColor) {
5102 $this->color=$aColor;
5103 }
5104
5105 function SetLegend($aLegend,$aCSIM='',$aCSIMAlt='',$aCSIMWinTarget='') {
5106 $this->legend = $aLegend;
5107 $this->legendcsimtarget = $aCSIM;
5108 $this->legendcsimwintarget = $aCSIMWinTarget;
5109 $this->legendcsimalt = $aCSIMAlt;
5110 }
5111
5112 function SetWeight($aWeight) {
5113 $this->weight=$aWeight;
5114 }
5115
5116 function SetLineWeight($aWeight=1) {
5117 $this->line_weight=$aWeight;
5118 }
5119
5120 function SetCenter($aCenter=true) {
5121 $this->center = $aCenter;
5122 }
5123
5124 // This method gets called by Graph class to plot anything that should go
5125 // into the margin after the margin color has been set.
5126 function StrokeMargin($aImg) {
5127 return true;
5128 }
5129
5130 // Framework function the chance for each plot class to set a legend
5131 function Legend($aGraph) {
5132 if( $this->legend != "" )
5133 $aGraph->legend->Add($this->legend,$this->color,"",0,$this->legendcsimtarget,
5134 $this->legendcsimalt,$this->legendcsimwintarget);
5135 }
5136
5137} // Class
5138
5139
5140//===================================================
5141// CLASS PlotLine
5142// Description:
5143// Data container class to hold properties for a static
5144// line that is drawn directly in the plot area.
5145// Usefull to add static borders inside a plot to show
5146// for example set-values
5147//===================================================
5148class PlotLine {
5149 public $scaleposition, $direction=-1;
5150 protected $weight=1;
5151 protected $color="black";
5152 private $legend='',$hidelegend=false, $legendcsimtarget='', $legendcsimalt='',$legendcsimwintarget='';
5153 private $iLineStyle='solid';
5154
5155//---------------
5156// CONSTRUCTOR
5157 function PlotLine($aDir=HORIZONTAL,$aPos=0,$aColor="black",$aWeight=1) {
5158 $this->direction = $aDir;
5159 $this->color=$aColor;
5160 $this->weight=$aWeight;
5161 $this->scaleposition=$aPos;
5162 }
5163
5164//---------------
5165// PUBLIC METHODS
5166
5167 function SetLegend($aLegend,$aCSIM='',$aCSIMAlt='',$aCSIMWinTarget='') {
5168 $this->legend = $aLegend;
5169 $this->legendcsimtarget = $aCSIM;
5170 $this->legendcsimwintarget = $aCSIMWinTarget;
5171 $this->legendcsimalt = $aCSIMAlt;
5172 }
5173
5174 function HideLegend($f=true) {
5175 $this->hidelegend = $f;
5176 }
5177
5178 function SetPosition($aScalePosition) {
5179 $this->scaleposition=$aScalePosition;
5180 }
5181
5182 function SetDirection($aDir) {
5183 $this->direction = $aDir;
5184 }
5185
5186 function SetColor($aColor) {
5187 $this->color=$aColor;
5188 }
5189
5190 function SetWeight($aWeight) {
5191 $this->weight=$aWeight;
5192 }
5193
5194 function SetLineStyle($aStyle) {
5195 $this->iLineStyle = $aStyle;
5196 }
5197
5198//---------------
5199// PRIVATE METHODS
5200
5201 function DoLegend(&$graph) {
5202 if( !$this->hidelegend )
5203 $this->Legend($graph);
5204 }
5205
5206 // Framework function the chance for each plot class to set a legend
5207 function Legend(&$aGraph) {
5208 if( $this->legend != "" ) {
5209 $dummyPlotMark = new PlotMark();
5210 $lineStyle = 1;
5211 $aGraph->legend->Add($this->legend,$this->color,$dummyPlotMark,$lineStyle,
5212 $this->legendcsimtarget,$this->legendcsimalt,$this->legendcsimwintarget);
5213 }
5214 }
5215
5216 function PreStrokeAdjust($aGraph) {
5217 // Nothing to do
5218 }
5219
5220 function Stroke($aImg,$aXScale,$aYScale) {
5221 $aImg->SetColor($this->color);
5222 $aImg->SetLineWeight($this->weight);
5223 $oldStyle = $aImg->SetLineStyle($this->iLineStyle);
5224 if( $this->direction == VERTICAL ) {
5225 $ymin_abs=$aYScale->Translate($aYScale->GetMinVal());
5226 $ymax_abs=$aYScale->Translate($aYScale->GetMaxVal());
5227 $xpos_abs=$aXScale->Translate($this->scaleposition);
5228 $aImg->StyleLine($xpos_abs, $ymin_abs, $xpos_abs, $ymax_abs);
5229 }
5230 elseif( $this->direction == HORIZONTAL ) {
5231 $xmin_abs=$aXScale->Translate($aXScale->GetMinVal());
5232 $xmax_abs=$aXScale->Translate($aXScale->GetMaxVal());
5233 $ypos_abs=$aYScale->Translate($this->scaleposition);
5234 $aImg->StyleLine($xmin_abs, $ypos_abs, $xmax_abs, $ypos_abs);
5235 }
5236 else {
5237 JpGraphError::RaiseL(25125);//(" Illegal direction for static line");
5238 }
5239 $aImg->SetLineStyle($oldStyle);
5240 }
5241}
5242
5243// <EOF>
5244?>
Note: See TracBrowser for help on using the repository browser.