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] + 0;
655       $this->VMax = $Data[0][$YSerieName] + 0;
656
657       foreach ( $Data as $Key => $Values )
658        {
659         if (isset($Data[$Key][$YSerieName]))
660          {
661           $Value = $Data[$Key][$YSerieName] + 0;
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] + 0;
762       $this->VXMax = $Data[0][$XSerieName] + 0;
763
764       foreach ( $Data as $Key => $Values )
765        {
766         if (isset($Data[$Key][$XSerieName]))
767          {
768           $Value = $Data[$Key][$XSerieName] + 0;
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   /* Draw the data legends */
981   function drawLegend($XPos,$YPos,$DataDescription,$R,$G,$B,$Rs=-1,$Gs=-1,$Bs=-1,$Rt=0,$Gt=0,$Bt=0,$Border=TRUE)
982    {
983     /* Validate the Data and DataDescription array */
984     $this->validateDataDescription("drawLegend",$DataDescription);
985
986     if ( !isset($DataDescription["Description"]) )
987      return(-1);
988
989     $C_TextColor =$this->AllocateColor($this->Picture,$Rt,$Gt,$Bt);
990
991     /* <-10->[8]<-4->Text<-10-> */
992     $MaxWidth = 0; $MaxHeight = 8;
993     foreach($DataDescription["Description"] as $Key => $Value)
994      {
995       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
996       $TextWidth  = $Position[2]-$Position[0];
997       $TextHeight = $Position[1]-$Position[7];
998       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
999       $MaxHeight = $MaxHeight + $TextHeight + 4;
1000      }
1001     $MaxHeight = $MaxHeight - 5;
1002     $MaxWidth  = $MaxWidth + 32;
1003
1004     if ( $Rs == -1 || $Gs == -1 || $Bs == -1 )
1005      { $Rs = $R-30; $Gs = $G-30; $Bs = $B-30; }
1006
1007     if ( $Border )
1008      {
1009       $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$Rs,$Gs,$Bs);
1010       $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B);
1011      }
1012
1013     $YOffset = 4 + $this->FontSize; $ID = 0;
1014     foreach($DataDescription["Description"] as $Key => $Value)
1015      {
1016       $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"]);
1017       imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value);
1018
1019       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1020       $TextHeight = $Position[1]-$Position[7];
1021
1022       $YOffset = $YOffset + $TextHeight + 4;
1023       $ID++;
1024      }
1025    }
1026
1027   /* Draw the data legends */
1028   function drawPieLegend($XPos,$YPos,$Data,$DataDescription,$R,$G,$B)
1029    {
1030     /* Validate the Data and DataDescription array */
1031     $this->validateDataDescription("drawPieLegend",$DataDescription,FALSE);
1032     $this->validateData("drawPieLegend",$Data);
1033
1034     if ( !isset($DataDescription["Position"]) )
1035      return(-1);
1036
1037     $C_TextColor =$this->AllocateColor($this->Picture,0,0,0);
1038
1039     /* <-10->[8]<-4->Text<-10-> */
1040     $MaxWidth = 0; $MaxHeight = 8;
1041     foreach($Data as $Key => $Value)
1042      {
1043       $Value2 = $Value[$DataDescription["Position"]];
1044       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value2);
1045       $TextWidth = $Position[2]-$Position[0];
1046       $TextHeight = $Position[1]-$Position[7];
1047       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
1048
1049       $MaxHeight = $MaxHeight + $TextHeight + 4;
1050      }
1051     $MaxHeight = $MaxHeight - 3;
1052     $MaxWidth  = $MaxWidth + 32;
1053
1054     $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$R-30,$G-30,$B-30);
1055     $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B);
1056
1057     $YOffset = 4 + $this->FontSize; $ID = 0;
1058     foreach($Data as $Key => $Value)
1059      {
1060       $Value2     = $Value[$DataDescription["Position"]];
1061       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value2);
1062       $TextHeight = $Position[1]-$Position[7];
1063       $this->drawFilledRectangle($XPos+10,$YPos+$YOffset-6,$XPos+14,$YPos+$YOffset-2,$this->Palette[$ID]["R"],$this->Palette[$ID]["G"],$this->Palette[$ID]["B"]);
1064
1065       imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value2);
1066       $YOffset = $YOffset + $TextHeight + 4;
1067       $ID++;
1068      }
1069    }
1070
1071   /* Draw the graph title */
1072   function drawTitle($XPos,$YPos,$Value,$R,$G,$B,$XPos2=-1,$YPos2=-1,$Shadow=FALSE)
1073    {
1074     $C_TextColor = $this->AllocateColor($this->Picture,$R,$G,$B);
1075
1076     if ( $XPos2 != -1 )
1077      {
1078       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1079       $TextWidth = $Position[2]-$Position[0];
1080       $XPos      = floor(( $XPos2 - $XPos - $TextWidth ) / 2 ) + $XPos;
1081      }
1082
1083     if ( $YPos2 != -1 )
1084      {
1085       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1086       $TextHeight = $Position[5]-$Position[3];
1087       $YPos       = floor(( $YPos2 - $YPos - $TextHeight ) / 2 ) + $YPos;
1088      }
1089
1090     if ( $Shadow )
1091      {
1092       $C_ShadowColor = $this->AllocateColor($this->Picture,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor);
1093       imagettftext($this->Picture,$this->FontSize,0,$XPos+$this->ShadowXDistance,$YPos+$this->ShadowYDistance,$C_ShadowColor,$this->FontName,$Value);
1094      }
1095
1096     imagettftext($this->Picture,$this->FontSize,0,$XPos,$YPos,$C_TextColor,$this->FontName,$Value);
1097    }
1098
1099   /* Draw a text box with text align & alpha properties */
1100   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)
1101    {
1102     $Position   = imageftbbox($this->FontSize,$Angle,$this->FontName,$Text);
1103     $TextWidth  = $Position[2]-$Position[0];
1104     $TextHeight = $Position[5]-$Position[3];
1105     $AreaWidth  = $X2 - $X1;
1106     $AreaHeight = $Y2 - $Y1;
1107
1108     if ( $BgR != -1 && $BgG != -1 && $BgB != -1 )
1109      $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$BgR,$BgG,$BgB,FALSE,$Alpha);
1110
1111     if ( $Align == ALIGN_TOP_LEFT )      { $X = $X1+1; $Y = $Y1+$this->FontSize+1; }
1112     if ( $Align == ALIGN_TOP_CENTER )    { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y1+$this->FontSize+1; }
1113     if ( $Align == ALIGN_TOP_RIGHT )     { $X = $X2-$TextWidth-1; $Y = $Y1+$this->FontSize+1; }
1114     if ( $Align == ALIGN_LEFT )          { $X = $X1+1; $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1115     if ( $Align == ALIGN_CENTER )        { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1116     if ( $Align == ALIGN_RIGHT )         { $X = $X2-$TextWidth-1; $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1117     if ( $Align == ALIGN_BOTTOM_LEFT )   { $X = $X1+1; $Y = $Y2-1; }
1118     if ( $Align == ALIGN_BOTTOM_CENTER ) { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y2-1; }
1119     if ( $Align == ALIGN_BOTTOM_RIGHT )  { $X = $X2-$TextWidth-1; $Y = $Y2-1; }
1120
1121     $C_TextColor   =$this->AllocateColor($this->Picture,$R,$G,$B);
1122     $C_ShadowColor =$this->AllocateColor($this->Picture,0,0,0);
1123     if ( $Shadow )
1124      imagettftext($this->Picture,$this->FontSize,$Angle,$X+1,$Y+1,$C_ShadowColor,$this->FontName,$Text);
1125
1126     imagettftext($this->Picture,$this->FontSize,$Angle,$X,$Y,$C_TextColor,$this->FontName,$Text);
1127    }
1128
1129   /* Compute and draw the scale */
1130   function drawTreshold($Value,$R,$G,$B,$ShowLabel=FALSE,$ShowOnRight=FALSE,$TickWidth=4,$FreeText=NULL)
1131    {
1132     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
1133     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
1134     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
1135
1136     $C_TextColor =$this->AllocateColor($this->Picture,$R,$G,$B);
1137     $Y = $this->GArea_Y2 - ($Value - $this->VMin) * $this->DivisionRatio;
1138
1139     if ( $Y <= $this->GArea_Y1 || $Y >= $this->GArea_Y2 )
1140      return(-1);
1141
1142     if ( $TickWidth == 0 )
1143      $this->drawLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$R,$G,$B);
1144     else
1145      $this->drawDottedLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$TickWidth,$R,$G,$B);
1146
1147     if ( $ShowLabel )
1148      {
1149       if ( $FreeText == NULL )
1150        { $Label = $Value; } else { $Label = $FreeText; }
1151
1152       if ( $ShowOnRight )
1153        imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X2+2,$Y+($this->FontSize/2),$C_TextColor,$this->FontName,$Label);
1154       else
1155        imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1+2,$Y-($this->FontSize/2),$C_TextColor,$this->FontName,$Label);
1156      }
1157    }
1158
1159   /* This function put a label on a specific point */
1160   function setLabel($Data,$DataDescription,$SerieName,$ValueName,$Caption,$R=210,$G=210,$B=210)
1161    {
1162     /* Validate the Data and DataDescription array */
1163     $this->validateDataDescription("setLabel",$DataDescription);
1164     $this->validateData("setLabel",$Data);
1165     $ShadowFactor = 100;
1166     $C_Label      =$this->AllocateColor($this->Picture,$R,$G,$B);
1167     $C_Shadow     =$this->AllocateColor($this->Picture,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1168     $C_TextColor  =$this->AllocateColor($this->Picture,0,0,0);
1169
1170     $Cp = 0; $Found = FALSE;
1171     foreach ( $Data as $Key => $Value )
1172      {
1173       if ( $Data[$Key][$DataDescription["Position"]] == $ValueName )
1174        { $NumericalValue = $Data[$Key][$SerieName]; $Found = TRUE; }
1175       if ( !$Found )
1176        $Cp++;
1177      }
1178
1179     $XPos = $this->GArea_X1 + $this->GAreaXOffset + ( $this->DivisionWidth * $Cp ) + 2;
1180     $YPos = $this->GArea_Y2 - ($NumericalValue - $this->VMin) * $this->DivisionRatio;
1181
1182     $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
1183     $TextHeight = $Position[3] - $Position[5];
1184     $TextWidth  = $Position[2]-$Position[0] + 2;
1185     $TextOffset = floor($TextHeight/2);
1186
1187     // Shadow
1188     $Poly = array($XPos+1,$YPos+1,$XPos + 9,$YPos - $TextOffset,$XPos + 8,$YPos + $TextOffset + 2);
1189     imagefilledpolygon($this->Picture,$Poly,3,$C_Shadow);
1190     $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos - $TextOffset - .2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1191     $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos + $TextOffset + 2.2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1192     $this->drawFilledRectangle($XPos + 9,$YPos - $TextOffset-.2,$XPos + 13 + $TextWidth,$YPos + $TextOffset + 2.2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1193
1194     // Label background
1195     $Poly = array($XPos,$YPos,$XPos + 8,$YPos - $TextOffset - 1,$XPos + 8,$YPos + $TextOffset + 1);
1196     imagefilledpolygon($this->Picture,$Poly,3,$C_Label);
1197     $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos - $TextOffset - 1.2,$R,$G,$B);
1198     $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos + $TextOffset + 1.2,$R,$G,$B);
1199     $this->drawFilledRectangle($XPos + 8,$YPos - $TextOffset - 1.2,$XPos + 12 + $TextWidth,$YPos + $TextOffset + 1.2,$R,$G,$B);
1200
1201     imagettftext($this->Picture,$this->FontSize,0,$XPos + 10,$YPos + $TextOffset,$C_TextColor,$this->FontName,$Caption);
1202    }
1203
1204   /* This function draw a plot graph */
1205   function drawPlotGraph($Data,$DataDescription,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1,$Shadow=FALSE)
1206    {
1207     /* Validate the Data and DataDescription array */
1208     $this->validateDataDescription("drawPlotGraph",$DataDescription);
1209     $this->validateData("drawPlotGraph",$Data);
1210
1211     $GraphID = 0;
1212     $Ro = $R2; $Go = $G2; $Bo = $B2;
1213
1214     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1215      {
1216       $ID = 0;
1217       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1218        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1219
1220       $R = $this->Palette[$ColorID]["R"];
1221       $G = $this->Palette[$ColorID]["G"];
1222       $B = $this->Palette[$ColorID]["B"];
1223       $R2 = $Ro; $G2 = $Go; $B2 = $Bo;
1224
1225       if ( isset($DataDescription["Symbol"][$ColName]) )
1226        {
1227         $Is_Alpha = ((ord ( file_get_contents ($DataDescription["Symbol"][$ColName], false, null, 25, 1)) & 6) & 4) == 4;
1228
1229         $Infos       = getimagesize($DataDescription["Symbol"][$ColName]);
1230         $ImageWidth  = $Infos[0];
1231         $ImageHeight = $Infos[1];
1232         $Symbol      = imagecreatefromgif($DataDescription["Symbol"][$ColName]);
1233        }
1234
1235       $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1236       $Hsize = round($BigRadius/2);
1237       $R3 = -1; $G3 = -1; $B3 = -1;
1238       foreach ( $Data as $Key => $Values )
1239        {
1240         $Value = $Data[$Key][$ColName];
1241         $YPos  = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1242
1243         /* Save point into the image map if option activated */
1244         if ( $this->BuildMap )
1245          $this->addToImageMap($XPos-$Hsize,$YPos-$Hsize,$XPos+1+$Hsize,$YPos+$Hsize+1,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Plot");
1246
1247         if ( is_numeric($Value) )
1248          {
1249           if ( !isset($DataDescription["Symbol"][$ColName]) )
1250            {
1251
1252             if ( $Shadow )
1253              {
1254               if ( $R3 !=-1 && $G3 !=-1 && $B3 !=-1 )
1255                $this->drawFilledCircle($XPos+2,$YPos+2,$BigRadius,$R3,$G3,$B3);
1256               else
1257                {
1258                 $R3 = $this->Palette[$ColorID]["R"]-20; if ( $R3 < 0 ) { $R3 = 0; }
1259                 $G3 = $this->Palette[$ColorID]["G"]-20; if ( $G3 < 0 ) { $G3 = 0; }
1260                 $B3 = $this->Palette[$ColorID]["B"]-20; if ( $B3 < 0 ) { $B3 = 0; }
1261                 $this->drawFilledCircle($XPos+2,$YPos+2,$BigRadius,$R3,$G3,$B3);
1262                }
1263              }
1264
1265             $this->drawFilledCircle($XPos+1,$YPos+1,$BigRadius,$R,$G,$B);
1266
1267             if ( $SmallRadius != 0 )
1268              {
1269               if ( $R2 !=-1 && $G2 !=-1 && $B2 !=-1 )
1270                $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R2,$G2,$B2);
1271               else
1272                {
1273                 $R2 = $this->Palette[$ColorID]["R"]-15; if ( $R2 < 0 ) { $R2 = 0; }
1274                 $G2 = $this->Palette[$ColorID]["G"]-15; if ( $G2 < 0 ) { $G2 = 0; }
1275                 $B2 = $this->Palette[$ColorID]["B"]-15; if ( $B2 < 0 ) { $B2 = 0; }
1276
1277                 $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R2,$G2,$B2);
1278                }
1279              }
1280            }
1281           else
1282            {
1283             imagecopymerge($this->Picture,$Symbol,$XPos+1-$ImageWidth/2,$YPos+1-$ImageHeight/2,0,0,$ImageWidth,$ImageHeight,100);
1284            }
1285          }
1286
1287         $XPos = $XPos + $this->DivisionWidth;
1288        }
1289       $GraphID++;
1290      }
1291    }
1292
1293   /* This function draw a plot graph in an X/Y space */
1294   function drawXYPlotGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1,$Shadow=TRUE)
1295    {
1296     $R = $this->Palette[$PaletteID]["R"];
1297     $G = $this->Palette[$PaletteID]["G"];
1298     $B = $this->Palette[$PaletteID]["B"];
1299     $R3 = -1; $G3 = -1; $B3 = -1;
1300
1301     $YLast = -1; $XLast = -1;
1302     foreach ( $Data as $Key => $Values )
1303      {
1304       if ( isset($Data[$Key][$YSerieName]) && isset($Data[$Key][$XSerieName]) )
1305        {
1306         $X = $Data[$Key][$XSerieName];
1307         $Y = $Data[$Key][$YSerieName];
1308
1309         $Y = $this->GArea_Y2 - (($Y-$this->VMin) * $this->DivisionRatio);
1310         $X = $this->GArea_X1 + (($X-$this->VXMin) * $this->XDivisionRatio);
1311
1312
1313         if ( $Shadow )
1314          {
1315           if ( $R3 !=-1 && $G3 !=-1 && $B3 !=-1 )
1316            $this->drawFilledCircle($X+2,$Y+2,$BigRadius,$R3,$G3,$B3);
1317           else
1318            {
1319             $R3 = $this->Palette[$PaletteID]["R"]-20; if ( $R < 0 ) { $R = 0; }
1320             $G3 = $this->Palette[$PaletteID]["G"]-20; if ( $G < 0 ) { $G = 0; }
1321             $B3 = $this->Palette[$PaletteID]["B"]-20; if ( $B < 0 ) { $B = 0; }
1322             $this->drawFilledCircle($X+2,$Y+2,$BigRadius,$R3,$G3,$B3);
1323            }
1324          }
1325
1326         $this->drawFilledCircle($X+1,$Y+1,$BigRadius,$R,$G,$B);
1327
1328         if ( $R2 !=-1 && $G2 !=-1 && $B2 !=-1 )
1329          $this->drawFilledCircle($X+1,$Y+1,$SmallRadius,$R2,$G2,$B2);
1330         else
1331          {
1332           $R2 = $this->Palette[$PaletteID]["R"]+20; if ( $R > 255 ) { $R = 255; }
1333           $G2 = $this->Palette[$PaletteID]["G"]+20; if ( $G > 255 ) { $G = 255; }
1334           $B2 = $this->Palette[$PaletteID]["B"]+20; if ( $B > 255 ) { $B = 255; }
1335           $this->drawFilledCircle($X+1,$Y+1,$SmallRadius,$R2,$G2,$B2);
1336          }
1337        }
1338      }
1339
1340    }
1341
1342   /* This function draw an area between two series */
1343   function drawArea($Data,$Serie1,$Serie2,$R,$G,$B,$Alpha = 50)
1344    {
1345     /* Validate the Data and DataDescription array */
1346     $this->validateData("drawArea",$Data);
1347
1348     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1349     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1350
1351     $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1352     $C_White         =$this->AllocateColor($this->Layers[0],255,255,255);
1353     imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1354     imagecolortransparent($this->Layers[0],$C_White);
1355
1356     $C_Graph =$this->AllocateColor($this->Layers[0],$R,$G,$B);
1357
1358     $XPos     = $this->GAreaXOffset;
1359     $LastXPos = -1;
1360     foreach ( $Data as $Key => $Values )
1361      {
1362       $Value1 = $Data[$Key][$Serie1];
1363       $Value2 = $Data[$Key][$Serie2];
1364       $YPos1  = $LayerHeight - (($Value1-$this->VMin) * $this->DivisionRatio);
1365       $YPos2  = $LayerHeight - (($Value2-$this->VMin) * $this->DivisionRatio);
1366
1367       if ( $LastXPos != -1 )
1368        {
1369         $Points   = "";
1370         $Points[] = $LastXPos; $Points[] = $LastYPos1;
1371         $Points[] = $LastXPos; $Points[] = $LastYPos2;
1372         $Points[] = $XPos; $Points[] = $YPos2;
1373         $Points[] = $XPos; $Points[] = $YPos1;
1374
1375         imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph);
1376        }
1377
1378       $LastYPos1 = $YPos1;
1379       $LastYPos2 = $YPos2;
1380       $LastXPos  = $XPos;
1381
1382       $XPos = $XPos + $this->DivisionWidth;
1383      }
1384
1385     imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1386     imagedestroy($this->Layers[0]);
1387    }
1388
1389
1390   /* This function write the values of the specified series */
1391   function writeValues($Data,$DataDescription,$Series)
1392    {
1393     /* Validate the Data and DataDescription array */
1394     $this->validateDataDescription("writeValues",$DataDescription);
1395     $this->validateData("writeValues",$Data);
1396
1397     if ( !is_array($Series) ) { $Series = array($Series); }
1398
1399     foreach($Series as $Key => $Serie)
1400      {
1401       $ID = 0;
1402       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1403        { if ( $keyI == $Serie ) { $ColorID = $ID; }; $ID++; }
1404
1405       $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1406       $XLast = -1;
1407       foreach ( $Data as $Key => $Values )
1408        {
1409         if ( isset($Data[$Key][$Serie]) && is_numeric($Data[$Key][$Serie]))
1410          {
1411           $Value = $Data[$Key][$Serie];
1412           $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1413
1414           $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Value);
1415           $Width  = $Positions[2] - $Positions[6]; $XOffset = $XPos - ($Width/2);
1416           $Height = $Positions[3] - $Positions[7]; $YOffset = $YPos - 4;
1417
1418           $C_TextColor =$this->AllocateColor($this->Picture,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1419           imagettftext($this->Picture,$this->FontSize,0,$XOffset,$YOffset,$C_TextColor,$this->FontName,$Value);
1420          }
1421         $XPos = $XPos + $this->DivisionWidth;
1422        }
1423
1424      }
1425    }
1426
1427   /* This function draw a line graph */
1428   function drawLineGraph($Data,$DataDescription,$SerieName="")
1429    {
1430     /* Validate the Data and DataDescription array */
1431     $this->validateDataDescription("drawLineGraph",$DataDescription);
1432     $this->validateData("drawLineGraph",$Data);
1433
1434     $GraphID = 0;
1435     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1436      {
1437       $ID = 0;
1438       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1439        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1440
1441       if ( $SerieName == "" || $SerieName == $ColName )
1442        {
1443         $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1444         $XLast = -1;
1445         foreach ( $Data as $Key => $Values )
1446          {
1447           if ( isset($Data[$Key][$ColName]))
1448            {
1449             $Value = $Data[$Key][$ColName];
1450             $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1451
1452             /* Save point into the image map if option activated */
1453             if ( $this->BuildMap )
1454              $this->addToImageMap($XPos-3,$YPos-3,$XPos+3,$YPos+3,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Line");
1455
1456             if (!is_numeric($Value)) { $XLast = -1; }
1457             if ( $XLast != -1 )
1458              $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1459
1460             $XLast = $XPos;
1461             $YLast = $YPos;
1462             if (!is_numeric($Value)) { $XLast = -1; }
1463            }
1464           $XPos = $XPos + $this->DivisionWidth;
1465          }
1466         $GraphID++;
1467        }
1468      }
1469    }
1470
1471   /* This function draw a line graph */
1472   function drawXYGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0)
1473    {
1474     $YLast = -1; $XLast = -1;
1475     foreach ( $Data as $Key => $Values )
1476      {
1477       if ( isset($Data[$Key][$YSerieName]) && isset($Data[$Key][$XSerieName]) )
1478        {
1479         $X = $Data[$Key][$XSerieName];
1480         $Y = $Data[$Key][$YSerieName];
1481
1482         $Y = $this->GArea_Y2 - (($Y-$this->VMin) * $this->DivisionRatio);
1483         $X = $this->GArea_X1 + (($X-$this->VXMin) * $this->XDivisionRatio);
1484         if ($XLast != -1 && $YLast != -1)
1485          {
1486           $this->drawLine($XLast,$YLast,$X,$Y,$this->Palette[$PaletteID]["R"],$this->Palette[$PaletteID]["G"],$this->Palette[$PaletteID]["B"],TRUE);
1487          }
1488
1489         $XLast = $X;
1490         $YLast = $Y;
1491        }
1492      }
1493    }
1494
1495   /* This function draw a cubic curve */
1496   function drawCubicCurve($Data,$DataDescription,$Accuracy=.1,$SerieName="")
1497    {
1498     /* Validate the Data and DataDescription array */
1499     $this->validateDataDescription("drawCubicCurve",$DataDescription);
1500     $this->validateData("drawCubicCurve",$Data);
1501
1502     $GraphID = 0;
1503     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1504      {
1505       if ( $SerieName == "" || $SerieName == $ColName )
1506        {
1507         $XIn = ""; $Yin = ""; $Yt = ""; $U = "";
1508         $XIn[0] = 0; $YIn[0] = 0;
1509
1510         $ID = 0;
1511         foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1512          { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1513
1514         $Index = 1;
1515         $XLast = -1; $Missing = "";
1516         foreach ( $Data as $Key => $Values )
1517          {
1518           if ( isset($Data[$Key][$ColName]) )
1519            {
1520             $Value = $Data[$Key][$ColName];
1521             $XIn[$Index] = $Index;
1522             $YIn[$Index] = $Value;
1523             if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; }
1524             $Index++;
1525            }
1526          }
1527         $Index--;
1528
1529         $Yt[0] = 0;
1530         $Yt[1] = 0;
1531         $U[1]  = 0;
1532         for($i=2;$i<=$Index-1;$i++)
1533          {
1534           $Sig    = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]);
1535           $p      = $Sig * $Yt[$i-1] + 2;
1536           $Yt[$i] = ($Sig - 1) / $p;
1537           $U[$i]  = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]);
1538           $U[$i]  = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p;
1539          }
1540
1541         $qn = 0;
1542         $un = 0;
1543         $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1);
1544
1545         for($k=$Index-1;$k>=1;$k--)
1546          $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k];
1547
1548         $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1549         for($X=1;$X<=$Index;$X=$X+$Accuracy)
1550          {
1551           $klo = 1;
1552           $khi = $Index;
1553           $k   = $khi - $klo;
1554           while($k > 1)
1555            {
1556             $k = $khi - $klo;
1557             If ( $XIn[$k] >= $X )
1558              $khi = $k;
1559             else
1560              $klo = $k;
1561            }
1562           $klo = $khi - 1;
1563
1564           $h     = $XIn[$khi] - $XIn[$klo];
1565           $a     = ($XIn[$khi] - $X) / $h;
1566           $b     = ($X - $XIn[$klo]) / $h;
1567           $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6;
1568
1569           $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1570
1571           if ( $XLast != -1 && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]) )
1572            $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1573
1574           $XLast = $XPos;
1575           $YLast = $YPos;
1576           $XPos  = $XPos + $this->DivisionWidth * $Accuracy;
1577          }
1578
1579         // Add potentialy missing values
1580         $XPos  = $XPos - $this->DivisionWidth * $Accuracy;
1581         if ( $XPos < ($this->GArea_X2 - $this->GAreaXOffset) )
1582          {
1583           $YPos = $this->GArea_Y2 - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio);
1584           $this->drawLine($XLast,$YLast,$this->GArea_X2-$this->GAreaXOffset,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1585          }
1586
1587         $GraphID++;
1588        }
1589      }
1590    }
1591
1592   /* This function draw a filled cubic curve */
1593   function drawFilledCubicCurve($Data,$DataDescription,$Accuracy=.1,$Alpha=100,$AroundZero=FALSE)
1594    {
1595     /* Validate the Data and DataDescription array */
1596     $this->validateDataDescription("drawFilledCubicCurve",$DataDescription);
1597     $this->validateData("drawFilledCubicCurve",$Data);
1598
1599     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1600     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1601     $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1602     if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; }
1603
1604     $GraphID = 0;
1605     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1606      {
1607       $XIn = ""; $Yin = ""; $Yt = ""; $U = "";
1608       $XIn[0] = 0; $YIn[0] = 0;
1609
1610       $ID = 0;
1611       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1612        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1613
1614       $Index = 1;
1615       $XLast = -1; $Missing = "";
1616       foreach ( $Data as $Key => $Values )
1617        {
1618         $Value = $Data[$Key][$ColName];
1619         $XIn[$Index] = $Index;
1620         $YIn[$Index] = $Value;
1621         if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; }
1622         $Index++;
1623        }
1624       $Index--;
1625
1626       $Yt[0] = 0;
1627       $Yt[1] = 0;
1628       $U[1]  = 0;
1629       for($i=2;$i<=$Index-1;$i++)
1630        {
1631         $Sig    = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]);
1632         $p      = $Sig * $Yt[$i-1] + 2;
1633         $Yt[$i] = ($Sig - 1) / $p;
1634         $U[$i]  = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]);
1635         $U[$i]  = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p;
1636        }
1637
1638       $qn = 0;
1639       $un = 0;
1640       $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1);
1641
1642       for($k=$Index-1;$k>=1;$k--)
1643        $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k];
1644
1645       $Points   = "";
1646       $Points[] = $this->GAreaXOffset;
1647       $Points[] = $LayerHeight;
1648
1649       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1650       $C_White         =$this->AllocateColor($this->Layers[0],255,255,255);
1651       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1652       imagecolortransparent($this->Layers[0],$C_White);
1653
1654       $YLast = NULL;
1655       $XPos  = $this->GAreaXOffset; $PointsCount = 2;
1656       for($X=1;$X<=$Index;$X=$X+$Accuracy)
1657        {
1658         $klo = 1;
1659         $khi = $Index;
1660         $k   = $khi - $klo;
1661         while($k > 1)
1662          {
1663           $k = $khi - $klo;
1664           If ( $XIn[$k] >= $X )
1665            $khi = $k;
1666           else
1667            $klo = $k;
1668          }
1669         $klo = $khi - 1;
1670
1671         $h     = $XIn[$khi] - $XIn[$klo];
1672         $a     = ($XIn[$khi] - $X) / $h;
1673         $b     = ($X - $XIn[$klo]) / $h;
1674         $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6;
1675
1676         $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1677
1678         if ( $YLast != NULL && $AroundZero && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]))
1679          {
1680           $aPoints   = "";
1681           $aPoints[] = $XLast;
1682           $aPoints[] = $YLast;
1683           $aPoints[] = $XPos;
1684           $aPoints[] = $YPos;
1685           $aPoints[] = $XPos;
1686           $aPoints[] = $YZero;
1687           $aPoints[] = $XLast;
1688           $aPoints[] = $YZero;
1689
1690           $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1691           imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph);
1692          }
1693
1694         if ( !isset($Missing[floor($X)]) || $YLast == NULL )
1695          {
1696           $PointsCount++;
1697           $Points[] = $XPos;
1698           $Points[] = $YPos;
1699          }
1700         else
1701          {
1702           $PointsCount++; $Points[] = $XLast; $Points[] = $LayerHeight;
1703          }
1704
1705         $YLast = $YPos; $XLast = $XPos;
1706         $XPos  = $XPos + $this->DivisionWidth * $Accuracy;
1707        }
1708
1709       // Add potentialy missing values
1710       $XPos  = $XPos - $this->DivisionWidth * $Accuracy;
1711       if ( $XPos < ($LayerWidth-$this->GAreaXOffset) )
1712        {
1713         $YPos = $LayerHeight - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio);
1714
1715         if ( $YLast != NULL && $AroundZero )
1716          {
1717           $aPoints   = "";
1718           $aPoints[] = $XLast;
1719           $aPoints[] = $YLast;
1720           $aPoints[] = $LayerWidth-$this->GAreaXOffset;
1721           $aPoints[] = $YPos;
1722           $aPoints[] = $LayerWidth-$this->GAreaXOffset;
1723           $aPoints[] = $YZero;
1724           $aPoints[] = $XLast;
1725           $aPoints[] = $YZero;
1726
1727           $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1728           imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph);
1729          }
1730
1731         if ( $YIn[$klo] != "" && $YIn[$khi] != "" || $YLast == NULL )
1732          {
1733           $PointsCount++;
1734           $Points[] = $LayerWidth-$this->GAreaXOffset;
1735           $Points[] = $YPos;
1736          }
1737        }
1738
1739       $Points[] = $LayerWidth-$this->GAreaXOffset;
1740       $Points[] = $LayerHeight;
1741
1742       if ( !$AroundZero )
1743        {
1744         $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1745         imagefilledpolygon($this->Layers[0],$Points,$PointsCount,$C_Graph);
1746        }
1747
1748       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1749       imagedestroy($this->Layers[0]);
1750
1751       $this->drawCubicCurve($Data,$DataDescription,$Accuracy,$ColName);
1752
1753       $GraphID++;
1754      }
1755    }
1756
1757   /* This function draw a filled line graph */
1758   function drawFilledLineGraph($Data,$DataDescription,$Alpha=100,$AroundZero=FALSE)
1759    {
1760     $Empty = -2147483647;
1761
1762     /* Validate the Data and DataDescription array */
1763     $this->validateDataDescription("drawFilledLineGraph",$DataDescription);
1764     $this->validateData("drawFilledLineGraph",$Data);
1765
1766     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1767     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1768
1769     $GraphID = 0;
1770     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1771      {
1772       $ID = 0;
1773       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1774        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1775
1776       $aPoints   = "";
1777       $aPoints[] = $this->GAreaXOffset;
1778       $aPoints[] = $LayerHeight;
1779
1780       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1781       $C_White         = $this->AllocateColor($this->Layers[0],255,255,255);
1782       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1783       imagecolortransparent($this->Layers[0],$C_White);
1784
1785       $XPos  = $this->GAreaXOffset;
1786       $XLast = -1; $PointsCount = 2;
1787       $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1788       if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; }
1789
1790       $YLast = $Empty;
1791       foreach ( $Data as $Key => $Values )
1792        {
1793         $Value = $Data[$Key][$ColName];
1794         $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1795
1796         /* Save point into the image map if option activated */
1797         if ( $this->BuildMap )
1798          $this->addToImageMap($XPos-3,$YPos-3,$XPos+3,$YPos+3,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"FLine");
1799
1800         if ( !is_numeric($Value) )
1801          {
1802           $PointsCount++;
1803           $aPoints[] = $XLast;
1804           $aPoints[] = $LayerHeight;
1805
1806           $YLast = $Empty;
1807          }
1808         else
1809          {
1810           $PointsCount++;
1811           if ( $YLast <> $Empty )
1812            { $aPoints[] = $XPos; $aPoints[] = $YPos; }
1813           else
1814            { $PointsCount++; $aPoints[] = $XPos; $aPoints[] = $LayerHeight; $aPoints[] = $XPos; $aPoints[] = $YPos; }
1815
1816           if ($YLast <> $Empty && $AroundZero)
1817            {
1818             $Points   = "";
1819             $Points[] = $XLast; $Points[] = $YLast;
1820             $Points[] = $XPos;
1821             $Points[] = $YPos;
1822             $Points[] = $XPos;
1823             $Points[] = $YZero;
1824             $Points[] = $XLast;
1825             $Points[] = $YZero;
1826
1827             $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1828             imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph);
1829            }
1830           $YLast = $YPos;
1831          }
1832
1833         $XLast = $XPos;
1834         $XPos  = $XPos + $this->DivisionWidth;
1835        }
1836       $aPoints[] = $LayerWidth - $this->GAreaXOffset;
1837       $aPoints[] = $LayerHeight;
1838
1839       if ( $AroundZero == FALSE )
1840        {
1841         $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1842         imagefilledpolygon($this->Layers[0],$aPoints,$PointsCount,$C_Graph);
1843        }
1844
1845       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1846       imagedestroy($this->Layers[0]);
1847       $GraphID++;
1848       $this->drawLineGraph($Data,$DataDescription,$ColName);
1849      }
1850    }
1851
1852   /* This function draw a bar graph */
1853   function drawOverlayBarGraph($Data,$DataDescription,$Alpha=50)
1854    {
1855     /* Validate the Data and DataDescription array */
1856     $this->validateDataDescription("drawOverlayBarGraph",$DataDescription);
1857     $this->validateData("drawOverlayBarGraph",$Data);
1858
1859     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1860     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1861
1862     $GraphID = 0;
1863     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1864      {
1865       $ID = 0;
1866       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1867        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1868
1869       $this->Layers[$GraphID] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1870       $C_White                = $this->AllocateColor($this->Layers[$GraphID],255,255,255);
1871       $C_Graph                = $this->AllocateColor($this->Layers[$GraphID],$this->Palette[$GraphID]["R"],$this->Palette[$GraphID]["G"],$this->Palette[$GraphID]["B"]);
1872       imagefilledrectangle($this->Layers[$GraphID],0,0,$LayerWidth,$LayerHeight,$C_White);
1873       imagecolortransparent($this->Layers[$GraphID],$C_White);
1874
1875       $XWidth = $this->DivisionWidth / 4;
1876       $XPos   = $this->GAreaXOffset;
1877       $YZero  = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1878       $XLast  = -1; $PointsCount = 2;
1879       foreach ( $Data as $Key => $Values )
1880        {
1881         if ( isset($Data[$Key][$ColName]) )
1882          {
1883           $Value = $Data[$Key][$ColName];
1884           if ( is_numeric($Value) )
1885            {
1886             $YPos  = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1887
1888             imagefilledrectangle($this->Layers[$GraphID],$XPos-$XWidth,$YPos,$XPos+$XWidth,$YZero,$C_Graph);
1889
1890             $X1 = floor($XPos - $XWidth + $this->GArea_X1); $Y1 = floor($YPos+$this->GArea_Y1) + .2;
1891             $X2 = floor($XPos + $XWidth + $this->GArea_X1); $Y2 = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1892             if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; }
1893             if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; }
1894
1895             /* Save point into the image map if option activated */
1896             if ( $this->BuildMap )
1897              $this->addToImageMap($X1,min($Y1,$Y2),$X2,max($Y1,$Y2),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"oBar");
1898
1899             $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1900            }
1901          }
1902         $XPos = $XPos + $this->DivisionWidth;
1903        }
1904
1905       $GraphID++;
1906      }
1907
1908     for($i=0;$i<=($GraphID-1);$i++)
1909      {
1910       imagecopymerge($this->Picture,$this->Layers[$i],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1911       imagedestroy($this->Layers[$i]);
1912      }
1913    }
1914
1915   /* This function draw a bar graph */
1916   function drawBarGraph($Data,$DataDescription,$Shadow=FALSE,$Alpha=100)
1917    {
1918     /* Validate the Data and DataDescription array */
1919     $this->validateDataDescription("drawBarGraph",$DataDescription);
1920     $this->validateData("drawBarGraph",$Data);
1921
1922     $GraphID      = 0;
1923     $Series       = count($DataDescription["Values"]);
1924     $SeriesWidth  = $this->DivisionWidth / ($Series+1);
1925     $SerieXOffset = $this->DivisionWidth / 2 - $SeriesWidth / 2;
1926
1927     $YZero  = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1928     if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; }
1929
1930     $SerieID = 0;
1931     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1932      {
1933       $ID = 0;
1934       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1935        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1936
1937       $XPos  = $this->GArea_X1 + $this->GAreaXOffset - $SerieXOffset + $SeriesWidth * $SerieID;
1938       $XLast = -1;
1939       foreach ( $Data as $Key => $Values )
1940        {
1941         if ( isset($Data[$Key][$ColName]))
1942          {
1943           if ( is_numeric($Data[$Key][$ColName]) )
1944            {
1945             $Value = $Data[$Key][$ColName];
1946             $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1947
1948             /* Save point into the image map if option activated */
1949             if ( $this->BuildMap )
1950              {
1951               $this->addToImageMap($XPos+1,min($YZero,$YPos),$XPos+$SeriesWidth-1,max($YZero,$YPos),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Bar");
1952              }
1953
1954             if ( $Shadow && $Alpha == 100 )
1955              $this->drawRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,25,25,25,TRUE,$Alpha);
1956
1957             $this->drawFilledRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha);
1958            }
1959          }
1960         $XPos = $XPos + $this->DivisionWidth;
1961        }
1962       $SerieID++;
1963      }
1964    }
1965
1966   /* This function draw a stacked bar graph */
1967   function drawStackedBarGraph($Data,$DataDescription,$Alpha=50,$Contiguous=FALSE)
1968    {
1969     /* Validate the Data and DataDescription array */
1970     $this->validateDataDescription("drawBarGraph",$DataDescription);
1971     $this->validateData("drawBarGraph",$Data);
1972
1973     $GraphID      = 0;
1974     $Series       = count($DataDescription["Values"]);
1975     if ( $Contiguous )
1976      $SeriesWidth  = $this->DivisionWidth;
1977     else
1978      $SeriesWidth  = $this->DivisionWidth * .8;
1979
1980     $YZero  = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1981     if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; }
1982
1983     $SerieID = 0; $LastValue = "";
1984     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1985      {
1986       $ID = 0;
1987       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1988        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1989
1990       $XPos  = $this->GArea_X1 + $this->GAreaXOffset - $SeriesWidth / 2;
1991       $XLast = -1;
1992       foreach ( $Data as $Key => $Values )
1993        {
1994         if ( isset($Data[$Key][$ColName]))
1995          {
1996           if ( is_numeric($Data[$Key][$ColName]) )
1997            {
1998             $Value = $Data[$Key][$ColName];
1999
2000             if ( isset($LastValue[$Key]) )
2001              {
2002               $YPos    = $this->GArea_Y2 - ((($Value+$LastValue[$Key])-$this->VMin) * $this->DivisionRatio);
2003               $YBottom = $this->GArea_Y2 - (($LastValue[$Key]-$this->VMin) * $this->DivisionRatio);
2004               $LastValue[$Key] += $Value;
2005              }
2006             else
2007              {
2008               $YPos    = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
2009               $YBottom = $YZero;
2010               $LastValue[$Key] = $Value;
2011              }
2012
2013             /* Save point into the image map if option activated */
2014             if ( $this->BuildMap )
2015              $this->addToImageMap($XPos+1,min($YBottom,$YPos),$XPos+$SeriesWidth-1,max($YBottom,$YPos),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"sBar");
2016
2017             $this->drawFilledRectangle($XPos+1,$YBottom,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha);
2018            }
2019          }
2020         $XPos = $XPos + $this->DivisionWidth;
2021        }
2022       $SerieID++;
2023      }
2024    }
2025
2026   /* This function draw a limits bar graphs */
2027   function drawLimitsGraph($Data,$DataDescription,$R=0,$G=0,$B=0)
2028    {
2029     /* Validate the Data and DataDescription array */
2030     $this->validateDataDescription("drawLimitsGraph",$DataDescription);
2031     $this->validateData("drawLimitsGraph",$Data);
2032
2033     $XWidth = $this->DivisionWidth / 4;
2034     $XPos   = $this->GArea_X1 + $this->GAreaXOffset;
2035
2036     foreach ( $Data as $Key => $Values )
2037      {
2038       $Min     = $Data[$Key][$DataDescription["Values"][0]];
2039       $Max     = $Data[$Key][$DataDescription["Values"][0]];
2040       $GraphID = 0; $MaxID = 0; $MinID = 0;
2041       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2042        {
2043         if ( isset($Data[$Key][$ColName]) )
2044          {
2045           if ( $Data[$Key][$ColName] > $Max && is_numeric($Data[$Key][$ColName]))
2046            { $Max = $Data[$Key][$ColName]; $MaxID = $GraphID; }
2047          }
2048         if ( isset($Data[$Key][$ColName]) && is_numeric($Data[$Key][$ColName]))
2049          {
2050           if ( $Data[$Key][$ColName] < $Min )
2051            { $Min = $Data[$Key][$ColName]; $MinID = $GraphID; }
2052           $GraphID++;
2053          }
2054        }
2055
2056       $YPos = $this->GArea_Y2 - (($Max-$this->VMin) * $this->DivisionRatio);
2057       $X1 = floor($XPos - $XWidth); $Y1 = floor($YPos) - .2;
2058       $X2 = floor($XPos + $XWidth);
2059       if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; }
2060       if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; }
2061
2062       $YPos = $this->GArea_Y2 - (($Min-$this->VMin) * $this->DivisionRatio);
2063       $Y2 = floor($YPos) + .2;
2064
2065       $this->drawLine(floor($XPos)-.2,$Y1+1,floor($XPos)-.2,$Y2-1,$R,$G,$B,TRUE);
2066       $this->drawLine(floor($XPos)+.2,$Y1+1,floor($XPos)+.2,$Y2-1,$R,$G,$B,TRUE);
2067       $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$MaxID]["R"],$this->Palette[$MaxID]["G"],$this->Palette[$MaxID]["B"],FALSE);
2068       $this->drawLine($X1,$Y2,$X2,$Y2,$this->Palette[$MinID]["R"],$this->Palette[$MinID]["G"],$this->Palette[$MinID]["B"],FALSE);
2069
2070       $XPos = $XPos + $this->DivisionWidth;
2071      }
2072    }
2073
2074   /* This function draw radar axis centered on the graph area */
2075   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)
2076    {
2077     /* Validate the Data and DataDescription array */
2078     $this->validateDataDescription("drawRadarAxis",$DataDescription);
2079     $this->validateData("drawRadarAxis",$Data);
2080
2081     $C_TextColor = $this->AllocateColor($this->Picture,$A_R,$A_G,$A_B);
2082
2083     /* Draw radar axis */
2084     $Points  = count($Data);
2085     $Radius  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2086     $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1;
2087     $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1;
2088
2089     /* Search for the max value */
2090     if ( $MaxValue == -1 )
2091      {
2092       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2093        {
2094         foreach ( $Data as $Key => $Values )
2095          {
2096           if ( isset($Data[$Key][$ColName]))
2097            if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; }
2098          }
2099        }
2100      }
2101
2102     /* Draw the mosaic */
2103     if ( $Mosaic )
2104      {
2105       $RadiusScale = $Radius / $MaxValue;
2106       for ( $t=1; $t<=$MaxValue-1; $t++)
2107        {
2108         $TRadius  = $RadiusScale * $t;
2109         $LastX1   = -1;
2110
2111         for ( $i=0; $i<=$Points; $i++)
2112          {
2113           $Angle = -90 + $i * 360/$Points;
2114           $X1 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2115           $Y1 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2116           $X2 = cos($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $XCenter;
2117           $Y2 = sin($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $YCenter;
2118
2119           if ( $t % 2 == 1 && $LastX1 != -1)
2120            {
2121             $Plots   = "";
2122             $Plots[] = $X1; $Plots[] = $Y1;
2123             $Plots[] = $X2; $Plots[] = $Y2;
2124             $Plots[] = $LastX2; $Plots[] = $LastY2;
2125             $Plots[] = $LastX1; $Plots[] = $LastY1;
2126
2127             $C_Graph = $this->AllocateColor($this->Picture,250,250,250);
2128             imagefilledpolygon($this->Picture,$Plots,(count($Plots)+1)/2,$C_Graph);
2129            }
2130
2131           $LastX1 = $X1; $LastY1= $Y1;
2132           $LastX2 = $X2; $LastY2= $Y2;
2133          }
2134        }
2135      }
2136
2137
2138     /* Draw the spider web */
2139     for ( $t=1; $t<=$MaxValue; $t++)
2140      {
2141       $TRadius = ( $Radius / $MaxValue ) * $t;
2142       $LastX   = -1;
2143
2144       for ( $i=0; $i<=$Points; $i++)
2145        {
2146         $Angle = -90 + $i * 360/$Points;
2147         $X = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2148         $Y = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2149
2150         if ( $LastX != -1 )
2151          $this->drawDottedLine($LastX,$LastY,$X,$Y,4,$S_R,$S_G,$S_B);
2152
2153         $LastX = $X; $LastY= $Y;
2154        }
2155      }
2156
2157     /* Draw the axis */
2158     for ( $i=0; $i<=$Points; $i++)
2159      {
2160       $Angle = -90 + $i * 360/$Points;
2161       $X = cos($Angle * 3.1418 / 180 ) * $Radius + $XCenter;
2162       $Y = sin($Angle * 3.1418 / 180 ) * $Radius + $YCenter;
2163
2164       $this->drawLine($XCenter,$YCenter,$X,$Y,$A_R,$A_G,$A_B);
2165
2166       $XOffset = 0; $YOffset = 0;
2167       if (isset($Data[$i][$DataDescription["Position"]]))
2168        {
2169         $Label = $Data[$i][$DataDescription["Position"]];
2170
2171         $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Label);
2172         $Width  = $Positions[2] - $Positions[6];
2173         $Height = $Positions[3] - $Positions[7];
2174
2175         if ( $Angle >= 0 && $Angle <= 90 )
2176          $YOffset = $Height;
2177
2178         if ( $Angle > 90 && $Angle <= 180 )
2179          { $YOffset = $Height; $XOffset = -$Width; }
2180
2181         if ( $Angle > 180 && $Angle <= 270 )
2182          { $XOffset = -$Width; }
2183
2184         imagettftext($this->Picture,$this->FontSize,0,$X+$XOffset,$Y+$YOffset,$C_TextColor,$this->FontName,$Label);
2185        }
2186      }
2187
2188     /* Write the values */
2189     for ( $t=1; $t<=$MaxValue; $t++)
2190      {
2191       $TRadius = ( $Radius / $MaxValue ) * $t;
2192
2193       $Angle = -90 + 360 / $Points;
2194       $X1 = $XCenter;
2195       $Y1 = $YCenter - $TRadius;
2196       $X2 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2197       $Y2 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2198
2199       $XPos = floor(($X2-$X1)/2) + $X1;
2200       $YPos = floor(($Y2-$Y1)/2) + $Y1;
2201
2202       $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$t);
2203       $X = $XPos - ( $X+$Positions[2] - $X+$Positions[6] ) / 2;
2204       $Y = $YPos + $this->FontSize;
2205
2206       $this->drawFilledRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,240,240,240);
2207       $this->drawRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,220,220,220);
2208       imagettftext($this->Picture,$this->FontSize,0,$X,$Y,$C_TextColor,$this->FontName,$t);
2209      }
2210    }
2211
2212   /* This function draw a radar graph centered on the graph area */
2213   function drawRadar($Data,$DataDescription,$BorderOffset=10,$MaxValue=-1)
2214    {
2215     /* Validate the Data and DataDescription array */
2216     $this->validateDataDescription("drawRadar",$DataDescription);
2217     $this->validateData("drawRadar",$Data);
2218
2219     $Points  = count($Data);
2220     $Radius  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2221     $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1;
2222     $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1;
2223
2224     /* Search for the max value */
2225     if ( $MaxValue == -1 )
2226      {
2227       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2228        {
2229         foreach ( $Data as $Key => $Values )
2230          {
2231           if ( isset($Data[$Key][$ColName]))
2232            if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; }
2233          }
2234        }
2235      }
2236
2237     $GraphID = 0;
2238     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2239      {
2240       $ID = 0;
2241       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2242        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2243
2244       $Angle = -90;
2245       $XLast = -1;
2246       foreach ( $Data as $Key => $Values )
2247        {
2248         if ( isset($Data[$Key][$ColName]))
2249          {
2250           $Value    = $Data[$Key][$ColName];
2251           $Strength = ( $Radius / $MaxValue ) * $Value;
2252
2253           $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter;
2254           $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter;
2255
2256           if ( $XLast != -1 )
2257            $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2258
2259           if ( $XLast == -1 )
2260            { $FirstX = $XPos; $FirstY = $YPos; }
2261
2262           $Angle = $Angle + (360/$Points);
2263           $XLast = $XPos;
2264           $YLast = $YPos;
2265          }
2266        }
2267       $this->drawLine($XPos,$YPos,$FirstX,$FirstY,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2268       $GraphID++;
2269      }
2270    }
2271
2272   /* This function draw a radar graph centered on the graph area */
2273   function drawFilledRadar($Data,$DataDescription,$Alpha=50,$BorderOffset=10,$MaxValue=-1)
2274    {
2275     /* Validate the Data and DataDescription array */
2276     $this->validateDataDescription("drawFilledRadar",$DataDescription);
2277     $this->validateData("drawFilledRadar",$Data);
2278
2279     $Points      = count($Data);
2280     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
2281     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
2282     $Radius      = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2283     $XCenter     = ( $this->GArea_X2 - $this->GArea_X1 ) / 2;
2284     $YCenter     = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2;
2285
2286     /* Search for the max value */
2287     if ( $MaxValue == -1 )
2288      {
2289       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2290        {
2291         foreach ( $Data as $Key => $Values )
2292          {
2293           if ( isset($Data[$Key][$ColName]))
2294            if ( $Data[$Key][$ColName] > $MaxValue && is_numeric($Data[$Key][$ColName])) { $MaxValue = $Data[$Key][$ColName]; }
2295          }
2296        }
2297      }
2298
2299     $GraphID = 0;
2300     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2301      {
2302       $ID = 0;
2303       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2304        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2305
2306       $Angle = -90;
2307       $XLast = -1;
2308       $Plots = "";
2309       foreach ( $Data as $Key => $Values )
2310        {
2311         if ( isset($Data[$Key][$ColName]))
2312          {
2313           $Value    = $Data[$Key][$ColName];
2314           if ( !is_numeric($Value) ) { $Value = 0; }
2315           $Strength = ( $Radius / $MaxValue ) * $Value;
2316
2317           $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter;
2318           $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter;
2319
2320           $Plots[] = $XPos;
2321           $Plots[] = $YPos;
2322
2323           $Angle = $Angle + (360/$Points);
2324           $XLast = $XPos;
2325           $YLast = $YPos;
2326          }
2327        }
2328
2329       if (isset($Plots[0]))
2330        {
2331         $Plots[] = $Plots[0];
2332         $Plots[] = $Plots[1];
2333
2334         $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
2335         $C_White         = $this->AllocateColor($this->Layers[0],255,255,255);
2336         imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
2337         imagecolortransparent($this->Layers[0],$C_White);
2338
2339         $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2340         imagefilledpolygon($this->Layers[0],$Plots,(count($Plots)+1)/2,$C_Graph);
2341
2342         imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
2343         imagedestroy($this->Layers[0]);
2344
2345         for($i=0;$i<=count($Plots)-4;$i=$i+2)
2346          $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"]);
2347        }
2348
2349       $GraphID++;
2350      }
2351    }
2352
2353   /* This function draw a flat pie chart */
2354   function drawBasicPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$R=255,$G=255,$B=255,$Decimals=0)
2355    {
2356     /* Validate the Data and DataDescription array */
2357     $this->validateDataDescription("drawBasicPieGraph",$DataDescription,FALSE);
2358     $this->validateData("drawBasicPieGraph",$Data);
2359
2360     /* Determine pie sum */
2361     $Series = 0; $PieSum = 0;
2362     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2363      {
2364       if ( $ColName != $DataDescription["Position"] )
2365        {
2366         $Series++;
2367         foreach ( $Data as $Key => $Values )
2368          {
2369           if ( isset($Data[$Key][$ColName]))
2370            $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]];
2371          }
2372        }
2373      }
2374
2375     /* Validate serie */
2376     if ( $Series != 1 )
2377      RaiseFatal("Pie chart can only accept one serie of data.");
2378
2379     $SpliceRatio         = 360 / $PieSum;
2380     $SplicePercent       = 100 / $PieSum;
2381
2382     /* Calculate all polygons */
2383     $Angle    = 0; $TopPlots = "";
2384     foreach($iValues as $Key => $Value)
2385      {
2386       $TopPlots[$Key][] = $XPos;
2387       $TopPlots[$Key][] = $YPos;
2388
2389       /* Process labels position & size */
2390       $Caption = "";
2391       if ( !($DrawLabels == PIE_NOLABEL) )
2392        {
2393         $TAngle   = $Angle+($Value*$SpliceRatio/2);
2394         if ($DrawLabels == PIE_PERCENTAGE)
2395          $Caption  = (round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2396         elseif ($DrawLabels == PIE_LABELS)
2397          $Caption  = $iLabels[$Key];
2398         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2399          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2400         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2401          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2402
2403         $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2404         $TextWidth  = $Position[2]-$Position[0];
2405         $TextHeight = abs($Position[1])+abs($Position[3]);
2406
2407         $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius+10) + $XPos;
2408
2409         if ( $TAngle > 0 && $TAngle < 180 )
2410          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+10) + $YPos + 4;
2411         else
2412          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+4) + $YPos - ($TextHeight/2);
2413
2414         if ( $TAngle > 90 && $TAngle < 270 )
2415          $TX = $TX - $TextWidth;
2416
2417         $C_TextColor = $this->AllocateColor($this->Picture,70,70,70);
2418         imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2419        }
2420
2421       /* Process pie slices */
2422       for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
2423        {
2424         $TopX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos;
2425         $TopY = sin($iAngle * 3.1418 / 180 ) * $Radius + $YPos;
2426
2427         $TopPlots[$Key][] = $TopX;
2428         $TopPlots[$Key][] = $TopY;
2429        }
2430
2431       $TopPlots[$Key][] = $XPos;
2432       $TopPlots[$Key][] = $YPos;
2433
2434       $Angle = $iAngle;
2435      }
2436     $PolyPlots = $TopPlots;
2437
2438     /* Set array values type to float --- PHP Bug with imagefilledpolygon casting to integer */
2439     foreach ($TopPlots as $Key => $Value)
2440      { foreach ($TopPlots[$Key] as $Key2 => $Value2) { settype($TopPlots[$Key][$Key2],"float"); } }
2441
2442     /* Draw Top polygons */
2443     foreach ($PolyPlots as $Key => $Value)
2444      {
2445       $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
2446       imagefilledpolygon($this->Picture,$PolyPlots[$Key],(count($PolyPlots[$Key])+1)/2,$C_GraphLo);
2447      }
2448
2449     $this->drawCircle($XPos-.5,$YPos-.5,$Radius,$R,$G,$B);
2450     $this->drawCircle($XPos-.5,$YPos-.5,$Radius+.5,$R,$G,$B);
2451
2452     /* Draw Top polygons */
2453     foreach ($TopPlots as $Key => $Value)
2454      {
2455       for($j=0;$j<=count($TopPlots[$Key])-4;$j=$j+2)
2456        $this->drawLine($TopPlots[$Key][$j],$TopPlots[$Key][$j+1],$TopPlots[$Key][$j+2],$TopPlots[$Key][$j+3],$R,$G,$B);
2457      }
2458    }
2459
2460   function drawFlatPieGraphWithShadow($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals=0)
2461    {
2462     $this->drawFlatPieGraph($Data,$DataDescription,$XPos+$this->ShadowXDistance,$YPos+$this->ShadowYDistance,$Radius,PIE_NOLABEL,$SpliceDistance,$Decimals,TRUE);
2463     $this->drawFlatPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius,$DrawLabels,$SpliceDistance,$Decimals,FALSE);
2464    }
2465
2466   /* This function draw a flat pie chart */
2467   function drawFlatPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals=0,$AllBlack=FALSE)
2468    {
2469     /* Validate the Data and DataDescription array */
2470     $this->validateDataDescription("drawFlatPieGraph",$DataDescription,FALSE);
2471     $this->validateData("drawFlatPieGraph",$Data);
2472
2473     $ShadowStatus = $this->ShadowActive ; $this->ShadowActive = FALSE;
2474
2475     /* Determine pie sum */
2476     $Series = 0; $PieSum = 0;
2477     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2478      {
2479       if ( $ColName != $DataDescription["Position"] )
2480        {
2481         $Series++;
2482         foreach ( $Data as $Key => $Values )
2483          {
2484           if ( isset($Data[$Key][$ColName]))
2485            $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]];
2486          }
2487        }
2488      }
2489
2490     /* Validate serie */
2491     if ( $Series != 1 )
2492      {
2493       RaiseFatal("Pie chart can only accept one serie of data.");
2494       return(0);
2495      }
2496
2497     $SpliceRatio   = 360 / $PieSum;
2498     $SplicePercent = 100 / $PieSum;
2499
2500     /* Calculate all polygons */
2501     $Angle = 0; $TopPlots = "";
2502     foreach($iValues as $Key => $Value)
2503      {
2504       $XOffset = cos(($Angle+($Value/2*$SpliceRatio)) * 3.1418 / 180 ) * $SpliceDistance;
2505       $YOffset = sin(($Angle+($Value/2*$SpliceRatio)) * 3.1418 / 180 ) * $SpliceDistance;
2506
2507       $TopPlots[$Key][] = round($XPos + $XOffset);
2508       $TopPlots[$Key][] = round($YPos + $YOffset);
2509
2510       if ( $AllBlack )
2511        { $Rc = $this->ShadowRColor; $Gc = $this->ShadowGColor; $Bc = $this->ShadowBColor; }
2512       else
2513        { $Rc = $this->Palette[$Key]["R"]; $Gc = $this->Palette[$Key]["G"]; $Bc = $this->Palette[$Key]["B"]; }
2514
2515       $XLineLast = ""; $YLineLast = "";
2516
2517       /* Process labels position & size */
2518       $Caption = "";
2519       if ( !($DrawLabels == PIE_NOLABEL) )
2520        {
2521         $TAngle   = $Angle+($Value*$SpliceRatio/2);
2522         if ($DrawLabels == PIE_PERCENTAGE)
2523          $Caption  = (round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2524         elseif ($DrawLabels == PIE_LABELS)
2525          $Caption  = $iLabels[$Key];
2526         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2527          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2528         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2529          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2530
2531         $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2532         $TextWidth  = $Position[2]-$Position[0];
2533         $TextHeight = abs($Position[1])+abs($Position[3]);
2534
2535         $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius+10+$SpliceDistance) + $XPos;
2536
2537         if ( $TAngle > 0 && $TAngle < 180 )
2538          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+10+$SpliceDistance) + $YPos + 4;
2539         else
2540          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+$SpliceDistance+4) + $YPos - ($TextHeight/2);
2541
2542         if ( $TAngle > 90 && $TAngle < 270 )
2543          $TX = $TX - $TextWidth;
2544
2545         $C_TextColor = $this->AllocateColor($this->Picture,70,70,70);
2546         imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2547        }
2548
2549       /* Process pie slices */
2550       if ( !$AllBlack )
2551        $