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
7 http://pchart.sourceforge.net
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.
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.
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/>.
22 Class initialisation :
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)
41 addBorder($Width=3,$R=0,$G=0,$B=0)
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)
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)
90 setImageMap($Mode=TRUE,$GraphID="MyGraph")
92 getSavedImageMap($MapName,$Flush=TRUE)
97 /* Declare some script wide constants */
98 define("SCALE_NORMAL",1);
99 define("SCALE_ADDALL",2);
100 define("SCALE_START0",3);
101 define("SCALE_ADDALLSTART0",4);
102 define("PIE_PERCENTAGE", 1);
103 define("PIE_LABELS",2);
104 define("PIE_NOLABEL",3);
105 define("PIE_PERCENTAGE_LABEL", 4);
106 define("TARGET_GRAPHAREA",1);
107 define("TARGET_BACKGROUND",2);
108 define("ALIGN_TOP_LEFT",1);
109 define("ALIGN_TOP_CENTER",2);
110 define("ALIGN_TOP_RIGHT",3);
111 define("ALIGN_LEFT",4);
112 define("ALIGN_CENTER",5);
113 define("ALIGN_RIGHT",6);
114 define("ALIGN_BOTTOM_LEFT",7);
115 define("ALIGN_BOTTOM_CENTER",8);
116 define("ALIGN_BOTTOM_RIGHT",9);
118 /* pChart class definition */
121 /* Palettes definition */
122 var $Palette = array("0"=>array("R"=>188,"G"=>224,"B"=>46),
123 "1"=>array("R"=>224,"G"=>100,"B"=>46),
124 "2"=>array("R"=>224,"G"=>214,"B"=>46),
125 "3"=>array("R"=>46,"G"=>151,"B"=>224),
126 "4"=>array("R"=>176,"G"=>46,"B"=>224),
127 "5"=>array("R"=>224,"G"=>46,"B"=>117),
128 "6"=>array("R"=>92,"G"=>224,"B"=>46),
129 "7"=>array("R"=>224,"G"=>176,"B"=>46));
131 /* Some static vars used in the class */
135 var $ImageMap = NULL;
137 /* Error management */
138 var $ErrorReporting = FALSE;
139 var $ErrorInterface = "CLI";
141 var $ErrorFontName = "Fonts/pf_arma_five.ttf";
142 var $ErrorFontSize = 6;
144 /* vars related to the graphing area */
145 var $GArea_X1 = NULL;
146 var $GArea_Y1 = NULL;
147 var $GArea_X2 = NULL;
148 var $GArea_Y2 = NULL;
149 var $GAreaXOffset = NULL;
154 var $Divisions = NULL;
155 var $XDivisions = NULL;
156 var $DivisionHeight = NULL;
157 var $XDivisionHeight = NULL;
158 var $DivisionCount = NULL;
159 var $XDivisionCount = NULL;
160 var $DivisionRatio = NULL;
161 var $XDivisionRatio = NULL;
162 var $DivisionWidth = NULL;
163 var $DataCount = NULL;
164 var $Currency = "\$";
166 /* Text format related vars */
167 var $FontName = NULL;
168 var $FontSize = NULL;
169 var $DateFormat = "d/m/Y";
171 /* Lines format related vars */
173 var $LineDotSize = 0;
175 /* Layer related vars */
178 /* Set antialias quality : 0 is maximum, 100 minimum*/
179 var $AntialiasQuality = 0;
181 /* Shadow settings */
182 var $ShadowActive = FALSE;
183 var $ShadowXDistance = 1;
184 var $ShadowYDistance = 1;
185 var $ShadowRColor = 60;
186 var $ShadowGColor = 60;
187 var $ShadowBColor = 60;
188 var $ShadowAlpha = 50;
191 /* Image Map settings */
192 var $BuildMap = FALSE;
193 var $MapFunction = NULL;
194 var $tmpFolder = "tmp/";
197 /* This function create the background picture */
198 function pChart($XSize,$YSize)
200 $this->XSize = $XSize;
201 $this->YSize = $YSize;
202 $this->Picture = imagecreatetruecolor($XSize,$YSize);
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 $this->setFontProperties("tahoma.ttf",8);
209 /* Set if warnings should be reported */
210 function reportWarnings($Interface="CLI")
212 $this->ErrorReporting = TRUE;
213 $this->ErrorInterface = $Interface;
216 /* Set the font properties */
217 function setFontProperties($FontName,$FontSize)
219 $this->FontName = $FontName;
220 $this->FontSize = $FontSize;
223 /* Set the shadow properties */
224 function setShadowProperties($XDistance=1,$YDistance=1,$R=60,$G=60,$B=60,$Alpha=50,$Blur=0)
226 $this->ShadowActive = TRUE;
227 $this->ShadowXDistance = $XDistance;
228 $this->ShadowYDistance = $YDistance;
229 $this->ShadowRColor = $R;
230 $this->ShadowGColor = $G;
231 $this->ShadowBColor = $B;
232 $this->ShadowAlpha = $Alpha;
233 $this->ShadowBlur = $Blur;
236 /* Remove shadow option */
237 function clearShadow()
239 $this->ShadowActive = FALSE;
242 /* Set Palette color */
243 function setColorPalette($ID,$R,$G,$B)
245 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
246 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
247 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
249 $this->Palette[$ID]["R"] = $R;
250 $this->Palette[$ID]["G"] = $G;
251 $this->Palette[$ID]["B"] = $B;
254 /* Create a color palette shading from one color to another */
255 function createColorGradientPalette($R1,$G1,$B1,$R2,$G2,$B2,$Shades)
257 $RFactor = ($R2-$R1)/$Shades;
258 $GFactor = ($G2-$G1)/$Shades;
259 $BFactor = ($B2-$B1)/$Shades;
261 for($i=0;$i<=$Shades-1;$i++)
263 $this->Palette[$i]["R"] = $R1+$RFactor*$i;
264 $this->Palette[$i]["G"] = $G1+$GFactor*$i;
265 $this->Palette[$i]["B"] = $B1+$BFactor*$i;
269 /* Load Color Palette from file */
270 function loadColorPalette($FileName,$Delimiter=",")
272 $handle = @fopen($FileName,"r");
276 while (!feof($handle))
278 $buffer = fgets($handle, 4096);
279 $buffer = str_replace(chr(10),"",$buffer);
280 $buffer = str_replace(chr(13),"",$buffer);
281 $Values = split($Delimiter,$buffer);
282 if ( count($Values) == 3 )
284 $this->Palette[$ColorID]["R"] = $Values[0];
285 $this->Palette[$ColorID]["G"] = $Values[1];
286 $this->Palette[$ColorID]["B"] = $Values[2];
294 function setLineStyle($Width=1,$DotSize=0)
296 $this->LineWidth = $Width;
297 $this->LineDotSize = $DotSize;
300 /* Set currency symbol */
301 function setCurrency($Currency)
303 $this->Currency = $Currency;
306 /* Set the graph area location */
307 function setGraphArea($X1,$Y1,$X2,$Y2)
309 $this->GArea_X1 = $X1;
310 $this->GArea_Y1 = $Y1;
311 $this->GArea_X2 = $X2;
312 $this->GArea_Y2 = $Y2;
315 /* Prepare the graph area */
316 function drawGraphArea($R,$G,$B,$Stripe=FALSE)
318 $this->drawFilledRectangle($this->GArea_X1,$this->GArea_Y1,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B,FALSE);
319 $this->drawRectangle($this->GArea_X1,$this->GArea_Y1,$this->GArea_X2,$this->GArea_Y2,$R-40,$G-40,$B-40);
323 $R2 = $R-15; if ( $R2 < 0 ) { $R2 = 0; }
324 $G2 = $R-15; if ( $G2 < 0 ) { $G2 = 0; }
325 $B2 = $R-15; if ( $B2 < 0 ) { $B2 = 0; }
327 $LineColor =$this->AllocateColor($this->Picture,$R2,$G2,$B2);
328 $SkewWidth = $this->GArea_Y2-$this->GArea_Y1-1;
330 for($i=$this->GArea_X1-$SkewWidth;$i<=$this->GArea_X2;$i=$i+4)
332 $X1 = $i; $Y1 = $this->GArea_Y2;
333 $X2 = $i+$SkewWidth; $Y2 = $this->GArea_Y1;
336 if ( $X1 < $this->GArea_X1 )
337 { $X1 = $this->GArea_X1; $Y1 = $this->GArea_Y1 + $X2 - $this->GArea_X1 + 1; }
339 if ( $X2 >= $this->GArea_X2 )
340 { $Y2 = $this->GArea_Y1 + $X2 - $this->GArea_X2 +1; $X2 = $this->GArea_X2 - 1; }
341 // * Fixed in 1.27 * { $X2 = $this->GArea_X2 - 1; $Y2 = $this->GArea_Y2 - ($this->GArea_X2 - $X1); }
343 imageline($this->Picture,$X1,$Y1,$X2,$Y2+1,$LineColor);
348 /* Allow you to clear the scale : used if drawing multiple charts */
349 function clearScale()
355 $this->Divisions = NULL;
356 $this->XDivisions = NULL; }
358 /* Allow you to fix the scale, use this to bypass the automatic scaling */
359 function setFixedScale($VMin,$VMax,$Divisions=5,$VXMin=0,$VXMax=0,$XDivisions=5)
363 $this->Divisions = $Divisions;
367 $this->VXMin = $VXMin;
368 $this->VXMax = $VXMax;
369 $this->XDivisions = $XDivisions;
373 /* Wrapper to the drawScale() function allowing a second scale to be drawn */
374 function drawRightScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1)
376 $this->drawScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks,$Angle,$Decimals,$WithMargin,$SkipLabels,TRUE);
379 /* Compute and draw the scale */
380 function drawScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1,$RightScale=FALSE)
382 /* Validate the Data and DataDescription array */
383 $this->validateData("drawScale",$Data);
385 $C_TextColor =$this->AllocateColor($this->Picture,$R,$G,$B);
387 $this->drawLine($this->GArea_X1,$this->GArea_Y1,$this->GArea_X1,$this->GArea_Y2,$R,$G,$B);
388 $this->drawLine($this->GArea_X1,$this->GArea_Y2,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B);
390 if ( $this->VMin == NULL && $this->VMax == NULL)
392 if (isset($DataDescription["Values"][0]))
394 $this->VMin = $Data[0][$DataDescription["Values"][0]];
395 $this->VMax = $Data[0][$DataDescription["Values"][0]];
397 else { $this->VMin = 2147483647; $this->VMax = -2147483647; }
399 /* Compute Min and Max values */
400 if ( $ScaleMode == SCALE_NORMAL || $ScaleMode == SCALE_START0 )
402 if ( $ScaleMode == SCALE_START0 ) { $this->VMin = 0; }
404 foreach ( $Data as $Key => $Values )
406 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
408 if (isset($Data[$Key][$ColName]))
410 $Value = $Data[$Key][$ColName];
412 if ( is_numeric($Value) )
414 if ( $this->VMax < $Value) { $this->VMax = $Value; }
415 if ( $this->VMin > $Value) { $this->VMin = $Value; }
421 elseif ( $ScaleMode == SCALE_ADDALL || $ScaleMode == SCALE_ADDALLSTART0 ) /* Experimental */
423 if ( $ScaleMode == SCALE_ADDALLSTART0 ) { $this->VMin = 0; }
425 foreach ( $Data as $Key => $Values )
428 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
430 if (isset($Data[$Key][$ColName]))
432 $Value = $Data[$Key][$ColName];
433 if ( is_numeric($Value) )
437 if ( $this->VMax < $Sum) { $this->VMax = $Sum; }
438 if ( $this->VMin > $Sum) { $this->VMin = $Sum; }
442 if ( $this->VMax > preg_replace('/\.[0-9]+/','',$this->VMax) )
443 $this->VMax = preg_replace('/\.[0-9]+/','',$this->VMax)+1;
445 /* If all values are the same */
446 if ( $this->VMax == $this->VMin )
448 if ( $this->VMax >= 0 ) { $this->VMax++; }
449 else { $this->VMin--; }
452 $DataRange = $this->VMax - $this->VMin;
453 if ( $DataRange == 0 ) { $DataRange = .1; }
455 /* Compute automatic scaling */
456 $ScaleOk = FALSE; $Factor = 1;
457 $MinDivHeight = 25; $MaxDivs = ($this->GArea_Y2 - $this->GArea_Y1) / $MinDivHeight;
459 if ( $this->VMin == 0 && $this->VMax == 0 )
460 { $this->VMin = 0; $this->VMax = 2; $Scale = 1; $Divisions = 2;}
461 elseif ($MaxDivs > 1)
465 $Scale1 = ( $this->VMax - $this->VMin ) / $Factor;
466 $Scale2 = ( $this->VMax - $this->VMin ) / $Factor / 2;
467 $Scale4 = ( $this->VMax - $this->VMin ) / $Factor / 4;
469 if ( $Scale1 > 1 && $Scale1 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale1); $Scale = 1;}
470 if ( $Scale2 > 1 && $Scale2 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale2); $Scale = 2;}
473 if ( $Scale2 > 1 ) { $Factor = $Factor * 10; }
474 if ( $Scale2 < 1 ) { $Factor = $Factor / 10; }
478 if ( floor($this->VMax / $Scale / $Factor) != $this->VMax / $Scale / $Factor)
480 $GridID = floor ( $this->VMax / $Scale / $Factor) + 1;
481 $this->VMax = $GridID * $Scale * $Factor;
485 if ( floor($this->VMin / $Scale / $Factor) != $this->VMin / $Scale / $Factor)
487 $GridID = floor( $this->VMin / $Scale / $Factor);
488 $this->VMin = $GridID * $Scale * $Factor;
492 else /* Can occurs for small graphs */
495 if ( !isset($Divisions) )
498 if ($Scale == 1 && $Divisions%2 == 1)
502 $Divisions = $this->Divisions;
504 $this->DivisionCount = $Divisions;
506 $DataRange = $this->VMax - $this->VMin;
507 if ( $DataRange == 0 ) { $DataRange = .1; }
509 $this->DivisionHeight = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $Divisions;
510 $this->DivisionRatio = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $DataRange;
512 $this->GAreaXOffset = 0;
513 if ( count($Data) > 1 )
515 if ( $WithMargin == FALSE )
516 $this->DivisionWidth = ( $this->GArea_X2 - $this->GArea_X1 ) / (count($Data)-1);
519 $this->DivisionWidth = ( $this->GArea_X2 - $this->GArea_X1 ) / (count($Data));
520 $this->GAreaXOffset = $this->DivisionWidth / 2;
525 $this->DivisionWidth = $this->GArea_X2 - $this->GArea_X1;
526 $this->GAreaXOffset = $this->DivisionWidth / 2;
529 $this->DataCount = count($Data);
531 if ( $DrawTicks == FALSE )
534 $YPos = $this->GArea_Y2; $XMin = NULL;
535 for($i=1;$i<=$Divisions+1;$i++)
538 $this->drawLine($this->GArea_X2,$YPos,$this->GArea_X2+5,$YPos,$R,$G,$B);
540 $this->drawLine($this->GArea_X1,$YPos,$this->GArea_X1-5,$YPos,$R,$G,$B);
542 $Value = $this->VMin + ($i-1) * (( $this->VMax - $this->VMin ) / $Divisions);
543 $Value = round($Value * pow(10,$Decimals)) / pow(10,$Decimals);
544 if ( $DataDescription["Format"]["Y"] == "number" )
545 $Value = $Value.$DataDescription["Unit"]["Y"];
546 if ( $DataDescription["Format"]["Y"] == "time" )
547 $Value = $this->ToTime($Value);
548 if ( $DataDescription["Format"]["Y"] == "date" )
549 $Value = $this->ToDate($Value);
550 if ( $DataDescription["Format"]["Y"] == "metric" )
551 $Value = $this->ToMetric($Value);
552 if ( $DataDescription["Format"]["Y"] == "currency" )
553 $Value = $this->ToCurrency($Value);
555 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value);
556 $TextWidth = $Position[2]-$Position[0];
560 imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X2+10,$YPos+($this->FontSize/2),$C_TextColor,$this->FontName,$Value);
561 if ( $XMin < $this->GArea_X2+15+$TextWidth || $XMin == NULL ) { $XMin = $this->GArea_X2+15+$TextWidth; }
565 imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1-10-$TextWidth,$YPos+($this->FontSize/2),$C_TextColor,$this->FontName,$Value);
566 if ( $XMin > $this->GArea_X1-10-$TextWidth || $XMin == NULL ) { $XMin = $this->GArea_X1-10-$TextWidth; }
569 $YPos = $YPos - $this->DivisionHeight;
572 /* Write the Y Axis caption if set */
573 if ( isset($DataDescription["Axis"]["Y"]) )
575 $Position = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["Y"]);
576 $TextHeight = abs($Position[1])+abs($Position[3]);
577 $TextTop = (($this->GArea_Y2 - $this->GArea_Y1) / 2) + $this->GArea_Y1 + ($TextHeight/2);
580 imagettftext($this->Picture,$this->FontSize,90,$XMin+$this->FontSize,$TextTop,$C_TextColor,$this->FontName,$DataDescription["Axis"]["Y"]);
582 imagettftext($this->Picture,$this->FontSize,90,$XMin-$this->FontSize,$TextTop,$C_TextColor,$this->FontName,$DataDescription["Axis"]["Y"]);
585 /* Horizontal Axis */
586 $XPos = $this->GArea_X1 + $this->GAreaXOffset;
587 $ID = 1; $YMax = NULL;
588 foreach ( $Data as $Key => $Values )
590 if ( $ID % $SkipLabels == 0 )
592 $this->drawLine(floor($XPos),$this->GArea_Y2,floor($XPos),$this->GArea_Y2+5,$R,$G,$B);
593 $Value = $Data[$Key][$DataDescription["Position"]];
594 if ( $DataDescription["Format"]["X"] == "number" )
595 $Value = $Value.$DataDescription["Unit"]["X"];
596 if ( $DataDescription["Format"]["X"] == "time" )
597 $Value = $this->ToTime($Value);
598 if ( $DataDescription["Format"]["X"] == "date" )
599 $Value = $this->ToDate($Value);
600 if ( $DataDescription["Format"]["X"] == "metric" )
601 $Value = $this->ToMetric($Value);
602 if ( $DataDescription["Format"]["X"] == "currency" )
603 $Value = $this->ToCurrency($Value);
605 $Position = imageftbbox($this->FontSize,$Angle,$this->FontName,$Value);
606 $TextWidth = abs($Position[2])+abs($Position[0]);
607 $TextHeight = abs($Position[1])+abs($Position[3]);
611 $YPos = $this->GArea_Y2+18;
612 imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-floor($TextWidth/2),$YPos,$C_TextColor,$this->FontName,$Value);
616 $YPos = $this->GArea_Y2+10+$TextHeight;
618 imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
620 imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)+$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
622 if ( $YMax < $YPos || $YMax == NULL ) { $YMax = $YPos; }
625 $XPos = $XPos + $this->DivisionWidth;
629 /* Write the X Axis caption if set */
630 if ( isset($DataDescription["Axis"]["X"]) )
632 $Position = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["X"]);
633 $TextWidth = abs($Position[2])+abs($Position[0]);
634 $TextLeft = (($this->GArea_X2 - $this->GArea_X1) / 2) + $this->GArea_X1 + ($TextWidth/2);
635 imagettftext($this->Picture,$this->FontSize,0,$TextLeft,$YMax+$this->FontSize+5,$C_TextColor,$this->FontName,$DataDescription["Axis"]["X"]);
639 /* Compute and draw the scale for X/Y charts */
640 function drawXYScale($Data,$DataDescription,$YSerieName,$XSerieName,$R,$G,$B,$WithMargin=0,$Angle=0,$Decimals=1)
642 /* Validate the Data and DataDescription array */
643 $this->validateData("drawScale",$Data);
645 $C_TextColor =$this->AllocateColor($this->Picture,$R,$G,$B);
647 $this->drawLine($this->GArea_X1,$this->GArea_Y1,$this->GArea_X1,$this->GArea_Y2,$R,$G,$B);
648 $this->drawLine($this->GArea_X1,$this->GArea_Y2,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B);
650 /* Process Y scale */
651 if ( $this->VMin == NULL && $this->VMax == NULL)
653 $this->VMin = $Data[0][$YSerieName];
654 $this->VMax = $Data[0][$YSerieName];
656 foreach ( $Data as $Key => $Values )
658 if (isset($Data[$Key][$YSerieName]))
660 $Value = $Data[$Key][$YSerieName];
661 if ( $this->VMax < $Value) { $this->VMax = $Value; }
662 if ( $this->VMin > $Value) { $this->VMin = $Value; }
666 if ( $this->VMax > preg_replace('/\.[0-9]+/','',$this->VMax) )
667 $this->VMax = preg_replace('/\.[0-9]+/','',$this->VMax)+1;
669 $DataRange = $this->VMax - $this->VMin;
670 if ( $DataRange == 0 ) { $DataRange = .1; }
672 /* Compute automatic scaling */
673 $ScaleOk = FALSE; $Factor = 1;
674 $MinDivHeight = 25; $MaxDivs = ($this->GArea_Y2 - $this->GArea_Y1) / $MinDivHeight;
676 if ( $this->VMin == 0 && $this->VMax == 0 )
677 { $this->VMin = 0; $this->VMax = 2; $Scale = 1; $Divisions = 2;}
678 elseif ($MaxDivs > 1)
682 $Scale1 = ( $this->VMax - $this->VMin ) / $Factor;
683 $Scale2 = ( $this->VMax - $this->VMin ) / $Factor / 2;
684 $Scale4 = ( $this->VMax - $this->VMin ) / $Factor / 4;
686 if ( $Scale1 > 1 && $Scale1 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale1); $Scale = 1;}
687 if ( $Scale2 > 1 && $Scale2 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale2); $Scale = 2;}
690 if ( $Scale2 > 1 ) { $Factor = $Factor * 10; }
691 if ( $Scale2 < 1 ) { $Factor = $Factor / 10; }
695 if ( floor($this->VMax / $Scale / $Factor) != $this->VMax / $Scale / $Factor)
697 $GridID = floor ( $this->VMax / $Scale / $Factor) + 1;
698 $this->VMax = $GridID * $Scale * $Factor;
702 if ( floor($this->VMin / $Scale / $Factor) != $this->VMin / $Scale / $Factor)
704 $GridID = floor( $this->VMin / $Scale / $Factor);
705 $this->VMin = $GridID * $Scale * $Factor;
709 else /* Can occurs for small graphs */
712 if ( !isset($Divisions) )
715 if ( $this->isRealInt(($this->VMax-$this->VMin)/($Divisions-1)))
717 elseif ( $this->isRealInt(($this->VMax-$this->VMin)/($Divisions+1)))
721 $Divisions = $this->Divisions;
723 $this->DivisionCount = $Divisions;
725 $DataRange = $this->VMax - $this->VMin;
726 if ( $DataRange == 0 ) { $DataRange = .1; }
728 $this->DivisionHeight = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $Divisions;
729 $this->DivisionRatio = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $DataRange;
731 $YPos = $this->GArea_Y2; $XMin = NULL;
732 for($i=1;$i<=$Divisions+1;$i++)
734 $this->drawLine($this->GArea_X1,$YPos,$this->GArea_X1-5,$YPos,$R,$G,$B);
735 $Value = $this->VMin + ($i-1) * (( $this->VMax - $this->VMin ) / $Divisions);
736 $Value = round($Value * pow(10,$Decimals)) / pow(10,$Decimals);
737 if ( $DataDescription["Format"]["Y"] == "number" )
738 $Value = $Value.$DataDescription["Unit"]["Y"];
739 if ( $DataDescription["Format"]["Y"] == "time" )
740 $Value = $this->ToTime($Value);
741 if ( $DataDescription["Format"]["Y"] == "date" )
742 $Value = $this->ToDate($Value);
743 if ( $DataDescription["Format"]["Y"] == "metric" )
744 $Value = $this->ToMetric($Value);
745 if ( $DataDescription["Format"]["Y"] == "currency" )
746 $Value = $this->ToCurrency($Value);
748 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value);
749 $TextWidth = $Position[2]-$Position[0];
750 imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1-10-$TextWidth,$YPos+($this->FontSize/2),$C_TextColor,$this->FontName,$Value);
752 if ( $XMin > $this->GArea_X1-10-$TextWidth || $XMin == NULL ) { $XMin = $this->GArea_X1-10-$TextWidth; }
754 $YPos = $YPos - $this->DivisionHeight;
757 /* Process X scale */
758 if ( $this->VXMin == NULL && $this->VXMax == NULL)
760 $this->VXMin = $Data[0][$XSerieName];
761 $this->VXMax = $Data[0][$XSerieName];
763 foreach ( $Data as $Key => $Values )
765 if (isset($Data[$Key][$XSerieName]))
767 $Value = $Data[$Key][$XSerieName];
768 if ( $this->VXMax < $Value) { $this->VXMax = $Value; }
769 if ( $this->VXMin > $Value) { $this->VXMin = $Value; }
773 if ( $this->VXMax > preg_replace('/\.[0-9]+/','',$this->VXMax) )
774 $this->VXMax = preg_replace('/\.[0-9]+/','',$this->VXMax)+1;
776 $DataRange = $this->VMax - $this->VMin;
777 if ( $DataRange == 0 ) { $DataRange = .1; }
779 /* Compute automatic scaling */
780 $ScaleOk = FALSE; $Factor = 1;
781 $MinDivWidth = 25; $MaxDivs = ($this->GArea_X2 - $this->GArea_X1) / $MinDivWidth;
783 if ( $this->VXMin == 0 && $this->VXMax == 0 )
784 { $this->VXMin = 0; $this->VXMax = 2; $Scale = 1; $XDivisions = 2;}
785 elseif ($MaxDivs > 1)
789 $Scale1 = ( $this->VXMax - $this->VXMin ) / $Factor;
790 $Scale2 = ( $this->VXMax - $this->VXMin ) / $Factor / 2;
791 $Scale4 = ( $this->VXMax - $this->VXMin ) / $Factor / 4;
793 if ( $Scale1 > 1 && $Scale1 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $XDivisions = floor($Scale1); $Scale = 1;}
794 if ( $Scale2 > 1 && $Scale2 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $XDivisions = floor($Scale2); $Scale = 2;}
797 if ( $Scale2 > 1 ) { $Factor = $Factor * 10; }
798 if ( $Scale2 < 1 ) { $Factor = $Factor / 10; }
802 if ( floor($this->VXMax / $Scale / $Factor) != $this->VXMax / $Scale / $Factor)
804 $GridID = floor ( $this->VXMax / $Scale / $Factor) + 1;
805 $this->VXMax = $GridID * $Scale * $Factor;
809 if ( floor($this->VXMin / $Scale / $Factor) != $this->VXMin / $Scale / $Factor)
811 $GridID = floor( $this->VXMin / $Scale / $Factor);
812 $this->VXMin = $GridID * $Scale * $Factor;
816 else /* Can occurs for small graphs */
819 if ( !isset($XDivisions) )
822 if ( $this->isRealInt(($this->VXMax-$this->VXMin)/($XDivisions-1)))
824 elseif ( $this->isRealInt(($this->VXMax-$this->VXMin)/($XDivisions+1)))
828 $XDivisions = $this->XDivisions;
830 $this->XDivisionCount = $Divisions;
831 $this->DataCount = $Divisions + 2;
833 $XDataRange = $this->VXMax - $this->VXMin;
834 if ( $XDataRange == 0 ) { $XDataRange = .1; }
836 $this->DivisionWidth = ( $this->GArea_X2 - $this->GArea_X1 ) / $XDivisions;
837 $this->XDivisionRatio = ( $this->GArea_X2 - $this->GArea_X1 ) / $XDataRange;
839 $XPos = $this->GArea_X1; $YMax = NULL;
840 for($i=1;$i<=$XDivisions+1;$i++)
842 $this->drawLine($XPos,$this->GArea_Y2,$XPos,$this->GArea_Y2+5,$R,$G,$B);
844 $Value = $this->VXMin + ($i-1) * (( $this->VXMax - $this->VXMin ) / $XDivisions);
845 $Value = round($Value * pow(10,$Decimals)) / pow(10,$Decimals);
846 if ( $DataDescription["Format"]["Y"] == "number" )
847 $Value = $Value.$DataDescription["Unit"]["Y"];
848 if ( $DataDescription["Format"]["Y"] == "time" )
849 $Value = $this->ToTime($Value);
850 if ( $DataDescription["Format"]["Y"] == "date" )
851 $Value = $this->ToDate($Value);
852 if ( $DataDescription["Format"]["Y"] == "metric" )
853 $Value = $this->ToMetric($Value);
854 if ( $DataDescription["Format"]["Y"] == "currency" )
855 $Value = $this->ToCurrency($Value);
857 $Position = imageftbbox($this->FontSize,$Angle,$this->FontName,$Value);
858 $TextWidth = abs($Position[2])+abs($Position[0]);
859 $TextHeight = abs($Position[1])+abs($Position[3]);
863 $YPos = $this->GArea_Y2+18;
864 imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-floor($TextWidth/2),$YPos,$C_TextColor,$this->FontName,$Value);
868 $YPos = $this->GArea_Y2+10+$TextHeight;
870 imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
872 imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)+$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
875 if ( $YMax < $YPos || $YMax == NULL ) { $YMax = $YPos; }
877 $XPos = $XPos + $this->DivisionWidth;
880 /* Write the Y Axis caption if set */
881 if ( isset($DataDescription["Axis"]["Y"]) )
883 $Position = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["Y"]);
884 $TextHeight = abs($Position[1])+abs($Position[3]);
885 $TextTop = (($this->GArea_Y2 - $this->GArea_Y1) / 2) + $this->GArea_Y1 + ($TextHeight/2);
886 imagettftext($this->Picture,$this->FontSize,90,$XMin-$this->FontSize,$TextTop,$C_TextColor,$this->FontName,$DataDescription["Axis"]["Y"]);
889 /* Write the X Axis caption if set */
890 if ( isset($DataDescription["Axis"]["X"]) )
892 $Position = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["X"]);
893 $TextWidth = abs($Position[2])+abs($Position[0]);
894 $TextLeft = (($this->GArea_X2 - $this->GArea_X1) / 2) + $this->GArea_X1 + ($TextWidth/2);
895 imagettftext($this->Picture,$this->FontSize,0,$TextLeft,$YMax+$this->FontSize+5,$C_TextColor,$this->FontName,$DataDescription["Axis"]["X"]);
899 /* Compute and draw the scale */
900 function drawGrid($LineWidth,$Mosaic=TRUE,$R=220,$G=220,$B=220,$Alpha=100)
905 $LayerWidth = $this->GArea_X2-$this->GArea_X1;
906 $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
908 $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
909 $C_White =$this->AllocateColor($this->Layers[0],255,255,255);
910 imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
911 imagecolortransparent($this->Layers[0],$C_White);
913 $C_Rectangle =$this->AllocateColor($this->Layers[0],250,250,250);
915 $YPos = $LayerHeight; //$this->GArea_Y2-1;
917 for($i=0;$i<=$this->DivisionCount;$i++)
920 $YPos = $YPos - $this->DivisionHeight;
922 if ( $YPos <= 0 ) { $YPos = 1; }
926 imagefilledrectangle($this->Layers[0],1,$YPos,$LayerWidth-1,$LastY,$C_Rectangle);
929 imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
930 imagedestroy($this->Layers[0]);
933 /* Horizontal lines */
934 $YPos = $this->GArea_Y2 - $this->DivisionHeight;
935 for($i=1;$i<=$this->DivisionCount;$i++)
937 if ( $YPos > $this->GArea_Y1 && $YPos < $this->GArea_Y2 )
938 $this->drawDottedLine($this->GArea_X1,$YPos,$this->GArea_X2,$YPos,$LineWidth,$R,$G,$B);
940 $YPos = $YPos - $this->DivisionHeight;
944 if ( $this->GAreaXOffset == 0 )
945 { $XPos = $this->GArea_X1 + $this->DivisionWidth + $this->GAreaXOffset; $ColCount = $this->DataCount-2; }
947 { $XPos = $this->GArea_X1 + $this->GAreaXOffset; $ColCount = floor( ($this->GArea_X2 - $this->GArea_X1) / $this->DivisionWidth ); }
949 for($i=1;$i<=$ColCount;$i++)
951 if ( $XPos > $this->GArea_X1 && $XPos < $this->GArea_X2 )
952 $this->drawDottedLine(floor($XPos),$this->GArea_Y1,floor($XPos),$this->GArea_Y2,$LineWidth,$R,$G,$B);
953 $XPos = $XPos + $this->DivisionWidth;
957 /* retrieve the legends size */
958 function getLegendBoxSize($DataDescription)
960 if ( !isset($DataDescription["Description"]) )
963 /* <-10->[8]<-4->Text<-10-> */
964 $MaxWidth = 0; $MaxHeight = 8;
965 foreach($DataDescription["Description"] as $Key => $Value)
967 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value);
968 $TextWidth = $Position[2]-$Position[0];
969 $TextHeight = $Position[1]-$Position[7];
970 if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
971 $MaxHeight = $MaxHeight + $TextHeight + 4;
973 $MaxHeight = $MaxHeight - 3;
974 $MaxWidth = $MaxWidth + 32;
976 return(array($MaxWidth,$MaxHeight));
979 function getPieLegendBoxSize($Data)
981 $MaxWidth = 0; $MaxHeight = 8;
982 foreach($Data as $Value)
984 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value['Keys']);
985 $TextWidth = $Position[2]-$Position[0];
986 $TextHeight = $Position[1]-$Position[7];
987 if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
988 $MaxHeight = $MaxHeight + $TextHeight + 4;
990 $MaxHeight = $MaxHeight - 3;
991 $MaxWidth = $MaxWidth + 32;
993 return(array($MaxWidth,$MaxHeight));
996 /* Draw the data legends */
997 function drawLegend($XPos,$YPos,$DataDescription,$R,$G,$B,$Rs=-1,$Gs=-1,$Bs=-1,$Rt=0,$Gt=0,$Bt=0,$Border=TRUE)
999 /* Validate the Data and DataDescription array */
1000 $this->validateDataDescription("drawLegend",$DataDescription);
1002 if ( !isset($DataDescription["Description"]) )
1005 $C_TextColor =$this->AllocateColor($this->Picture,$Rt,$Gt,$Bt);
1007 /* <-10->[8]<-4->Text<-10-> */
1008 $MaxWidth = 0; $MaxHeight = 8;
1009 foreach($DataDescription["Description"] as $Key => $Value)
1011 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1012 $TextWidth = $Position[2]-$Position[0];
1013 $TextHeight = $Position[1]-$Position[7];
1014 if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
1015 $MaxHeight = $MaxHeight + $TextHeight + 4;
1017 $MaxHeight = $MaxHeight - 5;
1018 $MaxWidth = $MaxWidth + 32;
1020 if ( $Rs == -1 || $Gs == -1 || $Bs == -1 )
1021 { $Rs = $R-30; $Gs = $G-30; $Bs = $B-30; }
1025 $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$Rs,$Gs,$Bs);
1026 $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B);
1029 $YOffset = 4 + $this->FontSize; $ID = 0;
1030 foreach($DataDescription["Description"] as $Key => $Value)
1032 $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"]);
1033 imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value);
1035 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1036 $TextHeight = $Position[1]-$Position[7];
1038 $YOffset = $YOffset + $TextHeight + 4;
1043 /* Draw the data legends */
1044 function drawPieLegend($XPos,$YPos,$Data,$DataDescription,$R,$G,$B)
1046 /* Validate the Data and DataDescription array */
1047 $this->validateDataDescription("drawPieLegend",$DataDescription,FALSE);
1048 $this->validateData("drawPieLegend",$Data);
1050 if ( !isset($DataDescription["Position"]) )
1053 $C_TextColor =$this->AllocateColor($this->Picture,0,0,0);
1055 /* <-10->[8]<-4->Text<-10-> */
1056 $MaxWidth = 0; $MaxHeight = 8;
1057 foreach($Data as $Key => $Value)
1059 $Value2 = $Value[$DataDescription["Position"]];
1060 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value2);
1061 $TextWidth = $Position[2]-$Position[0];
1062 $TextHeight = $Position[1]-$Position[7];
1063 if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
1065 $MaxHeight = $MaxHeight + $TextHeight + 4;
1067 $MaxHeight = $MaxHeight - 3;
1068 $MaxWidth = $MaxWidth + 32;
1070 $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$R-30,$G-30,$B-30);
1071 $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B);
1073 $YOffset = 4 + $this->FontSize; $ID = 0;
1074 foreach($Data as $Key => $Value)
1076 $Value2 = $Value[$DataDescription["Position"]];
1077 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value2);
1078 $TextHeight = $Position[1]-$Position[7];
1079 $this->drawFilledRectangle($XPos+10,$YPos+$YOffset-6,$XPos+14,$YPos+$YOffset-2,$this->Palette[$ID]["R"],$this->Palette[$ID]["G"],$this->Palette[$ID]["B"]);
1081 imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value2);
1082 $YOffset = $YOffset + $TextHeight + 4;
1087 /* Draw the graph title */
1088 function drawTitle($XPos,$YPos,$Value,$R,$G,$B,$XPos2=-1,$YPos2=-1,$Shadow=FALSE)
1090 $C_TextColor = $this->AllocateColor($this->Picture,$R,$G,$B);
1094 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1095 $TextWidth = $Position[2]-$Position[0];
1096 $XPos = floor(( $XPos2 - $XPos - $TextWidth ) / 2 ) + $XPos;
1101 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1102 $TextHeight = $Position[5]-$Position[3];
1103 $YPos = floor(( $YPos2 - $YPos - $TextHeight ) / 2 ) + $YPos;
1108 $C_ShadowColor = $this->AllocateColor($this->Picture,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor);
1109 imagettftext($this->Picture,$this->FontSize,0,$XPos+$this->ShadowXDistance,$YPos+$this->ShadowYDistance,$C_ShadowColor,$this->FontName,$Value);
1112 imagettftext($this->Picture,$this->FontSize,0,$XPos,$YPos,$C_TextColor,$this->FontName,$Value);
1115 /* Draw a text box with text align & alpha properties */
1116 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)
1118 $Position = imageftbbox($this->FontSize,$Angle,$this->FontName,$Text);
1119 $TextWidth = $Position[2]-$Position[0];
1120 $TextHeight = $Position[5]-$Position[3];
1121 $AreaWidth = $X2 - $X1;
1122 $AreaHeight = $Y2 - $Y1;
1124 if ( $BgR != -1 && $BgG != -1 && $BgB != -1 )
1125 $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$BgR,$BgG,$BgB,FALSE,$Alpha);
1127 if ( $Align == ALIGN_TOP_LEFT ) { $X = $X1+1; $Y = $Y1+$this->FontSize+1; }
1128 if ( $Align == ALIGN_TOP_CENTER ) { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y1+$this->FontSize+1; }
1129 if ( $Align == ALIGN_TOP_RIGHT ) { $X = $X2-$TextWidth-1; $Y = $Y1+$this->FontSize+1; }
1130 if ( $Align == ALIGN_LEFT ) { $X = $X1+1; $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1131 if ( $Align == ALIGN_CENTER ) { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1132 if ( $Align == ALIGN_RIGHT ) { $X = $X2-$TextWidth-1; $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1133 if ( $Align == ALIGN_BOTTOM_LEFT ) { $X = $X1+1; $Y = $Y2-1; }
1134 if ( $Align == ALIGN_BOTTOM_CENTER ) { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y2-1; }
1135 if ( $Align == ALIGN_BOTTOM_RIGHT ) { $X = $X2-$TextWidth-1; $Y = $Y2-1; }
1137 $C_TextColor =$this->AllocateColor($this->Picture,$R,$G,$B);
1138 $C_ShadowColor =$this->AllocateColor($this->Picture,0,0,0);
1140 imagettftext($this->Picture,$this->FontSize,$Angle,$X+1,$Y+1,$C_ShadowColor,$this->FontName,$Text);
1142 imagettftext($this->Picture,$this->FontSize,$Angle,$X,$Y,$C_TextColor,$this->FontName,$Text);
1145 /* Compute and draw the scale */
1146 function drawTreshold($Value,$R,$G,$B,$ShowLabel=FALSE,$ShowOnRight=FALSE,$TickWidth=4,$FreeText=NULL)
1148 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
1149 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
1150 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
1152 $C_TextColor =$this->AllocateColor($this->Picture,$R,$G,$B);
1153 $Y = $this->GArea_Y2 - ($Value - $this->VMin) * $this->DivisionRatio;
1155 if ( $Y <= $this->GArea_Y1 || $Y >= $this->GArea_Y2 )
1158 if ( $TickWidth == 0 )
1159 $this->drawLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$R,$G,$B);
1161 $this->drawDottedLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$TickWidth,$R,$G,$B);
1165 if ( $FreeText == NULL )
1166 { $Label = $Value; } else { $Label = $FreeText; }
1169 imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X2+2,$Y+($this->FontSize/2),$C_TextColor,$this->FontName,$Label);
1171 imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1+2,$Y-($this->FontSize/2),$C_TextColor,$this->FontName,$Label);
1175 /* This function put a label on a specific point */
1176 function setLabel($Data,$DataDescription,$SerieName,$ValueName,$Caption,$R=210,$G=210,$B=210)
1178 /* Validate the Data and DataDescription array */
1179 $this->validateDataDescription("setLabel",$DataDescription);
1180 $this->validateData("setLabel",$Data);
1181 $ShadowFactor = 100;
1182 $C_Label =$this->AllocateColor($this->Picture,$R,$G,$B);
1183 $C_Shadow =$this->AllocateColor($this->Picture,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1184 $C_TextColor =$this->AllocateColor($this->Picture,0,0,0);
1186 $Cp = 0; $Found = FALSE;
1187 foreach ( $Data as $Key => $Value )
1189 if ( $Data[$Key][$DataDescription["Position"]] == $ValueName )
1190 { $NumericalValue = $Data[$Key][$SerieName]; $Found = TRUE; }
1195 $XPos = $this->GArea_X1 + $this->GAreaXOffset + ( $this->DivisionWidth * $Cp ) + 2;
1196 $YPos = $this->GArea_Y2 - ($NumericalValue - $this->VMin) * $this->DivisionRatio;
1198 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
1199 $TextHeight = $Position[3] - $Position[5];
1200 $TextWidth = $Position[2]-$Position[0] + 2;
1201 $TextOffset = floor($TextHeight/2);
1204 $Poly = array($XPos+1,$YPos+1,$XPos + 9,$YPos - $TextOffset,$XPos + 8,$YPos + $TextOffset + 2);
1205 imagefilledpolygon($this->Picture,$Poly,3,$C_Shadow);
1206 $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos - $TextOffset - .2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1207 $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos + $TextOffset + 2.2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1208 $this->drawFilledRectangle($XPos + 9,$YPos - $TextOffset-.2,$XPos + 13 + $TextWidth,$YPos + $TextOffset + 2.2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1211 $Poly = array($XPos,$YPos,$XPos + 8,$YPos - $TextOffset - 1,$XPos + 8,$YPos + $TextOffset + 1);
1212 imagefilledpolygon($this->Picture,$Poly,3,$C_Label);
1213 $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos - $TextOffset - 1.2,$R,$G,$B);
1214 $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos + $TextOffset + 1.2,$R,$G,$B);
1215 $this->drawFilledRectangle($XPos + 8,$YPos - $TextOffset - 1.2,$XPos + 12 + $TextWidth,$YPos + $TextOffset + 1.2,$R,$G,$B);
1217 imagettftext($this->Picture,$this->FontSize,0,$XPos + 10,$YPos + $TextOffset,$C_TextColor,$this->FontName,$Caption);
1220 /* This function draw a plot graph */
1221 function drawPlotGraph($Data,$DataDescription,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1,$Shadow=FALSE)
1223 /* Validate the Data and DataDescription array */
1224 $this->validateDataDescription("drawPlotGraph",$DataDescription);
1225 $this->validateData("drawPlotGraph",$Data);
1228 $Ro = $R2; $Go = $G2; $Bo = $B2;
1230 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1233 foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1234 { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1236 $R = $this->Palette[$ColorID]["R"];
1237 $G = $this->Palette[$ColorID]["G"];
1238 $B = $this->Palette[$ColorID]["B"];
1239 $R2 = $Ro; $G2 = $Go; $B2 = $Bo;
1241 if ( isset($DataDescription["Symbol"][$ColName]) )
1243 $Is_Alpha = ((ord ( file_get_contents ($DataDescription["Symbol"][$ColName], false, null, 25, 1)) & 6) & 4) == 4;
1245 $Infos = getimagesize($DataDescription["Symbol"][$ColName]);
1246 $ImageWidth = $Infos[0];
1247 $ImageHeight = $Infos[1];
1248 $Symbol = imagecreatefromgif($DataDescription["Symbol"][$ColName]);
1251 $XPos = $this->GArea_X1 + $this->GAreaXOffset;
1252 $Hsize = round($BigRadius/2);
1253 $R3 = -1; $G3 = -1; $B3 = -1;
1254 foreach ( $Data as $Key => $Values )
1256 $Value = $Data[$Key][$ColName];
1257 $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1259 /* Save point into the image map if option activated */
1260 if ( $this->BuildMap )
1261 $this->addToImageMap($XPos-$Hsize,$YPos-$Hsize,$XPos+1+$Hsize,$YPos+$Hsize+1,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Plot");
1263 if ( is_numeric($Value) )
1265 if ( !isset($DataDescription["Symbol"][$ColName]) )
1270 if ( $R3 !=-1 && $G3 !=-1 && $B3 !=-1 )
1271 $this->drawFilledCircle($XPos+2,$YPos+2,$BigRadius,$R3,$G3,$B3);
1274 $R3 = $this->Palette[$ColorID]["R"]-20; if ( $R3 < 0 ) { $R3 = 0; }
1275 $G3 = $this->Palette[$ColorID]["G"]-20; if ( $G3 < 0 ) { $G3 = 0; }
1276 $B3 = $this->Palette[$ColorID]["B"]-20; if ( $B3 < 0 ) { $B3 = 0; }
1277 $this->drawFilledCircle($XPos+2,$YPos+2,$BigRadius,$R3,$G3,$B3);
1281 $this->drawFilledCircle($XPos+1,$YPos+1,$BigRadius,$R,$G,$B);
1283 if ( $SmallRadius != 0 )
1285 if ( $R2 !=-1 && $G2 !=-1 && $B2 !=-1 )
1286 $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R2,$G2,$B2);
1289 $R2 = $this->Palette[$ColorID]["R"]-15; if ( $R2 < 0 ) { $R2 = 0; }
1290 $G2 = $this->Palette[$ColorID]["G"]-15; if ( $G2 < 0 ) { $G2 = 0; }
1291 $B2 = $this->Palette[$ColorID]["B"]-15; if ( $B2 < 0 ) { $B2 = 0; }
1293 $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R2,$G2,$B2);
1299 imagecopymerge($this->Picture,$Symbol,$XPos+1-$ImageWidth/2,$YPos+1-$ImageHeight/2,0,0,$ImageWidth,$ImageHeight,100);
1303 $XPos = $XPos + $this->DivisionWidth;
1309 /* This function draw a plot graph in an X/Y space */
1310 function drawXYPlotGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1,$Shadow=TRUE)
1312 $R = $this->Palette[$PaletteID]["R"];
1313 $G = $this->Palette[$PaletteID]["G"];
1314 $B = $this->Palette[$PaletteID]["B"];
1315 $R3 = -1; $G3 = -1; $B3 = -1;
1317 $YLast = -1; $XLast = -1;
1318 foreach ( $Data as $Key => $Values )
1320 if ( isset($Data[$Key][$YSerieName]) && isset($Data[$Key][$XSerieName]) )
1322 $X = $Data[$Key][$XSerieName];
1323 $Y = $Data[$Key][$YSerieName];
1325 $Y = $this->GArea_Y2 - (($Y-$this->VMin) * $this->DivisionRatio);
1326 $X = $this->GArea_X1 + (($X-$this->VXMin) * $this->XDivisionRatio);
1331 if ( $R3 !=-1 && $G3 !=-1 && $B3 !=-1 )
1332 $this->drawFilledCircle($X+2,$Y+2,$BigRadius,$R3,$G3,$B3);
1335 $R3 = $this->Palette[$PaletteID]["R"]-20; if ( $R < 0 ) { $R = 0; }
1336 $G3 = $this->Palette[$PaletteID]["G"]-20; if ( $G < 0 ) { $G = 0; }
1337 $B3 = $this->Palette[$PaletteID]["B"]-20; if ( $B < 0 ) { $B = 0; }
1338 $this->drawFilledCircle($X+2,$Y+2,$BigRadius,$R3,$G3,$B3);
1342 $this->drawFilledCircle($X+1,$Y+1,$BigRadius,$R,$G,$B);
1344 if ( $R2 !=-1 && $G2 !=-1 && $B2 !=-1 )
1345 $this->drawFilledCircle($X+1,$Y+1,$SmallRadius,$R2,$G2,$B2);
1348 $R2 = $this->Palette[$PaletteID]["R"]+20; if ( $R > 255 ) { $R = 255; }
1349 $G2 = $this->Palette[$PaletteID]["G"]+20; if ( $G > 255 ) { $G = 255; }
1350 $B2 = $this->Palette[$PaletteID]["B"]+20; if ( $B > 255 ) { $B = 255; }
1351 $this->drawFilledCircle($X+1,$Y+1,$SmallRadius,$R2,$G2,$B2);
1358 /* This function draw an area between two series */
1359 function drawArea($Data,$Serie1,$Serie2,$R,$G,$B,$Alpha = 50)
1361 /* Validate the Data and DataDescription array */
1362 $this->validateData("drawArea",$Data);
1364 $LayerWidth = $this->GArea_X2-$this->GArea_X1;
1365 $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1367 $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1368 $C_White =$this->AllocateColor($this->Layers[0],255,255,255);
1369 imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1370 imagecolortransparent($this->Layers[0],$C_White);
1372 $C_Graph =$this->AllocateColor($this->Layers[0],$R,$G,$B);
1374 $XPos = $this->GAreaXOffset;
1376 foreach ( $Data as $Key => $Values )
1378 $Value1 = $Data[$Key][$Serie1];
1379 $Value2 = $Data[$Key][$Serie2];
1380 $YPos1 = $LayerHeight - (($Value1-$this->VMin) * $this->DivisionRatio);
1381 $YPos2 = $LayerHeight - (($Value2-$this->VMin) * $this->DivisionRatio);
1383 if ( $LastXPos != -1 )
1386 $Points[] = $LastXPos; $Points[] = $LastYPos1;
1387 $Points[] = $LastXPos; $Points[] = $LastYPos2;
1388 $Points[] = $XPos; $Points[] = $YPos2;
1389 $Points[] = $XPos; $Points[] = $YPos1;
1391 imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph);
1394 $LastYPos1 = $YPos1;
1395 $LastYPos2 = $YPos2;
1398 $XPos = $XPos + $this->DivisionWidth;
1401 imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1402 imagedestroy($this->Layers[0]);
1406 /* This function write the values of the specified series */
1407 function writeValues($Data,$DataDescription,$Series)
1409 /* Validate the Data and DataDescription array */
1410 $this->validateDataDescription("writeValues",$DataDescription);
1411 $this->validateData("writeValues",$Data);
1413 if ( !is_array($Series) ) { $Series = array($Series); }
1415 foreach($Series as $Key => $Serie)
1418 foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1419 { if ( $keyI == $Serie ) { $ColorID = $ID; }; $ID++; }
1421 $XPos = $this->GArea_X1 + $this->GAreaXOffset;
1423 foreach ( $Data as $Key => $Values )
1425 if ( isset($Data[$Key][$Serie]) && is_numeric($Data[$Key][$Serie]))
1427 $Value = $Data[$Key][$Serie];
1428 $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1430 $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Value);
1431 $Width = $Positions[2] - $Positions[6]; $XOffset = $XPos - ($Width/2);
1432 $Height = $Positions[3] - $Positions[7]; $YOffset = $YPos - 4;
1434 $C_TextColor =$this->AllocateColor($this->Picture,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1435 imagettftext($this->Picture,$this->FontSize,0,$XOffset,$YOffset,$C_TextColor,$this->FontName,$Value);
1437 $XPos = $XPos + $this->DivisionWidth;
1443 /* This function draw a line graph */
1444 function drawLineGraph($Data,$DataDescription,$SerieName="")
1446 /* Validate the Data and DataDescription array */
1447 $this->validateDataDescription("drawLineGraph",$DataDescription);
1448 $this->validateData("drawLineGraph",$Data);
1451 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1454 foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1455 { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1457 if ( $SerieName == "" || $SerieName == $ColName )
1459 $XPos = $this->GArea_X1 + $this->GAreaXOffset;
1461 foreach ( $Data as $Key => $Values )
1463 if ( isset($Data[$Key][$ColName]))
1465 $Value = $Data[$Key][$ColName];
1466 $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1468 /* Save point into the image map if option activated */
1469 if ( $this->BuildMap )
1470 $this->addToImageMap($XPos-3,$YPos-3,$XPos+3,$YPos+3,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Line");
1472 if (!is_numeric($Value)) { $XLast = -1; }
1474 $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1478 if (!is_numeric($Value)) { $XLast = -1; }
1480 $XPos = $XPos + $this->DivisionWidth;
1487 /* This function draw a line graph */
1488 function drawXYGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0)
1490 $YLast = -1; $XLast = -1;
1491 foreach ( $Data as $Key => $Values )
1493 if ( isset($Data[$Key][$YSerieName]) && isset($Data[$Key][$XSerieName]) )
1495 $X = $Data[$Key][$XSerieName];
1496 $Y = $Data[$Key][$YSerieName];
1498 $Y = $this->GArea_Y2 - (($Y-$this->VMin) * $this->DivisionRatio);
1499 $X = $this->GArea_X1 + (($X-$this->VXMin) * $this->XDivisionRatio);
1501 if ($XLast != -1 && $YLast != -1)
1503 $this->drawLine($XLast,$YLast,$X,$Y,$this->Palette[$PaletteID]["R"],$this->Palette[$PaletteID]["G"],$this->Palette[$PaletteID]["B"],TRUE);
1512 /* This function draw a cubic curve */
1513 function drawCubicCurve($Data,$DataDescription,$Accuracy=.1,$SerieName="")
1515 /* Validate the Data and DataDescription array */
1516 $this->validateDataDescription("drawCubicCurve",$DataDescription);
1517 $this->validateData("drawCubicCurve",$Data);
1520 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1522 if ( $SerieName == "" || $SerieName == $ColName )
1524 $XIn = ""; $Yin = ""; $Yt = ""; $U = "";
1525 $XIn[0] = 0; $YIn[0] = 0;
1528 foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1529 { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1532 $XLast = -1; $Missing = "";
1533 foreach ( $Data as $Key => $Values )
1535 if ( isset($Data[$Key][$ColName]) )
1537 $Value = $Data[$Key][$ColName];
1538 $XIn[$Index] = $Index;
1539 $YIn[$Index] = $Value;
1540 if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; }
1549 for($i=2;$i<=$Index-1;$i++)
1551 $Sig = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]);
1552 $p = $Sig * $Yt[$i-1] + 2;
1553 $Yt[$i] = ($Sig - 1) / $p;
1554 $U[$i] = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]);
1555 $U[$i] = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p;
1560 $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1);
1562 for($k=$Index-1;$k>=1;$k--)
1563 $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k];
1565 $XPos = $this->GArea_X1 + $this->GAreaXOffset;
1566 for($X=1;$X<=$Index;$X=$X+$Accuracy)
1574 If ( $XIn[$k] >= $X )
1581 $h = $XIn[$khi] - $XIn[$klo];
1582 $a = ($XIn[$khi] - $X) / $h;
1583 $b = ($X - $XIn[$klo]) / $h;
1584 $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6;
1586 $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1588 if ( $XLast != -1 && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]) )
1589 $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1593 $XPos = $XPos + $this->DivisionWidth * $Accuracy;
1596 // Add potentialy missing values
1597 $XPos = $XPos - $this->DivisionWidth * $Accuracy;
1598 if ( $XPos < ($this->GArea_X2 - $this->GAreaXOffset) )
1600 $YPos = $this->GArea_Y2 - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio);
1601 $this->drawLine($XLast,$YLast,$this->GArea_X2-$this->GAreaXOffset,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1609 /* This function draw a filled cubic curve */
1610 function drawFilledCubicCurve($Data,$DataDescription,$Accuracy=.1,$Alpha=100,$AroundZero=FALSE)
1612 /* Validate the Data and DataDescription array */
1613 $this->validateDataDescription("drawFilledCubicCurve",$DataDescription);
1614 $this->validateData("drawFilledCubicCurve",$Data);
1616 $LayerWidth = $this->GArea_X2-$this->GArea_X1;
1617 $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1618 $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1619 if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; }
1622 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1624 $XIn = ""; $Yin = ""; $Yt = ""; $U = "";
1625 $XIn[0] = 0; $YIn[0] = 0;
1628 foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1629 { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1632 $XLast = -1; $Missing = "";
1633 foreach ( $Data as $Key => $Values )
1635 $Value = $Data[$Key][$ColName];
1636 $XIn[$Index] = $Index;
1637 $YIn[$Index] = $Value;
1638 if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; }
1646 for($i=2;$i<=$Index-1;$i++)
1648 $Sig = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]);
1649 $p = $Sig * $Yt[$i-1] + 2;
1650 $Yt[$i] = ($Sig - 1) / $p;
1651 $U[$i] = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]);
1652 $U[$i] = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p;
1657 $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1);
1659 for($k=$Index-1;$k>=1;$k--)
1660 $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k];
1663 $Points[] = $this->GAreaXOffset;
1664 $Points[] = $LayerHeight;
1666 $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1667 $C_White =$this->AllocateColor($this->Layers[0],255,255,255);
1668 imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1669 imagecolortransparent($this->Layers[0],$C_White);
1672 $XPos = $this->GAreaXOffset; $PointsCount = 2;
1673 for($X=1;$X<=$Index;$X=$X+$Accuracy)
1681 If ( $XIn[$k] >= $X )
1688 $h = $XIn[$khi] - $XIn[$klo];
1689 $a = ($XIn[$khi] - $X) / $h;
1690 $b = ($X - $XIn[$klo]) / $h;
1691 $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6;
1693 $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1695 if ( $YLast != NULL && $AroundZero && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]))
1698 $aPoints[] = $XLast;
1699 $aPoints[] = $YLast;
1703 $aPoints[] = $YZero;
1704 $aPoints[] = $XLast;
1705 $aPoints[] = $YZero;
1707 $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1708 imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph);
1711 if ( !isset($Missing[floor($X)]) || $YLast == NULL )
1719 $PointsCount++; $Points[] = $XLast; $Points[] = $LayerHeight;
1722 $YLast = $YPos; $XLast = $XPos;
1723 $XPos = $XPos + $this->DivisionWidth * $Accuracy;
1726 // Add potentialy missing values
1727 $XPos = $XPos - $this->DivisionWidth * $Accuracy;
1728 if ( $XPos < ($LayerWidth-$this->GAreaXOffset) )
1730 $YPos = $LayerHeight - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio);
1732 if ( $YLast != NULL && $AroundZero )
1735 $aPoints[] = $XLast;
1736 $aPoints[] = $YLast;
1737 $aPoints[] = $LayerWidth-$this->GAreaXOffset;
1739 $aPoints[] = $LayerWidth-$this->GAreaXOffset;
1740 $aPoints[] = $YZero;
1741 $aPoints[] = $XLast;
1742 $aPoints[] = $YZero;
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],$aPoints,4,$C_Graph);
1748 if ( $YIn[$klo] != "" && $YIn[$khi] != "" || $YLast == NULL )
1751 $Points[] = $LayerWidth-$this->GAreaXOffset;
1756 $Points[] = $LayerWidth-$this->GAreaXOffset;
1757 $Points[] = $LayerHeight;
1761 $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1762 imagefilledpolygon($this->Layers[0],$Points,$PointsCount,$C_Graph);
1765 imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1766 imagedestroy($this->Layers[0]);
1768 $this->drawCubicCurve($Data,$DataDescription,$Accuracy,$ColName);
1774 /* This function draw a filled line graph */
1775 function drawFilledLineGraph($Data,$DataDescription,$Alpha=100,$AroundZero=FALSE)
1777 $Empty = -2147483647;
1779 /* Validate the Data and DataDescription array */
1780 $this->validateDataDescription("drawFilledLineGraph",$DataDescription);
1781 $this->validateData("drawFilledLineGraph",$Data);
1783 $LayerWidth = $this->GArea_X2-$this->GArea_X1;
1784 $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1787 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1790 foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1791 { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1794 $aPoints[] = $this->GAreaXOffset;
1795 $aPoints[] = $LayerHeight;
1797 $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1798 $C_White = $this->AllocateColor($this->Layers[0],255,255,255);
1799 imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1800 imagecolortransparent($this->Layers[0],$C_White);
1802 $XPos = $this->GAreaXOffset;
1803 $XLast = -1; $PointsCount = 2;
1804 $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1805 if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; }
1808 foreach ( $Data as $Key => $Values )
1810 $Value = $Data[$Key][$ColName];
1811 $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1813 /* Save point into the image map if option activated */
1814 if ( $this->BuildMap )
1815 $this->addToImageMap($XPos-3,$YPos-3,$XPos+3,$YPos+3,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"FLine");
1817 if ( !is_numeric($Value) )
1820 $aPoints[] = $XLast;
1821 $aPoints[] = $LayerHeight;
1828 if ( $YLast <> $Empty )
1829 { $aPoints[] = $XPos; $aPoints[] = $YPos; }
1831 { $PointsCount++; $aPoints[] = $XPos; $aPoints[] = $LayerHeight; $aPoints[] = $XPos; $aPoints[] = $YPos; }
1833 if ($YLast <> $Empty && $AroundZero)
1836 $Points[] = $XLast; $Points[] = $YLast;
1844 $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1845 imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph);
1851 $XPos = $XPos + $this->DivisionWidth;
1853 $aPoints[] = $LayerWidth - $this->GAreaXOffset;
1854 $aPoints[] = $LayerHeight;
1856 if ( $AroundZero == FALSE )
1858 $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1859 imagefilledpolygon($this->Layers[0],$aPoints,$PointsCount,$C_Graph);
1862 imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1863 imagedestroy($this->Layers[0]);
1865 $this->drawLineGraph($Data,$DataDescription,$ColName);
1869 /* This function draw a bar graph */
1870 function drawOverlayBarGraph($Data,$DataDescription,$Alpha=50)
1872 /* Validate the Data and DataDescription array */
1873 $this->validateDataDescription("drawOverlayBarGraph",$DataDescription);
1874 $this->validateData("drawOverlayBarGraph",$Data);
1876 $LayerWidth = $this->GArea_X2-$this->GArea_X1;
1877 $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1880 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1883 foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1884 { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1886 $this->Layers[$GraphID] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1887 $C_White = $this->AllocateColor($this->Layers[$GraphID],255,255,255);
1888 $C_Graph = $this->AllocateColor($this->Layers[$GraphID],$this->Palette[$GraphID]["R"],$this->Palette[$GraphID]["G"],$this->Palette[$GraphID]["B"]);
1889 imagefilledrectangle($this->Layers[$GraphID],0,0,$LayerWidth,$LayerHeight,$C_White);
1890 imagecolortransparent($this->Layers[$GraphID],$C_White);
1892 $XWidth = $this->DivisionWidth / 4;
1893 $XPos = $this->GAreaXOffset;
1894 $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1895 $XLast = -1; $PointsCount = 2;
1896 foreach ( $Data as $Key => $Values )
1898 if ( isset($Data[$Key][$ColName]) )
1900 $Value = $Data[$Key][$ColName];
1901 if ( is_numeric($Value) )
1903 $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1905 imagefilledrectangle($this->Layers[$GraphID],$XPos-$XWidth,$YPos,$XPos+$XWidth,$YZero,$C_Graph);
1907 $X1 = floor($XPos - $XWidth + $this->GArea_X1); $Y1 = floor($YPos+$this->GArea_Y1) + .2;
1908 $X2 = floor($XPos + $XWidth + $this->GArea_X1); $Y2 = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1909 if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; }
1910 if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; }
1912 /* Save point into the image map if option activated */
1913 if ( $this->BuildMap )
1914 $this->addToImageMap($X1,min($Y1,$Y2),$X2,max($Y1,$Y2),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"oBar");
1916 $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1919 $XPos = $XPos + $this->DivisionWidth;
1925 for($i=0;$i<=($GraphID-1);$i++)
1927 imagecopymerge($this->Picture,$this->Layers[$i],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1928 imagedestroy($this->Layers[$i]);
1932 /* This function draw a bar graph */
1933 function drawBarGraph($Data,$DataDescription,$Shadow=FALSE,$Alpha=100)
1935 /* Validate the Data and DataDescription array */
1936 $this->validateDataDescription("drawBarGraph",$DataDescription);
1937 $this->validateData("drawBarGraph",$Data);
1940 $Series = count($DataDescription["Values"]);
1941 $SeriesWidth = $this->DivisionWidth / ($Series+1);
1942 $SerieXOffset = $this->DivisionWidth / 2 - $SeriesWidth / 2;
1944 $YZero = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1945 if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; }
1948 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1951 foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1952 { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1954 $XPos = $this->GArea_X1 + $this->GAreaXOffset - $SerieXOffset + $SeriesWidth * $SerieID;
1956 foreach ( $Data as $Key => $Values )
1958 if ( isset($Data[$Key][$ColName]))
1960 if ( is_numeric($Data[$Key][$ColName]) )
1962 $Value = $Data[$Key][$ColName];
1963 $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1965 /* Save point into the image map if option activated */
1966 if ( $this->BuildMap )
1968 $this->addToImageMap($XPos+1,min($YZero,$YPos),$XPos+$SeriesWidth-1,max($YZero,$YPos),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Bar");
1971 if ( $Shadow && $Alpha == 100 )
1972 $this->drawRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,25,25,25,TRUE,$Alpha);
1974 $this->drawFilledRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha);
1977 $XPos = $XPos + $this->DivisionWidth;
1983 /* This function draw a stacked bar graph */
1984 function drawStackedBarGraph($Data,$DataDescription,$Alpha=50,$Contiguous=FALSE)
1986 /* Validate the Data and DataDescription array */
1987 $this->validateDataDescription("drawBarGraph",$DataDescription);
1988 $this->validateData("drawBarGraph",$Data);
1991 $Series = count($DataDescription["Values"]);
1993 $SeriesWidth = $this->DivisionWidth;
1995 $SeriesWidth = $this->DivisionWidth * .8;
1997 $YZero = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1998 if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; }
2000 $SerieID = 0; $LastValue = "";
2001 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2004 foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2005 { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2007 $XPos = $this->GArea_X1 + $this->GAreaXOffset - $SeriesWidth / 2;
2009 foreach ( $Data as $Key => $Values )
2011 if ( isset($Data[$Key][$ColName]))
2013 if ( is_numeric($Data[$Key][$ColName]) )
2015 $Value = $Data[$Key][$ColName];
2017 if ( isset($LastValue[$Key]) )
2019 $YPos = $this->GArea_Y2 - ((($Value+$LastValue[$Key])-$this->VMin) * $this->DivisionRatio);
2020 $YBottom = $this->GArea_Y2 - (($LastValue[$Key]-$this->VMin) * $this->DivisionRatio);
2021 $LastValue[$Key] += $Value;
2025 $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
2027 $LastValue[$Key] = $Value;
2030 /* Save point into the image map if option activated */
2031 if ( $this->BuildMap )
2032 $this->addToImageMap($XPos+1,min($YBottom,$YPos),$XPos+$SeriesWidth-1,max($YBottom,$YPos),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"sBar");
2034 $this->drawFilledRectangle($XPos+1,$YBottom,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha);
2037 $XPos = $XPos + $this->DivisionWidth;
2043 /* This function draw a limits bar graphs */
2044 function drawLimitsGraph($Data,$DataDescription,$R=0,$G=0,$B=0)
2046 /* Validate the Data and DataDescription array */
2047 $this->validateDataDescription("drawLimitsGraph",$DataDescription);
2048 $this->validateData("drawLimitsGraph",$Data);
2050 $XWidth = $this->DivisionWidth / 4;
2051 $XPos = $this->GArea_X1 + $this->GAreaXOffset;
2053 foreach ( $Data as $Key => $Values )
2055 $Min = $Data[$Key][$DataDescription["Values"][0]];
2056 $Max = $Data[$Key][$DataDescription["Values"][0]];
2057 $GraphID = 0; $MaxID = 0; $MinID = 0;
2058 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2060 if ( isset($Data[$Key][$ColName]) )
2062 if ( $Data[$Key][$ColName] > $Max && is_numeric($Data[$Key][$ColName]))
2063 { $Max = $Data[$Key][$ColName]; $MaxID = $GraphID; }
2065 if ( isset($Data[$Key][$ColName]) && is_numeric($Data[$Key][$ColName]))
2067 if ( $Data[$Key][$ColName] < $Min )
2068 { $Min = $Data[$Key][$ColName]; $MinID = $GraphID; }
2073 $YPos = $this->GArea_Y2 - (($Max-$this->VMin) * $this->DivisionRatio);
2074 $X1 = floor($XPos - $XWidth); $Y1 = floor($YPos) - .2;
2075 $X2 = floor($XPos + $XWidth);
2076 if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; }
2077 if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; }
2079 $YPos = $this->GArea_Y2 - (($Min-$this->VMin) * $this->DivisionRatio);
2080 $Y2 = floor($YPos) + .2;
2082 $this->drawLine(floor($XPos)-.2,$Y1+1,floor($XPos)-.2,$Y2-1,$R,$G,$B,TRUE);
2083 $this->drawLine(floor($XPos)+.2,$Y1+1,floor($XPos)+.2,$Y2-1,$R,$G,$B,TRUE);
2084 $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$MaxID]["R"],$this->Palette[$MaxID]["G"],$this->Palette[$MaxID]["B"],FALSE);
2085 $this->drawLine($X1,$Y2,$X2,$Y2,$this->Palette[$MinID]["R"],$this->Palette[$MinID]["G"],$this->Palette[$MinID]["B"],FALSE);
2087 $XPos = $XPos + $this->DivisionWidth;
2091 /* This function draw radar axis centered on the graph area */
2092 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,$valueMod=1)
2094 /* Validate the Data and DataDescription array */
2095 $this->validateDataDescription("drawRadarAxis",$DataDescription);
2096 $this->validateData("drawRadarAxis",$Data);
2098 $C_TextColor = $this->AllocateColor($this->Picture,$A_R,$A_G,$A_B);
2100 /* Draw radar axis */
2101 $Points = count($Data);
2102 $Radius = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2103 $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1;
2104 $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1;
2106 /* Search for the max value */
2107 if ( $MaxValue == -1 )
2109 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2111 foreach ( $Data as $Key => $Values )
2113 if ( isset($Data[$Key][$ColName]))
2114 if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; }
2119 /* Draw the mosaic */
2122 $RadiusScale = $Radius / $MaxValue;
2123 for ( $t=1; $t<=$MaxValue-1; $t++)
2125 $TRadius = $RadiusScale * $t;
2128 for ( $i=0; $i<=$Points; $i++)
2130 $Angle = -90 + $i * 360/$Points;
2131 $X1 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2132 $Y1 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2133 $X2 = cos($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $XCenter;
2134 $Y2 = sin($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $YCenter;
2136 if ( $t % 2 == 1 && $LastX1 != -1)
2139 $Plots[] = $X1; $Plots[] = $Y1;
2140 $Plots[] = $X2; $Plots[] = $Y2;
2141 $Plots[] = $LastX2; $Plots[] = $LastY2;
2142 $Plots[] = $LastX1; $Plots[] = $LastY1;
2144 $C_Graph = $this->AllocateColor($this->Picture,250,250,250);
2145 imagefilledpolygon($this->Picture,$Plots,(count($Plots)+1)/2,$C_Graph);
2148 $LastX1 = $X1; $LastY1= $Y1;
2149 $LastX2 = $X2; $LastY2= $Y2;
2155 /* Draw the spider web */
2156 for ( $t=1; $t<=$MaxValue; $t++)
2158 $TRadius = ( $Radius / $MaxValue ) * $t;
2161 for ( $i=0; $i<=$Points; $i++)
2163 $Angle = -90 + $i * 360/$Points;
2164 $X = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2165 $Y = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2168 $this->drawDottedLine($LastX,$LastY,$X,$Y,4,$S_R,$S_G,$S_B);
2170 $LastX = $X; $LastY= $Y;
2175 for ( $i=0; $i<=$Points; $i++)
2177 $Angle = -90 + $i * 360/$Points;
2178 $X = cos($Angle * 3.1418 / 180 ) * $Radius + $XCenter;
2179 $Y = sin($Angle * 3.1418 / 180 ) * $Radius + $YCenter;
2181 $this->drawLine($XCenter,$YCenter,$X,$Y,$A_R,$A_G,$A_B);
2183 $XOffset = 0; $YOffset = 0;
2184 if (isset($Data[$i][$DataDescription["Position"]]))
2186 $Label = $Data[$i][$DataDescription["Position"]];
2188 $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Label);
2189 $Width = $Positions[2] - $Positions[6];
2190 $Height = $Positions[3] - $Positions[7];
2192 if ( $Angle >= 0 && $Angle <= 90 )
2195 if ( $Angle > 90 && $Angle <= 180 )
2196 { $YOffset = $Height; $XOffset = -$Width; }
2198 if ( $Angle > 180 && $Angle <= 270 )
2199 { $XOffset = -$Width; }
2201 imagettftext($this->Picture,$this->FontSize,0,$X+$XOffset,$Y+$YOffset,$C_TextColor,$this->FontName,$Label);
2203 if ( $this->BuildMap )
2205 $vecX = $X - $XCenter;
2206 $vecY = $Y - $YCenter;
2208 // get a perpendicular vector
2214 $vecLength = sqrt($vecX * $vecX + $vecY * $vecY);
2215 $vecX = $vecX / $vecLength;
2216 $vecY = $vecY / $vecLength;
2219 foreach ($DataDescription['Description'] as $key => $value) {
2220 $tooltipValue .= $value.' : '.sprintf("%.2f", $Data[$i][$key]).';';
2225 array($X+$vecX*-$offset,$Y+$vecY*-$offset),
2226 array($X+$vecX*+$offset,$Y+$vecY*+$offset),
2227 array($XCenter+$vecX*+$offset,$YCenter+$vecY*+$offset),
2228 array($XCenter+$vecX*-$offset,$YCenter+$vecY*-$offset),
2230 $this->addPolyToImageMap($poly,$Label,$tooltipValue,'Radar');
2235 /* Write the values */
2236 for ( $t=1; $t<=$MaxValue; $t++)
2238 if ($t % $valueMod != 0)
2241 $TRadius = ( $Radius / $MaxValue ) * $t;
2243 $Angle = -90 + 360 / $Points;
2245 $Y1 = $YCenter - $TRadius;
2246 $X2 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2247 $Y2 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2249 $XPos = floor(($X2-$X1)/2) + $X1;
2250 $YPos = floor(($Y2-$Y1)/2) + $Y1;
2252 $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$t);
2253 $X = $XPos - ( $X+$Positions[2] - $X+$Positions[6] ) / 2;
2254 $Y = $YPos + $this->FontSize;
2256 $this->drawFilledRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,240,240,240);
2257 $this->drawRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,220,220,220);
2258 imagettftext($this->Picture,$this->FontSize,0,$X,$Y,$C_TextColor,$this->FontName,$t);
2262 /* This function draw a radar graph centered on the graph area */
2263 function drawRadar($Data,$DataDescription,$BorderOffset=10,$MaxValue=-1)
2265 /* Validate the Data and DataDescription array */
2266 $this->validateDataDescription("drawRadar",$DataDescription);
2267 $this->validateData("drawRadar",$Data);
2269 $Points = count($Data);
2270 $Radius = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2271 $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1;
2272 $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1;
2274 /* Search for the max value */
2275 if ( $MaxValue == -1 )
2277 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2279 foreach ( $Data as $Key => $Values )
2281 if ( isset($Data[$Key][$ColName]))
2282 if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; }
2288 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2291 foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2292 { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2296 foreach ( $Data as $Key => $Values )
2298 if ( isset($Data[$Key][$ColName]))
2300 $Value = $Data[$Key][$ColName];
2301 $Strength = ( $Radius / $MaxValue ) * $Value;
2303 $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter;
2304 $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter;
2307 $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2310 { $FirstX = $XPos; $FirstY = $YPos; }
2312 $Angle = $Angle + (360/$Points);
2317 $this->drawLine($XPos,$YPos,$FirstX,$FirstY,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2322 /* This function draw a radar graph centered on the graph area */
2323 function drawFilledRadar($Data,$DataDescription,$Alpha=50,$BorderOffset=10,$MaxValue=-1)
2325 /* Validate the Data and DataDescription array */
2326 $this->validateDataDescription("drawFilledRadar",$DataDescription);
2327 $this->validateData("drawFilledRadar",$Data);
2329 $Points = count($Data);
2330 $LayerWidth = $this->GArea_X2-$this->GArea_X1;
2331 $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
2332 $Radius = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2333 $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2;
2334 $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2;
2336 /* Search for the max value */
2337 if ( $MaxValue == -1 )
2339 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2341 foreach ( $Data as $Key => $Values )
2343 if ( isset($Data[$Key][$ColName]))
2344 if ( $Data[$Key][$ColName] > $MaxValue && is_numeric($Data[$Key][$ColName])) { $MaxValue = $Data[$Key][$ColName]; }
2350 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2353 foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2354 { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2359 foreach ( $Data as $Key => $Values )
2361 if ( isset($Data[$Key][$ColName]))
2363 $Value = $Data[$Key][$ColName];
2364 if ( !is_numeric($Value) ) { $Value = 0; }
2365 $Strength = ( $Radius / $MaxValue ) * $Value;
2367 $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter;
2368 $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter;
2373 $Angle = $Angle + (360/$Points);
2379 if (isset($Plots[0]))
2381 $Plots[] = $Plots[0];
2382 $Plots[] = $Plots[1];
2384 $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
2385 $C_White = $this->AllocateColor($this->Layers[0],255,255,255);
2386 imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
2387 imagecolortransparent($this->Layers[0],$C_White);
2389 $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2390 imagefilledpolygon($this->Layers[0],$Plots,(count($Plots)+1)/2,$C_Graph);
2392 imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
2393 imagedestroy($this->Layers[0]);
2395 for($i=0;$i<=count($Plots)-4;$i=$i+2)
2396 $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"]);
2403 /* This function draw a flat pie chart */
2404 function drawBasicPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$R=255,$G=255,$B=255,$Decimals=0)
2406 /* Validate the Data and DataDescription array */
2407 $this->validateDataDescription("drawBasicPieGraph",$DataDescription,FALSE);
2408 $this->validateData("drawBasicPieGraph",$Data);
2410 /* Determine pie sum */
2411 $Series = 0; $PieSum = 0;
2412 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2414 if ( $ColName != $DataDescription["Position"] )
2417 foreach ( $Data as $Key => $Values )
2419 if ( isset($Data[$Key][$ColName]))
2420 $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]];
2425 /* Validate serie */
2427 RaiseFatal("Pie chart can only accept one serie of data.");
2429 $SpliceRatio = 360 / $PieSum;
2430 $SplicePercent = 100 / $PieSum;
2432 /* Calculate all polygons */
2433 $Angle = 0; $TopPlots = "";
2434 foreach($iValues as $Key => $Value)
2436 $TopPlots[$Key][] = $XPos;
2437 $TopPlots[$Key][] = $YPos;
2439 /* Process labels position & size */
2441 if ( !($DrawLabels == PIE_NOLABEL) )
2443 $TAngle = $Angle+($Value*$SpliceRatio/2);
2444 if ($DrawLabels == PIE_PERCENTAGE)
2445 $Caption = (round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2446 elseif ($DrawLabels == PIE_LABELS)
2447 $Caption = $iLabels[$Key];
2448 elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2449 $Caption = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2450 elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2451 $Caption = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2453 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2454 $TextWidth = $Position[2]-$Position[0];
2455 $TextHeight = abs($Position[1])+abs($Position[3]);
2457 $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius+10) + $XPos;
2459 if ( $TAngle > 0 && $TAngle < 180 )
2460 $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+10) + $YPos + 4;
2462 $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+4) + $YPos - ($TextHeight/2);
2464 if ( $TAngle > 90 && $TAngle < 270 )
2465 $TX = $TX - $TextWidth;
2467 $C_TextColor = $this->AllocateColor($this->Picture,70,70,70);
2468 imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2471 /* Process pie slices */
2472 for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
2474 $TopX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos;
2475 $TopY = sin($iAngle * 3.1418 / 180 ) * $Radius + $YPos;
2477 $TopPlots[$Key][] = $TopX;
2478 $TopPlots[$Key][] = $TopY;
2481 $TopPlots[$Key][] = $XPos;
2482 $TopPlots[$Key][] = $YPos;
2486 $PolyPlots = $TopPlots;
2488 /* Set array values type to float --- PHP Bug with imagefilledpolygon casting to integer */
2489 foreach ($TopPlots as $Key => $Value)
2490 { foreach ($TopPlots[$Key] as $Key2 => $Value2) { settype($TopPlots[$Key][$Key2],"float"); } }
2492 /* Draw Top polygons */
2493 foreach ($PolyPlots as $Key => $Value)
2495 $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
2496 imagefilledpolygon($this->Picture,$PolyPlots[$Key],(count($PolyPlots[$Key])+1)/2,$C_GraphLo);
2499 $this->drawCircle($XPos-.5,$YPos-.5,$Radius,$R,$G,$B);
2500 $this->drawCircle($XPos-.5,$YPos-.5,$Radius+.5,$R,$G,$B);
2502 /* Draw Top polygons */
2503 foreach ($TopPlots as $Key => $Value)
2505 for($j=0;$j<=count($TopPlots[$Key])-4;$j=$j+2)
2506 $this->drawLine($TopPlots[$Key][$j],$TopPlots[$Key][$j+1],$TopPlots[$Key][$j+2],$TopPlots[$Key][$j+3],$R,$G,$B);
2510 function drawFlatPieGraphWithShadow($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals=0)
2512 $this->drawFlatPieGraph($Data,$DataDescription,$XPos+$this->ShadowXDistance,$YPos+$this->ShadowYDistance,$Radius,PIE_NOLABEL,$SpliceDistance,$Decimals,TRUE);
2513 $this->drawFlatPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius,$DrawLabels,$SpliceDistance,$Decimals,FALSE);
2516 /* This function draw a flat pie chart */
2517 function drawFlatPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals=0,$AllBlack=FALSE)
2519 /* Validate the Data and DataDescription array */
2520 $this->validateDataDescription("drawFlatPieGraph",$DataDescription,FALSE);
2521 $this->validateData("drawFlatPieGraph",$Data);
2523 $ShadowStatus = $this->ShadowActive ; $this->ShadowActive = FALSE;
2525 /* Determine pie sum */
2526 $Series = 0; $PieSum = 0;
2527 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2529 if ( $ColName != $DataDescription["Position"] )
2532 foreach ( $Data as $Key => $Values )
2534 if ( isset($Data[$Key][$ColName]))
2535 $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]];
2540 /* Validate serie */
2543 RaiseFatal("Pie chart can only accept one serie of data.");
2547 $SpliceRatio = 360 / $PieSum;
2548 $SplicePercent = 100 / $PieSum;
2550 /* Calculate all polygons */
2551 $Angle = 0; $TopPlots = "";
2552 foreach($iValues as $Key => $Value)
2554 $XOffset = cos(($Angle+($Value/2*$SpliceRatio)) * 3.1418 / 180 ) * $SpliceDistance;
2555 $YOffset = sin(($Angle+($Value/2*$SpliceRatio)) * 3.1418 / 180 ) * $SpliceDistance;
2557 $TopPlots[$Key][] = round($XPos + $XOffset);
2558 $TopPlots[$Key][] = round($YPos + $YOffset);
2561 { $Rc = $this->ShadowRColor; $Gc = $this->ShadowGColor; $Bc = $this->ShadowBColor; }
2563 { $Rc = $this->Palette[$Key]["R"]; $Gc = $this->Palette[$Key]["G"]; $Bc = $this->Palette[$Key]["B"]; }
2565 $XLineLast = ""; $YLineLast = "";
2567 /* Process labels position & size */
2569 if ( !($DrawLabels == PIE_NOLABEL) )
2571 $TAngle = $Angle+($Value*$SpliceRatio/2);
2572 if ($DrawLabels == PIE_PERCENTAGE)
2573 $Caption = (round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2574 elseif ($DrawLabels == PIE_LABELS)
2575 $Caption = $iLabels[$Key];
2576 elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2577 $Caption = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2578 elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2579 $Caption = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2581 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2582 $TextWidth = $Position[2]-$Position[0];
2583 $TextHeight = abs($Position[1])+abs($Position[3]);
2585 $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius+10+$SpliceDistance) + $XPos;
2587 if ( $TAngle > 0 && $TAngle < 180 )
2588 $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+10+$SpliceDistance) + $YPos + 4;
2590 $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+$SpliceDistance+4) + $YPos - ($TextHeight/2);
2592 if ( $TAngle > 90 && $TAngle < 270 )
2593 $TX = $TX - $TextWidth;
2595 $C_TextColor = $this->AllocateColor($this->Picture,70,70,70);
2596 imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2599 /* Process pie slices */
2601 $LineColor = $this->AllocateColor($this->Picture,$Rc,$Gc,$Bc);
2603 $LineColor = $this->AllocateColor($this->Picture,$Rc,$Gc,$Bc);
2605 $XLineLast = ""; $YLineLast = "";
2606 for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
2608 $PosX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos + $XOffset;
2609 $PosY = sin($iAngle * 3.1418 / 180 ) * $Radius + $YPos + $YOffset;
2611 $TopPlots[$Key][] = round($PosX); $TopPlots[$Key][] = round($PosY);
2613 if ( $iAngle == $Angle || $iAngle == $Angle+$Value*$SpliceRatio || $iAngle +.5 > $Angle+$Value*$SpliceRatio)
2614 $this->drawLine($XPos+$XOffset,$YPos+$YOffset,$PosX,$PosY,$Rc,$Gc,$Bc);
2616 if ( $XLineLast != "" )
2617 $this->drawLine($XLineLast,$YLineLast,$PosX,$PosY,$Rc,$Gc,$Bc);
2619 $XLineLast = $PosX; $YLineLast = $PosY;
2622 $TopPlots[$Key][] = round($XPos + $XOffset); $TopPlots[$Key][] = round($YPos + $YOffset);
2626 $PolyPlots = $TopPlots;
2628 /* Draw Top polygons */
2629 foreach ($PolyPlots as $Key => $Value)
2632 $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
2634 $C_GraphLo = $this->AllocateColor($this->Picture,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor);
2636 imagefilledpolygon($this->Picture,$PolyPlots[$Key],(count($PolyPlots[$Key])+1)/2,$C_GraphLo);
2638 $this->ShadowActive = $ShadowStatus;
2641 /* This function draw a pseudo-3D pie chart */
2642 function drawPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$EnhanceColors=TRUE,$Skew=60,$SpliceHeight=20,$SpliceDistance=0,$Decimals=0)
2644 /* Validate the Data and DataDescription array */
2645 $this->validateDataDescription("drawPieGraph",$DataDescription,FALSE);
2646 $this->validateData("drawPieGraph",$Data);
2648 /* Determine pie sum */
2649 $Series = 0; $PieSum = 0; $rPieSum = 0;
2650 foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2652 if ( $ColName != $DataDescription["Position"] )
2655 foreach ( $Data as $Key => $Values )
2656 if ( isset($Data[$Key][$ColName]))
2658 if ( $Data[$Key][$ColName] == 0 )
2659 { $iValues[] = 0; $rValues[] = 0; $iLabels[] = $Data[$Key][$DataDescription["Position"]]; }
2660 // Removed : $PieSum++; $rValues[] = 1;
2662 { $PieSum += $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]]; $rValues[] = $Data[$Key][$ColName]; $rPieSum += $Data[$Key][$ColName];}
2667 /* Validate serie */
2669 RaiseFatal("Pie chart can only accept one serie of data.");
2671 $SpliceDistanceRatio = $SpliceDistance;
2672 $SkewHeight = ($Radius * $Skew) / 100;
2673 $SpliceRatio = (360 - $SpliceDistanceRatio * count($iValues) ) / $PieSum;
2674 $SplicePercent = 100 / $PieSum;
2675 $rSplicePercent = 100 / $rPieSum;
2677 /* Calculate all polygons */
2678 $Angle = 0; $CDev = 5;
2679 $TopPlots = ""; $BotPlots = "";
2680 $aTopPlots = ""; $aBotPlots = "";
2681 foreach($iValues as $Key => $Value)
2683 $XCenterPos = cos(($Angle-$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $XPos;
2684 $YCenterPos = sin(($Angle-$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $YPos;
2685 $XCenterPos2 = cos(($Angle+$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $XPos;
2686 $YCenterPos2 = sin(($Angle+$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $YPos;
2688 $TopPlots[$Key][] = round($XCenterPos); $BotPlots[$Key][] = round($XCenterPos);
2689 $TopPlots[$Key][] = round($YCenterPos); $BotPlots[$Key][] = round($YCenterPos + $SpliceHeight);
2690 $aTopPlots[$Key][] = $XCenterPos; $aBotPlots[$Key][] = $XCenterPos;
2691 $aTopPlots[$Key][] = $YCenterPos; $aBotPlots[$Key][] = $YCenterPos + $SpliceHeight;
2693 /* Process labels position & size */
2695 if ( !($DrawLabels == PIE_NOLABEL) )
2697 $TAngle = $Angle+($Value*$SpliceRatio/2);
2698 if ($DrawLabels == PIE_PERCENTAGE)
2699 $Caption = (round($rValues[$Key] * pow(10,$Decimals) * $rSplicePercent)/pow(10,$Decimals))."%";
2700 elseif ($DrawLabels == PIE_LABELS)
2701 $Caption = $iLabels[$Key];
2702 elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2703 $Caption = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2705 $Position = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2706 $TextWidth = $Position[2]-$Position[0];
2707 $TextHeight = abs($Position[1])+abs($Position[3]);
2709 $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius + 10)+ $XPos;
2711 if ( $TAngle > 0 && $TAngle < 180 )
2712 $TY = sin(($TAngle) * 3.1418 / 180 ) * ($SkewHeight + 10) + $YPos + $SpliceHeight + 4;
2714 $TY = sin(($TAngle) * 3.1418 / 180 ) * ($SkewHeight + 4) + $YPos - ($TextHeight/2);
2716 if ( $TAngle > 90 && $TAngle < 270 )
2717 $TX = $TX - $TextWidth;
2719 $C_TextColor = $this->AllocateColor($this->Picture,70,70,70);
2720 imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2723 /* Process pie slices */
2724 for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
2726 $TopX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos;
2727 $TopY = sin($iAngle * 3.1418 / 180 ) * $SkewHeight + $YPos;
2729 $TopPlots[$Key][] = round($TopX); $BotPlots[$Key][] = round($TopX);
2730 $TopPlots[$Key][] = round($TopY); $BotPlots[$Key][] = round($TopY + $SpliceHeight);
2731 $aTopPlots[$Key][] = $TopX; $aBotPlots[$Key][] = $TopX;
2732 $aTopPlots[$Key][] = $TopY; $aBotPlots[$Key][] = $TopY + $SpliceHeight;
2735 $TopPlots[$Key][] = round($XCenterPos2); $BotPlots[$Key][] = round($XCenterPos2);
2736 $TopPlots[$Key][] = round($YCenterPos2); $BotPlots[$Key][] = round($YCenterPos2 + $SpliceHeight);
2737 $aTopPlots[$Key][] = $XCenterPos2; $aBotPlots[$Key][] = $XCenterPos2;
2738 $aTopPlots[$Key][] = $YCenterPos2; $aBotPlots[$Key][] = $YCenterPos2 + $SpliceHeight;
2740 $Angle = $iAngle + $SpliceDistanceRatio;
2743 /* Draw Bottom polygons */
2744 foreach($iValues as $Key => $Value)
2746 $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"],-20);
2747 imagefilledpolygon($this->Picture,$BotPlots[$Key],(count($BotPlots[$Key])+1)/2,$C_GraphLo);
2749 if ( $EnhanceColors ) { $En = -10; } else { $En = 0; }
2751 for($j=0;$j<=count($aBotPlots[$Key])-4;$j=$j+2)
2752 $this->drawLine($aBotPlots[$Key][$j],$aBotPlots[$Key][$j+1],$aBotPlots[$Key][$j+2],$aBotPlots[$Key][$j+3],$this->Palette[$Key]["R"]+$En,$this->Palette[$Key]["G"]+$En,$this->Palette[$Key]["B"]+$En);
2755 /* Draw pie layers */
2756 if ( $EnhanceColors ) { $ColorRatio = 30 / $SpliceHeight; } else { $ColorRatio = 25 / $SpliceHeight; }
2757 for($i=$SpliceHeight-1;$i>=1;$i--)
2759 foreach($iValues as $Key => $Value)
2761 $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"],-10);
2762 $Plots = ""; $Plot = 0;
2763 foreach($TopPlots[$Key] as $Key2 => $Value2)
2766 if ( $Plot % 2 == 1 )
2769 $Plots[] = $Value2+$i;
2771 imagefilledpolygon($this->Picture,$Plots,(count($Plots)+1)/2,$C_GraphLo);
2773 $Index = count($Plots);
2774 if ($EnhanceColors ) {$ColorFactor = -20 + ($SpliceHeight - $i) * $ColorRatio; } else { $ColorFactor = 0; }
2776 $this->drawAntialiasPixel($Plots[0],$Plots[1],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor);
2777 $this->drawAntialiasPixel($Plots[2],$Plots[3],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor);
2778 $this->drawAntialiasPixel($Plots[$Index-4],$Plots[$Index-3],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor);
2782 if ( $this->BuildMap )
2784 // Add points to Image Map.
2785 foreach ($TopPlots as $key => $PointArr)
2787 $serieName = $Data[$key][$DataDescription['Values'][1]];
2788 $serieValue = $Data[$key][$DataDescription['Values'][0]];
2790 // last point of the arc
2791 $lastX = $PointArr[count($PointArr)-4];
2792 $lastY = $PointArr[count($PointArr)-3];
2794 // the point at the middle
2795 $middleX = $PointArr[0];
2796 $middleY = $PointArr[1];
2798 // first point in the arc
2799 $firstX = $PointArr[2];
2800 $firstY = $PointArr[3];
2802 // point on the first third of the arc
2803 $firstThird = count($PointArr)/3;
2804 $firstThirdX = $PointArr[$firstThird + ($firstThird % 2)];
2805 $firstThirdY = $PointArr[$firstThird + ($firstThird % 2)+1];
2807 // point on the second third of the arc
2808 $secondThird = count($PointArr)/3*2;
2809 $secondThirdX = $PointArr[$secondThird + ($secondThird % 2)];
2810 $secondThirdY = $PointArr[$secondThird + ($secondThird % 2)+1];
2812 // Will create three polygons for every piece of the pie. In such way
2813 // no polygon will be concave. JS only works with convex polygons.
2815 array($middleX,$middleY),
2816 array($firstX,$firstY),
2817 array($firstThirdX,$firstThirdY),
2819 $this->addPolyToImageMap($poly,$serieName,$serieValue,"Pie");
2822 array($middleX,$middleY),
2823 array($firstThirdX,$firstThirdY),
2824 array($secondThirdX,$secondThirdY),
2826 $this->addPolyToImageMap($poly,$serieName,$serieValue,"Pie");
2829 array($middleX,$middleY),
2830 array($secondThirdX,$secondThirdY),
2831 array($lastX,$lastY),
2833 $this->addPolyToImageMap($poly,$serieName,$serieValue,"Pie");
2837 /* Draw Top polygons */
2838 for($Key=count($iValues)-1;$Key>=0;$Key--)
2840 $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
2841 imagefilledpolygon($this->Picture,$TopPlots[$Key],(count($TopPlots[$Key])+1)/2,$C_GraphLo);
2843 if ( $EnhanceColors ) { $En = 10; } else { $En = 0; }
2844 for($j=0;$j<=count($aTopPlots[$Key])-4;$j=$j+2)
2845 $this->drawLine($aTopPlots[$Key][$j],$aTopPlots[$Key][$j+1],$aTopPlots[$Key][$j+2],$aTopPlots[$Key][$j+3],$this->Palette[$Key]["R"]+$En,$this->Palette[$Key]["G"]+$En,$this->Palette[$Key]["B"]+$En);
2849 /* This function can be used to set the background color */
2850 function drawBackground($R,$G,$B)
2852 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2853 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2854 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2856 $C_Background = $this->AllocateColor($this->Picture,$R,$G,$B);
2857 imagefilledrectangle($this->Picture,0,0,$this->XSize,$this->YSize,$C_Background);
2860 /* This function can be used to set the background color */
2861 function drawGraphAreaGradient($R,$G,$B,$Decay,$Target=TARGET_GRAPHAREA)
2863 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2864 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2865 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2867 if ( $Target == TARGET_GRAPHAREA ) { $X1 = $this->GArea_X1+1; $X2 = $this->GArea_X2-1; $Y1 = $this->GArea_Y1+1; $Y2 = $this->GArea_Y2; }
2868 if ( $Target == TARGET_BACKGROUND ) { $X1 = 0; $X2 = $this->XSize; $Y1 = 0; $Y2 = $this->YSize; }
2870 /* Positive gradient */
2873 $YStep = ($Y2 - $Y1 - 2) / $Decay;
2874 for($i=0;$i<=$Decay;$i++)
2877 $Yi1 = $Y1 + ( $i * $YStep );
2878 $Yi2 = ceil( $Yi1 + ( $i * $YStep ) + $YStep );
2879 if ( $Yi2 >= $Yi2 ) { $Yi2 = $Y2-1; }
2881 $C_Background = $this->AllocateColor($this->Picture,$R,$G,$B);
2882 imagefilledrectangle($this->Picture,$X1,$Yi1,$X2,$Yi2,$C_Background);
2886 /* Negative gradient */
2889 $YStep = ($Y2 - $Y1 - 2) / -$Decay;
2890 $Yi1 = $Y1; $Yi2 = $Y1+$YStep;
2891 for($i=-$Decay;$i>=0;$i--)
2894 $C_Background = $this->AllocateColor($this->Picture,$R,$G,$B);
2895 imagefilledrectangle($this->Picture,$X1,$Yi1,$X2,$Yi2,$C_Background);
2899 if ( $Yi2 >= $Yi2 ) { $Yi2 = $Y2-1; }
2904 /* This function create a rectangle with antialias */
2905 function drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B)
2907 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2908 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2909 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2911 $C_Rectangle = $this->AllocateColor($this->Picture,$R,$G,$B);
2913 $X1=$X1-.2;$Y1=$Y1-.2;
2914 $X2=$X2+.2;$Y2=$Y2+.2;
2915 $this->drawLine($X1,$Y1,$X2,$Y1,$R,$G,$B);
2916 $this->drawLine($X2,$Y1,$X2,$Y2,$R,$G,$B);
2917 $this->drawLine($X2,$Y2,$X1,$Y2,$R,$G,$B);
2918 $this->drawLine($X1,$Y2,$X1,$Y1,$R,$G,$B);
2921 /* This function create a filled rectangle with antialias */
2922 function drawFilledRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B,$DrawBorder=TRUE,$Alpha=100,$NoFallBack=FALSE)
2924 if ( $X2 < $X1 ) { list($X1, $X2) = array($X2, $X1); }
2925 if ( $Y2 < $Y1 ) { list($Y1, $Y2) = array($Y2, $Y1); }
2927 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2928 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2929 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2931 if ( $Alpha == 100 )
2933 /* Process shadows */
2934 if ( $this->ShadowActive && !$NoFallBack )
2936 $this->drawFilledRectangle($X1+$this->ShadowXDistance,$Y1+$this->ShadowYDistance,$X2+$this->ShadowXDistance,$Y2+$this->ShadowYDistance,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,FALSE,$this->ShadowAlpha,TRUE);
2937 if ( $this->ShadowBlur != 0 )
2939 $AlphaDecay = ($this->ShadowAlpha / $this->ShadowBlur);
2941 for($i=1; $i<=$this->ShadowBlur; $i++)
2942 $this->drawFilledRectangle($X1+$this->ShadowXDistance-$i/2,$Y1+$this->ShadowYDistance-$i/2,$X2+$this->ShadowXDistance-$i/2,$Y2+$this->ShadowYDistance-$i/2,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,FALSE,$this->ShadowAlpha-$AlphaDecay*$i,TRUE);
2943 for($i=1; $i<=$this->ShadowBlur; $i++)
2944 $this->drawFilledRectangle($X1+$this->ShadowXDistance+$i/2,$Y1+$this->ShadowYDistance+$i/2,$X2+$this->ShadowXDistance+$i/2,$Y2+$this->ShadowYDistance+$i/2,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,FALSE,$this->ShadowAlpha-$AlphaDecay*$i,TRUE);
2948 $C_Rectangle = $this->AllocateColor($this->Picture,$R,$G,$B);
2949 imagefilledrectangle($this->Picture,round($X1),round($Y1),round($X2),round($Y2),$C_Rectangle);
2953 $LayerWidth = abs($X2-$X1)+2;
2954 $LayerHeight = abs($Y2-$Y1)+2;
2956 $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
2957 $C_White = $this->AllocateColor($this->Layers[0],255,255,255);
2958 imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
2959 imagecolortransparent($this->Layers[0],$C_White);
2961 $C_Rectangle = $this->AllocateColor($this->Layers[0],$R,$G,$B);
2962 imagefilledrectangle($this->Layers[0],round(1),round(1),round($LayerWidth-1),round($LayerHeight-1),$C_Rectangle);
2964 imagecopymerge($this->Picture,$this->Layers[0],round(min($X1,$X2)-1),round(min($Y1,$Y2)-1),0,0,$LayerWidth,$LayerHeight,$Alpha);
2965 imagedestroy($this->Layers[0]);
2970 $ShadowSettings = $this->ShadowActive; $this->ShadowActive = FALSE;
2971 $this->drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B);
2972 $this->ShadowActive = $ShadowSettings;
2976 /* This function create a rectangle with rounded corners and antialias */
2977 function drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
2979 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2980 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2981 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2983 $C_Rectangle = $this->AllocateColor($this->Picture,$R,$G,$B);
2985 $Step = 90 / ((3.1418 * $Radius)/2);
2987 for($i=0;$i<=90;$i=$i+$Step)
2989 $X = cos(($i+180)*3.1418/180) * $Radius + $X1 + $Radius;
2990 $Y = sin(($i+180)*3.1418/180) * $Radius + $Y1 + $Radius;
2991 $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2993 $X = cos(($i-90)*3.1418/180) * $Radius + $X2 - $Radius;
2994 $Y = sin(($i-90)*3.1418/180) * $Radius + $Y1 + $Radius;
2995 $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2997 $X = cos(($i)*3.1418/180) * $Radius + $X2 - $Radius;
2998 $Y = sin(($i)*3.1418/180) * $Radius + $Y2 - $Radius;
2999 $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
3001 $X = cos(($i+90)*3.1418/180) * $Radius + $X1 + $Radius;
3002 $Y = sin(($i+90)*3.1418/180) * $Radius + $Y2 - $Radius;
3003 $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
3006 $X1=$X1-.2;$Y1=$Y1-.2;
3007 $X2=$X2+.2;$Y2=$Y2+.2;
3008 $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$R,$G,$B);
3009 $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$R,$G,$B);
3010 $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$R,$G,$B);
3011 $this->drawLine($X1,$Y2-$Radius,$X1,$Y1+$Radius,$R,$G,$B);
3014 /* This function create a filled rectangle with rounded corners and antialias */
3015 function drawFilledRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
3017 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3018 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3019 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3021 $C_Rectangle = $this->AllocateColor($this->Picture,$R,$G,$B);
3023 $Step = 90 / ((3.1418 * $Radius)/2);
3025 for($i=0;$i<=90;$i=$i+$Step)
3027 $Xi1 = cos(($i+180)*3.1418/180) * $Radius + $X1 + $Radius;
3028 $Yi1 = sin(($i+180)*3.1418/180) * $Radius + $Y1 + $Radius;
3030 $Xi2 = cos(($i-90)*3.1418/180) * $Radius + $X2 - $Radius;
3031 $Yi2 = sin(($i-90)*3.1418/180) * $Radius + $Y1 + $Radius;
3033 $Xi3 = cos(($i)*3.1418/180) * $Radius + $X2 - $Radius;
3034 $Yi3 = sin(($i)*3.1418/180) * $Radius + $Y2 - $Radius;
3036 $Xi4 = cos(($i+90)*3.1418/180) * $Radius + $X1 + $Radius;
3037 $Yi4 = sin(($i+90)*3.1418/180) * $Radius + $Y2 - $Radius;
3039 imageline($this->Picture,$Xi1,$Yi1,$X1+$Radius,$Yi1,$C_Rectangle);
3040 imageline($this->Picture,$X2-$Radius,$Yi2,$Xi2,$Yi2,$C_Rectangle);
3041 imageline($this->Picture,$X2-$Radius,$Yi3,$Xi3,$Yi3,$C_Rectangle);
3042 imageline($this->Picture,$Xi4,$Yi4,$X1+$Radius,$Yi4,$C_Rectangle);
3044 $this->drawAntialiasPixel($Xi1,$Yi1,$R,$G,$B);
3045 $this->drawAntialiasPixel($Xi2,$Yi2,$R,$G,$B);
3046 $this->drawAntialiasPixel($Xi3,$Yi3,$R,$G,$B);
3047 $this->drawAntialiasPixel($Xi4,$Yi4,$R,$G,$B);
3050 imagefilledrectangle($this->Picture,$X1,$Y1+$Radius,$X2,$Y2-$Radius,$C_Rectangle);
3051 imagefilledrectangle($this->Picture,$X1+$Radius,$Y1,$X2-$Radius,$Y2,$C_Rectangle);
3053 $X1=$X1-.2;$Y1=$Y1-.2;
3054 $X2=$X2+.2;$Y2=$Y2+.2;
3055 $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$R,$G,$B);
3056 $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$R,$G,$B);
3057 $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$R,$G,$B);
3058 $this->drawLine($X1,$Y2-$Radius,$X1,$Y1+$Radius,$R,$G,$B);
3061 /* This function create a circle with antialias */
3062 function drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
3064 if ( $Width == 0 ) { $Width = $Height; }
3065 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3066 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3067 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3069 $C_Circle = $this->AllocateColor($this->Picture,$R,$G,$B);
3070 $Step = 360 / (2 * 3.1418 * max($Width,$Height));
3072 for($i=0;$i<=360;$i=$i+$Step)
3074 $X = cos($i*3.1418/180) * $Height + $Xc;
3075 $Y = sin($i*3.1418/180) * $Width + $Yc;
3076 $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
3080 /* This function create a filled circle/ellipse with antialias */
3081 function drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
3083 if ( $Width == 0 ) { $Width = $Height; }
3084 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3085 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3086 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3088 $C_Circle = $this->AllocateColor($this->Picture,$R,$G,$B);
3089 $Step = 360 / (2 * 3.1418 * max($Width,$Height));
3091 for($i=90;$i<=270;$i=$i+$Step)
3093 $X1 = cos($i*3.1418/180) * $Height + $Xc;
3094 $Y1 = sin($i*3.1418/180) * $Width + $Yc;
3095 $X2 = cos((180-$i)*3.1418/180) * $Height + $Xc;
3096 $Y2 = sin((180-$i)*3.1418/180) * $Width + $Yc;
3098 $this->drawAntialiasPixel($X1-1,$Y1-1,$R,$G,$B);
3099 $this->drawAntialiasPixel($X2-1,$Y2-1,$R,$G,$B);
3101 if ( ($Y1-1) > $Yc - max($Width,$Height) )
3102 imageline($this->Picture,$X1,$Y1-1,$X2-1,$Y2-1,$C_Circle);
3106 /* This function will draw a filled ellipse */
3107 function drawEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
3108 { $this->drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width); }
3110 /* This function will draw an ellipse */
3111 function drawFilledEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
3112 { $this->drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width); }
3114 /* This function create a line with antialias */
3115 function drawLine($X1,$Y1,$X2,$Y2,$R,$G,$B,$GraphFunction=FALSE)
3117 if ( $this->LineDotSize > 1 ) { $this->drawDottedLine($X1,$Y1,$X2,$Y2,$this->LineDotSize,$R,$G,$B,$GraphFunction); return(0); }
3118 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3119 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3120 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3122 $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1));
3123 if ( $Distance == 0 )
3125 $XStep = ($X2-$X1) / $Distance;
3126 $YStep = ($Y2-$Y1) / $Distance;
3128 for($i=0;$i<=$Distance;$i++)
3130 $X = $i * $XStep + $X1;
3131 $Y = $i * $YStep + $Y1;
3133 if ( ($X >= $this->GArea_X1 && $X <= $this->GArea_X2 && $Y >= $this->GArea_Y1 && $Y <= $this->GArea_Y2) || !$GraphFunction )
3135 if ( $this->LineWidth == 1 )
3136 $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
3139 $StartOffset = -($this->LineWidth/2); $EndOffset = ($this->LineWidth/2);
3140 for($j=$StartOffset;$j<=$EndOffset;$j++)
3141 $this->drawAntialiasPixel($X+$j,$Y+$j,$R,$G,$B);
3147 /* This function create a line with antialias */
3148 function drawDottedLine($X1,$Y1,$X2,$Y2,$DotSize,$R,$G,$B,$GraphFunction=FALSE)
3150 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3151 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3152 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3154 $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1));
3156 $XStep = ($X2-$X1) / $Distance;
3157 $YStep = ($Y2-$Y1) / $Distance;
3160 for($i=0;$i<=$Distance;$i++)
3162 $X = $i * $XStep + $X1;
3163 $Y = $i * $YStep + $Y1;
3165 if ( $DotIndex <= $DotSize)
3167 if ( ($X >= $this->GArea_X1 && $X <= $this->GArea_X2 && $Y >= $this->GArea_Y1 && $Y <= $this->GArea_Y2) || !$GraphFunction )
3169 if ( $this->LineWidth == 1 )
3170 $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
3173 $StartOffset = -($this->LineWidth/2); $EndOffset = ($this->LineWidth/2);
3174 for($j=$StartOffset;$j<=$EndOffset;$j++)
3175 $this->drawAntialiasPixel($X+$j,$Y+$j,$R,$G,$B);
3181 if ( $DotIndex == $DotSize * 2 )
3186 /* Load a PNG file and draw it over the chart */
3187 function drawFromPNG($FileName,$X,$Y,$Alpha=100)
3188 { $this->drawFromPicture(1,$FileName,$X,$Y,$Alpha); }
3190 /* Load a GIF file and draw it over the chart */
3191 function drawFromGIF($FileName,$X,$Y,$Alpha=100)
3192 { $this->drawFromPicture(2,$FileName,$X,$Y,$Alpha); }
3194 /* Load a JPEG file and draw it over the chart */
3195 function drawFromJPG($FileName,$X,$Y,$Alpha=100)
3196 { $this->drawFromPicture(3,$FileName,$X,$Y,$Alpha); }
3198 /* Generic loader function for external pictures */
3199 function drawFromPicture($PicType,$FileName,$X,$Y,$Alpha=100)
3201 if ( file_exists($FileName))
3203 $Infos = getimagesize($FileName);
3205 $Height = $Infos[1];
3206 if ( $PicType == 1 ) { $Raster = imagecreatefrompng($FileName); }
3207 if ( $PicType == 2 ) { $Raster = imagecreatefromgif($FileName); }
3208 if ( $PicType == 3 ) { $Raster = imagecreatefromjpeg($FileName); }
3210 imagecopymerge($this->Picture,$Raster,$X,$Y,0,0,$Width,$Height,$Alpha);
3211 imagedestroy($Raster);
3215 /* Draw an alpha pixel */
3216 function drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B)
3218 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3219 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3220 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3222 if ( $X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize )
3225 $RGB2 = imagecolorat($this->Picture, $X, $Y);
3226 $R2 = ($RGB2 >> 16) & 0xFF;
3227 $G2 = ($RGB2 >> 8) & 0xFF;
3230 $iAlpha = (100 - $Alpha)/100;
3231 $Alpha = $Alpha / 100;
3233 $Ra = floor($R*$Alpha+$R2*$iAlpha);
3234 $Ga = floor($G*$Alpha+$G2*$iAlpha);
3235 $Ba = floor($B*$Alpha+$B2*$iAlpha);
3237 $C_Aliased = $this->AllocateColor($this->Picture,$Ra,$Ga,$Ba);
3238 imagesetpixel($this->Picture,$X,$Y,$C_Aliased);
3242 function AllocateColor($Picture,$R,$G,$B,$Factor=0)
3247 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3248 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3249 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3251 return(imagecolorallocate($Picture,$R,$G,$B));
3254 /* Add a border to the picture */
3255 function addBorder($Size=3,$R=0,$G=0,$B=0)
3257 $Width = $this->XSize+2*$Size;
3258 $Height = $this->YSize+2*$Size;
3260 $Resampled = imagecreatetruecolor($Width,$Height);
3261 $C_Background = $this->AllocateColor($Resampled,$R,$G,$B);
3262 imagefilledrectangle($Resampled,0,0,$Width,$Height,$C_Background);
3264 imagecopy($Resampled,$this->Picture,$Size,$Size,0,0,$this->XSize,$this->YSize);
3265 imagedestroy($this->Picture);
3267 $this->XSize = $Width;
3268 $this->YSize = $Height;
3270 $this->Picture = imagecreatetruecolor($this->XSize,$this->YSize);
3271 $C_White = $this->AllocateColor($this->Picture,255,255,255);
3272 imagefilledrectangle($this->Picture,0,0,$this->XSize,$this->YSize,$C_White);
3273 imagecolortransparent($this->Picture,$C_White);
3274 imagecopy($this->Picture,$Resampled,0,0,0,0,$this->XSize,$this->YSize);
3277 /* Render the current picture to a file */
3278 function Render($FileName)
3280 if ( $this->ErrorReporting )
3281 $this->printErrors($this->ErrorInterface);
3283 /* Save image map if requested */
3284 if ( $this->BuildMap )
3285 $this->SaveImageMap();
3287 imagepng($this->Picture,$FileName);
3290 /* Render the current picture to STDOUT */
3293 if ( $this->ErrorReporting )
3294 $this->printErrors("GD");
3296 /* Save image map if requested */
3297 if ( $this->BuildMap )
3298 $this->SaveImageMap();
3300 header('Content-type: image/png');
3301 imagepng($this->Picture);
3304 /* Private functions for internal processing */
3305 function drawAntialiasPixel($X,$Y,$R,$G,$B,$Alpha=100,$NoFallBack=FALSE)
3307 /* Process shadows */
3308 if ( $this->ShadowActive && !$NoFallBack )
3310 $this->drawAntialiasPixel($X+$this->ShadowXDistance,$Y+$this->ShadowYDistance,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,$this->ShadowAlpha,TRUE);
3311 if ( $this->ShadowBlur != 0 )
3313 $AlphaDecay = ($this->ShadowAlpha / $this->ShadowBlur);
3315 for($i=1; $i<=$this->ShadowBlur; $i++)
3316 $this->drawAntialiasPixel($X+$this->ShadowXDistance-$i/2,$Y+$this->ShadowYDistance-$i/2,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,$this->ShadowAlpha-$AlphaDecay*$i,TRUE);
3317 for($i=1; $i<=$this->ShadowBlur; $i++)
3318 $this->drawAntialiasPixel($X+$this->ShadowXDistance+$i/2,$Y+$this->ShadowYDistance+$i/2,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,$this->ShadowAlpha-$AlphaDecay*$i,TRUE);
3322 if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3323 if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3324 if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3330 if ( $Xi == $X && $Yi == $Y)
3332 if ( $Alpha == 100 )
3334 $C_Aliased = $this->AllocateColor($this->Picture,$R,$G,$B);
3335 imagesetpixel($this->Picture,$X,$Y,$C_Aliased);
3338 $this->drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B);
3342 $Alpha1 = (((1 - ($X - floor($X))) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha;
3343 if ( $Alpha1 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi,$Alpha1,$R,$G,$B); }
3345 $Alpha2 = ((($X - floor($X)) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha;
3346 if ( $Alpha2 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi,$Alpha2,$R,$G,$B); }
3348 $Alpha3 = (((1 - ($X - floor($X))) * ($Y - floor($Y)) * 100) / 100) * $Alpha;
3349 if ( $Alpha3 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi+1,$Alpha3,$R,$G,$B); }
3351 $Alpha4 = ((($X - floor($X)) * ($Y - floor($Y)) * 100) / 100) * $Alpha;
3352 if ( $Alpha4 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi+1,$Alpha4,$R,$G,$B); }
3356 /* Validate data contained in the description array */
3357 function validateDataDescription($FunctionName,&$DataDescription,$DescriptionRequired=TRUE)
3359 if (!isset($DataDescription["Position"]))
3361 $this->Errors[] = "[Warning] ".$FunctionName." - Y Labels are not set.";
3362 $DataDescription["Position"] = "Name";
3365 if ( $DescriptionRequired )
3367 if (!isset($DataDescription["Description"]))
3369 $this->Errors[] = "[Warning] ".$FunctionName." - Series descriptions are not set.";
3370 foreach($DataDescription["Values"] as $key => $Value)
3372 $DataDescription["Description"][$Value] = $Value;
3376 if (count($DataDescription["Description"]) < count($DataDescription["Values"]))
3378 $this->Errors[] = "[Warning] ".$FunctionName." - Some series descriptions are not set.";
3379 foreach($DataDescription["Values"] as $key => $Value)
3381 if ( !isset($DataDescription["Description"][$Value]))
3382 $DataDescription["Description"][$Value] = $Value;
3388 /* Validate data contained in the data array */
3389 function validateData($FunctionName,&$Data)
3391 $DataSummary = array();
3393 foreach($Data as $key => $Values)
3395 foreach($Values as $key2 => $Value)
3397 if (!isset($DataSummary[$key2]))
3398 $DataSummary[$key2] = 1;
3400 $DataSummary[$key2]++;
3404 if ( max($DataSummary) == 0 )
3405 $this->Errors[] = "[Warning] ".$FunctionName." - No data set.";
3407 foreach($DataSummary as $key => $Value)
3409 if ($Value < max($DataSummary))
3411 $this->Errors[] = "[Warning] ".$FunctionName." - Missing data in serie ".$key.".";
3416 /* Print all error messages on the CLI or graphically */
3417 function printErrors($Mode="CLI")
3419 if (count($this->Errors) == 0)
3422 if ( $Mode == "CLI" )
3424 foreach($this->Errors as $key => $Value)
3427 elseif ( $Mode == "GD" )
3429 $this->setLineStyle($Width=1);
3431 foreach($this->Errors as $key => $Value)
3433 $Position = imageftbbox($this->ErrorFontSize,0,$this->ErrorFontName,$Value);
3434 $TextWidth = $Position[2]-$Position[0];
3435 if ( $TextWidth > $MaxWidth ) { $MaxWidth = $TextWidth; }
3437 $this->drawFilledRoundedRectangle($this->XSize-($MaxWidth+20),$this->YSize-(20+(($this->ErrorFontSize+4)*count($this->Errors))),$this->XSize-10,$this->YSize-10,6,233,185,185);
3438 $this->drawRoundedRectangle($this->XSize-($MaxWidth+20),$this->YSize-(20+(($this->ErrorFontSize+4)*count($this->Errors))),$this->XSize-10,$this->YSize-10,6,193,145,145);
3440 $C_TextColor = $this->AllocateColor($this->Picture,133,85,85);
3441 $YPos = $this->YSize - (18 + (count($this->Errors)-1) * ($this->ErrorFontSize + 4));
3442 foreach($this->Errors as $key => $Value)
3444 imagettftext($this->Picture,$this->ErrorFontSize,0,$this->XSize-($MaxWidth+15),$YPos,$C_TextColor,$this->ErrorFontName,$Value);
3445 $YPos = $YPos + ($this->ErrorFontSize + 4);
3450 /* Activate the image map creation process */
3451 function setImageMap($Mode=TRUE,$GraphID="MyGraph")
3453 $this->BuildMap = $Mode;
3454 $this->MapID = $GraphID;
3457 /* Add a box into the image map */
3458 function addToImageMap($X1,$Y1,$X2,$Y2,$SerieName,$Value,$CallerFunction)
3460 $poly = array(array($X1,$Y1),array($X2,$Y1),array($X2,$Y2),array($X1,$Y2));
3461 $this->addPolyToImageMap($poly,$SerieName,$Value,$CallerFunction);
3464 function addPolyToImageMap($poly,$SerieName,$Value,$CallerFunction)
3466 if ( $this->MapFunction == NULL || $this->MapFunction == $CallerFunction)
3468 $this->ImageMap[] = array(
3469 'n' => (string)$SerieName,
3470 'v' => (string)$Value,
3473 $this->MapFunction = $CallerFunction;
3477 /* Draw image map to the current chart image */
3478 function debugImageMap()
3480 foreach ($this->ImageMap as $polygon)
3483 foreach ($polygon['p'] as $point)
3485 $points[] = $point[0];
3486 $points[] = $point[1];
3489 $color = $this->AllocateColor($this->Picture,rand(0,255),rand(0,255),rand(0,255));
3490 imagefilledpolygon($this->Picture,$points,(count($points)+1)/2,$color);
3495 /* Get the current image map */
3496 function getImageMap()
3498 return $this->ImageMap;
3501 /* Load and cleanup the image map from disk */
3502 function getSavedImageMap($MapName,$Flush=TRUE)
3504 /* Strip HTML query strings */
3505 $Values = $this->tmpFolder.$MapName;
3506 $Value = split("\?",$Values);
3507 $FileName = $Value[0];
3509 if ( file_exists($FileName) )
3511 $Handle = fopen($FileName, "r");
3512 $MapContent = fread($Handle, filesize($FileName));
3523 header("HTTP/1.0 404 Not Found");
3528 /* Save the image map to the disk */
3529 function SaveImageMap()
3531 if ( !$this->BuildMap ) { return(-1); }
3533 if ( $this->ImageMap == NULL )
3535 $this->Errors[] = "[Warning] SaveImageMap - Image map is empty.";
3539 $Handle = fopen($this->tmpFolder.$this->MapID, 'w');
3542 $this->Errors[] = "[Warning] SaveImageMap - Cannot save the image map.";
3547 fwrite($Handle, serialize($this->getImageMap()));
3552 /* Convert seconds to a time format string */
3553 function ToTime($Value)
3555 $Hour = floor($Value/3600);
3556 $Minute = floor(($Value - $Hour*3600)/60);
3557 $Second = floor($Value - $Hour*3600 - $Minute*60);
3559 if (strlen($Hour) == 1 ) { $Hour = "0".$Hour; }
3560 if (strlen($Minute) == 1 ) { $Minute = "0".$Minute; }
3561 if (strlen($Second) == 1 ) { $Second = "0".$Second; }
3563 return($Hour.":".$Minute.":".$Second);
3566 /* Convert to metric system */
3567 function ToMetric($Value)
3569 $Go = floor($Value/1000000000);
3570 $Mo = floor(($Value - $Go*1000000000)/1000000);
3571 $Ko = floor(($Value - $Go*1000000000 - $Mo*1000000)/1000);
3572 $o = floor($Value - $Go*1000000000 - $Mo*1000000 - $Ko*1000);
3574 if ($Go != 0) { return($Go.".".$Mo."g"); }
3575 if ($Mo != 0) { return($Mo.".".$ko."m"); }
3576 if ($Ko != 0) { return($Ko.".".$o)."k"; }
3580 /* Convert to curency */
3581 function ToCurrency($Value)
3583 $Go = floor($Value/1000000000);
3584 $Mo = floor(($Value - $Go*1000000000)/1000000);
3585 $Ko = floor(($Value - $Go*1000000000 - $Mo*1000000)/1000);
3586 $o = floor($Value - $Go*1000000000 - $Mo*1000000 - $Ko*1000);
3588 if ( strlen($o) == 1 ) { $o = "00".$o; }
3589 if ( strlen($o) == 2 ) { $o = "0".$o; }
3592 if ( $Ko != 0 ) { $ResultString = $Ko.".".$ResultString; }
3593 if ( $Mo != 0 ) { $ResultString = $Mo.".".$ResultString; }
3594 if ( $Go != 0 ) { $ResultString = $Go.".".$ResultString; }
3596 $ResultString = $this->Currency.$ResultString;
3597 return($ResultString);
3600 /* Set date format for axis labels */
3601 function setDateFormat($Format)
3603 $this->DateFormat = $Format;
3606 /* Convert TS to a date format string */
3607 function ToDate($Value)
3609 return(date($this->DateFormat,$Value));
3612 /* Check if a number is a full integer (for scaling) */
3613 function isRealInt($Value)
3615 if ($Value == floor($Value))
3621 function RaiseFatal($Message)
3623 echo "[FATAL] ".$Message."\r\n";