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

Last change on this file since 346 was 346, checked in by roby, 4 years ago

ulteriori modifiche per adeguamento a php7
Client: aggiornamento jpgraph

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