1<?php
2 /*
3     pChart - a PHP class to build charts!
4     Copyright (C) 2008 Jean-Damien POGOLOTTI
5     Version  1.27d last updated on 09/30/08
6
7     http://pchart.sourceforge.net
8
9     This program is free software: you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation, either version 1,2,3 of the License, or
12     (at your option) any later version.
13
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18
19     You should have received a copy of the GNU General Public License
20     along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22     Class initialisation :
23      pChart($XSize,$YSize)
24     Draw methods :
25      drawBackground($R,$G,$B)
26      drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B)
27      drawFilledRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B,$DrawBorder=TRUE,$Alpha=100)
28      drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
29      drawFilledRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
30      drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
31      drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
32      drawEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
33      drawFilledEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
34      drawLine($X1,$Y1,$X2,$Y2,$R,$G,$B,$GraphFunction=FALSE)
35      drawDottedLine($X1,$Y1,$X2,$Y2,$DotSize,$R,$G,$B)
36      drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B)
37      drawFromPNG($FileName,$X,$Y,$Alpha=100)
38      drawFromGIF($FileName,$X,$Y,$Alpha=100)
39      drawFromJPG($FileName,$X,$Y,$Alpha=100)
40     Graph setup methods :
41      addBorder($Width=3,$R=0,$G=0,$B=0)
42      clearScale()
43      clearShadow()
44      createColorGradientPalette($R1,$G1,$B1,$R2,$G2,$B2,$Shades)
45      drawGraphArea($R,$G,$B,$Stripe=FALSE)
46      drawScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1,$RightScale=FALSE)
47      drawRightScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1)
48      drawXYScale($Data,$DataDescription,$YSerieName,$XSerieName,$R,$G,$B,$WithMargin=0,$Angle=0,$Decimals=1)
49      drawGrid($LineWidth,$Mosaic=TRUE,$R=220,$G=220,$B=220,$Alpha=100)
50      drawLegend($XPos,$YPos,$DataDescription,$R,$G,$B,$Rs=-1,$Gs=-1,$Bs=-1,$Rt=0,$Gt=0,$Bt=0,$Border=FALSE)
51      drawPieLegend($XPos,$YPos,$Data,$DataDescription,$R,$G,$B)
52      drawTitle($XPos,$YPos,$Value,$R,$G,$B,$XPos2=-1,$YPos2=-1,$Shadow=FALSE)
53      drawTreshold($Value,$R,$G,$B,$ShowLabel=FALSE,$ShowOnRight=FALSE,$TickWidth=4,$FreeText=NULL)
54      drawArea($Data,$Serie1,$Serie2,$R,$G,$B,$Alpha = 50)
55      drawRadarAxis($Data,$DataDescription,$Mosaic=TRUE,$BorderOffset=10,$A_R=60,$A_G=60,$A_B=60,$S_R=200,$S_G=200,$S_B=200,$MaxValue=-1)
56      drawGraphAreaGradient($R,$G,$B,$Decay,$Target=TARGET_GRAPHAREA)
57      drawTextBox($X1,$Y1,$X2,$Y2,$Text,$Angle=0,$R=255,$G=255,$B=255,$Align=ALIGN_LEFT,$Shadow=TRUE,$BgR=-1,$BgG=-1,$BgB=-1,$Alpha=100)
58      getLegendBoxSize($DataDescription)
59      loadColorPalette($FileName,$Delimiter=",")
60      reportWarnings($Interface="CLI")
61      setGraphArea($X1,$Y1,$X2,$Y2)
62      setLabel($Data,$DataDescription,$SerieName,$ValueName,$Caption,$R=210,$G=210,$B=210)
63      setColorPalette($ID,$R,$G,$B)
64      setCurrency($Currency)
65      setDateFormat($Format)
66      setFontProperties($FontName,$FontSize)
67      setLineStyle($Width=1,$DotSize=0)
68      setFixedScale($VMin,$VMax,$Divisions=5,$VXMin=0,$VXMin=0,$XDivisions=5)
69      setShadowProperties($XDistance=1,$YDistance=1,$R=60,$G=60,$B=60,$Alpha)
70      writeValues($Data,$DataDescription,$Series)
71    Graphs methods :
72      drawPlotGraph($Data,$DataDescription,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1,$Shadow=FALSE)
73      drawXYPlotGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1)
74      drawLineGraph($Data,$DataDescription,$SerieName="")
75      drawXYGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0)
76      drawFilledLineGraph($Data,$DataDescription,$Alpha=100,$AroundZero=FALSE)
77      drawCubicCurve($Data,$DataDescription,$Accuracy=.1,$SerieName="")
78      drawFilledCubicCurve($Data,$DataDescription,$Accuracy=.1,$Alpha=100,$AroundZero=FALSE)
79      drawOverlayBarGraph($Data,$DataDescription,$Alpha=50)
80      drawBarGraph($Data,$DataDescription,$Shadow=FALSE)
81      drawStackedBarGraph($Data,$DataDescription,$Alpha=50,$Contiguous=FALSE)
82      drawLimitsGraph($Data,$DataDescription,$R=0,$G=0,$B=0)
83      drawRadar($Data,$DataDescription,$BorderOffset=10,$MaxValue=-1)
84      drawFilledRadar($Data,$DataDescription,$Alpha=50,$BorderOffset=10,$MaxValue=-1)
85      drawBasicPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$R=255,$G=255,$B=255,$Decimals=0)
86      drawFlatPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals = 0)
87      drawFlatPieGraphWithShadow($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals = 0)
88      drawPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$EnhanceColors=TRUE,$Skew=60,$SpliceHeight=20,$SpliceDistance=0,$Decimals=0)
89     Other methods :
90      setImageMap($Mode=TRUE,$GraphID="MyGraph")
91      getImageMap($MapName,$Flush=TRUE)
92      Render($FileName)
93      Stroke()
94 */
95
96 /* Declare some script wide constants */
97 define("SCALE_NORMAL",1);
98 define("SCALE_ADDALL",2);
99 define("SCALE_START0",3);
100 define("SCALE_ADDALLSTART0",4);
101 define("PIE_PERCENTAGE", 1);
102 define("PIE_LABELS",2);
103 define("PIE_NOLABEL",3);
104 define("PIE_PERCENTAGE_LABEL", 4);
105 define("TARGET_GRAPHAREA",1);
106 define("TARGET_BACKGROUND",2);
107 define("ALIGN_TOP_LEFT",1);
108 define("ALIGN_TOP_CENTER",2);
109 define("ALIGN_TOP_RIGHT",3);
110 define("ALIGN_LEFT",4);
111 define("ALIGN_CENTER",5);
112 define("ALIGN_RIGHT",6);
113 define("ALIGN_BOTTOM_LEFT",7);
114 define("ALIGN_BOTTOM_CENTER",8);
115 define("ALIGN_BOTTOM_RIGHT",9);
116
117 /* pChart class definition */
118 class pChart
119  {
120   /* Palettes definition */
121   var $Palette = array("0"=>array("R"=>188,"G"=>224,"B"=>46),
122                        "1"=>array("R"=>224,"G"=>100,"B"=>46),
123                        "2"=>array("R"=>224,"G"=>214,"B"=>46),
124                        "3"=>array("R"=>46,"G"=>151,"B"=>224),
125                        "4"=>array("R"=>176,"G"=>46,"B"=>224),
126                        "5"=>array("R"=>224,"G"=>46,"B"=>117),
127                        "6"=>array("R"=>92,"G"=>224,"B"=>46),
128                        "7"=>array("R"=>224,"G"=>176,"B"=>46));
129
130   /* Some static vars used in the class */
131   var $XSize          = NULL;
132   var $YSize          = NULL;
133   var $Picture        = NULL;
134   var $ImageMap       = NULL;
135
136   /* Error management */
137   var $ErrorReporting = FALSE;
138   var $ErrorInterface = "CLI";
139   var $Errors         = NULL;
140   var $ErrorFontName  = "Fonts/pf_arma_five.ttf";
141   var $ErrorFontSize  = 6;
142
143   /* vars related to the graphing area */
144   var $GArea_X1        = NULL;
145   var $GArea_Y1        = NULL;
146   var $GArea_X2        = NULL;
147   var $GArea_Y2        = NULL;
148   var $GAreaXOffset    = NULL;
149   var $VMax            = NULL;
150   var $VMin            = NULL;
151   var $VXMax           = NULL;
152   var $VXMin           = NULL;
153   var $Divisions       = NULL;
154   var $XDivisions      = NULL;
155   var $DivisionHeight  = NULL;
156   var $XDivisionHeight = NULL;
157   var $DivisionCount   = NULL;
158   var $XDivisionCount  = NULL;
159   var $DivisionRatio   = NULL;
160   var $XDivisionRatio  = NULL;
161   var $DivisionWidth   = NULL;
162   var $DataCount       = NULL;
163   var $Currency        = "\$";
164
165   /* Text format related vars */
166   var $FontName       = NULL;
167   var $FontSize       = NULL;
168   var $DateFormat     = "d/m/Y";
169
170   /* Lines format related vars */
171   var $LineWidth      = 1;
172   var $LineDotSize    = 0;
173
174   /* Layer related vars */
175   var $Layers         = NULL;
176
177   /* Set antialias quality : 0 is maximum, 100 minimum*/
178   var $AntialiasQuality = 0;
179
180   /* Shadow settings */
181   var $ShadowActive    = FALSE;
182   var $ShadowXDistance = 1;
183   var $ShadowYDistance = 1;
184   var $ShadowRColor    = 60;
185   var $ShadowGColor    = 60;
186   var $ShadowBColor    = 60;
187   var $ShadowAlpha     = 50;
188   var $ShadowBlur      = 0;
189
190   /* Image Map settings */
191   var $BuildMap         = FALSE;
192   var $MapFunction      = NULL;
193   var $tmpFolder        = "tmp/";
194   var $MapID            = NULL;
195
196   /* This function create the background picture */
197   function pChart($XSize,$YSize)
198    {
199     $this->XSize   = $XSize;
200     $this->YSize   = $YSize;
201     $this->Picture = imagecreatetruecolor($XSize,$YSize);
202
203     $C_White =$this->AllocateColor($this->Picture,255,255,255);
204     imagefilledrectangle($this->Picture,0,0,$XSize,$YSize,$C_White);
205     imagecolortransparent($this->Picture,$C_White);
206
207     $this->setFontProperties("tahoma.ttf",8);
208    }
209
210  /* Set if warnings should be reported */
211  function reportWarnings($Interface="CLI")
212   {
213    $this->ErrorReporting = TRUE;
214    $this->ErrorInterface = $Interface;
215    }
216
217   /* Set the font properties */
218   function setFontProperties($FontName,$FontSize)
219    {
220     $this->FontName = $FontName;
221     $this->FontSize = $FontSize;
222    }
223
224   /* Set the shadow properties */
225   function setShadowProperties($XDistance=1,$YDistance=1,$R=60,$G=60,$B=60,$Alpha=50,$Blur=0)
226    {
227     $this->ShadowActive    = TRUE;
228     $this->ShadowXDistance = $XDistance;
229     $this->ShadowYDistance = $YDistance;
230     $this->ShadowRColor    = $R;
231     $this->ShadowGColor    = $G;
232     $this->ShadowBColor    = $B;
233     $this->ShadowAlpha     = $Alpha;
234     $this->ShadowBlur      = $Blur;
235    }
236
237   /* Remove shadow option */
238   function clearShadow()
239    {
240     $this->ShadowActive = FALSE;
241    }
242
243   /* Set Palette color */
244   function setColorPalette($ID,$R,$G,$B)
245    {
246     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
247     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
248     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
249
250     $this->Palette[$ID]["R"] = $R;
251     $this->Palette[$ID]["G"] = $G;
252     $this->Palette[$ID]["B"] = $B;
253    }
254
255   /* Create a color palette shading from one color to another */
256   function createColorGradientPalette($R1,$G1,$B1,$R2,$G2,$B2,$Shades)
257    {
258     $RFactor = ($R2-$R1)/$Shades;
259     $GFactor = ($G2-$G1)/$Shades;
260     $BFactor = ($B2-$B1)/$Shades;
261
262     for($i=0;$i<=$Shades-1;$i++)
263      {
264       $this->Palette[$i]["R"] = $R1+$RFactor*$i;
265       $this->Palette[$i]["G"] = $G1+$GFactor*$i;
266       $this->Palette[$i]["B"] = $B1+$BFactor*$i;
267      }
268    }
269
270   /* Load Color Palette from file */
271   function loadColorPalette($FileName,$Delimiter=",")
272    {
273     $handle  = @fopen($FileName,"r");
274     $ColorID = 0;
275     if ($handle)
276      {
277       while (!feof($handle))
278        {
279         $buffer = fgets($handle, 4096);
280         $buffer = str_replace(chr(10),"",$buffer);
281         $buffer = str_replace(chr(13),"",$buffer);
282         $Values = split($Delimiter,$buffer);
283         if ( count($Values) == 3 )
284          {
285           $this->Palette[$ColorID]["R"] = $Values[0];
286           $this->Palette[$ColorID]["G"] = $Values[1];
287           $this->Palette[$ColorID]["B"] = $Values[2];
288           $ColorID++;
289          }
290        }
291      }
292    }
293
294   /* Set line style */
295  function setLineStyle($Width=1,$DotSize=0)
296   {
297    $this->LineWidth   = $Width;
298    $this->LineDotSize = $DotSize;
299   }
300
301   /* Set currency symbol */
302   function setCurrency($Currency)
303    {
304     $this->Currency = $Currency;
305    }
306
307   /* Set the graph area location */
308   function setGraphArea($X1,$Y1,$X2,$Y2)
309    {
310     $this->GArea_X1 = $X1;
311     $this->GArea_Y1 = $Y1;
312     $this->GArea_X2 = $X2;
313     $this->GArea_Y2 = $Y2;
314    }
315
316   /* Prepare the graph area */
317   function drawGraphArea($R,$G,$B,$Stripe=FALSE)
318    {
319     $this->drawFilledRectangle($this->GArea_X1,$this->GArea_Y1,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B,FALSE);
320     $this->drawRectangle($this->GArea_X1,$this->GArea_Y1,$this->GArea_X2,$this->GArea_Y2,$R-40,$G-40,$B-40);
321
322     if ( $Stripe )
323      {
324       $R2 = $R-15; if ( $R2 < 0 ) { $R2 = 0; }
325       $G2 = $R-15; if ( $G2 < 0 ) { $G2 = 0; }
326       $B2 = $R-15; if ( $B2 < 0 ) { $B2 = 0; }
327
328       $LineColor =$this->AllocateColor($this->Picture,$R2,$G2,$B2);
329       $SkewWidth = $this->GArea_Y2-$this->GArea_Y1-1;
330
331       for($i=$this->GArea_X1-$SkewWidth;$i<=$this->GArea_X2;$i=$i+4)
332        {
333         $X1 = $i;            $Y1 = $this->GArea_Y2;
334         $X2 = $i+$SkewWidth; $Y2 = $this->GArea_Y1;
335
336
337         if ( $X1 < $this->GArea_X1 )
338          { $X1 = $this->GArea_X1; $Y1 = $this->GArea_Y1 + $X2 - $this->GArea_X1 + 1; }
339
340         if ( $X2 >= $this->GArea_X2 )
341          { $Y2 = $this->GArea_Y1 + $X2 - $this->GArea_X2 +1; $X2 = $this->GArea_X2 - 1; }
342// * Fixed in 1.27 *         { $X2 = $this->GArea_X2 - 1; $Y2 = $this->GArea_Y2 - ($this->GArea_X2 - $X1); }
343
344         imageline($this->Picture,$X1,$Y1,$X2,$Y2+1,$LineColor);
345        }
346      }
347    }
348
349   /* Allow you to clear the scale : used if drawing multiple charts */
350   function clearScale()
351    {
352     $this->VMin       = NULL;
353     $this->VMax       = NULL;
354     $this->VXMin      = NULL;
355     $this->VXMax      = NULL;
356     $this->Divisions  = NULL;
357     $this->XDivisions = NULL;    }
358
359   /* Allow you to fix the scale, use this to bypass the automatic scaling */
360   function setFixedScale($VMin,$VMax,$Divisions=5,$VXMin=0,$VXMax=0,$XDivisions=5)
361    {
362     $this->VMin      = $VMin;
363     $this->VMax      = $VMax;
364     $this->Divisions = $Divisions;
365
366     if ( !$VXMin == 0 )
367      {
368       $this->VXMin      = $VXMin;
369       $this->VXMax      = $VXMax;
370       $this->XDivisions = $XDivisions;
371      }
372    }
373
374   /* Wrapper to the drawScale() function allowing a second scale to be drawn */
375   function drawRightScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1)
376    {
377     $this->drawScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks,$Angle,$Decimals,$WithMargin,$SkipLabels,TRUE);
378    }
379
380   /* Compute and draw the scale */
381   function drawScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1,$RightScale=FALSE)
382    {
383     /* Validate the Data and DataDescription array */
384     $this->validateData("drawScale",$Data);
385
386     $C_TextColor         =$this->AllocateColor($this->Picture,$R,$G,$B);
387
388     $this->drawLine($this->GArea_X1,$this->GArea_Y1,$this->GArea_X1,$this->GArea_Y2,$R,$G,$B);
389     $this->drawLine($this->GArea_X1,$this->GArea_Y2,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B);
390
391     if ( $this->VMin == NULL && $this->VMax == NULL)
392      {
393       if (isset($DataDescription["Values"][0]))
394        {
395         $this->VMin = $Data[0][$DataDescription["Values"][0]];
396         $this->VMax = $Data[0][$DataDescription["Values"][0]];
397        }
398       else { $this->VMin = 2147483647; $this->VMax = -2147483647; }
399
400       /* Compute Min and Max values */
401       if ( $ScaleMode == SCALE_NORMAL || $ScaleMode == SCALE_START0 )
402        {
403         if ( $ScaleMode == SCALE_START0 ) { $this->VMin = 0; }
404
405         foreach ( $Data as $Key => $Values )
406          {
407           foreach ( $DataDescription["Values"] as $Key2 => $ColName )
408            {
409             if (isset($Data[$Key][$ColName]))
410              {
411               $Value = $Data[$Key][$ColName];
412
413               if ( is_numeric($Value) )
414                {
415                 if ( $this->VMax < $Value) { $this->VMax = $Value; }
416                 if ( $this->VMin > $Value) { $this->VMin = $Value; }
417                }
418              }
419            }
420          }
421        }
422       elseif ( $ScaleMode == SCALE_ADDALL || $ScaleMode == SCALE_ADDALLSTART0 ) /* Experimental */
423        {
424         if ( $ScaleMode == SCALE_ADDALLSTART0 ) { $this->VMin = 0; }
425
426         foreach ( $Data as $Key => $Values )
427          {
428           $Sum = 0;
429           foreach ( $DataDescription["Values"] as $Key2 => $ColName )
430            {
431             if (isset($Data[$Key][$ColName]))
432              {
433               $Value = $Data[$Key][$ColName];
434               if ( is_numeric($Value) )
435                $Sum  += $Value;
436              }
437            }
438           if ( $this->VMax < $Sum) { $this->VMax = $Sum; }
439           if ( $this->VMin > $Sum) { $this->VMin = $Sum; }
440          }
441        }
442
443       if ( $this->VMax > preg_replace('/\.[0-9]+/','',$this->VMax) )
444        $this->VMax = preg_replace('/\.[0-9]+/','',$this->VMax)+1;
445
446       /* If all values are the same */
447       if ( $this->VMax == $this->VMin )
448        {
449         if ( $this->VMax >= 0 ) { $this->VMax++; }
450         else { $this->VMin--; }
451        }
452
453       $DataRange = $this->VMax - $this->VMin;
454       if ( $DataRange == 0 ) { $DataRange = .1; }
455
456       /* Compute automatic scaling */
457       $ScaleOk = FALSE; $Factor = 1;
458       $MinDivHeight = 25; $MaxDivs = ($this->GArea_Y2 - $this->GArea_Y1) / $MinDivHeight;
459
460       if ( $this->VMin == 0 && $this->VMax == 0 )
461        { $this->VMin = 0; $this->VMax = 2; $Scale = 1; $Divisions = 2;}
462       elseif ($MaxDivs > 1)
463        {
464         while(!$ScaleOk)
465          {
466           $Scale1 = ( $this->VMax - $this->VMin ) / $Factor;
467           $Scale2 = ( $this->VMax - $this->VMin ) / $Factor / 2;
468           $Scale4 = ( $this->VMax - $this->VMin ) / $Factor / 4;
469
470           if ( $Scale1 > 1 && $Scale1 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale1); $Scale = 1;}
471           if ( $Scale2 > 1 && $Scale2 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale2); $Scale = 2;}
472           if (!$ScaleOk)
473            {
474             if ( $Scale2 > 1 ) { $Factor = $Factor * 10; }
475             if ( $Scale2 < 1 ) { $Factor = $Factor / 10; }
476            }
477          }
478
479         if ( floor($this->VMax / $Scale / $Factor) != $this->VMax / $Scale / $Factor)
480          {
481           $GridID     = floor ( $this->VMax / $Scale / $Factor) + 1;
482           $this->VMax = $GridID * $Scale * $Factor;
483           $Divisions++;
484          }
485
486         if ( floor($this->VMin / $Scale / $Factor) != $this->VMin / $Scale / $Factor)
487          {
488           $GridID     = floor( $this->VMin / $Scale / $Factor);
489           $this->VMin = $GridID * $Scale * $Factor;
490           $Divisions++;
491          }
492        }
493       else /* Can occurs for small graphs */
494        $Scale = 1;
495
496       if ( !isset($Divisions) )
497        $Divisions = 2;
498
499       if ($Scale == 1 && $Divisions%2 == 1)
500        $Divisions--;
501      }
502     else
503      $Divisions = $this->Divisions;
504
505     $this->DivisionCount = $Divisions;
506
507     $DataRange = $this->VMax - $this->VMin;
508     if ( $DataRange == 0 ) { $DataRange = .1; }
509
510     $this->DivisionHeight = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $Divisions;
511     $this->DivisionRatio  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $DataRange;
512
513     $this->GAreaXOffset  = 0;
514     if ( count($Data) > 1 )
515      {
516       if ( $WithMargin == FALSE )
517        $this->DivisionWidth = ( $this->GArea_X2 - $this->GArea_X1 ) / (count($Data)-1);
518       else
519        {
520         $this->DivisionWidth = ( $this->GArea_X2 - $this->GArea_X1 ) / (count($Data));
521         $this->GAreaXOffset  = $this->DivisionWidth / 2;
522        }
523      }
524     else
525      {
526       $this->DivisionWidth = $this->GArea_X2 - $this->GArea_X1;
527       $this->GAreaXOffset  = $this->DivisionWidth / 2;
528      }
529
530     $this->DataCount = count($Data);
531
532     if ( $DrawTicks == FALSE )
533      return(0);
534
535     $YPos = $this->GArea_Y2; $XMin = NULL;
536     for($i=1;$i<=$Divisions+1;$i++)
537      {
538       if ( $RightScale )
539        $this->drawLine($this->GArea_X2,$YPos,$this->GArea_X2+5,$YPos,$R,$G,$B);
540       else
541        $this->drawLine($this->GArea_X1,$YPos,$this->GArea_X1-5,$YPos,$R,$G,$B);
542
543       $Value     = $this->VMin + ($i-1) * (( $this->VMax - $this->VMin ) / $Divisions);
544       $Value     = round($Value * pow(10,$Decimals)) / pow(10,$Decimals);
545       if ( $DataDescription["Format"]["Y"] == "number" )
546        $Value = $Value.$DataDescription["Unit"]["Y"];
547       if ( $DataDescription["Format"]["Y"] == "time" )
548        $Value = $this->ToTime($Value);
549       if ( $DataDescription["Format"]["Y"] == "date" )
550        $Value = $this->ToDate($Value);
551       if ( $DataDescription["Format"]["Y"] == "metric" )
552        $Value = $this->ToMetric($Value);
553       if ( $DataDescription["Format"]["Y"] == "currency" )
554        $Value = $this->ToCurrency($Value);
555
556       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value);
557       $TextWidth = $Position[2]-$Position[0];
558
559       if ( $RightScale )
560        {
561         imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X2+10,$YPos+($this->FontSize/2),$C_TextColor,$this->FontName,$Value);
562         if ( $XMin < $this->GArea_X2+15+$TextWidth || $XMin == NULL ) { $XMin = $this->GArea_X2+15+$TextWidth; }
563        }
564       else
565        {
566         imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1-10-$TextWidth,$YPos+($this->FontSize/2),$C_TextColor,$this->FontName,$Value);
567         if ( $XMin > $this->GArea_X1-10-$TextWidth || $XMin == NULL ) { $XMin = $this->GArea_X1-10-$TextWidth; }
568        }
569
570       $YPos = $YPos - $this->DivisionHeight;
571      }
572
573     /* Write the Y Axis caption if set */
574     if ( isset($DataDescription["Axis"]["Y"]) )
575      {
576       $Position   = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["Y"]);
577       $TextHeight = abs($Position[1])+abs($Position[3]);
578       $TextTop    = (($this->GArea_Y2 - $this->GArea_Y1) / 2) + $this->GArea_Y1 + ($TextHeight/2);
579
580       if ( $RightScale )
581        imagettftext($this->Picture,$this->FontSize,90,$XMin+$this->FontSize,$TextTop,$C_TextColor,$this->FontName,$DataDescription["Axis"]["Y"]);
582       else
583        imagettftext($this->Picture,$this->FontSize,90,$XMin-$this->FontSize,$TextTop,$C_TextColor,$this->FontName,$DataDescription["Axis"]["Y"]);
584      }
585
586     /* Horizontal Axis */
587     $XPos = $this->GArea_X1 + $this->GAreaXOffset;
588     $ID = 1; $YMax = NULL;
589     foreach ( $Data as $Key => $Values )
590      {
591       if ( $ID % $SkipLabels == 0 )
592        {
593         $this->drawLine(floor($XPos),$this->GArea_Y2,floor($XPos),$this->GArea_Y2+5,$R,$G,$B);
594         $Value      = $Data[$Key][$DataDescription["Position"]];
595         if ( $DataDescription["Format"]["X"] == "number" )
596          $Value = $Value.$DataDescription["Unit"]["X"];
597         if ( $DataDescription["Format"]["X"] == "time" )
598          $Value = $this->ToTime($Value);
599         if ( $DataDescription["Format"]["X"] == "date" )
600          $Value = $this->ToDate($Value);
601         if ( $DataDescription["Format"]["X"] == "metric" )
602          $Value = $this->ToMetric($Value);
603         if ( $DataDescription["Format"]["X"] == "currency" )
604          $Value = $this->ToCurrency($Value);
605
606         $Position   = imageftbbox($this->FontSize,$Angle,$this->FontName,$Value);
607         $TextWidth  = abs($Position[2])+abs($Position[0]);
608         $TextHeight = abs($Position[1])+abs($Position[3]);
609
610         if ( $Angle == 0 )
611          {
612           $YPos = $this->GArea_Y2+18;
613           imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-floor($TextWidth/2),$YPos,$C_TextColor,$this->FontName,$Value);
614          }
615         else
616          {
617           $YPos = $this->GArea_Y2+10+$TextHeight;
618           if ( $Angle <= 90 )
619            imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
620           else
621            imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)+$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
622          }
623         if ( $YMax < $YPos || $YMax == NULL ) { $YMax = $YPos; }
624        }
625
626       $XPos = $XPos + $this->DivisionWidth;
627       $ID++;
628      }
629
630    /* Write the X Axis caption if set */
631    if ( isset($DataDescription["Axis"]["X"]) )
632      {
633       $Position   = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["X"]);
634       $TextWidth  = abs($Position[2])+abs($Position[0]);
635       $TextLeft   = (($this->GArea_X2 - $this->GArea_X1) / 2) + $this->GArea_X1 + ($TextWidth/2);
636       imagettftext($this->Picture,$this->FontSize,0,$TextLeft,$YMax+$this->FontSize+5,$C_TextColor,$this->FontName,$DataDescription["Axis"]["X"]);
637      }
638    }
639
640   /* Compute and draw the scale for X/Y charts */
641   function drawXYScale($Data,$DataDescription,$YSerieName,$XSerieName,$R,$G,$B,$WithMargin=0,$Angle=0,$Decimals=1)
642    {
643     /* Validate the Data and DataDescription array */
644     $this->validateData("drawScale",$Data);
645
646     $C_TextColor =$this->AllocateColor($this->Picture,$R,$G,$B);
647
648     $this->drawLine($this->GArea_X1,$this->GArea_Y1,$this->GArea_X1,$this->GArea_Y2,$R,$G,$B);
649     $this->drawLine($this->GArea_X1,$this->GArea_Y2,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B);
650
651     /* Process Y scale */
652     if ( $this->VMin == NULL && $this->VMax == NULL)
653      {
654       $this->VMin = $Data[0][$YSerieName];
655       $this->VMax = $Data[0][$YSerieName];
656
657       foreach ( $Data as $Key => $Values )
658        {
659         if (isset($Data[$Key][$YSerieName]))
660          {
661           $Value = $Data[$Key][$YSerieName];
662           if ( $this->VMax < $Value) { $this->VMax = $Value; }
663           if ( $this->VMin > $Value) { $this->VMin = $Value; }
664          }
665        }
666
667       if ( $this->VMax > preg_replace('/\.[0-9]+/','',$this->VMax) )
668        $this->VMax = preg_replace('/\.[0-9]+/','',$this->VMax)+1;
669
670       $DataRange = $this->VMax - $this->VMin;
671       if ( $DataRange == 0 ) { $DataRange = .1; }
672
673       /* Compute automatic scaling */
674       $ScaleOk = FALSE; $Factor = 1;
675       $MinDivHeight = 25; $MaxDivs = ($this->GArea_Y2 - $this->GArea_Y1) / $MinDivHeight;
676
677       if ( $this->VMin == 0 && $this->VMax == 0 )
678        { $this->VMin = 0; $this->VMax = 2; $Scale = 1; $Divisions = 2;}
679       elseif ($MaxDivs > 1)
680        {
681         while(!$ScaleOk)
682          {
683           $Scale1 = ( $this->VMax - $this->VMin ) / $Factor;
684           $Scale2 = ( $this->VMax - $this->VMin ) / $Factor / 2;
685           $Scale4 = ( $this->VMax - $this->VMin ) / $Factor / 4;
686
687           if ( $Scale1 > 1 && $Scale1 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale1); $Scale = 1;}
688           if ( $Scale2 > 1 && $Scale2 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale2); $Scale = 2;}
689           if (!$ScaleOk)
690            {
691             if ( $Scale2 > 1 ) { $Factor = $Factor * 10; }
692             if ( $Scale2 < 1 ) { $Factor = $Factor / 10; }
693            }
694          }
695
696         if ( floor($this->VMax / $Scale / $Factor) != $this->VMax / $Scale / $Factor)
697          {
698           $GridID     = floor ( $this->VMax / $Scale / $Factor) + 1;
699           $this->VMax = $GridID * $Scale * $Factor;
700           $Divisions++;
701          }
702
703         if ( floor($this->VMin / $Scale / $Factor) != $this->VMin / $Scale / $Factor)
704          {
705           $GridID     = floor( $this->VMin / $Scale / $Factor);
706           $this->VMin = $GridID * $Scale * $Factor;
707           $Divisions++;
708          }
709        }
710       else /* Can occurs for small graphs */
711        $Scale = 1;
712
713       if ( !isset($Divisions) )
714        $Divisions = 2;
715
716       if ( $this->isRealInt(($this->VMax-$this->VMin)/($Divisions-1)))
717        $Divisions--;
718       elseif ( $this->isRealInt(($this->VMax-$this->VMin)/($Divisions+1)))
719        $Divisions++;
720      }
721     else
722      $Divisions = $this->Divisions;
723
724     $this->DivisionCount = $Divisions;
725
726     $DataRange = $this->VMax - $this->VMin;
727     if ( $DataRange == 0 ) { $DataRange = .1; }
728
729     $this->DivisionHeight = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $Divisions;
730     $this->DivisionRatio  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $DataRange;
731
732     $YPos = $this->GArea_Y2; $XMin = NULL;
733     for($i=1;$i<=$Divisions+1;$i++)
734      {
735       $this->drawLine($this->GArea_X1,$YPos,$this->GArea_X1-5,$YPos,$R,$G,$B);
736       $Value     = $this->VMin + ($i-1) * (( $this->VMax - $this->VMin ) / $Divisions);
737       $Value     = round($Value * pow(10,$Decimals)) / pow(10,$Decimals);
738       if ( $DataDescription["Format"]["Y"] == "number" )
739        $Value = $Value.$DataDescription["Unit"]["Y"];
740       if ( $DataDescription["Format"]["Y"] == "time" )
741        $Value = $this->ToTime($Value);
742       if ( $DataDescription["Format"]["Y"] == "date" )
743        $Value = $this->ToDate($Value);
744       if ( $DataDescription["Format"]["Y"] == "metric" )
745        $Value = $this->ToMetric($Value);
746       if ( $DataDescription["Format"]["Y"] == "currency" )
747        $Value = $this->ToCurrency($Value);
748
749       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value);
750       $TextWidth = $Position[2]-$Position[0];
751       imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1-10-$TextWidth,$YPos+($this->FontSize/2),$C_TextColor,$this->FontName,$Value);
752
753       if ( $XMin > $this->GArea_X1-10-$TextWidth || $XMin == NULL ) { $XMin = $this->GArea_X1-10-$TextWidth; }
754
755       $YPos = $YPos - $this->DivisionHeight;
756      }
757
758     /* Process X scale */
759     if ( $this->VXMin == NULL && $this->VXMax == NULL)
760      {
761       $this->VXMin = $Data[0][$XSerieName];
762       $this->VXMax = $Data[0][$XSerieName];
763
764       foreach ( $Data as $Key => $Values )
765        {
766         if (isset($Data[$Key][$XSerieName]))
767          {
768           $Value = $Data[$Key][$XSerieName];
769           if ( $this->VXMax < $Value) { $this->VXMax = $Value; }
770           if ( $this->VXMin > $Value) { $this->VXMin = $Value; }
771          }
772        }
773
774       if ( $this->VXMax > preg_replace('/\.[0-9]+/','',$this->VXMax) )
775        $this->VXMax = preg_replace('/\.[0-9]+/','',$this->VXMax)+1;
776
777       $DataRange = $this->VMax - $this->VMin;
778       if ( $DataRange == 0 ) { $DataRange = .1; }
779
780       /* Compute automatic scaling */
781       $ScaleOk = FALSE; $Factor = 1;
782       $MinDivWidth = 25; $MaxDivs = ($this->GArea_X2 - $this->GArea_X1) / $MinDivWidth;
783
784       if ( $this->VXMin == 0 && $this->VXMax == 0 )
785        { $this->VXMin = 0; $this->VXMax = 2; $Scale = 1; $XDivisions = 2;}
786       elseif ($MaxDivs > 1)
787        {
788         while(!$ScaleOk)
789          {
790           $Scale1 = ( $this->VXMax - $this->VXMin ) / $Factor;
791           $Scale2 = ( $this->VXMax - $this->VXMin ) / $Factor / 2;
792           $Scale4 = ( $this->VXMax - $this->VXMin ) / $Factor / 4;
793
794           if ( $Scale1 > 1 && $Scale1 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $XDivisions = floor($Scale1); $Scale = 1;}
795           if ( $Scale2 > 1 && $Scale2 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $XDivisions = floor($Scale2); $Scale = 2;}
796           if (!$ScaleOk)
797            {
798             if ( $Scale2 > 1 ) { $Factor = $Factor * 10; }
799             if ( $Scale2 < 1 ) { $Factor = $Factor / 10; }
800            }
801          }
802
803         if ( floor($this->VXMax / $Scale / $Factor) != $this->VXMax / $Scale / $Factor)
804          {
805           $GridID     = floor ( $this->VXMax / $Scale / $Factor) + 1;
806           $this->VXMax = $GridID * $Scale * $Factor;
807           $XDivisions++;
808          }
809
810         if ( floor($this->VXMin / $Scale / $Factor) != $this->VXMin / $Scale / $Factor)
811          {
812           $GridID     = floor( $this->VXMin / $Scale / $Factor);
813           $this->VXMin = $GridID * $Scale * $Factor;
814           $XDivisions++;
815          }
816        }
817       else /* Can occurs for small graphs */
818        $Scale = 1;
819
820       if ( !isset($XDivisions) )
821        $XDivisions = 2;
822
823       if ( $this->isRealInt(($this->VXMax-$this->VXMin)/($XDivisions-1)))
824        $XDivisions--;
825       elseif ( $this->isRealInt(($this->VXMax-$this->VXMin)/($XDivisions+1)))
826        $XDivisions++;
827      }
828     else
829      $XDivisions = $this->XDivisions;
830
831     $this->XDivisionCount = $Divisions;
832     $this->DataCount      = $Divisions + 2;
833
834     $XDataRange = $this->VXMax - $this->VXMin;
835     if ( $XDataRange == 0 ) { $XDataRange = .1; }
836
837     $this->DivisionWidth   = ( $this->GArea_X2 - $this->GArea_X1 ) / $XDivisions;
838     $this->XDivisionRatio  = ( $this->GArea_X2 - $this->GArea_X1 ) / $XDataRange;
839
840     $XPos = $this->GArea_X1; $YMax = NULL;
841     for($i=1;$i<=$XDivisions+1;$i++)
842      {
843       $this->drawLine($XPos,$this->GArea_Y2,$XPos,$this->GArea_Y2+5,$R,$G,$B);
844
845       $Value     = $this->VXMin + ($i-1) * (( $this->VXMax - $this->VXMin ) / $XDivisions);
846       $Value     = round($Value * pow(10,$Decimals)) / pow(10,$Decimals);
847       if ( $DataDescription["Format"]["Y"] == "number" )
848        $Value = $Value.$DataDescription["Unit"]["Y"];
849       if ( $DataDescription["Format"]["Y"] == "time" )
850        $Value = $this->ToTime($Value);
851       if ( $DataDescription["Format"]["Y"] == "date" )
852        $Value = $this->ToDate($Value);
853       if ( $DataDescription["Format"]["Y"] == "metric" )
854        $Value = $this->ToMetric($Value);
855       if ( $DataDescription["Format"]["Y"] == "currency" )
856        $Value = $this->ToCurrency($Value);
857
858       $Position   = imageftbbox($this->FontSize,$Angle,$this->FontName,$Value);
859       $TextWidth  = abs($Position[2])+abs($Position[0]);
860       $TextHeight = abs($Position[1])+abs($Position[3]);
861
862       if ( $Angle == 0 )
863        {
864         $YPos = $this->GArea_Y2+18;
865         imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-floor($TextWidth/2),$YPos,$C_TextColor,$this->FontName,$Value);
866        }
867       else
868        {
869         $YPos = $this->GArea_Y2+10+$TextHeight;
870         if ( $Angle <= 90 )
871          imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
872         else
873          imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)+$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
874         }
875
876       if ( $YMax < $YPos || $YMax == NULL ) { $YMax = $YPos; }
877
878       $XPos = $XPos + $this->DivisionWidth;
879      }
880
881     /* Write the Y Axis caption if set */
882     if ( isset($DataDescription["Axis"]["Y"]) )
883      {
884       $Position   = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["Y"]);
885       $TextHeight = abs($Position[1])+abs($Position[3]);
886       $TextTop    = (($this->GArea_Y2 - $this->GArea_Y1) / 2) + $this->GArea_Y1 + ($TextHeight/2);
887       imagettftext($this->Picture,$this->FontSize,90,$XMin-$this->FontSize,$TextTop,$C_TextColor,$this->FontName,$DataDescription["Axis"]["Y"]);
888      }
889
890     /* Write the X Axis caption if set */
891     if ( isset($DataDescription["Axis"]["X"]) )
892      {
893       $Position   = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["X"]);
894       $TextWidth  = abs($Position[2])+abs($Position[0]);
895       $TextLeft   = (($this->GArea_X2 - $this->GArea_X1) / 2) + $this->GArea_X1 + ($TextWidth/2);
896       imagettftext($this->Picture,$this->FontSize,0,$TextLeft,$YMax+$this->FontSize+5,$C_TextColor,$this->FontName,$DataDescription["Axis"]["X"]);
897      }
898    }
899
900   /* Compute and draw the scale */
901   function drawGrid($LineWidth,$Mosaic=TRUE,$R=220,$G=220,$B=220,$Alpha=100)
902    {
903     /* Draw mosaic */
904     if ( $Mosaic )
905      {
906       $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
907       $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
908
909       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
910       $C_White         =$this->AllocateColor($this->Layers[0],255,255,255);
911       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
912       imagecolortransparent($this->Layers[0],$C_White);
913
914       $C_Rectangle =$this->AllocateColor($this->Layers[0],250,250,250);
915
916       $YPos  = $LayerHeight; //$this->GArea_Y2-1;
917       $LastY = $YPos;
918       for($i=0;$i<=$this->DivisionCount;$i++)
919        {
920         $LastY = $YPos;
921         $YPos  = $YPos - $this->DivisionHeight;
922
923         if ( $YPos <= 0 ) { $YPos = 1; }
924
925         if ( $i % 2 == 0 )
926          {
927           imagefilledrectangle($this->Layers[0],1,$YPos,$LayerWidth-1,$LastY,$C_Rectangle);
928          }
929        }
930       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
931       imagedestroy($this->Layers[0]);
932      }
933
934     /* Horizontal lines */
935     $YPos = $this->GArea_Y2 - $this->DivisionHeight;
936     for($i=1;$i<=$this->DivisionCount;$i++)
937      {
938       if ( $YPos > $this->GArea_Y1 && $YPos < $this->GArea_Y2 )
939        $this->drawDottedLine($this->GArea_X1,$YPos,$this->GArea_X2,$YPos,$LineWidth,$R,$G,$B);
940
941       $YPos = $YPos - $this->DivisionHeight;
942      }
943
944     /* Vertical lines */
945     if ( $this->GAreaXOffset == 0 )
946      { $XPos = $this->GArea_X1 + $this->DivisionWidth + $this->GAreaXOffset; $ColCount = $this->DataCount-2; }
947     else
948      { $XPos = $this->GArea_X1 + $this->GAreaXOffset; $ColCount = floor( ($this->GArea_X2 - $this->GArea_X1) / $this->DivisionWidth ); }
949
950     for($i=1;$i<=$ColCount;$i++)
951      {
952       if ( $XPos > $this->GArea_X1 && $XPos < $this->GArea_X2 )
953        $this->drawDottedLine(floor($XPos),$this->GArea_Y1,floor($XPos),$this->GArea_Y2,$LineWidth,$R,$G,$B);
954       $XPos = $XPos + $this->DivisionWidth;
955      }
956    }
957
958   /* retrieve the legends size */
959   function getLegendBoxSize($DataDescription)
960    {
961     if ( !isset($DataDescription["Description"]) )
962      return(-1);
963
964     /* <-10->[8]<-4->Text<-10-> */
965     $MaxWidth = 0; $MaxHeight = 8;
966     foreach($DataDescription["Description"] as $Key => $Value)
967      {
968       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
969       $TextWidth  = $Position[2]-$Position[0];
970       $TextHeight = $Position[1]-$Position[7];
971       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
972       $MaxHeight = $MaxHeight + $TextHeight + 4;
973      }
974     $MaxHeight = $MaxHeight - 3;
975     $MaxWidth  = $MaxWidth + 32;
976
977     return(array($MaxWidth,$MaxHeight));
978    }
979
980   /* retrieve the pie legends size */
981   function getPieLegendBoxSize($Data, $DataDescription)
982    {
983     if ( !isset($DataDescription["Position"]) )
984      return(-1);
985
986     /* <-10->[8]<-4->Text<-10-> */
987     $MaxWidth = 0; $MaxHeight = 8;
988     foreach($Data as $DataPoint)
989      {
990       $Value = $DataPoint[$DataDescription["Position"]];
991       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
992       $TextWidth  = $Position[2]-$Position[0];
993       $TextHeight = $Position[1]-$Position[7];
994       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
995       $MaxHeight = $MaxHeight + $TextHeight + 4;
996      }
997
998     $MaxHeight = $MaxHeight - 3;
999     $MaxWidth  = $MaxWidth + 32;
1000
1001     return(array($MaxWidth,$MaxHeight));
1002    }
1003
1004   /* Draw the data legends */
1005   function drawLegend($XPos,$YPos,$DataDescription,$R,$G,$B,$Rs=-1,$Gs=-1,$Bs=-1,$Rt=0,$Gt=0,$Bt=0,$Border=TRUE)
1006    {
1007     /* Validate the Data and DataDescription array */
1008     $this->validateDataDescription("drawLegend",$DataDescription);
1009
1010     if ( !isset($DataDescription["Description"]) )
1011      return(-1);
1012
1013     $C_TextColor =$this->AllocateColor($this->Picture,$Rt,$Gt,$Bt);
1014
1015     /* <-10->[8]<-4->Text<-10-> */
1016     $MaxWidth = 0; $MaxHeight = 8;
1017     foreach($DataDescription["Description"] as $Key => $Value)
1018      {
1019       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1020       $TextWidth  = $Position[2]-$Position[0];
1021       $TextHeight = $Position[1]-$Position[7];
1022       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
1023       $MaxHeight = $MaxHeight + $TextHeight + 4;
1024      }
1025     $MaxHeight = $MaxHeight - 5;
1026     $MaxWidth  = $MaxWidth + 32;
1027
1028     if ( $Rs == -1 || $Gs == -1 || $Bs == -1 )
1029      { $Rs = $R-30; $Gs = $G-30; $Bs = $B-30; }
1030
1031     if ( $Border )
1032      {
1033       $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$Rs,$Gs,$Bs);
1034       $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B);
1035      }
1036
1037     $YOffset = 4 + $this->FontSize; $ID = 0;
1038     foreach($DataDescription["Description"] as $Key => $Value)
1039      {
1040       $this->drawFilledRoundedRectangle($XPos+10,$YPos+$YOffset-4,$XPos+14,$YPos+$YOffset-4,2,$this->Palette[$ID]["R"],$this->Palette[$ID]["G"],$this->Palette[$ID]["B"]);
1041       imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value);
1042
1043       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1044       $TextHeight = $Position[1]-$Position[7];
1045
1046       $YOffset = $YOffset + $TextHeight + 4;
1047       $ID++;
1048      }
1049    }
1050
1051   /* Draw the data legends */
1052   function drawPieLegend($XPos,$YPos,$Data,$DataDescription,$R,$G,$B)
1053    {
1054     /* Validate the Data and DataDescription array */
1055     $this->validateDataDescription("drawPieLegend",$DataDescription,FALSE);
1056     $this->validateData("drawPieLegend",$Data);
1057
1058     if ( !isset($DataDescription["Position"]) )
1059      return(-1);
1060
1061     $C_TextColor =$this->AllocateColor($this->Picture,0,0,0);
1062
1063     /* <-10->[8]<-4->Text<-10-> */
1064     $MaxWidth = 0; $MaxHeight = 8;
1065     foreach($Data as $Key => $Value)
1066      {
1067       $Value2 = $Value[$DataDescription["Position"]];
1068       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value2);
1069       $TextWidth = $Position[2]-$Position[0];
1070       $TextHeight = $Position[1]-$Position[7];
1071       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
1072
1073       $MaxHeight = $MaxHeight + $TextHeight + 4;
1074      }
1075     $MaxHeight = $MaxHeight - 3;
1076     $MaxWidth  = $MaxWidth + 32;
1077
1078     $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$R-30,$G-30,$B-30);
1079     $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B);
1080
1081     $YOffset = 4 + $this->FontSize; $ID = 0;
1082     foreach($Data as $Key => $Value)
1083      {
1084       $Value2     = $Value[$DataDescription["Position"]];
1085       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value2);
1086       $TextHeight = $Position[1]-$Position[7];
1087       $this->drawFilledRectangle($XPos+10,$YPos+$YOffset-6,$XPos+14,$YPos+$YOffset-2,$this->Palette[$ID]["R"],$this->Palette[$ID]["G"],$this->Palette[$ID]["B"]);
1088
1089       imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value2);
1090       $YOffset = $YOffset + $TextHeight + 4;
1091       $ID++;
1092      }
1093    }
1094
1095   /* Draw the graph title */
1096   function drawTitle($XPos,$YPos,$Value,$R,$G,$B,$XPos2=-1,$YPos2=-1,$Shadow=FALSE)
1097    {
1098     $C_TextColor = $this->AllocateColor($this->Picture,$R,$G,$B);
1099
1100     if ( $XPos2 != -1 )
1101      {
1102       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1103       $TextWidth = $Position[2]-$Position[0];
1104       $XPos      = floor(( $XPos2 - $XPos - $TextWidth ) / 2 ) + $XPos;
1105      }
1106
1107     if ( $YPos2 != -1 )
1108      {
1109       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1110       $TextHeight = $Position[5]-$Position[3];
1111       $YPos       = floor(( $YPos2 - $YPos - $TextHeight ) / 2 ) + $YPos;
1112      }
1113
1114     if ( $Shadow )
1115      {
1116       $C_ShadowColor = $this->AllocateColor($this->Picture,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor);
1117       imagettftext($this->Picture,$this->FontSize,0,$XPos+$this->ShadowXDistance,$YPos+$this->ShadowYDistance,$C_ShadowColor,$this->FontName,$Value);
1118      }
1119
1120     imagettftext($this->Picture,$this->FontSize,0,$XPos,$YPos,$C_TextColor,$this->FontName,$Value);
1121    }
1122
1123   /* Draw a text box with text align & alpha properties */
1124   function drawTextBox($X1,$Y1,$X2,$Y2,$Text,$Angle=0,$R=255,$G=255,$B=255,$Align=ALIGN_LEFT,$Shadow=TRUE,$BgR=-1,$BgG=-1,$BgB=-1,$Alpha=100)
1125    {
1126     $Position   = imageftbbox($this->FontSize,$Angle,$this->FontName,$Text);
1127     $TextWidth  = $Position[2]-$Position[0];
1128     $TextHeight = $Position[5]-$Position[3];
1129     $AreaWidth  = $X2 - $X1;
1130     $AreaHeight = $Y2 - $Y1;
1131
1132     if ( $BgR != -1 && $BgG != -1 && $BgB != -1 )
1133      $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$BgR,$BgG,$BgB,FALSE,$Alpha);
1134
1135     if ( $Align == ALIGN_TOP_LEFT )      { $X = $X1+1; $Y = $Y1+$this->FontSize+1; }
1136     if ( $Align == ALIGN_TOP_CENTER )    { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y1+$this->FontSize+1; }
1137     if ( $Align == ALIGN_TOP_RIGHT )     { $X = $X2-$TextWidth-1; $Y = $Y1+$this->FontSize+1; }
1138     if ( $Align == ALIGN_LEFT )          { $X = $X1+1; $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1139     if ( $Align == ALIGN_CENTER )        { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1140     if ( $Align == ALIGN_RIGHT )         { $X = $X2-$TextWidth-1; $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1141     if ( $Align == ALIGN_BOTTOM_LEFT )   { $X = $X1+1; $Y = $Y2-1; }
1142     if ( $Align == ALIGN_BOTTOM_CENTER ) { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y2-1; }
1143     if ( $Align == ALIGN_BOTTOM_RIGHT )  { $X = $X2-$TextWidth-1; $Y = $Y2-1; }
1144
1145     $C_TextColor   =$this->AllocateColor($this->Picture,$R,$G,$B);
1146     $C_ShadowColor =$this->AllocateColor($this->Picture,0,0,0);
1147     if ( $Shadow )
1148      imagettftext($this->Picture,$this->FontSize,$Angle,$X+1,$Y+1,$C_ShadowColor,$this->FontName,$Text);
1149
1150     imagettftext($this->Picture,$this->FontSize,$Angle,$X,$Y,$C_TextColor,$this->FontName,$Text);
1151    }
1152
1153   /* Compute and draw the scale */
1154   function drawTreshold($Value,$R,$G,$B,$ShowLabel=FALSE,$ShowOnRight=FALSE,$TickWidth=4,$FreeText=NULL)
1155    {
1156     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
1157     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
1158     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
1159
1160     $C_TextColor =$this->AllocateColor($this->Picture,$R,$G,$B);
1161     $Y = $this->GArea_Y2 - ($Value - $this->VMin) * $this->DivisionRatio;
1162
1163     if ( $Y <= $this->GArea_Y1 || $Y >= $this->GArea_Y2 )
1164      return(-1);
1165
1166     if ( $TickWidth == 0 )
1167      $this->drawLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$R,$G,$B);
1168     else
1169      $this->drawDottedLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$TickWidth,$R,$G,$B);
1170
1171     if ( $ShowLabel )
1172      {
1173       if ( $FreeText == NULL )
1174        { $Label = $Value; } else { $Label = $FreeText; }
1175
1176       if ( $ShowOnRight )
1177        imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X2+2,$Y+($this->FontSize/2),$C_TextColor,$this->FontName,$Label);
1178       else
1179        imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1+2,$Y-($this->FontSize/2),$C_TextColor,$this->FontName,$Label);
1180      }
1181    }
1182
1183   /* This function put a label on a specific point */
1184   function setLabel($Data,$DataDescription,$SerieName,$ValueName,$Caption,$R=210,$G=210,$B=210)
1185    {
1186     /* Validate the Data and DataDescription array */
1187     $this->validateDataDescription("setLabel",$DataDescription);
1188     $this->validateData("setLabel",$Data);
1189     $ShadowFactor = 100;
1190     $C_Label      =$this->AllocateColor($this->Picture,$R,$G,$B);
1191     $C_Shadow     =$this->AllocateColor($this->Picture,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1192     $C_TextColor  =$this->AllocateColor($this->Picture,0,0,0);
1193
1194     $Cp = 0; $Found = FALSE;
1195     foreach ( $Data as $Key => $Value )
1196      {
1197       if ( $Data[$Key][$DataDescription["Position"]] == $ValueName )
1198        { $NumericalValue = $Data[$Key][$SerieName]; $Found = TRUE; }
1199       if ( !$Found )
1200        $Cp++;
1201      }
1202
1203     $XPos = $this->GArea_X1 + $this->GAreaXOffset + ( $this->DivisionWidth * $Cp ) + 2;
1204     $YPos = $this->GArea_Y2 - ($NumericalValue - $this->VMin) * $this->DivisionRatio;
1205
1206     $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
1207     $TextHeight = $Position[3] - $Position[5];
1208     $TextWidth  = $Position[2]-$Position[0] + 2;
1209     $TextOffset = floor($TextHeight/2);
1210
1211     // Shadow
1212     $Poly = array($XPos+1,$YPos+1,$XPos + 9,$YPos - $TextOffset,$XPos + 8,$YPos + $TextOffset + 2);
1213     imagefilledpolygon($this->Picture,$Poly,3,$C_Shadow);
1214     $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos - $TextOffset - .2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1215     $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos + $TextOffset + 2.2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1216     $this->drawFilledRectangle($XPos + 9,$YPos - $TextOffset-.2,$XPos + 13 + $TextWidth,$YPos + $TextOffset + 2.2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1217
1218     // Label background
1219     $Poly = array($XPos,$YPos,$XPos + 8,$YPos - $TextOffset - 1,$XPos + 8,$YPos + $TextOffset + 1);
1220     imagefilledpolygon($this->Picture,$Poly,3,$C_Label);
1221     $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos - $TextOffset - 1.2,$R,$G,$B);
1222     $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos + $TextOffset + 1.2,$R,$G,$B);
1223     $this->drawFilledRectangle($XPos + 8,$YPos - $TextOffset - 1.2,$XPos + 12 + $TextWidth,$YPos + $TextOffset + 1.2,$R,$G,$B);
1224
1225     imagettftext($this->Picture,$this->FontSize,0,$XPos + 10,$YPos + $TextOffset,$C_TextColor,$this->FontName,$Caption);
1226    }
1227
1228   /* This function draw a plot graph */
1229   function drawPlotGraph($Data,$DataDescription,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1,$Shadow=FALSE)
1230    {
1231     /* Validate the Data and DataDescription array */
1232     $this->validateDataDescription("drawPlotGraph",$DataDescription);
1233     $this->validateData("drawPlotGraph",$Data);
1234
1235     $GraphID = 0;
1236     $Ro = $R2; $Go = $G2; $Bo = $B2;
1237
1238     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1239      {
1240       $ID = 0;
1241       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1242        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1243
1244       $R = $this->Palette[$ColorID]["R"];
1245       $G = $this->Palette[$ColorID]["G"];
1246       $B = $this->Palette[$ColorID]["B"];
1247       $R2 = $Ro; $G2 = $Go; $B2 = $Bo;
1248
1249       if ( isset($DataDescription["Symbol"][$ColName]) )
1250        {
1251         $Is_Alpha = ((ord ( file_get_contents ($DataDescription["Symbol"][$ColName], false, null, 25, 1)) & 6) & 4) == 4;
1252
1253         $Infos       = getimagesize($DataDescription["Symbol"][$ColName]);
1254         $ImageWidth  = $Infos[0];
1255         $ImageHeight = $Infos[1];
1256         $Symbol      = imagecreatefromgif($DataDescription["Symbol"][$ColName]);
1257        }
1258
1259       $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1260       $Hsize = round($BigRadius/2);
1261       $R3 = -1; $G3 = -1; $B3 = -1;
1262       foreach ( $Data as $Key => $Values )
1263        {
1264         $Value = $Data[$Key][$ColName];
1265         $YPos  = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1266
1267         /* Save point into the image map if option activated */
1268         if ( $this->BuildMap )
1269          $this->addToImageMap($XPos-$Hsize,$YPos-$Hsize,$XPos+1+$Hsize,$YPos+$Hsize+1,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Plot");
1270
1271         if ( is_numeric($Value) )
1272          {
1273           if ( !isset($DataDescription["Symbol"][$ColName]) )
1274            {
1275
1276             if ( $Shadow )
1277              {
1278               if ( $R3 !=-1 && $G3 !=-1 && $B3 !=-1 )
1279                $this->drawFilledCircle($XPos+2,$YPos+2,$BigRadius,$R3,$G3,$B3);
1280               else
1281                {
1282                 $R3 = $this->Palette[$ColorID]["R"]-20; if ( $R3 < 0 ) { $R3 = 0; }
1283                 $G3 = $this->Palette[$ColorID]["G"]-20; if ( $G3 < 0 ) { $G3 = 0; }
1284                 $B3 = $this->Palette[$ColorID]["B"]-20; if ( $B3 < 0 ) { $B3 = 0; }
1285                 $this->drawFilledCircle($XPos+2,$YPos+2,$BigRadius,$R3,$G3,$B3);
1286                }
1287              }
1288
1289             $this->drawFilledCircle($XPos+1,$YPos+1,$BigRadius,$R,$G,$B);
1290
1291             if ( $SmallRadius != 0 )
1292              {
1293               if ( $R2 !=-1 && $G2 !=-1 && $B2 !=-1 )
1294                $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R2,$G2,$B2);
1295               else
1296                {
1297                 $R2 = $this->Palette[$ColorID]["R"]-15; if ( $R2 < 0 ) { $R2 = 0; }
1298                 $G2 = $this->Palette[$ColorID]["G"]-15; if ( $G2 < 0 ) { $G2 = 0; }
1299                 $B2 = $this->Palette[$ColorID]["B"]-15; if ( $B2 < 0 ) { $B2 = 0; }
1300
1301                 $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R2,$G2,$B2);
1302                }
1303              }
1304            }
1305           else
1306            {
1307             imagecopymerge($this->Picture,$Symbol,$XPos+1-$ImageWidth/2,$YPos+1-$ImageHeight/2,0,0,$ImageWidth,$ImageHeight,100);
1308            }
1309          }
1310
1311         $XPos = $XPos + $this->DivisionWidth;
1312        }
1313       $GraphID++;
1314      }
1315    }
1316
1317   /* This function draw a plot graph in an X/Y space */
1318   function drawXYPlotGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1,$Shadow=TRUE)
1319    {
1320     $R = $this->Palette[$PaletteID]["R"];
1321     $G = $this->Palette[$PaletteID]["G"];
1322     $B = $this->Palette[$PaletteID]["B"];
1323     $R3 = -1; $G3 = -1; $B3 = -1;
1324
1325     $YLast = -1; $XLast = -1;
1326     foreach ( $Data as $Key => $Values )
1327      {
1328       if ( isset($Data[$Key][$YSerieName]) && isset($Data[$Key][$XSerieName]) )
1329        {
1330         $X = $Data[$Key][$XSerieName];
1331         $Y = $Data[$Key][$YSerieName];
1332
1333         $Y = $this->GArea_Y2 - (($Y-$this->VMin) * $this->DivisionRatio);
1334         $X = $this->GArea_X1 + (($X-$this->VXMin) * $this->XDivisionRatio);
1335
1336
1337         if ( $Shadow )
1338          {
1339           if ( $R3 !=-1 && $G3 !=-1 && $B3 !=-1 )
1340            $this->drawFilledCircle($X+2,$Y+2,$BigRadius,$R3,$G3,$B3);
1341           else
1342            {
1343             $R3 = $this->Palette[$PaletteID]["R"]-20; if ( $R < 0 ) { $R = 0; }
1344             $G3 = $this->Palette[$PaletteID]["G"]-20; if ( $G < 0 ) { $G = 0; }
1345             $B3 = $this->Palette[$PaletteID]["B"]-20; if ( $B < 0 ) { $B = 0; }
1346             $this->drawFilledCircle($X+2,$Y+2,$BigRadius,$R3,$G3,$B3);
1347            }
1348          }
1349
1350         $this->drawFilledCircle($X+1,$Y+1,$BigRadius,$R,$G,$B);
1351
1352         if ( $R2 !=-1 && $G2 !=-1 && $B2 !=-1 )
1353          $this->drawFilledCircle($X+1,$Y+1,$SmallRadius,$R2,$G2,$B2);
1354         else
1355          {
1356           $R2 = $this->Palette[$PaletteID]["R"]+20; if ( $R > 255 ) { $R = 255; }
1357           $G2 = $this->Palette[$PaletteID]["G"]+20; if ( $G > 255 ) { $G = 255; }
1358           $B2 = $this->Palette[$PaletteID]["B"]+20; if ( $B > 255 ) { $B = 255; }
1359           $this->drawFilledCircle($X+1,$Y+1,$SmallRadius,$R2,$G2,$B2);
1360          }
1361        }
1362      }
1363
1364    }
1365
1366   /* This function draw an area between two series */
1367   function drawArea($Data,$Serie1,$Serie2,$R,$G,$B,$Alpha = 50)
1368    {
1369     /* Validate the Data and DataDescription array */
1370     $this->validateData("drawArea",$Data);
1371
1372     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1373     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1374
1375     $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1376     $C_White         =$this->AllocateColor($this->Layers[0],255,255,255);
1377     imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1378     imagecolortransparent($this->Layers[0],$C_White);
1379
1380     $C_Graph =$this->AllocateColor($this->Layers[0],$R,$G,$B);
1381
1382     $XPos     = $this->GAreaXOffset;
1383     $LastXPos = -1;
1384     foreach ( $Data as $Key => $Values )
1385      {
1386       $Value1 = $Data[$Key][$Serie1];
1387       $Value2 = $Data[$Key][$Serie2];
1388       $YPos1  = $LayerHeight - (($Value1-$this->VMin) * $this->DivisionRatio);
1389       $YPos2  = $LayerHeight - (($Value2-$this->VMin) * $this->DivisionRatio);
1390
1391       if ( $LastXPos != -1 )
1392        {
1393         $Points   = "";
1394         $Points[] = $LastXPos; $Points[] = $LastYPos1;
1395         $Points[] = $LastXPos; $Points[] = $LastYPos2;
1396         $Points[] = $XPos; $Points[] = $YPos2;
1397         $Points[] = $XPos; $Points[] = $YPos1;
1398
1399         imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph);
1400        }
1401
1402       $LastYPos1 = $YPos1;
1403       $LastYPos2 = $YPos2;
1404       $LastXPos  = $XPos;
1405
1406       $XPos = $XPos + $this->DivisionWidth;
1407      }
1408
1409     imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1410     imagedestroy($this->Layers[0]);
1411    }
1412
1413
1414   /* This function write the values of the specified series */
1415   function writeValues($Data,$DataDescription,$Series)
1416    {
1417     /* Validate the Data and DataDescription array */
1418     $this->validateDataDescription("writeValues",$DataDescription);
1419     $this->validateData("writeValues",$Data);
1420
1421     if ( !is_array($Series) ) { $Series = array($Series); }
1422
1423     foreach($Series as $Key => $Serie)
1424      {
1425       $ID = 0;
1426       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1427        { if ( $keyI == $Serie ) { $ColorID = $ID; }; $ID++; }
1428
1429       $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1430       $XLast = -1;
1431       foreach ( $Data as $Key => $Values )
1432        {
1433         if ( isset($Data[$Key][$Serie]) && is_numeric($Data[$Key][$Serie]))
1434          {
1435           $Value = $Data[$Key][$Serie];
1436           $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1437
1438           $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Value);
1439           $Width  = $Positions[2] - $Positions[6]; $XOffset = $XPos - ($Width/2);
1440           $Height = $Positions[3] - $Positions[7]; $YOffset = $YPos - 4;
1441
1442           $C_TextColor =$this->AllocateColor($this->Picture,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1443           imagettftext($this->Picture,$this->FontSize,0,$XOffset,$YOffset,$C_TextColor,$this->FontName,$Value);
1444          }
1445         $XPos = $XPos + $this->DivisionWidth;
1446        }
1447
1448      }
1449    }
1450
1451   /* This function draw a line graph */
1452   function drawLineGraph($Data,$DataDescription,$SerieName="")
1453    {
1454     /* Validate the Data and DataDescription array */
1455     $this->validateDataDescription("drawLineGraph",$DataDescription);
1456     $this->validateData("drawLineGraph",$Data);
1457
1458     $GraphID = 0;
1459     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1460      {
1461       $ID = 0;
1462       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1463        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1464
1465       if ( $SerieName == "" || $SerieName == $ColName )
1466        {
1467         $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1468         $XLast = -1;
1469         foreach ( $Data as $Key => $Values )
1470          {
1471           if ( isset($Data[$Key][$ColName]))
1472            {
1473             $Value = $Data[$Key][$ColName];
1474             $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1475
1476             /* Save point into the image map if option activated */
1477             if ( $this->BuildMap )
1478              $this->addToImageMap($XPos-3,$YPos-3,$XPos+3,$YPos+3,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Line");
1479
1480             if (!is_numeric($Value)) { $XLast = -1; }
1481             if ( $XLast != -1 )
1482              $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1483
1484             $XLast = $XPos;
1485             $YLast = $YPos;
1486             if (!is_numeric($Value)) { $XLast = -1; }
1487            }
1488           $XPos = $XPos + $this->DivisionWidth;
1489          }
1490         $GraphID++;
1491        }
1492      }
1493    }
1494
1495   /* This function draw a line graph */
1496   function drawXYGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0)
1497    {
1498     $YLast = -1; $XLast = -1;
1499     foreach ( $Data as $Key => $Values )
1500      {
1501       if ( isset($Data[$Key][$YSerieName]) && isset($Data[$Key][$XSerieName]) )
1502        {
1503         $X = $Data[$Key][$XSerieName];
1504         $Y = $Data[$Key][$YSerieName];
1505
1506         $Y = $this->GArea_Y2 - (($Y-$this->VMin) * $this->DivisionRatio);
1507         $X = $this->GArea_X1 + (($X-$this->VXMin) * $this->XDivisionRatio);
1508
1509         if ($XLast != -1 && $YLast != -1)
1510          {
1511           $this->drawLine($XLast,$YLast,$X,$Y,$this->Palette[$PaletteID]["R"],$this->Palette[$PaletteID]["G"],$this->Palette[$PaletteID]["B"],TRUE);
1512          }
1513
1514         $XLast = $X;
1515         $YLast = $Y;
1516        }
1517      }
1518    }
1519
1520   /* This function draw a cubic curve */
1521   function drawCubicCurve($Data,$DataDescription,$Accuracy=.1,$SerieName="")
1522    {
1523     /* Validate the Data and DataDescription array */
1524     $this->validateDataDescription("drawCubicCurve",$DataDescription);
1525     $this->validateData("drawCubicCurve",$Data);
1526
1527     $GraphID = 0;
1528     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1529      {
1530       if ( $SerieName == "" || $SerieName == $ColName )
1531        {
1532         $XIn = ""; $Yin = ""; $Yt = ""; $U = "";
1533         $XIn[0] = 0; $YIn[0] = 0;
1534
1535         $ID = 0;
1536         foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1537          { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1538
1539         $Index = 1;
1540         $XLast = -1; $Missing = "";
1541         foreach ( $Data as $Key => $Values )
1542          {
1543           if ( isset($Data[$Key][$ColName]) )
1544            {
1545             $Value = $Data[$Key][$ColName];
1546             $XIn[$Index] = $Index;
1547             $YIn[$Index] = $Value;
1548             if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; }
1549             $Index++;
1550            }
1551          }
1552         $Index--;
1553
1554         $Yt[0] = 0;
1555         $Yt[1] = 0;
1556         $U[1]  = 0;
1557         for($i=2;$i<=$Index-1;$i++)
1558          {
1559           $Sig    = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]);
1560           $p      = $Sig * $Yt[$i-1] + 2;
1561           $Yt[$i] = ($Sig - 1) / $p;
1562           $U[$i]  = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]);
1563           $U[$i]  = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p;
1564          }
1565
1566         $qn = 0;
1567         $un = 0;
1568         $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1);
1569
1570         for($k=$Index-1;$k>=1;$k--)
1571          $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k];
1572
1573         $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1574         for($X=1;$X<=$Index;$X=$X+$Accuracy)
1575          {
1576           $klo = 1;
1577           $khi = $Index;
1578           $k   = $khi - $klo;
1579           while($k > 1)
1580            {
1581             $k = $khi - $klo;
1582             If ( $XIn[$k] >= $X )
1583              $khi = $k;
1584             else
1585              $klo = $k;
1586            }
1587           $klo = $khi - 1;
1588
1589           $h     = $XIn[$khi] - $XIn[$klo];
1590           $a     = ($XIn[$khi] - $X) / $h;
1591           $b     = ($X - $XIn[$klo]) / $h;
1592           $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6;
1593
1594           $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1595
1596           if ( $XLast != -1 && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]) )
1597            $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1598
1599           $XLast = $XPos;
1600           $YLast = $YPos;
1601           $XPos  = $XPos + $this->DivisionWidth * $Accuracy;
1602          }
1603
1604         // Add potentialy missing values
1605         $XPos  = $XPos - $this->DivisionWidth * $Accuracy;
1606         if ( $XPos < ($this->GArea_X2 - $this->GAreaXOffset) )
1607          {
1608           $YPos = $this->GArea_Y2 - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio);
1609           $this->drawLine($XLast,$YLast,$this->GArea_X2-$this->GAreaXOffset,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1610          }
1611
1612         $GraphID++;
1613        }
1614      }
1615    }
1616
1617   /* This function draw a filled cubic curve */
1618   function drawFilledCubicCurve($Data,$DataDescription,$Accuracy=.1,$Alpha=100,$AroundZero=FALSE)
1619    {
1620     /* Validate the Data and DataDescription array */
1621     $this->validateDataDescription("drawFilledCubicCurve",$DataDescription);
1622     $this->validateData("drawFilledCubicCurve",$Data);
1623
1624     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1625     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1626     $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1627     if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; }
1628
1629     $GraphID = 0;
1630     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1631      {
1632       $XIn = ""; $Yin = ""; $Yt = ""; $U = "";
1633       $XIn[0] = 0; $YIn[0] = 0;
1634
1635       $ID = 0;
1636       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1637        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1638
1639       $Index = 1;
1640       $XLast = -1; $Missing = "";
1641       foreach ( $Data as $Key => $Values )
1642        {
1643         $Value = $Data[$Key][$ColName];
1644         $XIn[$Index] = $Index;
1645         $YIn[$Index] = $Value;
1646         if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; }
1647         $Index++;
1648        }
1649       $Index--;
1650
1651       $Yt[0] = 0;
1652       $Yt[1] = 0;
1653       $U[1]  = 0;
1654       for($i=2;$i<=$Index-1;$i++)
1655        {
1656         $Sig    = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]);
1657         $p      = $Sig * $Yt[$i-1] + 2;
1658         $Yt[$i] = ($Sig - 1) / $p;
1659         $U[$i]  = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]);
1660         $U[$i]  = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p;
1661        }
1662
1663       $qn = 0;
1664       $un = 0;
1665       $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1);
1666
1667       for($k=$Index-1;$k>=1;$k--)
1668        $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k];
1669
1670       $Points   = "";
1671       $Points[] = $this->GAreaXOffset;
1672       $Points[] = $LayerHeight;
1673
1674       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1675       $C_White         =$this->AllocateColor($this->Layers[0],255,255,255);
1676       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1677       imagecolortransparent($this->Layers[0],$C_White);
1678
1679       $YLast = NULL;
1680       $XPos  = $this->GAreaXOffset; $PointsCount = 2;
1681       for($X=1;$X<=$Index;$X=$X+$Accuracy)
1682        {
1683         $klo = 1;
1684         $khi = $Index;
1685         $k   = $khi - $klo;
1686         while($k > 1)
1687          {
1688           $k = $khi - $klo;
1689           If ( $XIn[$k] >= $X )
1690            $khi = $k;
1691           else
1692            $klo = $k;
1693          }
1694         $klo = $khi - 1;
1695
1696         $h     = $XIn[$khi] - $XIn[$klo];
1697         $a     = ($XIn[$khi] - $X) / $h;
1698         $b     = ($X - $XIn[$klo]) / $h;
1699         $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6;
1700
1701         $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1702
1703         if ( $YLast != NULL && $AroundZero && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]))
1704          {
1705           $aPoints   = "";
1706           $aPoints[] = $XLast;
1707           $aPoints[] = $YLast;
1708           $aPoints[] = $XPos;
1709           $aPoints[] = $YPos;
1710           $aPoints[] = $XPos;
1711           $aPoints[] = $YZero;
1712           $aPoints[] = $XLast;
1713           $aPoints[] = $YZero;
1714
1715           $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1716           imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph);
1717          }
1718
1719         if ( !isset($Missing[floor($X)]) || $YLast == NULL )
1720          {
1721           $PointsCount++;
1722           $Points[] = $XPos;
1723           $Points[] = $YPos;
1724          }
1725         else
1726          {
1727           $PointsCount++; $Points[] = $XLast; $Points[] = $LayerHeight;
1728          }
1729
1730         $YLast = $YPos; $XLast = $XPos;
1731         $XPos  = $XPos + $this->DivisionWidth * $Accuracy;
1732        }
1733
1734       // Add potentialy missing values
1735       $XPos  = $XPos - $this->DivisionWidth * $Accuracy;
1736       if ( $XPos < ($LayerWidth-$this->GAreaXOffset) )
1737        {
1738         $YPos = $LayerHeight - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio);
1739
1740         if ( $YLast != NULL && $AroundZero )
1741          {
1742           $aPoints   = "";
1743           $aPoints[] = $XLast;
1744           $aPoints[] = $YLast;
1745           $aPoints[] = $LayerWidth-$this->GAreaXOffset;
1746           $aPoints[] = $YPos;
1747           $aPoints[] = $LayerWidth-$this->GAreaXOffset;
1748           $aPoints[] = $YZero;
1749           $aPoints[] = $XLast;
1750           $aPoints[] = $YZero;
1751
1752           $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1753           imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph);
1754          }
1755
1756         if ( $YIn[$klo] != "" && $YIn[$khi] != "" || $YLast == NULL )
1757          {
1758           $PointsCount++;
1759           $Points[] = $LayerWidth-$this->GAreaXOffset;
1760           $Points[] = $YPos;
1761          }
1762        }
1763
1764       $Points[] = $LayerWidth-$this->GAreaXOffset;
1765       $Points[] = $LayerHeight;
1766
1767       if ( !$AroundZero )
1768        {
1769         $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1770         imagefilledpolygon($this->Layers[0],$Points,$PointsCount,$C_Graph);
1771        }
1772
1773       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1774       imagedestroy($this->Layers[0]);
1775
1776       $this->drawCubicCurve($Data,$DataDescription,$Accuracy,$ColName);
1777
1778       $GraphID++;
1779      }
1780    }
1781
1782   /* This function draw a filled line graph */
1783   function drawFilledLineGraph($Data,$DataDescription,$Alpha=100,$AroundZero=FALSE)
1784    {
1785     $Empty = -2147483647;
1786
1787     /* Validate the Data and DataDescription array */
1788     $this->validateDataDescription("drawFilledLineGraph",$DataDescription);
1789     $this->validateData("drawFilledLineGraph",$Data);
1790
1791     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1792     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1793
1794     $GraphID = 0;
1795     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1796      {
1797       $ID = 0;
1798       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1799        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1800
1801       $aPoints   = "";
1802       $aPoints[] = $this->GAreaXOffset;
1803       $aPoints[] = $LayerHeight;
1804
1805       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1806       $C_White         = $this->AllocateColor($this->Layers[0],255,255,255);
1807       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1808       imagecolortransparent($this->Layers[0],$C_White);
1809
1810       $XPos  = $this->GAreaXOffset;
1811       $XLast = -1; $PointsCount = 2;
1812       $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1813       if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; }
1814
1815       $YLast = $Empty;
1816       foreach ( $Data as $Key => $Values )
1817        {
1818         $Value = $Data[$Key][$ColName];
1819         $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1820
1821         /* Save point into the image map if option activated */
1822         if ( $this->BuildMap )
1823          $this->addToImageMap($XPos-3,$YPos-3,$XPos+3,$YPos+3,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"FLine");
1824
1825         if ( !is_numeric($Value) )
1826          {
1827           $PointsCount++;
1828           $aPoints[] = $XLast;
1829           $aPoints[] = $LayerHeight;
1830
1831           $YLast = $Empty;
1832          }
1833         else
1834          {
1835           $PointsCount++;
1836           if ( $YLast <> $Empty )
1837            { $aPoints[] = $XPos; $aPoints[] = $YPos; }
1838           else
1839            { $PointsCount++; $aPoints[] = $XPos; $aPoints[] = $LayerHeight; $aPoints[] = $XPos; $aPoints[] = $YPos; }
1840
1841           if ($YLast <> $Empty && $AroundZero)
1842            {
1843             $Points   = "";
1844             $Points[] = $XLast; $Points[] = $YLast;
1845             $Points[] = $XPos;
1846             $Points[] = $YPos;
1847             $Points[] = $XPos;
1848             $Points[] = $YZero;
1849             $Points[] = $XLast;
1850             $Points[] = $YZero;
1851
1852             $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1853             imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph);
1854            }
1855           $YLast = $YPos;
1856          }
1857
1858         $XLast = $XPos;
1859         $XPos  = $XPos + $this->DivisionWidth;
1860        }
1861       $aPoints[] = $LayerWidth - $this->GAreaXOffset;
1862       $aPoints[] = $LayerHeight;
1863
1864       if ( $AroundZero == FALSE )
1865        {
1866         $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1867         imagefilledpolygon($this->Layers[0],$aPoints,$PointsCount,$C_Graph);
1868        }
1869
1870       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1871       imagedestroy($this->Layers[0]);
1872       $GraphID++;
1873       $this->drawLineGraph($Data,$DataDescription,$ColName);
1874      }
1875    }
1876
1877   /* This function draw a bar graph */
1878   function drawOverlayBarGraph($Data,$DataDescription,$Alpha=50)
1879    {
1880     /* Validate the Data and DataDescription array */
1881     $this->validateDataDescription("drawOverlayBarGraph",$DataDescription);
1882     $this->validateData("drawOverlayBarGraph",$Data);
1883
1884     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1885     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1886
1887     $GraphID = 0;
1888     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1889      {
1890       $ID = 0;
1891       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1892        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1893
1894       $this->Layers[$GraphID] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1895       $C_White                = $this->AllocateColor($this->Layers[$GraphID],255,255,255);
1896       $C_Graph                = $this->AllocateColor($this->Layers[$GraphID],$this->Palette[$GraphID]["R"],$this->Palette[$GraphID]["G"],$this->Palette[$GraphID]["B"]);
1897       imagefilledrectangle($this->Layers[$GraphID],0,0,$LayerWidth,$LayerHeight,$C_White);
1898       imagecolortransparent($this->Layers[$GraphID],$C_White);
1899
1900       $XWidth = $this->DivisionWidth / 4;
1901       $XPos   = $this->GAreaXOffset;
1902       $YZero  = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1903       $XLast  = -1; $PointsCount = 2;
1904       foreach ( $Data as $Key => $Values )
1905        {
1906         if ( isset($Data[$Key][$ColName]) )
1907          {
1908           $Value = $Data[$Key][$ColName];
1909           if ( is_numeric($Value) )
1910            {
1911             $YPos  = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1912
1913             imagefilledrectangle($this->Layers[$GraphID],$XPos-$XWidth,$YPos,$XPos+$XWidth,$YZero,$C_Graph);
1914
1915             $X1 = floor($XPos - $XWidth + $this->GArea_X1); $Y1 = floor($YPos+$this->GArea_Y1) + .2;
1916             $X2 = floor($XPos + $XWidth + $this->GArea_X1); $Y2 = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1917             if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; }
1918             if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; }
1919
1920             /* Save point into the image map if option activated */
1921             if ( $this->BuildMap )
1922              $this->addToImageMap($X1,min($Y1,$Y2),$X2,max($Y1,$Y2),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"oBar");
1923
1924             $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1925            }
1926          }
1927         $XPos = $XPos + $this->DivisionWidth;
1928        }
1929
1930       $GraphID++;
1931      }
1932
1933     for($i=0;$i<=($GraphID-1);$i++)
1934      {
1935       imagecopymerge($this->Picture,$this->Layers[$i],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1936       imagedestroy($this->Layers[$i]);
1937      }
1938    }
1939
1940   /* This function draw a bar graph */
1941   function drawBarGraph($Data,$DataDescription,$Shadow=FALSE,$Alpha=100)
1942    {
1943     /* Validate the Data and DataDescription array */
1944     $this->validateDataDescription("drawBarGraph",$DataDescription);
1945     $this->validateData("drawBarGraph",$Data);
1946
1947     $GraphID      = 0;
1948     $Series       = count($DataDescription["Values"]);
1949     $SeriesWidth  = $this->DivisionWidth / ($Series+1);
1950     $SerieXOffset = $this->DivisionWidth / 2 - $SeriesWidth / 2;
1951
1952     $YZero  = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1953     if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; }
1954
1955     $SerieID = 0;
1956     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1957      {
1958       $ID = 0;
1959       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1960        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1961
1962       $XPos  = $this->GArea_X1 + $this->GAreaXOffset - $SerieXOffset + $SeriesWidth * $SerieID;
1963       $XLast = -1;
1964       foreach ( $Data as $Key => $Values )
1965        {
1966         if ( isset($Data[$Key][$ColName]))
1967          {
1968           if ( is_numeric($Data[$Key][$ColName]) )
1969            {
1970             $Value = $Data[$Key][$ColName];
1971             $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1972
1973             /* Save point into the image map if option activated */
1974             if ( $this->BuildMap )
1975              {
1976               $this->addToImageMap($XPos+1,min($YZero,$YPos),$XPos+$SeriesWidth-1,max($YZero,$YPos),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Bar");
1977              }
1978
1979             if ( $Shadow && $Alpha == 100 )
1980              $this->drawRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,25,25,25,TRUE,$Alpha);
1981
1982             $this->drawFilledRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha);
1983            }
1984          }
1985         $XPos = $XPos + $this->DivisionWidth;
1986        }
1987       $SerieID++;
1988      }
1989    }
1990
1991   /* This function draw a stacked bar graph */
1992   function drawStackedBarGraph($Data,$DataDescription,$Alpha=50,$Contiguous=FALSE)
1993    {
1994     /* Validate the Data and DataDescription array */
1995     $this->validateDataDescription("drawBarGraph",$DataDescription);
1996     $this->validateData("drawBarGraph",$Data);
1997
1998     $GraphID      = 0;
1999     $Series       = count($DataDescription["Values"]);
2000     if ( $Contiguous )
2001      $SeriesWidth  = $this->DivisionWidth;
2002     else
2003      $SeriesWidth  = $this->DivisionWidth * .8;
2004
2005     $YZero  = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
2006     if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; }
2007
2008     $SerieID = 0; $LastValue = "";
2009     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2010      {
2011       $ID = 0;
2012       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2013        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2014
2015       $XPos  = $this->GArea_X1 + $this->GAreaXOffset - $SeriesWidth / 2;
2016       $XLast = -1;
2017       foreach ( $Data as $Key => $Values )
2018        {
2019         if ( isset($Data[$Key][$ColName]))
2020          {
2021           if ( is_numeric($Data[$Key][$ColName]) )
2022            {
2023             $Value = $Data[$Key][$ColName];
2024
2025             if ( isset($LastValue[$Key]) )
2026              {
2027               $YPos    = $this->GArea_Y2 - ((($Value+$LastValue[$Key])-$this->VMin) * $this->DivisionRatio);
2028               $YBottom = $this->GArea_Y2 - (($LastValue[$Key]-$this->VMin) * $this->DivisionRatio);
2029               $LastValue[$Key] += $Value;
2030              }
2031             else
2032              {
2033               $YPos    = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
2034               $YBottom = $YZero;
2035               $LastValue[$Key] = $Value;
2036              }
2037
2038             /* Save point into the image map if option activated */
2039             if ( $this->BuildMap )
2040              $this->addToImageMap($XPos+1,min($YBottom,$YPos),$XPos+$SeriesWidth-1,max($YBottom,$YPos),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"sBar");
2041
2042             $this->drawFilledRectangle($XPos+1,$YBottom,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha);
2043            }
2044          }
2045         $XPos = $XPos + $this->DivisionWidth;
2046        }
2047       $SerieID++;
2048      }
2049    }
2050
2051   /* This function draw a limits bar graphs */
2052   function drawLimitsGraph($Data,$DataDescription,$R=0,$G=0,$B=0)
2053    {
2054     /* Validate the Data and DataDescription array */
2055     $this->validateDataDescription("drawLimitsGraph",$DataDescription);
2056     $this->validateData("drawLimitsGraph",$Data);
2057
2058     $XWidth = $this->DivisionWidth / 4;
2059     $XPos   = $this->GArea_X1 + $this->GAreaXOffset;
2060
2061     foreach ( $Data as $Key => $Values )
2062      {
2063       $Min     = $Data[$Key][$DataDescription["Values"][0]];
2064       $Max     = $Data[$Key][$DataDescription["Values"][0]];
2065       $GraphID = 0; $MaxID = 0; $MinID = 0;
2066       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2067        {
2068         if ( isset($Data[$Key][$ColName]) )
2069          {
2070           if ( $Data[$Key][$ColName] > $Max && is_numeric($Data[$Key][$ColName]))
2071            { $Max = $Data[$Key][$ColName]; $MaxID = $GraphID; }
2072          }
2073         if ( isset($Data[$Key][$ColName]) && is_numeric($Data[$Key][$ColName]))
2074          {
2075           if ( $Data[$Key][$ColName] < $Min )
2076            { $Min = $Data[$Key][$ColName]; $MinID = $GraphID; }
2077           $GraphID++;
2078          }
2079        }
2080
2081       $YPos = $this->GArea_Y2 - (($Max-$this->VMin) * $this->DivisionRatio);
2082       $X1 = floor($XPos - $XWidth); $Y1 = floor($YPos) - .2;
2083       $X2 = floor($XPos + $XWidth);
2084       if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; }
2085       if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; }
2086
2087       $YPos = $this->GArea_Y2 - (($Min-$this->VMin) * $this->DivisionRatio);
2088       $Y2 = floor($YPos) + .2;
2089
2090       $this->drawLine(floor($XPos)-.2,$Y1+1,floor($XPos)-.2,$Y2-1,$R,$G,$B,TRUE);
2091       $this->drawLine(floor($XPos)+.2,$Y1+1,floor($XPos)+.2,$Y2-1,$R,$G,$B,TRUE);
2092       $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$MaxID]["R"],$this->Palette[$MaxID]["G"],$this->Palette[$MaxID]["B"],FALSE);
2093       $this->drawLine($X1,$Y2,$X2,$Y2,$this->Palette[$MinID]["R"],$this->Palette[$MinID]["G"],$this->Palette[$MinID]["B"],FALSE);
2094
2095       $XPos = $XPos + $this->DivisionWidth;
2096      }
2097    }
2098
2099   /* This function draw radar axis centered on the graph area */
2100   function drawRadarAxis($Data,$DataDescription,$Mosaic=TRUE,$BorderOffset=10,$A_R=60,$A_G=60,$A_B=60,$S_R=200,$S_G=200,$S_B=200,$MaxValue=-1)
2101    {
2102     /* Validate the Data and DataDescription array */
2103     $this->validateDataDescription("drawRadarAxis",$DataDescription);
2104     $this->validateData("drawRadarAxis",$Data);
2105
2106     $C_TextColor = $this->AllocateColor($this->Picture,$A_R,$A_G,$A_B);
2107
2108     /* Draw radar axis */
2109     $Points  = count($Data);
2110     $Radius  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2111     $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1;
2112     $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1;
2113
2114     /* Search for the max value */
2115     if ( $MaxValue == -1 )
2116      {
2117       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2118        {
2119         foreach ( $Data as $Key => $Values )
2120          {
2121           if ( isset($Data[$Key][$ColName]))
2122            if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; }
2123          }
2124        }
2125      }
2126
2127     /* Draw the mosaic */
2128     if ( $Mosaic )
2129      {
2130       $RadiusScale = $Radius / $MaxValue;
2131       for ( $t=1; $t<=$MaxValue-1; $t++)
2132        {
2133         $TRadius  = $RadiusScale * $t;
2134         $LastX1   = -1;
2135
2136         for ( $i=0; $i<=$Points; $i++)
2137          {
2138           $Angle = -90 + $i * 360/$Points;
2139           $X1 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2140           $Y1 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2141           $X2 = cos($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $XCenter;
2142           $Y2 = sin($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $YCenter;
2143
2144           if ( $t % 2 == 1 && $LastX1 != -1)
2145            {
2146             $Plots   = "";
2147             $Plots[] = $X1; $Plots[] = $Y1;
2148             $Plots[] = $X2; $Plots[] = $Y2;
2149             $Plots[] = $LastX2; $Plots[] = $LastY2;
2150             $Plots[] = $LastX1; $Plots[] = $LastY1;
2151
2152             $C_Graph = $this->AllocateColor($this->Picture,250,250,250);
2153             imagefilledpolygon($this->Picture,$Plots,(count($Plots)+1)/2,$C_Graph);
2154            }
2155
2156           $LastX1 = $X1; $LastY1= $Y1;
2157           $LastX2 = $X2; $LastY2= $Y2;
2158          }
2159        }
2160      }
2161
2162
2163     /* Draw the spider web */
2164     for ( $t=1; $t<=$MaxValue; $t++)
2165      {
2166       $TRadius = ( $Radius / $MaxValue ) * $t;
2167       $LastX   = -1;
2168
2169       for ( $i=0; $i<=$Points; $i++)
2170        {
2171         $Angle = -90 + $i * 360/$Points;
2172         $X = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2173         $Y = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2174
2175         if ( $LastX != -1 )
2176          $this->drawDottedLine($LastX,$LastY,$X,$Y,4,$S_R,$S_G,$S_B);
2177
2178         $LastX = $X; $LastY= $Y;
2179        }
2180      }
2181
2182     /* Draw the axis */
2183     for ( $i=0; $i<=$Points; $i++)
2184      {
2185       $Angle = -90 + $i * 360/$Points;
2186       $X = cos($Angle * 3.1418 / 180 ) * $Radius + $XCenter;
2187       $Y = sin($Angle * 3.1418 / 180 ) * $Radius + $YCenter;
2188
2189       $this->drawLine($XCenter,$YCenter,$X,$Y,$A_R,$A_G,$A_B);
2190
2191       $XOffset = 0; $YOffset = 0;
2192       if (isset($Data[$i][$DataDescription["Position"]]))
2193        {
2194         $Label = $Data[$i][$DataDescription["Position"]];
2195
2196         $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Label);
2197         $Width  = $Positions[2] - $Positions[6];
2198         $Height = $Positions[3] - $Positions[7];
2199
2200         if ( $Angle >= 0 && $Angle <= 90 )
2201          $YOffset = $Height;
2202
2203         if ( $Angle > 90 && $Angle <= 180 )
2204          { $YOffset = $Height; $XOffset = -$Width; }
2205
2206         if ( $Angle > 180 && $Angle <= 270 )
2207          { $XOffset = -$Width; }
2208
2209         imagettftext($this->Picture,$this->FontSize,0,$X+$XOffset,$Y+$YOffset,$C_TextColor,$this->FontName,$Label);
2210        }
2211      }
2212
2213     /* Write the values */
2214     for ( $t=1; $t<=$MaxValue; $t++)
2215      {
2216       $TRadius = ( $Radius / $MaxValue ) * $t;
2217
2218       $Angle = -90 + 360 / $Points;
2219       $X1 = $XCenter;
2220       $Y1 = $YCenter - $TRadius;
2221       $X2 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2222       $Y2 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2223
2224       $XPos = floor(($X2-$X1)/2) + $X1;
2225       $YPos = floor(($Y2-$Y1)/2) + $Y1;
2226
2227       $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$t);
2228       $X = $XPos - ( $X+$Positions[2] - $X+$Positions[6] ) / 2;
2229       $Y = $YPos + $this->FontSize;
2230
2231       $this->drawFilledRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,240,240,240);
2232       $this->drawRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,220,220,220);
2233       imagettftext($this->Picture,$this->FontSize,0,$X,$Y,$C_TextColor,$this->FontName,$t);
2234      }
2235    }
2236
2237   /* This function draw a radar graph centered on the graph area */
2238   function drawRadar($Data,$DataDescription,$BorderOffset=10,$MaxValue=-1)
2239    {
2240     /* Validate the Data and DataDescription array */
2241     $this->validateDataDescription("drawRadar",$DataDescription);
2242     $this->validateData("drawRadar",$Data);
2243
2244     $Points  = count($Data);
2245     $Radius  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2246     $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1;
2247     $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1;
2248
2249     /* Search for the max value */
2250     if ( $MaxValue == -1 )
2251      {
2252       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2253        {
2254         foreach ( $Data as $Key => $Values )
2255          {
2256           if ( isset($Data[$Key][$ColName]))
2257            if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; }
2258          }
2259        }
2260      }
2261
2262     $GraphID = 0;
2263     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2264      {
2265       $ID = 0;
2266       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2267        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2268
2269       $Angle = -90;
2270       $XLast = -1;
2271       foreach ( $Data as $Key => $Values )
2272        {
2273         if ( isset($Data[$Key][$ColName]))
2274          {
2275           $Value    = $Data[$Key][$ColName];
2276           $Strength = ( $Radius / $MaxValue ) * $Value;
2277
2278           $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter;
2279           $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter;
2280
2281           if ( $XLast != -1 )
2282            $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2283
2284           if ( $XLast == -1 )
2285            { $FirstX = $XPos; $FirstY = $YPos; }
2286
2287           $Angle = $Angle + (360/$Points);
2288           $XLast = $XPos;
2289           $YLast = $YPos;
2290          }
2291        }
2292       $this->drawLine($XPos,$YPos,$FirstX,$FirstY,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2293       $GraphID++;
2294      }
2295    }
2296
2297   /* This function draw a radar graph centered on the graph area */
2298   function drawFilledRadar($Data,$DataDescription,$Alpha=50,$BorderOffset=10,$MaxValue=-1)
2299    {
2300     /* Validate the Data and DataDescription array */
2301     $this->validateDataDescription("drawFilledRadar",$DataDescription);
2302     $this->validateData("drawFilledRadar",$Data);
2303
2304     $Points      = count($Data);
2305     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
2306     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
2307     $Radius      = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2308     $XCenter     = ( $this->GArea_X2 - $this->GArea_X1 ) / 2;
2309     $YCenter     = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2;
2310
2311     /* Search for the max value */
2312     if ( $MaxValue == -1 )
2313      {
2314       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2315        {
2316         foreach ( $Data as $Key => $Values )
2317          {
2318           if ( isset($Data[$Key][$ColName]))
2319            if ( $Data[$Key][$ColName] > $MaxValue && is_numeric($Data[$Key][$ColName])) { $MaxValue = $Data[$Key][$ColName]; }
2320          }
2321        }
2322      }
2323
2324     $GraphID = 0;
2325     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2326      {
2327       $ID = 0;
2328       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2329        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2330
2331       $Angle = -90;
2332       $XLast = -1;
2333       $Plots = "";
2334       foreach ( $Data as $Key => $Values )
2335        {
2336         if ( isset($Data[$Key][$ColName]))
2337          {
2338           $Value    = $Data[$Key][$ColName];
2339           if ( !is_numeric($Value) ) { $Value = 0; }
2340           $Strength = ( $Radius / $MaxValue ) * $Value;
2341
2342           $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter;
2343           $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter;
2344
2345           $Plots[] = $XPos;
2346           $Plots[] = $YPos;
2347
2348           $Angle = $Angle + (360/$Points);
2349           $XLast = $XPos;
2350           $YLast = $YPos;
2351          }
2352        }
2353
2354       if (isset($Plots[0]))
2355        {
2356         $Plots[] = $Plots[0];
2357         $Plots[] = $Plots[1];
2358
2359         $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
2360         $C_White         = $this->AllocateColor($this->Layers[0],255,255,255);
2361         imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
2362         imagecolortransparent($this->Layers[0],$C_White);
2363
2364         $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2365         imagefilledpolygon($this->Layers[0],$Plots,(count($Plots)+1)/2,$C_Graph);
2366
2367         imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
2368         imagedestroy($this->Layers[0]);
2369
2370         for($i=0;$i<=count($Plots)-4;$i=$i+2)
2371          $this->drawLine($Plots[$i]+$this->GArea_X1,$Plots[$i+1]+$this->GArea_Y1,$Plots[$i+2]+$this->GArea_X1,$Plots[$i+3]+$this->GArea_Y1,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2372        }
2373
2374       $GraphID++;
2375      }
2376    }
2377
2378   /* This function draw a flat pie chart */
2379   function drawBasicPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$R=255,$G=255,$B=255,$Decimals=0)
2380    {
2381     /* Validate the Data and DataDescription array */
2382     $this->validateDataDescription("drawBasicPieGraph",$DataDescription,FALSE);
2383     $this->validateData("drawBasicPieGraph",$Data);
2384
2385     /* Determine pie sum */
2386     $Series = 0; $PieSum = 0;
2387     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2388      {
2389       if ( $ColName != $DataDescription["Position"] )
2390        {
2391         $Series++;
2392         foreach ( $Data as $Key => $Values )
2393          {
2394           if ( isset($Data[$Key][$ColName]))
2395            $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]];
2396          }
2397        }
2398      }
2399
2400     /* Validate serie */
2401     if ( $Series != 1 )
2402      RaiseFatal("Pie chart can only accept one serie of data.");
2403
2404     $SpliceRatio         = 360 / $PieSum;
2405     $SplicePercent       = 100 / $PieSum;
2406
2407     /* Calculate all polygons */
2408     $Angle    = 0; $TopPlots = "";
2409     foreach($iValues as $Key => $Value)
2410      {
2411       $TopPlots[$Key][] = $XPos;
2412       $TopPlots[$Key][] = $YPos;
2413
2414       /* Process labels position & size */
2415       $Caption = "";
2416       if ( !($DrawLabels == PIE_NOLABEL) )
2417        {
2418         $TAngle   = $Angle+($Value*$SpliceRatio/2);
2419         if ($DrawLabels == PIE_PERCENTAGE)
2420          $Caption  = (round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2421         elseif ($DrawLabels == PIE_LABELS)
2422          $Caption  = $iLabels[$Key];
2423         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2424          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2425         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2426          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2427
2428         $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2429         $TextWidth  = $Position[2]-$Position[0];
2430         $TextHeight = abs($Position[1])+abs($Position[3]);
2431
2432         $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius+10) + $XPos;
2433
2434         if ( $TAngle > 0 && $TAngle < 180 )
2435          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+10) + $YPos + 4;
2436         else
2437          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+4) + $YPos - ($TextHeight/2);
2438
2439         if ( $TAngle > 90 && $TAngle < 270 )
2440          $TX = $TX - $TextWidth;
2441
2442         $C_TextColor = $this->AllocateColor($this->Picture,70,70,70);
2443         imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2444        }
2445
2446       /* Process pie slices */
2447       for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
2448        {
2449         $TopX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos;
2450         $TopY = sin($iAngle * 3.1418 / 180 ) * $Radius + $YPos;
2451
2452         $TopPlots[$Key][] = $TopX;
2453         $TopPlots[$Key][] = $TopY;
2454        }
2455
2456       $TopPlots[$Key][] = $XPos;
2457       $TopPlots[$Key][] = $YPos;
2458
2459       $Angle = $iAngle;
2460      }
2461     $PolyPlots = $TopPlots;
2462
2463     /* Set array values type to float --- PHP Bug with imagefilledpolygon casting to integer */
2464     foreach ($TopPlots as $Key => $Value)
2465      { foreach ($TopPlots[$Key] as $Key2 => $Value2) { settype($TopPlots[$Key][$Key2],"float"); } }
2466
2467     /* Draw Top polygons */
2468     foreach ($PolyPlots as $Key => $Value)
2469      {
2470       $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
2471       imagefilledpolygon($this->Picture,$PolyPlots[$Key],(count($PolyPlots[$Key])+1)/2,$C_GraphLo);
2472      }
2473
2474     $this->drawCircle($XPos-.5,$YPos-.5,$Radius,$R,$G,$B);
2475     $this->drawCircle($XPos-.5,$YPos-.5,$Radius+.5,$R,$G,$B);
2476
2477     /* Draw Top polygons */
2478     foreach ($TopPlots as $Key => $Value)
2479      {
2480       for($j=0;$j<=count($TopPlots[$Key])-4;$j=$j+2)
2481        $this->drawLine($TopPlots[$Key][$j],$TopPlots[$Key][$j+1],$TopPlots[$Key][$j+2],$TopPlots[$Key][$j+3],$R,$G,$B);
2482      }
2483    }
2484
2485   function drawFlatPieGraphWithShadow($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals=0)
2486    {
2487     $this->drawFlatPieGraph($Data,$DataDescription,$XPos+$this->ShadowXDistance,$YPos+$this->ShadowYDistance,$Radius,PIE_NOLABEL,$SpliceDistance,$Decimals,TRUE);
2488     $this->drawFlatPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius,$DrawLabels,$SpliceDistance,$Decimals,FALSE);
2489    }
2490
2491   /* This function draw a flat pie chart */
2492   function drawFlatPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals=0,$AllBlack=FALSE)
2493    {
2494     /* Validate the Data and DataDescription array */
2495     $this->validateDataDescription("drawFlatPieGraph",$DataDescription,FALSE);
2496     $this->validateData("drawFlatPieGraph",$Data);
2497
2498     $ShadowStatus = $this->ShadowActive ; $this->ShadowActive = FALSE;
2499
2500     /* Determine pie sum */
2501     $Series = 0; $PieSum = 0;
2502     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2503      {
2504       if ( $ColName != $DataDescription["Position"] )
2505        {
2506         $Series++;
2507         foreach ( $Data as $Key => $Values )
2508          {
2509           if ( isset($Data[$Key][$ColName]))
2510            $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]];
2511          }
2512        }
2513      }
2514
2515     /* Validate serie */
2516     if ( $Series != 1 )
2517      {
2518       RaiseFatal("Pie chart can only accept one serie of data.");
2519       return(0);
2520      }
2521
2522     $SpliceRatio   = 360 / $PieSum;
2523     $SplicePercent = 100 / $PieSum;
2524
2525     /* Calculate all polygons */
2526     $Angle = 0; $TopPlots = "";
2527     foreach($iValues as $Key => $Value)
2528      {
2529       $XOffset = cos(($Angle+($Value/2*$SpliceRatio)) * 3.1418 / 180 ) * $SpliceDistance;
2530       $YOffset = sin(($Angle+($Value/2*$SpliceRatio)) * 3.1418 / 180 ) * $SpliceDistance;
2531
2532       $TopPlots[$Key][] = round($XPos + $XOffset);
2533       $TopPlots[$Key][] = round($YPos + $YOffset);
2534
2535       if ( $AllBlack )
2536        { $Rc = $this->ShadowRColor; $Gc = $this->ShadowGColor; $Bc = $this->ShadowBColor; }
2537       else
2538        { $Rc = $this->Palette[$Key]["R"]; $Gc = $this->Palette[$Key]["G"]; $Bc = $this->Palette[$Key]["B"]; }
2539
2540       $XLineLast = ""; $YLineLast = "";
2541
2542       /* Process labels position & size */
2543       $Caption = "";
2544       if ( !($DrawLabels == PIE_NOLABEL) )
2545        {
2546         $TAngle   = $Angle+($Value*$SpliceRatio/2);
2547         if ($DrawLabels == PIE_PERCENTAGE)
2548          $Caption  = (round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2549         elseif ($DrawLabels == PIE_LABELS)
2550          $Caption  = $iLabels[$Key];
2551         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2552          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2553         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2554          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2555
2556         $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2557         $TextWidth  = $Position[2]-$Position[0];
2558         $TextHeight = abs($Position[1])+abs($Position[3]);
2559
2560         $TX =