1<?php 2/** 3 * pChart - a PHP class to build charts! 4 * Copyright (C) 2008 Jean-Damien POGOLOTTI 5 * Version 2.0 6 * 7 * http://pchart.sourceforge.net 8 * 9 * This program is free software: you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation, either version 1,2,3 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program. If not, see <http://www.gnu.org/licenses/>. 21 */ 22 23require_once(dirname(__FILE__).'/ConversionHelpers.php'); 24require_once(dirname(__FILE__).'/ShadowProperties.php'); 25require_once(dirname(__FILE__).'/Color.php'); 26require_once(dirname(__FILE__).'/Palette.php'); 27require_once(dirname(__FILE__).'/ICanvas.php'); 28require_once(dirname(__FILE__).'/GridStyle.php'); 29require_once(dirname(__FILE__).'/ScaleStyle.php'); 30 31/* Declare some script wide constants */ 32define ( "SCALE_NORMAL", 1 ); 33define ( "SCALE_ADDALL", 2 ); 34define ( "SCALE_START0", 3 ); 35define ( "SCALE_ADDALLSTART0", 4 ); 36define ( "TARGET_GRAPHAREA", 1 ); 37define ( "TARGET_BACKGROUND", 2 ); 38define ( "ALIGN_TOP_LEFT", 1 ); 39define ( "ALIGN_TOP_CENTER", 2 ); 40define ( "ALIGN_TOP_RIGHT", 3 ); 41define ( "ALIGN_LEFT", 4 ); 42define ( "ALIGN_CENTER", 5 ); 43define ( "ALIGN_RIGHT", 6 ); 44define ( "ALIGN_BOTTOM_LEFT", 7 ); 45define ( "ALIGN_BOTTOM_CENTER", 8 ); 46define ( "ALIGN_BOTTOM_RIGHT", 9 ); 47 48/** 49 * pChart class definition 50 */ 51class pChart { 52 protected $palette; 53 54 /* Some static vars used in the class */ 55 protected $XSize = NULL; 56 protected $YSize = NULL; 57 protected $Picture = NULL; 58 protected $ImageMap = NULL; 59 60 /* Error management */ 61 protected $ErrorReporting = FALSE; 62 protected $ErrorInterface = "CLI"; 63 protected $Errors = NULL; 64 protected $ErrorFontName = "Fonts/pf_arma_five.ttf"; 65 protected $ErrorFontSize = 6; 66 67 /* vars related to the graphing area */ 68 protected $GArea_X1 = NULL; 69 protected $GArea_Y1 = NULL; 70 protected $GArea_X2 = NULL; 71 protected $GArea_Y2 = NULL; 72 protected $GAreaXOffset = NULL; 73 protected $VMax = NULL; 74 protected $VMin = NULL; 75 protected $VXMax = NULL; 76 protected $VXMin = NULL; 77 protected $Divisions = NULL; 78 protected $XDivisions = NULL; 79 protected $DivisionHeight = NULL; 80 protected $XDivisionHeight = NULL; 81 protected $DivisionCount = NULL; 82 protected $XDivisionCount = NULL; 83 protected $DivisionRatio = NULL; 84 protected $XDivisionRatio = NULL; 85 protected $DivisionWidth = NULL; 86 protected $DataCount = NULL; 87 88 /* Text format related vars */ 89 protected $FontName = NULL; 90 protected $FontSize = NULL; 91 protected $DateFormat = "d/m/Y"; 92 93 /* Lines format related vars */ 94 protected $LineWidth = 1; 95 protected $LineDotSize = 0; 96 97 /* Shadow settings */ 98 private $shadowProperties; 99 100 /* Image Map settings */ 101 protected $BuildMap = FALSE; 102 protected $MapFunction = NULL; 103 protected $tmpFolder = "tmp/"; 104 protected $MapID = NULL; 105 106 /** 107 * @brief An abstract ICanvas onto which we draw the chart 108 * 109 * @todo This probably shouldn't be protected, I'm still working 110 * on how the modules are going to break down between the various 111 * chart types. 112 */ 113 protected $canvas = null; 114 115 /** 116 * This function create the background picture 117 */ 118 function __construct($XSize, $YSize, ICanvas $canvas) { 119 $this->palette = Palette::defaultPalette(); 120 121 $this->XSize = $XSize; 122 $this->YSize = $YSize; 123 124 $this->setFontProperties ( "tahoma.ttf", 8 ); 125 126 $this->shadowProperties = ShadowProperties::FromDefaults(); 127 128 $this->canvas = $canvas; 129 } 130 131 /** 132 * Set if warnings should be reported 133 */ 134 function reportWarnings($Interface = "CLI") { 135 $this->ErrorReporting = TRUE; 136 $this->ErrorInterface = $Interface; 137 } 138 139 /** 140 * Set the font properties 141 */ 142 function setFontProperties($FontName, $FontSize) { 143 $this->FontName = $FontName; 144 $this->FontSize = $FontSize; 145 } 146 147 public function setPalette(Palette $newPalette) { 148 $this->palette = $newPalette; 149 } 150 151 /** 152 * Set the shadow properties 153 */ 154 function setShadowProperties($XDistance = 1, $YDistance = 1, Color $color = null, $Alpha = 50, $Blur = 0) { 155 if ($color == null) { 156 $color = new Color(60, 60, 60); 157 } 158 159 $this->shadowProperties = ShadowProperties::FromSettings($XDistance, 160 $YDistance, 161 $color, 162 $Alpha, 163 $Blur); 164 } 165 166 /** 167 * Remove shadow option 168 */ 169 function clearShadow() { 170 $this->shadowProperties = ShadowProperties::FromDefaults(); 171 } 172 173 /** 174 * Load Color Palette from file 175 */ 176 function loadColorPalette($FileName, $Delimiter = ",") { 177 $handle = @fopen ( $FileName, "r" ); 178 179 if ($handle == null) { 180 throw new Exception("Failed to open file in loadColorPalette"); 181 } 182 183 $ColorID = 0; 184 if ($handle) { 185 while ( ! feof ( $handle ) ) { 186 $buffer = fgets ( $handle, 4096 ); 187 $buffer = str_replace ( chr ( 10 ), "", $buffer ); 188 $buffer = str_replace ( chr ( 13 ), "", $buffer ); 189 $Values = explode ( $Delimiter, $buffer ); 190 if (count ( $Values ) == 3) { 191 $this->palette->colors[$ColorID] = new Color($Values[0], 192 $Values[1], 193 $Values[2]); 194 $ColorID ++; 195 } 196 } 197 } 198 } 199 200 /** 201 * Set line style 202 */ 203 function setLineStyle($Width = 1, $DotSize = 0) { 204 $this->LineWidth = $Width; 205 $this->LineDotSize = $DotSize; 206 } 207 208 /** 209 * Set the graph area location 210 */ 211 function setGraphArea($X1, $Y1, $X2, $Y2) { 212 $this->GArea_X1 = $X1; 213 $this->GArea_Y1 = $Y1; 214 $this->GArea_X2 = $X2; 215 $this->GArea_Y2 = $Y2; 216 } 217 218 /** 219 * Prepare the graph area 220 */ 221 private function drawGraphArea(BackgroundStyle $style) { 222 $this->canvas->drawFilledRectangle(new Point($this->GArea_X1, $this->GArea_Y1), 223 new Point($this->GArea_X2, $this->GArea_Y2), 224 $style->getBackgroundColor(), 225 $this->shadowProperties, FALSE ); 226 $this->canvas->drawRectangle(new Point($this->GArea_X1, $this->GArea_Y1), 227 new Point($this->GArea_X2, $this->GArea_Y2), 228 $style->getBackgroundColor()->addRGBIncrement(-40), 229 $style->getBorderWidth(), 230 $style->getBorderDotSize(), 231 $this->shadowProperties); 232 233 if ($style->useStripe()) { 234 $color2 = $style->getBackgroundColor()->addRGBIncrement(-15); 235 236 $SkewWidth = $this->GArea_Y2 - $this->GArea_Y1 - 1; 237 238 for($i = $this->GArea_X1 - $SkewWidth; $i <= $this->GArea_X2; $i = $i + 4) { 239 $X1 = $i; 240 $Y1 = $this->GArea_Y2; 241 $X2 = $i + $SkewWidth; 242 $Y2 = $this->GArea_Y1; 243 244 if ($X1 < $this->GArea_X1) { 245 $X1 = $this->GArea_X1; 246 $Y1 = $this->GArea_Y1 + $X2 - $this->GArea_X1 + 1; 247 } 248 249 if ($X2 >= $this->GArea_X2) { 250 $Y2 = $this->GArea_Y1 + $X2 - $this->GArea_X2 + 1; 251 $X2 = $this->GArea_X2 - 1; 252 } 253 254 $this->canvas->drawLine(new Point($X1, $Y1), 255 new Point($X2, $Y2 + 1), 256 $color2, 257 1, 258 0, 259 ShadowProperties::NoShadow()); 260 } 261 } 262 } 263 264 public function drawGraphBackground(BackgroundStyle $style) { 265 $this->drawGraphArea($style); 266 $this->drawGraphAreaGradient($style); 267 } 268 269 /** 270 * Allow you to clear the scale : used if drawing multiple charts 271 */ 272 function clearScale() { 273 $this->VMin = NULL; 274 $this->VMax = NULL; 275 $this->VXMin = NULL; 276 $this->VXMax = NULL; 277 $this->Divisions = NULL; 278 $this->XDivisions = NULL; 279 } 280 281 /** 282 * Allow you to fix the scale, use this to bypass the automatic scaling 283 */ 284 function setFixedScale($VMin, $VMax, $Divisions = 5, $VXMin = 0, $VXMax = 0, $XDivisions = 5) { 285 $this->VMin = $VMin; 286 $this->VMax = $VMax; 287 $this->Divisions = $Divisions; 288 289 if (! $VXMin == 0) { 290 $this->VXMin = $VXMin; 291 $this->VXMax = $VXMax; 292 $this->XDivisions = $XDivisions; 293 } 294 } 295 296 /** 297 * Wrapper to the drawScale() function allowing a second scale to 298 * be drawn 299 */ 300 function drawRightScale(pData $data, ScaleStyle $style, $Angle = 0, $Decimals = 1, $WithMargin = FALSE, $SkipLabels = 1) { 301 $this->drawScale($data, $style, $Angle, $Decimals, $WithMargin, $SkipLabels, TRUE ); 302 } 303 304 /** 305 * Compute and draw the scale 306 */ 307 function drawScale(pData $Data, ScaleStyle $style, $Angle = 0, $Decimals = 1, $WithMargin = FALSE, $SkipLabels = 1, $RightScale = FALSE) { 308 /* Validate the Data and DataDescription array */ 309 $this->validateData ( "drawScale", $Data->getData() ); 310 311 $this->canvas->drawLine(new Point($this->GArea_X1, $this->GArea_Y1), 312 new Point($this->GArea_X1, $this->GArea_Y2), 313 $style->getColor(), 314 $style->getLineWidth(), 315 $style->getLineDotSize(), 316 $this->shadowProperties); 317 $this->canvas->drawLine(new Point($this->GArea_X1, $this->GArea_Y2), 318 new Point($this->GArea_X2, $this->GArea_Y2), 319 $style->getColor(), 320 $style->getLineWidth(), 321 $style->getLineDotSize(), 322 $this->shadowProperties); 323 324 if ($this->VMin == NULL && $this->VMax == NULL) { 325 $Divisions = $this->calculateDivisions($Data, $style); 326 } else 327 $Divisions = $this->Divisions; 328 329 $this->DivisionCount = $Divisions; 330 331 $DataRange = $this->VMax - $this->VMin; 332 if ($DataRange == 0) { 333 $DataRange = .1; 334 } 335 336 $this->DivisionHeight = ($this->GArea_Y2 - $this->GArea_Y1) / $Divisions; 337 $this->DivisionRatio = ($this->GArea_Y2 - $this->GArea_Y1) / $DataRange; 338 339 $this->GAreaXOffset = 0; 340 if (count ( $Data->getData() ) > 1) { 341 if ($WithMargin == FALSE) 342 $this->DivisionWidth = ($this->GArea_X2 - $this->GArea_X1) / (count ( $Data->getData() ) - 1); 343 else { 344 $this->DivisionWidth = ($this->GArea_X2 - $this->GArea_X1) / (count ( $Data->getData() )); 345 $this->GAreaXOffset = $this->DivisionWidth / 2; 346 } 347 } else { 348 $this->DivisionWidth = $this->GArea_X2 - $this->GArea_X1; 349 $this->GAreaXOffset = $this->DivisionWidth / 2; 350 } 351 352 $this->DataCount = count ( $Data->getData() ); 353 354 if ($style->getDrawTicks() == FALSE) 355 return (0); 356 357 $YPos = $this->GArea_Y2; 358 $XMin = NULL; 359 for($i = 1; $i <= $Divisions + 1; $i ++) { 360 if ($RightScale) 361 $this->canvas->drawLine(new Point($this->GArea_X2, $YPos), 362 new Point($this->GArea_X2 + 5, $YPos), 363 $style->getColor(), 364 $style->getLineWidth(), 365 $style->getLineDotSize(), 366 $this->shadowProperties); 367 else 368 $this->canvas->drawLine(new Point($this->GArea_X1, $YPos), 369 new Point($this->GArea_X1 - 5, $YPos), 370 $style->getColor(), 371 $style->getLineWidth(), 372 $style->getLineDotSize(), 373 $this->shadowProperties); 374 375 $Value = $this->VMin + ($i - 1) * (($this->VMax - $this->VMin) / $Divisions); 376 $Value = round ( $Value * pow ( 10, $Decimals ) ) / pow ( 10, $Decimals ); 377 $Value = $this->convertValueForDisplay($Value, 378 $Data->getDataDescription()->getYFormat(), 379 $Data->getDataDescription()->getYUnit()); 380 381 $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); 382 $TextWidth = $Position [2] - $Position [0]; 383 384 if ($RightScale) { 385 $this->canvas->drawText($this->FontSize, 0, 386 new Point($this->GArea_X2 + 10, 387 $YPos + ($this->FontSize / 2)), 388 $style->getColor(), 389 $this->FontName, 390 $Value, 391 ShadowProperties::NoShadow()); 392 if ($XMin < $this->GArea_X2 + 15 + $TextWidth || $XMin == NULL) { 393 $XMin = $this->GArea_X2 + 15 + $TextWidth; 394 } 395 } else { 396 $this->canvas->drawText($this->FontSize, 397 0, 398 new Point($this->GArea_X1 - 10 - $TextWidth, 399 $YPos + ($this->FontSize / 2)), 400 $style->getColor(), 401 $this->FontName, 402 $Value, 403 ShadowProperties::NoShadow()); 404 if ($XMin > $this->GArea_X1 - 10 - $TextWidth || $XMin == NULL) { 405 $XMin = $this->GArea_X1 - 10 - $TextWidth; 406 } 407 } 408 409 $YPos = $YPos - $this->DivisionHeight; 410 } 411 412 /* Write the Y Axis caption if set */ 413 if ($Data->getDataDescription()->getYAxisName() != '') { 414 $Position = imageftbbox ( $this->FontSize, 90, $this->FontName, $Data->getDataDescription()->getYAxisName() ); 415 $TextHeight = abs ( $Position [1] ) + abs ( $Position [3] ); 416 $TextTop = (($this->GArea_Y2 - $this->GArea_Y1) / 2) + $this->GArea_Y1 + ($TextHeight / 2); 417 418 if ($RightScale) { 419 $this->canvas->drawText($this->FontSize, 90, 420 new Point($XMin + $this->FontSize, 421 $TextTop), 422 $style->getColor(), $this->FontName, 423 $Data->getDataDescription()->getYAxisName(), 424 ShadowProperties::NoShadow()); 425 } 426 else { 427 $this->canvas->drawText($this->FontSize, 90, 428 new Point($XMin - $this->FontSize, 429 $TextTop), 430 $style->getColor(), $this->FontName, 431 $Data->getDataDescription()->getYAxisName(), 432 ShadowProperties::NoShadow()); 433 } 434 } 435 436 /* Horizontal Axis */ 437 $XPos = $this->GArea_X1 + $this->GAreaXOffset; 438 $ID = 1; 439 $YMax = NULL; 440 foreach ( $Data->getData() as $Values ) { 441 if ($ID % $SkipLabels == 0) { 442 $this->canvas->drawLine(new Point(floor($XPos), $this->GArea_Y2), 443 new Point(floor($XPos), $this->GArea_Y2 + 5), 444 $style->getColor(), 445 $style->getLineWidth(), 446 $style->getLineDotSize(), 447 $this->shadowProperties); 448 $Value = $Values[$Data->getDataDescription()->getPosition()]; 449 $Value = $this->convertValueForDisplay($Value, 450 $Data->getDataDescription()->getXFormat(), 451 $Data->getDataDescription()->getXUnit()); 452 453 $Position = imageftbbox ( $this->FontSize, $Angle, $this->FontName, $Value ); 454 $TextWidth = abs ( $Position [2] ) + abs ( $Position [0] ); 455 $TextHeight = abs ( $Position [1] ) + abs ( $Position [3] ); 456 457 if ($Angle == 0) { 458 $YPos = $this->GArea_Y2 + 18; 459 $this->canvas->drawText($this->FontSize, 460 $Angle, 461 new Point(floor ( $XPos ) - floor ( $TextWidth / 2 ), 462 $YPos), 463 $style->getColor(), 464 $this->FontName, 465 $Value, 466 ShadowProperties::NoShadow()); 467 } else { 468 $YPos = $this->GArea_Y2 + 10 + $TextHeight; 469 if ($Angle <= 90) { 470 $this->canvas->drawText($this->FontSize, 471 $Angle, 472 new Point(floor($XPos) - $TextWidth + 5, 473 $YPos), 474 $style->getColor(), 475 $this->FontName, 476 $Value, 477 ShadowProperties::NoShadow()); 478 } 479 else { 480 $this->canvas->drawText($this->FontSize, 481 $Angle, 482 new Point(floor ( $XPos ) + $TextWidth + 5, 483 $YPos), 484 $style->getColor(), 485 $this->FontName, 486 $Value, 487 ShadowProperties::NoShadow()); 488 } 489 } 490 if ($YMax < $YPos || $YMax == NULL) { 491 $YMax = $YPos; 492 } 493 } 494 495 $XPos = $XPos + $this->DivisionWidth; 496 $ID ++; 497 } 498 499 /* Write the X Axis caption if set */ 500 if ($Data->getDataDescription()->getXAxisName() != '') { 501 $Position = imageftbbox ( $this->FontSize, 90, 502 $this->FontName, 503 $Data->getDataDescription()->getXAxisName()); 504 $TextWidth = abs ( $Position [2] ) + abs ( $Position [0] ); 505 $TextLeft = (($this->GArea_X2 - $this->GArea_X1) / 2) + $this->GArea_X1 + ($TextWidth / 2); 506 $this->canvas->drawText($this->FontSize, 0, 507 new Point($TextLeft, 508 $YMax + $this->FontSize + 5), 509 $style->getColor(), $this->FontName, 510 $Data->getDataDescription()->getXAxisName(), 511 ShadowProperties::NoShadow()); 512 } 513 } 514 515 /** 516 * Calculate the number of divisions that the Y axis will be 517 * divided into. This is a function of the range of Y values the 518 * data covers, as well as the scale style. Divisions should have 519 * some minimum size in screen coordinates in order that the 520 * divisions are clearly visible, so this is also a function of 521 * the graph size in screen coordinates. 522 * 523 * This method returns the number of divisions, but it also has 524 * side-effects on some class data members. This needs to be 525 * refactored to make it clearer what is and isn't affected. 526 */ 527 private function calculateDivisions(pData $Data, ScaleStyle $style) { 528 if (isset ( $Data->getDataDescription()->values[0] )) { 529 /* Pointless temporary is necessary because you can't 530 * directly apply an array index to the return value 531 * of a function in PHP */ 532 $dataArray = $Data->getData(); 533 $this->VMin = $dataArray[0] [$Data->getDataDescription()->values[0]]; 534 $this->VMax = $dataArray[0] [$Data->getDataDescription()->values[0]]; 535 } else { 536 $this->VMin = 2147483647; 537 $this->VMax = - 2147483647; 538 } 539 540 /* Compute Min and Max values */ 541 if ($style->getScaleMode() == SCALE_NORMAL 542 || $style->getScaleMode() == SCALE_START0) { 543 if ($style->getScaleMode() == SCALE_START0) { 544 $this->VMin = 0; 545 } 546 547 foreach ( $Data->getData() as $Values ) { 548 foreach ( $Data->getDataDescription()->values as $ColName ) { 549 if (isset ( $Values[$ColName] )) { 550 $Value = $Values[$ColName]; 551 552 if (is_numeric ( $Value )) { 553 if ($this->VMax < $Value) { 554 $this->VMax = $Value; 555 } 556 if ($this->VMin > $Value) { 557 $this->VMin = $Value; 558 } 559 } 560 } 561 } 562 } 563 } elseif ($style->getScaleMode() == SCALE_ADDALL || $style->getScaleMode() == SCALE_ADDALLSTART0 ) /* Experimental */ { 564 if ($style->getScaleMode() == SCALE_ADDALLSTART0) { 565 $this->VMin = 0; 566 } 567 568 foreach ( $Data->getData() as $Values ) { 569 $Sum = 0; 570 foreach ( $Data->getDataDescription()->values as $ColName ) { 571 $dataArray = $Data->getData(); 572 if (isset ( $Values[$ColName] )) { 573 $Value = $Values[$ColName]; 574 if (is_numeric ( $Value )) 575 $Sum += $Value; 576 } 577 } 578 if ($this->VMax < $Sum) { 579 $this->VMax = $Sum; 580 } 581 if ($this->VMin > $Sum) { 582 $this->VMin = $Sum; 583 } 584 } 585 } 586 587 $this->VMax = ceil($this->VMax); 588 589 /* If all values are the same */ 590 if ($this->VMax == $this->VMin) { 591 if ($this->VMax >= 0) { 592 $this->VMax ++; 593 } else { 594 $this->VMin --; 595 } 596 } 597 598 $DataRange = $this->VMax - $this->VMin; 599 if ($DataRange == 0) { 600 $DataRange = .1; 601 } 602 603 $this->calculateScales($Scale, $Divisions); 604 605 if (! isset ( $Divisions )) 606 $Divisions = 2; 607 608 if ($Scale == 1 && $Divisions % 2 == 1) 609 $Divisions --; 610 611 return $Divisions; 612 } 613 614 /** 615 * Compute and draw the scale for X/Y charts 616 */ 617 function drawXYScale(pData $Data, ScaleStyle $style, $YSerieName, $XSerieName, $Angle = 0, $Decimals = 1) { 618 /* Validate the Data and DataDescription array */ 619 $this->validateData ( "drawScale", $Data->getData()); 620 621 $this->canvas->drawLine(new Point($this->GArea_X1, $this->GArea_Y1), 622 new Point($this->GArea_X1, $this->GArea_Y2), 623 $style->getColor(), 624 $style->getLineWidth(), 625 $style->getLineDotSize(), 626 $this->shadowProperties); 627 $this->canvas->drawLine(new Point($this->GArea_X1, $this->GArea_Y2), 628 new Point($this->GArea_X2, $this->GArea_Y2), 629 $style->getColor(), 630 $style->getLineWidth(), 631 $style->getLineDotSize(), 632 $this->shadowProperties); 633 634 /* Process Y scale */ 635 if ($this->VMin == NULL && $this->VMax == NULL) { 636 $this->VMin = $Data->getSeriesMin($YSerieName); 637 $this->VMax = $Data->getSeriesMax($YSerieName); 638 639 /** @todo The use of ceil() here is questionable if all 640 * the values are much less than 1, AIUI */ 641 $this->VMax = ceil($this->VMax); 642 643 $DataRange = $this->VMax - $this->VMin; 644 if ($DataRange == 0) { 645 $DataRange = .1; 646 } 647 648 self::computeAutomaticScaling($this->GArea_Y1, 649 $this->GArea_Y2, 650 $this->VMin, 651 $this->VMax, 652 $Divisions); 653 } else 654 $Divisions = $this->Divisions; 655 656 $this->DivisionCount = $Divisions; 657 658 $DataRange = $this->VMax - $this->VMin; 659 if ($DataRange == 0) { 660 $DataRange = .1; 661 } 662 663 $this->DivisionHeight = ($this->GArea_Y2 - $this->GArea_Y1) / $Divisions; 664 $this->DivisionRatio = ($this->GArea_Y2 - $this->GArea_Y1) / $DataRange; 665 666 $YPos = $this->GArea_Y2; 667 $XMin = NULL; 668 for($i = 1; $i <= $Divisions + 1; $i ++) { 669 $this->canvas->drawLine(new Point($this->GArea_X1, $YPos), 670 new Point($this->GArea_X1 - 5, $YPos), 671 $style->getColor(), 672 $style->getLineWidth(), 673 $style->getLineDotSize(), 674 $this->shadowProperties); 675 $Value = $this->VMin + ($i - 1) * (($this->VMax - $this->VMin) / $Divisions); 676 $Value = round ( $Value * pow ( 10, $Decimals ) ) / pow ( 10, $Decimals ); 677 $Value = $this->convertValueForDisplay($Value, 678 $Data->getDataDescription()->getYFormat(), 679 $Data->getDataDescription()->getYUnit()); 680 681 $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); 682 $TextWidth = $Position [2] - $Position [0]; 683 $this->canvas->drawText($this->FontSize, 684 0, 685 new Point($this->GArea_X1 - 10 - $TextWidth, 686 $YPos + ($this->FontSize / 2)), 687 $style->getColor(), 688 $this->FontName, 689 $Value, 690 $this->shadowProperties); 691 692 if ($XMin > $this->GArea_X1 - 10 - $TextWidth || $XMin == NULL) { 693 $XMin = $this->GArea_X1 - 10 - $TextWidth; 694 } 695 696 $YPos = $YPos - $this->DivisionHeight; 697 } 698 699 /* Process X scale */ 700 if ($this->VXMin == NULL && $this->VXMax == NULL) { 701 $this->VXMax = $Data->getSeriesMax($XSerieName); 702 $this->VXMin = $Data->getSeriesMin($XSerieName); 703 704 $this->VXMax = ceil($this->VXMax); 705 706 $DataRange = $this->VMax - $this->VMin; 707 if ($DataRange == 0) { 708 $DataRange = .1; 709 } 710 711 /* Compute automatic scaling */ 712 self::computeAutomaticScaling($this->GArea_X1, $this->GArea_X2, 713 $this->VXMin, $this->VXMax, 714 $XDivisions); 715 } else 716 $XDivisions = $this->XDivisions; 717 718 $this->XDivisionCount = $Divisions; 719 $this->DataCount = $Divisions + 2; 720 721 $XDataRange = $this->VXMax - $this->VXMin; 722 if ($XDataRange == 0) { 723 $XDataRange = .1; 724 } 725 726 $this->DivisionWidth = ($this->GArea_X2 - $this->GArea_X1) / $XDivisions; 727 $this->XDivisionRatio = ($this->GArea_X2 - $this->GArea_X1) / $XDataRange; 728 729 $XPos = $this->GArea_X1; 730 $YMax = NULL; 731 for($i = 1; $i <= $XDivisions + 1; $i ++) { 732 $this->canvas->drawLine(new Point($XPos, $this->GArea_Y2), 733 new Point($XPos, $this->GArea_Y2 + 5), 734 $style->getColor(), 735 $style->getLineWidth(), 736 $style->getLineDotSize(), 737 $this->shadowProperties); 738 739 $Value = $this->VXMin + ($i - 1) * (($this->VXMax - $this->VXMin) / $XDivisions); 740 $Value = round ( $Value * pow ( 10, $Decimals ) ) / pow ( 10, $Decimals ); 741 742 $Value = $this->convertValueForDisplay($Value, 743 $Data->getDataDescription()->getYFormat(), 744 $Data->getDataDescription()->getYUnit()); 745 746 $Position = imageftbbox ( $this->FontSize, $Angle, $this->FontName, $Value ); 747 $TextWidth = abs ( $Position [2] ) + abs ( $Position [0] ); 748 $TextHeight = abs ( $Position [1] ) + abs ( $Position [3] ); 749 750 if ($Angle == 0) { 751 $YPos = $this->GArea_Y2 + 18; 752 $this->canvas->drawText($this->FontSize, 753 $Angle, 754 new Point(floor ( $XPos ) - floor ( $TextWidth / 2 ), 755 $YPos), 756 $style->getColor(), 757 $this->FontName, 758 $Value, 759 $this->shadowProperties); 760 } else { 761 $YPos = $this->GArea_Y2 + 10 + $TextHeight; 762 if ($Angle <= 90) { 763 $this->canvas->drawText($this->FontSize, 764 $Angle, 765 new Point(floor ( $XPos ) - $TextWidth + 5, 766 $YPos), 767 $style->getColor(), 768 $this->FontName, 769 $Value, 770 $this->shadowProperties); 771 } 772 else { 773 $this->canvas->drawText($this->FontSize, 774 $Angle, 775 new Point(floor ( $XPos ) + $TextWidth + 5, 776 $YPos), 777 $style->getColor(), 778 $this->FontName, 779 $Value, 780 $this->shadowProperties); 781 } 782 } 783 784 if ($YMax < $YPos || $YMax == NULL) { 785 $YMax = $YPos; 786 } 787 788 $XPos = $XPos + $this->DivisionWidth; 789 } 790 791 /* Write the Y Axis caption if set */ 792 if ($Data->getDataDescription()->getYAxisName() != '') { 793 $Position = imageftbbox ( $this->FontSize, 90, $this->FontName, 794 $Data->getDataDescription()->getYAxisName()); 795 $TextHeight = abs ( $Position [1] ) + abs ( $Position [3] ); 796 $TextTop = (($this->GArea_Y2 - $this->GArea_Y1) / 2) + $this->GArea_Y1 + ($TextHeight / 2); 797 $this->canvas->drawText($this->FontSize, 798 90, 799 new Point($XMin - $this->FontSize, 800 $TextTop), 801 $style->getColor(), 802 $this->FontName, 803 $Data->getDataDescription()->getYAxisName(), 804 $this->shadowProperties); 805 } 806 807 /* Write the X Axis caption if set */ 808 $this->writeScaleXAxisCaption($Data, $style, $YMax); 809 } 810 811 private function drawGridMosaic(GridStyle $style, $divisionCount, $divisionHeight) { 812 $LayerHeight = $this->GArea_Y2 - $this->GArea_Y1; 813 814 $YPos = $LayerHeight - 1; //$this->GArea_Y2-1; 815 $LastY = $YPos; 816 817 for($i = 0; $i < $divisionCount; $i ++) { 818 $LastY = $YPos; 819 $YPos = $YPos - $divisionHeight; 820 821 if ($YPos <= 0) { 822 $YPos = 1; 823 } 824 825 if ($i % 2 == 0) { 826 $this->canvas->drawFilledRectangle(new Point($this->GArea_X1 + 1, 827 $this->GArea_Y1 + $YPos), 828 new Point($this->GArea_X2 - 1, 829 $this->GArea_Y1 + $LastY), 830 new Color(250, 250, 250), 831 ShadowProperties::NoShadow(), 832 false, 833 $style->getAlpha()); 834 } 835 } 836 } 837 838 /** 839 * Write the X Axis caption on the scale, if set 840 */ 841 private function writeScaleXAxisCaption(pData $data, ScaleStyle $style, $YMax) { 842 if ($data->getDataDescription()->getXAxisName() != '') { 843 $Position = imageftbbox ( $this->FontSize, 90, $this->FontName, $data->getDataDescription()->getXAxisName()); 844 $TextWidth = abs ( $Position [2] ) + abs ( $Position [0] ); 845 $TextLeft = (($this->GArea_X2 - $this->GArea_X1) / 2) + $this->GArea_X1 + ($TextWidth / 2); 846 $this->canvas->drawText($this->FontSize, 847 0, 848 new Point($TextLeft, 849 $YMax + $this->FontSize + 5), 850 $style->getColor(), 851 $this->FontName, 852 $data->getDataDescription()->getXAxisName(), 853 $this->shadowProperties); 854 } 855 856 } 857 858 /** 859 * Compute and draw the scale 860 */ 861 function drawGrid(GridStyle $style) { 862 /* Draw mosaic */ 863 if ($style->getMosaic()) { 864 $this->drawGridMosaic($style, $this->DivisionCount, $this->DivisionHeight); 865 } 866 867 /* Horizontal lines */ 868 $YPos = $this->GArea_Y2 - $this->DivisionHeight; 869 for($i = 1; $i <= $this->DivisionCount; $i ++) { 870 if ($YPos > $this->GArea_Y1 && $YPos < $this->GArea_Y2) 871 $this->canvas->drawDottedLine(new Point($this->GArea_X1, $YPos), 872 new Point($this->GArea_X2, $YPos), 873 $style->getLineWidth(), 874 $this->LineWidth, 875 $style->getColor(), 876 ShadowProperties::NoShadow()); 877 /** @todo There's some inconsistency here. The parameter 878 * $lineWidth appears to be used to control the dot size, 879 * not the line width? This is the same way it's always 880 * been done, although now it's more obvious that there's 881 * a problem. */ 882 883 $YPos = $YPos - $this->DivisionHeight; 884 } 885 886 /* Vertical lines */ 887 if ($this->GAreaXOffset == 0) { 888 $XPos = $this->GArea_X1 + $this->DivisionWidth + $this->GAreaXOffset; 889 $ColCount = $this->DataCount - 2; 890 } else { 891 $XPos = $this->GArea_X1 + $this->GAreaXOffset; 892 $ColCount = floor ( ($this->GArea_X2 - $this->GArea_X1) / $this->DivisionWidth ); 893 } 894 895 for($i = 1; $i <= $ColCount; $i ++) { 896 if ($XPos > $this->GArea_X1 && $XPos < $this->GArea_X2) 897 $this->canvas->drawDottedLine(new Point(floor($XPos), $this->GArea_Y1), 898 new Point(floor($XPos), $this->GArea_Y2), 899 $style->getLineWidth(), 900 $this->LineWidth, 901 $style->getcolor(), 902 $this->shadowProperties); 903 $XPos = $XPos + $this->DivisionWidth; 904 } 905 } 906 907 /** 908 * retrieve the legends size 909 */ 910 public function getLegendBoxSize($DataDescription) { 911 if (! isset ( $DataDescription->description)) 912 return (- 1); 913 914 /* <-10->[8]<-4->Text<-10-> */ 915 $MaxWidth = 0; 916 $MaxHeight = 8; 917 foreach ( $DataDescription->description as $Value ) { 918 $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); 919 $TextWidth = $Position [2] - $Position [0]; 920 $TextHeight = $Position [1] - $Position [7]; 921 if ($TextWidth > $MaxWidth) { 922 $MaxWidth = $TextWidth; 923 } 924 $MaxHeight = $MaxHeight + $TextHeight + 4; 925 } 926 $MaxHeight = $MaxHeight - 3; 927 $MaxWidth = $MaxWidth + 32; 928 929 return (array ($MaxWidth, $MaxHeight )); 930 } 931 932 /** 933 * Draw the data legends 934 */ 935 public function drawLegend($XPos, $YPos, $DataDescription, Color $color, Color $color2 = null, Color $color3 = null, $Border = TRUE) { 936 if ($color2 == null) { 937 $color2 = $color->addRGBIncrement(-30); 938 } 939 940 if ($color3 == null) { 941 $color3 = new Color(0, 0, 0); 942 } 943 944 /* Validate the Data and DataDescription array */ 945 $this->validateDataDescription("drawLegend", $DataDescription); 946 947 if (! isset ( $DataDescription->description)) 948 return (- 1); 949 950 /* <-10->[8]<-4->Text<-10-> */ 951 $MaxWidth = 0; 952 $MaxHeight = 8; 953 foreach ( $DataDescription->description as $Key => $Value ) { 954 $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); 955 $TextWidth = $Position [2] - $Position [0]; 956 $TextHeight = $Position [1] - $Position [7]; 957 if ($TextWidth > $MaxWidth) { 958 $MaxWidth = $TextWidth; 959 } 960 $MaxHeight = $MaxHeight + $TextHeight + 4; 961 } 962 $MaxHeight = $MaxHeight - 5; 963 $MaxWidth = $MaxWidth + 32; 964 965 if ($Border) { 966 $this->canvas->drawFilledRoundedRectangle(new Point($XPos + 1, $YPos + 1), 967 new Point($XPos + $MaxWidth + 1, 968 $YPos + $MaxHeight + 1), 969 5, $color2, 970 $this->LineWidth, 971 $this->LineDotSize, 972 $this->shadowProperties); 973 974 $this->canvas->drawFilledRoundedRectangle(new Point($XPos, $YPos), 975 new Point($XPos + $MaxWidth, 976 $YPos + $MaxHeight), 977 5, $color, 978 $this->LineWidth, 979 $this->LineDotSize, 980 $this->shadowProperties); 981 } 982 983 $YOffset = 4 + $this->FontSize; 984 $ID = 0; 985 foreach ( $DataDescription->description as $Key => $Value ) { 986 $this->canvas->drawFilledRoundedRectangle(new Point($XPos + 10, 987 $YPos + $YOffset - 4), 988 new Point($XPos + 14, 989 $YPos + $YOffset - 4), 990 2, 991 $this->palette->colors[$ID], 992 $this->LineWidth, 993 $this->LineDotSize, 994 $this->shadowProperties); 995 $this->canvas->drawText($this->FontSize, 996 0, 997 new Point($XPos + 22, 998 $YPos + $YOffset), 999 $color3, 1000 $this->FontName, 1001 $Value, 1002 $this->shadowProperties); 1003 1004 $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); 1005 $TextHeight = $Position [1] - $Position [7]; 1006 1007 $YOffset = $YOffset + $TextHeight + 4; 1008 $ID ++; 1009 } 1010 } 1011 1012 /** 1013 * Draw the graph title 1014 * 1015 * @todo Should we pass in a ShadowProperties object here? Or is 1016 * this a public function? 1017 */ 1018 public function drawTitle($XPos, $YPos, $Value, Color $color, $XPos2 = -1, $YPos2 = -1, ShadowProperties $shadowProperties = null) { 1019 if ($shadowProperties == null) { 1020 $shadowProperties = ShadowProperties::NoShadow(); 1021 } 1022 1023 if ($XPos2 != - 1) { 1024 $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); 1025 $TextWidth = $Position [2] - $Position [0]; 1026 $XPos = floor ( ($XPos2 - $XPos - $TextWidth) / 2 ) + $XPos; 1027 } 1028 1029 if ($YPos2 != - 1) { 1030 $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); 1031 $TextHeight = $Position [5] - $Position [3]; 1032 $YPos = floor ( ($YPos2 - $YPos - $TextHeight) / 2 ) + $YPos; 1033 } 1034 1035 $this->canvas->drawText($this->FontSize, 1036 0, 1037 new Point($XPos, $YPos), 1038 $color, 1039 $this->FontName, 1040 $Value, 1041 $shadowProperties); 1042 } 1043 1044 /** 1045 * Draw a text box with text align & alpha properties 1046 * 1047 * @param $point1 Minimum corner of the box 1048 * @param $point2 Maximum corner of the box 1049 * 1050 * @todo This should probably be a method on the ICanvas 1051 * interface, since it doesn't have anything specifically to do 1052 * with graphs 1053 */ 1054 public function drawTextBox(Point $point1, Point $point2, $Text, $Angle = 0, Color $color = null, $Align = ALIGN_LEFT, ShadowProperties $shadowProperties = null, Color $backgroundColor = null, $Alpha = 100) { 1055 if ($color == null) { 1056 $color = new Color(255, 255, 255); 1057 } 1058 1059 if ($shadowProperties == null) { 1060 $shadowProperties = ShadowProperties::NoShadow(); 1061 } 1062 1063 $Position = imageftbbox ( $this->FontSize, $Angle, $this->FontName, $Text ); 1064 $TextWidth = $Position [2] - $Position [0]; 1065 $TextHeight = $Position [5] - $Position [3]; 1066 $AreaWidth = $point2->getX() - $point1->getX(); 1067 $AreaHeight = $point2->getY() - $point1->getY(); 1068 1069 if ($backgroundColor != null) 1070 $this->canvas->drawFilledRectangle($point1, 1071 $point2, 1072 $backgroundColor, 1073 $shadowProperties, FALSE, $Alpha ); 1074 1075 if ($Align == ALIGN_TOP_LEFT) { 1076 $newPosition = $point1->addIncrement(1, $this->FontSize + 1); 1077 } 1078 if ($Align == ALIGN_TOP_CENTER) { 1079 $newPosition = $point1->addIncrement(($AreaWidth / 2) - ($TextWidth / 2), 1080 $this->FontSize + 1); 1081 } 1082 if ($Align == ALIGN_TOP_RIGHT) { 1083 $newPosition = new Point($point2->getX() - $TextWidth - 1, 1084 $point1->getY() + $this->FontSize + 1); 1085 } 1086 if ($Align == ALIGN_LEFT) { 1087 $newPosition = $point1->addIncrement(1, 1088 ($AreaHeight / 2) - ($TextHeight / 2)); 1089 } 1090 if ($Align == ALIGN_CENTER) { 1091 $newPosition = $point1->addIncrement(($AreaWidth / 2) - ($TextWidth / 2), 1092 ($AreaHeight / 2) - ($TextHeight / 2)); 1093 } 1094 if ($Align == ALIGN_RIGHT) { 1095 $newPosition = new Point($point2->getX() - $TextWidth - 1, 1096 $point1->getY() + ($AreaHeight / 2) - ($TextHeight / 2)); 1097 } 1098 if ($Align == ALIGN_BOTTOM_LEFT) { 1099 $newPosition = new Point($point1->getX() + 1, 1100 $point2->getY() - 1); 1101 } 1102 if ($Align == ALIGN_BOTTOM_CENTER) { 1103 $newPosition = new Point($point1->getX() + ($AreaWidth / 2) - ($TextWidth / 2), 1104 $point2->getY() - 1); 1105 } 1106 if ($Align == ALIGN_BOTTOM_RIGHT) { 1107 $newPosition = $point2->addIncrement(- $TextWidth - 1, 1108 -1); 1109 } 1110 1111 $this->canvas->drawText($this->FontSize, $Angle, $newPosition, $color, $this->FontName, $Text, $shadowProperties); 1112 } 1113 1114 /** 1115 * Compute and draw the scale 1116 * 1117 * @todo What is the method name a typo for? Threshold? 1118 */ 1119 function drawTreshold($Value, Color $color, $ShowLabel = FALSE, $ShowOnRight = FALSE, $TickWidth = 4, $FreeText = NULL) { 1120 $Y = $this->GArea_Y2 - ($Value - $this->VMin) * $this->DivisionRatio; 1121 1122 if ($Y <= $this->GArea_Y1 || $Y >= $this->GArea_Y2) 1123 return (- 1); 1124 1125 if ($TickWidth == 0) 1126 $this->canvas->drawLine(new Point($this->GArea_X1, $Y), 1127 new Point($this->GArea_X2, $Y), 1128 $color, 1129 $this->LineWidth, 1130 $this->LineDotSize, 1131 $this->shadowProperties); 1132 else 1133 $this->canvas->drawDottedLine(new Point($this->GArea_X1, $Y), 1134 new Point($this->GArea_X2, $Y), 1135 $TickWidth, 1136 $this->LineWidth, 1137 $color, 1138 $this->shadowProperties); 1139 1140 if ($ShowLabel) { 1141 if ($FreeText == NULL) { 1142 $Label = $Value; 1143 } else { 1144 $Label = $FreeText; 1145 } 1146 1147 if ($ShowOnRight) { 1148 $position = new Point($this->GArea_X2 + 2, 1149 $Y + ($this->FontSize/ 2)); 1150 } 1151 else { 1152 $position = new Point($this->GArea_X1 + 2, 1153 $Y - ($this->FontSize / 2)); 1154 } 1155 1156 $this->canvas->drawText($this->FontSize, 0, 1157 $position, 1158 $color, 1159 $this->FontName, 1160 $Label, 1161 ShadowProperties::NoShadow()); 1162 } 1163 } 1164 1165 /** 1166 * This function put a label on a specific point 1167 */ 1168 function setLabel($Data, $DataDescription, $SerieName, $ValueName, $Caption, Color $color = null) { 1169 if ($color == null) { 1170 $color = new Color(210, 210, 210); 1171 } 1172 1173 /* Validate the Data and DataDescription array */ 1174 $this->validateDataDescription ( "setLabel", $DataDescription ); 1175 $this->validateData ( "setLabel", $Data ); 1176 $ShadowFactor = 100; 1177 1178 $Cp = 0; 1179 $Found = FALSE; 1180 foreach ( $Data as $Value ) { 1181 if ($Value[$DataDescription->getPosition()] == $ValueName) { 1182 $NumericalValue = $Value[$SerieName]; 1183 $Found = TRUE; 1184 } 1185 if (! $Found) 1186 $Cp ++; 1187 } 1188 1189 $XPos = $this->GArea_X1 + $this->GAreaXOffset + ($this->DivisionWidth * $Cp) + 2; 1190 $YPos = $this->GArea_Y2 - ($NumericalValue - $this->VMin) * $this->DivisionRatio; 1191 1192 $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Caption ); 1193 $TextHeight = $Position [3] - $Position [5]; 1194 $TextWidth = $Position [2] - $Position [0] + 2; 1195 $TextOffset = floor ( $TextHeight / 2 ); 1196 1197 // Shadow 1198 $Poly = array ($XPos + 1, $YPos + 1, $XPos + 9, $YPos - $TextOffset, $XPos + 8, $YPos + $TextOffset + 2 ); 1199 1200 $this->canvas->drawFilledPolygon($Poly, 1201 3, 1202 $color->addRGBIncrement(-$ShadowFactor)); 1203 1204 $this->canvas->drawLine(new Point($XPos, $YPos + 1), 1205 new Point($XPos + 9, $YPos - $TextOffset - .2), 1206 $color->addRGBIncrement(-$ShadowFactor), 1207 $this->LineWidth, 1208 $this->LineDotSize, 1209 $this->shadowProperties); 1210 1211 $this->canvas->drawLine(new Point($XPos, $YPos + 1), 1212 new Point($XPos + 9, $YPos + $TextOffset + 2.2), 1213 $color->addRGBIncrement(-$ShadowFactor), 1214 $this->LineWidth, 1215 $this->LineDotSize, 1216 $this->shadowProperties); 1217 1218 $this->canvas->drawFilledRectangle(new Point($XPos + 9, 1219 $YPos - $TextOffset - .2), 1220 new Point($XPos + 13 + $TextWidth, 1221 $YPos + $TextOffset + 2.2), 1222 $color->addRGBIncrement(-$ShadowFactor), 1223 $this->shadowProperties); 1224 1225 // Label background 1226 $Poly = array ($XPos, $YPos, $XPos + 8, $YPos - $TextOffset - 1, $XPos + 8, $YPos + $TextOffset + 1 ); 1227 1228 $this->canvas->drawFilledPolygon($Poly, 3, $color); 1229 1230 /** @todo We draw exactly the same line twice, with the same settings. 1231 * Surely this is pointless? */ 1232 $this->canvas->drawLine(new Point($XPos - 1, $YPos), 1233 new Point($XPos + 8, $YPos - $TextOffset - 1.2), 1234 $color, 1235 $this->LineWidth, 1236 $this->LineDotSize, 1237 $this->shadowProperties); 1238 1239 $this->canvas->drawLine(new Point($XPos - 1, $YPos), 1240 new Point($XPos + 8, $YPos + $TextOffset + 1.2), 1241 $color, 1242 $this->LineWidth, 1243 $this->LineDotSize, 1244 $this->shadowProperties); 1245 $this->canvas->drawFilledRectangle(new Point($XPos + 8, 1246 $YPos - $TextOffset - 1.2), 1247 new Point($XPos + 12 + $TextWidth, 1248 $YPos + $TextOffset + 1.2), 1249 $color, 1250 $this->shadowProperties); 1251 1252 $this->canvas->drawText($this->FontSize, 1253 0, 1254 new Point($XPos + 10, $YPos + $TextOffset), 1255 new Color(0, 0, 0), 1256 $this->FontName, 1257 $Caption, 1258 ShadowProperties::NoShadow()); 1259 } 1260 1261 /** 1262 * This function draw a plot graph 1263 */ 1264 function drawPlotGraph($Data, $DataDescription, $BigRadius = 5, $SmallRadius = 2, Color $color2 = null, $Shadow = FALSE) { 1265 /* Validate the Data and DataDescription array */ 1266 $this->validateDataDescription ( "drawPlotGraph", $DataDescription ); 1267 $this->validateData ( "drawPlotGraph", $Data ); 1268 1269 $GraphID = 0; 1270 $colorO = $color2; 1271 1272 foreach ( $DataDescription->values as $ColName ) { 1273 $ColorID = $DataDescription->getColumnIndex($ColName); 1274 1275 $color = $this->palette->colors[$ColorID]; 1276 $color2 = $colorO; 1277 1278 if (isset ( $DataDescription->seriesSymbols[$ColName] )) { 1279 $Infos = getimagesize ( $DataDescription->seriesSymbols[$ColName] ); 1280 $ImageWidth = $Infos [0]; 1281 $ImageHeight = $Infos [1]; 1282 $Symbol = imagecreatefromgif ( $DataDescription->seriesSymbols[$ColName] ); 1283 } 1284 1285 $XPos = $this->GArea_X1 + $this->GAreaXOffset; 1286 $Hsize = round ( $BigRadius / 2 ); 1287 1288 $color3 = null; 1289 foreach ( $Data as $Values ) { 1290 $Value = $Values[$ColName]; 1291 $YPos = $this->GArea_Y2 - (($Value - $this->VMin) * $this->DivisionRatio); 1292 1293 /* Save point into the image map if option activated */ 1294 if ($this->BuildMap) 1295 $this->addToImageMap ( $XPos - $Hsize, $YPos - $Hsize, $XPos + 1 + $Hsize, $YPos + $Hsize + 1, $DataDescription->description[$ColName], $Values[$ColName] . $DataDescription->getYUnit(), "Plot" ); 1296 1297 if (is_numeric ( $Value )) { 1298 if (! isset ( $DataDescription->seriesSymbols[$ColName] )) { 1299 1300 if ($Shadow) { 1301 if ($color3 != null) { 1302 $this->canvas->drawFilledCircle(new Point($XPos + 2, 1303 $YPos + 2), 1304 $BigRadius, 1305 $color3, 1306 $this->shadowProperties); 1307 } 1308 else { 1309 $color3 = $this->palette->colors[$ColorID]->addRGBIncrement(-20); 1310 1311 $this->canvas->drawFilledCircle(new Point($XPos + 2, 1312 $YPos + 2), 1313 $BigRadius, 1314 $color3, 1315 $this->shadowProperties); 1316 } 1317 } 1318 1319 $this->canvas->drawFilledCircle(new Point($XPos + 1, 1320 $YPos + 1), 1321 $BigRadius, 1322 $color, 1323 $this->shadowProperties); 1324 1325 if ($SmallRadius != 0) { 1326 if ($color2 != null) { 1327 $this->canvas->drawFilledCircle(new Point($XPos + 1, 1328 $YPos + 1), 1329 $SmallRadius, 1330 $color2, 1331 $this->shadowProperties); 1332 } 1333 else { 1334 $color2 = $this->palette->colors[$ColorID]->addRGBIncrement(-15); 1335 1336 $this->canvas->drawFilledCircle(new Point($XPos + 1, 1337 $YPos + 1), 1338 $SmallRadius, 1339 $color2, 1340 $this->shadowProperties); 1341 } 1342 } 1343 } else { 1344 imagecopymerge ( $this->canvas->getPicture(), $Symbol, $XPos + 1 - $ImageWidth / 2, $YPos + 1 - $ImageHeight / 2, 0, 0, $ImageWidth, $ImageHeight, 100 ); 1345 } 1346 } 1347 1348 $XPos = $XPos + $this->DivisionWidth; 1349 } 1350 $GraphID ++; 1351 } 1352 } 1353 1354 1355 /** 1356 * Linearly Scale a given value 1357 * 1358 * using it's own minima/maxima and the desired output minima/maxima 1359 */ 1360 function linearScale($value, $istart, $istop, $ostart, $ostop){ 1361 $div = ($istop - $istart); 1362 if($div == 0.0) $div = 1; 1363 return $ostart + ($ostop - $ostart) * (($value - $istart) / $div); 1364 } 1365 1366 /** 1367 * @brief This function draw a plot graph in an X/Y space 1368 */ 1369 function drawXYPlotGraph(pData $DataSet, $YSerieName, $XSerieName, $PaletteID = 0, $BigRadius = 5, $SmallRadius = 2, Color $color2 = null, $Shadow = TRUE, $SizeSerieName = '') { 1370 $color = $this->palette->colors[$PaletteID]; 1371 1372 $color3 = null; 1373 1374 $Data = $DataSet->getData(); 1375 foreach ( $Data as $Values ) { 1376 if (isset($Values[$YSerieName]) && isset ($Values[$XSerieName])) { 1377 $X = $Values[$XSerieName]; 1378 $Y = $Values[$YSerieName]; 1379 1380 $Y = $this->GArea_Y2 - (($Y - $this->VMin) * $this->DivisionRatio); 1381 $X = $this->GArea_X1 + (($X - $this->VXMin) * $this->XDivisionRatio); 1382 1383 if(isset($Values[$SizeSerieName])){ 1384 $br = $this->linearScale( 1385 $Values[$SizeSerieName], 1386 $DataSet->getSeriesMin($SizeSerieName), 1387 $DataSet->getSeriesMax($SizeSerieName), 1388 $SmallRadius, 1389 $BigRadius 1390 ); 1391 $sr = $br; 1392 }else{ 1393 $br = $BigRadius; 1394 $sr = $SmallRadius; 1395 } 1396 1397 if ($Shadow) { 1398 if ($color3 != null) { 1399 $this->canvas->drawFilledCircle(new Point($X + 2, $Y + 2), 1400 $br, 1401 $color3, 1402 $this->shadowProperties); 1403 } 1404 else { 1405 $color3 = $this->palette->colors[$PaletteID]->addRGBIncrement(-20); 1406 $this->canvas->drawFilledCircle(new Point($X + 2, $Y + 2), 1407 $br, 1408 $color3, 1409 $this->shadowProperties); 1410 } 1411 } 1412 1413 $this->canvas->drawFilledCircle(new Point($X + 1, $Y + 1), 1414 $br, 1415 $color, 1416 $this->shadowProperties); 1417 1418 if ($color2 != null) { 1419 $this->canvas->drawFilledCircle(new Point($X + 1, $Y + 1), 1420 $sr, 1421 $color2, 1422 $this->shadowProperties); 1423 } 1424 else { 1425 $color2 = $this->palette->colors[$PaletteID]->addRGBIncrement(20); 1426 1427 $this->canvas->drawFilledCircle(new Point($X + 1, $Y + 1), 1428 $sr, 1429 $color2, 1430 $this->shadowProperties); 1431 } 1432 } 1433 } 1434 1435 } 1436 1437 /** 1438 * This function draw an area between two series 1439 */ 1440 function drawArea($Data, $Serie1, $Serie2, Color $color, $Alpha = 50) { 1441 /* Validate the Data and DataDescription array */ 1442 $this->validateData ( "drawArea", $Data ); 1443 1444 $LayerHeight = $this->GArea_Y2 - $this->GArea_Y1; 1445 1446 $XPos = $this->GAreaXOffset; 1447 $LastXPos = - 1; 1448 foreach ( $Data as $Values ) { 1449 $Value1 = $Values[$Serie1]; 1450 $Value2 = $Values[$Serie2]; 1451 $YPos1 = $LayerHeight - (($Value1 - $this->VMin) * $this->DivisionRatio); 1452 $YPos2 = $LayerHeight - (($Value2 - $this->VMin) * $this->DivisionRatio); 1453 1454 if ($LastXPos != - 1) { 1455 $Points = array(); 1456 $Points [] = $LastXPos + $this->GArea_X1; 1457 $Points [] = $LastYPos1 + $this->GArea_Y1; 1458 $Points [] = $LastXPos + $this->GArea_X1; 1459 $Points [] = $LastYPos2 + $this->GArea_Y1; 1460 $Points [] = $XPos + $this->GArea_X1; 1461 $Points [] = $YPos2 + $this->GArea_Y1; 1462 $Points [] = $XPos + $this->GArea_X1; 1463 $Points [] = $YPos1 + $this->GArea_Y1; 1464 1465 $this->canvas->drawFilledPolygon($Points, 1466 4, 1467 $color, 1468 $Alpha); 1469 } 1470 1471 $LastYPos1 = $YPos1; 1472 $LastYPos2 = $YPos2; 1473 $LastXPos = $XPos; 1474 1475 $XPos = $XPos + $this->DivisionWidth; 1476 } 1477 } 1478 1479 /** 1480 * This function write the values of the specified series 1481 */ 1482 function writeValues($Data, $DataDescription, $Series) { 1483 /* Validate the Data and DataDescription array */ 1484 $this->validateDataDescription ( "writeValues", $DataDescription ); 1485 $this->validateData ( "writeValues", $Data ); 1486 1487 if (! is_array ( $Series )) { 1488 $Series = array ($Series ); 1489 } 1490 1491 foreach ( $Series as $Serie ) { 1492 $ColorID = $DataDescription->getColumnIndex($Serie); 1493 1494 $XPos = $this->GArea_X1 + $this->GAreaXOffset; 1495 1496 foreach ( $Data as $Values ) { 1497 if (isset ( $Values[$Serie] ) && is_numeric ( $Values[$Serie] )) { 1498 $Value = $Values[$Serie]; 1499 $YPos = $this->GArea_Y2 - (($Value - $this->VMin) * $this->DivisionRatio); 1500 1501 $Positions = imagettfbbox ( $this->FontSize, 0, $this->FontName, $Value ); 1502 $Width = $Positions [2] - $Positions [6]; 1503 $XOffset = $XPos - ($Width / 2); 1504 $YOffset = $YPos - 4; 1505 1506 $this->canvas->drawText($this->FontSize, 1507 0, 1508 new Point($XOffset, $YOffset), 1509 $this->palette->colors[$ColorID], 1510 $this->FontName, 1511 $Value, 1512 ShadowProperties::NoShadow()); 1513 } 1514 $XPos = $XPos + $this->DivisionWidth; 1515 } 1516 1517 } 1518 } 1519 1520 /** 1521 * @brief Draws a line graph where the data gives Y values for a 1522 * series of regular positions along the X axis 1523 */ 1524 function drawLineGraph($Data, $DataDescription, $SerieName = "") { 1525 /* Validate the Data and DataDescription array */ 1526 $this->validateDataDescription ( "drawLineGraph", $DataDescription ); 1527 $this->validateData ( "drawLineGraph", $Data ); 1528 1529 $GraphID = 0; 1530 foreach ( $DataDescription->values as $ColName ) { 1531 $ColorID = $DataDescription->getColumnIndex($ColName); 1532 1533 if ($SerieName == "" || $SerieName == $ColName) { 1534 $XPos = $this->GArea_X1 + $this->GAreaXOffset; 1535 $XLast = - 1; 1536 foreach ( $Data as $Values ) { 1537 if (isset ( $Values[$ColName] )) { 1538 $Value = $Values[$ColName]; 1539 $YPos = $this->GArea_Y2 - (($Value - $this->VMin) * $this->DivisionRatio); 1540 1541 /* Save point into the image map if option activated */ 1542 if ($this->BuildMap) 1543 $this->addToImageMap ( $XPos - 3, $YPos - 3, $XPos + 3, $YPos + 3, $DataDescription->description[$ColName], $Values[$ColName] . $DataDescription->getYUnit(), "Line" ); 1544 1545 if (! is_numeric ( $Value )) { 1546 $XLast = - 1; 1547 } 1548 if ($XLast != - 1) 1549 $this->canvas->drawLine(new Point($XLast, $YLast), 1550 new Point($XPos, $YPos), 1551 $this->palette->colors[$ColorID], 1552 $this->LineWidth, 1553 $this->LineDotSize, 1554 $this->shadowProperties, 1555 new Point($this->GArea_X1, 1556 $this->GArea_Y1), 1557 new Point($this->GArea_X2, 1558 $this->GArea_Y2)); 1559 1560 $XLast = $XPos; 1561 $YLast = $YPos; 1562 if (! is_numeric ( $Value )) { 1563 $XLast = - 1; 1564 } 1565 } 1566 $XPos = $XPos + $this->DivisionWidth; 1567 } 1568 $GraphID ++; 1569 } 1570 } 1571 } 1572 1573 /** 1574 * @brief Draws a line graph where one series of data defines the 1575 * X position and another the Y position 1576 */ 1577 function drawXYGraph($Data, $YSerieName, $XSerieName, $PaletteID = 0) { 1578 $graphAreaMin = new Point($this->GArea_X1, $this->GArea_Y1); 1579 $graphAreaMax = new Point($this->GArea_X2, $this->GArea_Y2); 1580 1581 $lastPoint = null; 1582 1583 foreach ($Data as $Values) { 1584 if (isset ( $Values[$YSerieName] ) && isset ( $Values[$XSerieName] )) { 1585 $X = $Values[$XSerieName]; 1586 $Y = $Values[$YSerieName]; 1587 1588 $Y = $this->GArea_Y2 - (($Y - $this->VMin) * $this->DivisionRatio); 1589 $X = $this->GArea_X1 + (($X - $this->VXMin) * $this->XDivisionRatio); 1590 1591 $currentPoint = new Point($X, $Y); 1592 1593 if ($lastPoint != null) { 1594 $this->canvas->drawLine($lastPoint, 1595 $currentPoint, 1596 $this->palette->colors[$PaletteID], 1597 $this->LineWidth, 1598 $this->LineDotSize, 1599 $this->shadowProperties, 1600 $graphAreaMin, 1601 $graphAreaMax); 1602 } 1603 1604 $lastPoint = $currentPoint; 1605 } 1606 } 1607 } 1608 1609 /** 1610 * This function draw a cubic curve 1611 */ 1612 function drawCubicCurve(pData $data, $Accuracy = .1, $SerieName = "") { 1613 /* Validate the Data and DataDescription array */ 1614 $this->validateDataDescription("drawCubicCurve", 1615 $data->getDataDescription()); 1616 $this->validateData ( "drawCubicCurve", $data->getData() ); 1617 1618 $graphAreaMin = new Point($this->GArea_X1, $this->GArea_Y1); 1619 $graphAreaMax = new Point($this->GArea_X2, $this->GArea_Y2); 1620 1621 $GraphID = 0; 1622 foreach ( $data->getDataDescription()->values as $ColName ) { 1623 if ($SerieName == "" || $SerieName == $ColName) { 1624 /** @todo The next section of code has been duplicated by 1625 * copy & paste */ 1626 $XIn = array(); 1627 $YIn = array(); 1628 $Yt = ""; 1629 $U = ""; 1630 1631 $ColorID = $data->getDataDescription()->getColumnIndex($ColName); 1632 1633 $Index = 1; 1634 $XLast = - 1; 1635 $Missing = array(); 1636 1637 $data->getXYMap($ColName, $XIn, $YIn, $Missing, $Index); 1638 1639 assert(count($XIn) == count($YIn)); 1640 assert($Index + 1 >= count($XIn)); 1641 1642 $Yt [0] = 0; 1643 $Yt [1] = 0; 1644 $U [1] = 0; 1645 1646 $this->calculateCubicCurve($Yt, $XIn, $YIn, $U, $Index); 1647 1648 $Yt [$Index] = 0; 1649 1650 for($k = $Index - 1; $k >= 1; $k --) 1651 $Yt [$k] = $Yt [$k] * $Yt [$k + 1] + $U [$k]; 1652 1653 $XPos = $this->GArea_X1 + $this->GAreaXOffset; 1654 for($X = 1; $X <= $Index; $X = $X + $Accuracy) { 1655 1656 /* I believe here we're searching for the integral 1657 * value k such that $X lies between $XIn[k] and 1658 * $XIn[k-1] */ 1659 $klo = 1; 1660 $khi = $Index; 1661 $k = $khi - $klo; 1662 while ( $k > 1 ) { 1663 $k = $khi - $klo; 1664 If ($XIn [$k] >= $X) 1665 $khi = $k; 1666 else 1667 $klo = $k; 1668 } 1669 $klo = $khi - 1; 1670 1671 /* These assertions are to check my understanding 1672 * of the code. If they fail, it is my fault and 1673 * not a bug */ 1674 assert($khi = $klo + 1); 1675 assert($XIn[$klo] < $X); 1676 assert($X <= $XIn[$khi]); 1677 1678 $h = $XIn [$khi] - $XIn [$klo]; 1679 $a = ($XIn [$khi] - $X) / $h; 1680 $b = ($X - $XIn [$klo]) / $h; 1681 1682 /** 1683 * I believe this is the actual cubic Bezier 1684 * calculation. In parametric form: 1685 * 1686 * B(t) = (1-t)^3 * B0 1687 * + 3 (1-t)^2 * t * B1 1688 * + 3 (1-t) * t^2 * B2 1689 * + t^3 B3 1690 */ 1691 $Value = $a * $YIn [$klo] + $b * $YIn [$khi] + (($a * $a * $a - $a) * $Yt [$klo] + ($b * $b * $b - $b) * $Yt [$khi]) * ($h * $h) / 6; 1692 1693 $YPos = $this->GArea_Y2 - (($Value - $this->VMin) * $this->DivisionRatio); 1694 1695 if ($XLast != - 1 && ! isset ( $Missing [floor ( $X )] ) && ! isset ( $Missing [floor ( $X + 1 )] )) 1696 $this->canvas->drawLine(new Point($XLast, 1697 $YLast), 1698 new Point($XPos, 1699 $YPos), 1700 $this->palette->colors[$ColorID], 1701 $this->LineWidth, 1702 $this->LineDotSize, 1703 $this->shadowProperties, 1704 $graphAreaMin, 1705 $graphAreaMax); 1706 1707 $XLast = $XPos; 1708 $YLast = $YPos; 1709 $XPos = $XPos + $this->DivisionWidth * $Accuracy; 1710 } 1711 1712 // Add potentialy missing values 1713 $XPos = $XPos - $this->DivisionWidth * $Accuracy; 1714 if ($XPos < ($this->GArea_X2 - $this->GAreaXOffset)) { 1715 $YPos = $this->GArea_Y2 - (($YIn [$Index] - $this->VMin) * $this->DivisionRatio); 1716 $this->canvas->drawLine(new Point($XLast, 1717 $YLast), 1718 new Point($this->GArea_X2 - $this->GAreaXOffset, 1719 $YPos), 1720 $this->palette->colors[$ColorID], 1721 $this->LineWidth, 1722 $this->LineDotSize, 1723 $this->shadowProperties, 1724 $graphAreaMin, 1725 $graphAreaMax); 1726 } 1727 1728 $GraphID ++; 1729 } 1730 } 1731 } 1732 1733 /** 1734 * @todo I haven't figured out exactly what this bit of code does, 1735 * it's just an attempt to reduce code duplication 1736 */ 1737 private function calculateCubicCurve(array & $Yt, array $XIn, array $YIn, array & $U, $Index) { 1738 for($i = 2; $i <= $Index - 1; $i ++) { 1739 /* Typically $Sig will be 0.5, since each X value will be 1740 * one unit past the last. If there is missing data then 1741 * this ratio will change */ 1742 $Sig = ($XIn [$i] - $XIn [$i - 1]) / ($XIn [$i + 1] - $XIn [$i - 1]); 1743 1744 $p = $Sig * $Yt [$i - 1] + 2; 1745 1746 /* This Y value will nearly always be negative, thanks to 1747 * $Sig being 0.5 */ 1748 $Yt [$i] = ($Sig - 1) / $p; 1749 1750 /** @todo No idea what the following code is doing */ 1751 $U [$i] = ($YIn [$i + 1] - $YIn [$i]) / ($XIn [$i + 1] - $XIn [$i]) 1752 - ($YIn [$i] - $YIn [$i - 1]) / ($XIn [$i] - $XIn [$i - 1]); 1753 $U [$i] = (6 * $U [$i] / ($XIn [$i + 1] - $XIn [$i - 1]) - $Sig * $U [$i - 1]) / $p; 1754 } 1755 } 1756 1757 /** 1758 * This function draw a filled cubic curve 1759 */ 1760 function drawFilledCubicCurve(pData $data, $Accuracy = .1, $Alpha = 100, $AroundZero = FALSE) { 1761 /* Validate the Data and DataDescription array */ 1762 $this->validateDataDescription("drawFilledCubicCurve", 1763 $data->getDataDescription()); 1764 $this->validateData ( "drawFilledCubicCurve", $data->getData() ); 1765 1766 $LayerWidth = $this->GArea_X2 - $this->GArea_X1; 1767 $LayerHeight = $this->GArea_Y2 - $this->GArea_Y1; 1768 $YZero = $LayerHeight - ((0 - $this->VMin) * $this->DivisionRatio); 1769 if ($YZero > $LayerHeight) { 1770 $YZero = $LayerHeight; 1771 } 1772 1773 $GraphID = 0; 1774 foreach ($data->getDataDescription()->values as $ColName) { 1775 $XIn = array(); 1776 $YIn = array(); 1777 $Yt = array(); 1778 $U = array(); 1779 1780 $ColorID = $data->getDataDescription()->getColumnIndex($ColName); 1781 1782 $numElements = 1; 1783 $XLast = - 1; 1784 $Missing = array(); 1785 1786 $data->getXYMap($ColName, $XIn, $YIn, $Missing, $numElements); 1787 1788 $Yt [0] = 0; 1789 $Yt [1] = 0; 1790 $U [1] = 0; 1791 1792 $this->calculateCubicCurve($Yt, $XIn, $YIn, $U, $numElements); 1793 1794 $Yt [$numElements] = 0; 1795 1796 for($k = $numElements - 1; $k >= 1; $k --) 1797 $Yt [$k] = $Yt [$k] * $Yt [$k + 1] + $U [$k]; 1798 1799 $Points = ""; 1800 $Points [] = $this->GAreaXOffset + $this->GArea_X1; 1801 $Points [] = $LayerHeight + $this->GArea_Y1; 1802 1803 $YLast = NULL; 1804 $XPos = $this->GAreaXOffset; 1805 $PointsCount = 2; 1806 for($X = 1; $X <= $numElements; $X = $X + $Accuracy) { 1807 $klo = 1; 1808 $khi = $numElements; 1809 $k = $khi - $klo; 1810 while ( $k > 1 ) { 1811 $k = $khi - $klo; 1812 If ($XIn [$k] >= $X) 1813 $khi = $k; 1814 else 1815 $klo = $k; 1816 } 1817 $klo = $khi - 1; 1818 1819 $h = $XIn [$khi] - $XIn [$klo]; 1820 $a = ($XIn [$khi] - $X) / $h; 1821 $b = ($X - $XIn [$klo]) / $h; 1822 $Value = $a * $YIn [$klo] + $b * $YIn [$khi] + (($a * $a * $a - $a) * $Yt [$klo] + ($b * $b * $b - $b) * $Yt [$khi]) * ($h * $h) / 6; 1823 1824 $YPos = $LayerHeight - (($Value - $this->VMin) * $this->DivisionRatio); 1825 1826 if ($YLast != NULL && $AroundZero && ! isset ( $Missing [floor ( $X )] ) && ! isset ( $Missing [floor ( $X + 1 )] )) { 1827 $aPoints = ""; 1828 1829 $aPoints [] = $XLast + $this->GArea_X1; 1830 $aPoints [] = min($YLast + $this->GArea_Y1, $this->GArea_Y2); 1831 $aPoints [] = $XPos + $this->GArea_X1; 1832 $aPoints [] = min($YPos + $this->GArea_Y1, $this->GArea_Y2); 1833 $aPoints [] = $XPos + $this->GArea_X1; 1834 $aPoints [] = $YZero + $this->GArea_Y1; 1835 $aPoints [] = $XLast + $this->GArea_X1; 1836 $aPoints [] = $YZero + $this->GArea_Y1; 1837 1838 $this->canvas->drawFilledPolygon($aPoints, 1839 4, 1840 $this->palette->colors[$ColorID], 1841 $alpha); 1842 } 1843 1844 if (! isset ( $Missing [floor ( $X )] ) || $YLast == NULL) { 1845 $PointsCount ++; 1846 $Points [] = $XPos + $this->GArea_X1; 1847 $Points [] = min($YPos + $this->GArea_Y1, $this->GArea_Y2); 1848 } else { 1849 $PointsCount ++; 1850 $Points [] = $XLast + $this->GArea_X1; 1851 $Points [] = min($LayerHeight + $this->GArea_Y1, 1852 $this->GArea_Y2);; 1853 } 1854 1855 $YLast = $YPos; 1856 $XLast = $XPos; 1857 $XPos = $XPos + $this->DivisionWidth * $Accuracy; 1858 } 1859 1860 // Add potentialy missing values 1861 $XPos = $XPos - $this->DivisionWidth * $Accuracy; 1862 if ($XPos < ($LayerWidth - $this->GAreaXOffset)) { 1863 $YPos = $LayerHeight - (($YIn [$numElements] - $this->VMin) * $this->DivisionRatio); 1864 if ($YLast != NULL && $AroundZero) { 1865 $aPoints = ""; 1866 $aPoints [] = $XLast + $this->GArea_X1; 1867 $aPoints [] = max($YLast + $this->GArea_Y1, $this->GArea_Y1); 1868 $aPoints [] = $LayerWidth - $this->GAreaXOffset + $this->GArea_X1; 1869 $aPoints [] = max($YPos + $this->GArea_Y1, $this->GArea_Y1); 1870 $aPoints [] = $LayerWidth - $this->GAreaXOffset + $this->GArea_X1; 1871 $aPoints [] = max($YZero + $this->GArea_Y1, $this->GArea_Y1); 1872 $aPoints [] = $XLast + $this->GArea_X1; 1873 $aPoints [] = max($YZero + $this->GArea_Y1, $this->GArea_Y1); 1874 1875 $this->canvas->drawFilledPolygon($aPoints, 1876 4, 1877 $this->palette->colors[$ColorID], 1878 $alpha); 1879 } 1880 1881 if ($YIn [$klo] != "" && $YIn [$khi] != "" || $YLast == NULL) { 1882 $PointsCount ++; 1883 $Points [] = $LayerWidth 1884 - $this->GAreaXOffset 1885 + $this->GArea_X1; 1886 $Points [] = $YPos + $this->GArea_Y1; 1887 } 1888 } 1889 1890 $Points [] = $LayerWidth - $this->GAreaXOffset + $this->GArea_X1; 1891 $Points [] = $LayerHeight + $this->GArea_Y1; 1892 1893 if (! $AroundZero) { 1894 $this->canvas->drawFilledPolygon($Points, 1895 $PointsCount, 1896 $this->palette->colors[$ColorID], 1897 $Alpha); 1898 } 1899 1900 $this->drawCubicCurve($data, $Accuracy, $ColName ); 1901 1902 $GraphID ++; 1903 } 1904 } 1905 1906 /** 1907 * This function draw a filled line graph 1908 */ 1909 function drawFilledLineGraph($Data, $DataDescription, $Alpha = 100, $AroundZero = FALSE) { 1910 $Empty = - 2147483647; 1911 1912 /* Validate the Data and DataDescription array */ 1913 $this->validateDataDescription ( "drawFilledLineGraph", $DataDescription ); 1914 $this->validateData ( "drawFilledLineGraph", $Data ); 1915 1916 $LayerWidth = $this->GArea_X2 - $this->GArea_X1; 1917 $LayerHeight = $this->GArea_Y2 - $this->GArea_Y1; 1918 1919 $GraphID = 0; 1920 foreach ( $DataDescription->values as $ColName ) { 1921 $ColorID = $DataDescription->getColumnIndex($ColName); 1922 1923 $aPoints = array(); 1924 $aPoints [] = $this->GAreaXOffset + $this->GArea_X1; 1925 $aPoints [] = $LayerHeight + $this->GArea_Y1; 1926 1927 $XPos = $this->GAreaXOffset; 1928 $XLast = - 1; 1929 $PointsCount = 2; 1930 $YZero = $LayerHeight - ((0 - $this->VMin) * $this->DivisionRatio); 1931 if ($YZero > $LayerHeight) { 1932 $YZero = $LayerHeight; 1933 } 1934 1935 $YLast = $Empty; 1936 foreach (array_keys($Data) as $Key) { 1937 $Value = $Data [$Key] [$ColName]; 1938 $YPos = $LayerHeight - (($Value - $this->VMin) * $this->DivisionRatio); 1939 1940 /* Save point into the image map if option activated */ 1941 if ($this->BuildMap) 1942 $this->addToImageMap ( $XPos - 3, $YPos - 3, $XPos + 3, $YPos + 3, $DataDescription->description[$ColName], $Data [$Key] [$ColName] . $DataDescription->getYUnit(), "FLine" ); 1943 1944 if (! is_numeric ( $Value )) { 1945 $PointsCount ++; 1946 $aPoints [] = $XLast + $this->GArea_X1; 1947 $aPoints [] = $LayerHeight + $this->GArea_Y1; 1948 1949 $YLast = $Empty; 1950 } else { 1951 $PointsCount ++; 1952 if ($YLast != $Empty) { 1953 $aPoints [] = $XPos + $this->GArea_X1; 1954 $aPoints [] = $YPos + $this->GArea_Y1; 1955 } else { 1956 $PointsCount ++; 1957 1958 $aPoints [] = $XPos + $this->GArea_X1; 1959 $aPoints [] = $LayerHeight + $this->GArea_Y1; 1960 $aPoints [] = $XPos + $this->GArea_X1; 1961 $aPoints [] = $YPos + $this->GArea_Y1; 1962 } 1963 1964 if ($YLast != $Empty && $AroundZero) { 1965 $Points = ""; 1966 $Points [] = $XLast + $this->GArea_X1; 1967 $Points [] = $YLast + $this->GArea_Y1; 1968 $Points [] = $XPos + $this->GArea_X1; 1969 $Points [] = $YPos + $this->GArea_Y1; 1970 $Points [] = $XPos + $this->GArea_X1; 1971 $Points [] = $YZero + $this->GArea_Y1; 1972 $Points [] = $XLast + $this->GArea_X1; 1973 $Points [] = $YZero + $this->GArea_Y1; 1974 1975 $this->canvas->drawFilledPolygon($Points, 1976 4, 1977 $this->palette->colors[$ColorID], 1978 $Alpha); 1979 } 1980 $YLast = $YPos; 1981 } 1982 1983 $XLast = $XPos; 1984 $XPos = $XPos + $this->DivisionWidth; 1985 } 1986 $aPoints [] = $LayerWidth - $this->GAreaXOffset + $this->GArea_X1; 1987 $aPoints [] = $LayerHeight + $this->GArea_Y1; 1988 1989 if ($AroundZero == FALSE) { 1990 $this->canvas->drawFilledPolygon($aPoints, 1991 $PointsCount, 1992 $this->palette->colors[$ColorID], 1993 $Alpha); 1994 } 1995 1996 $GraphID ++; 1997 $this->drawLineGraph ( $Data, $DataDescription, $ColName ); 1998 } 1999 } 2000 2001 /** 2002 * This function draws a bar graph 2003 */ 2004 function drawOverlayBarGraph($Data, $DataDescription, $Alpha = 50) { 2005 /* Validate the Data and DataDescription array */ 2006 $this->validateDataDescription ( "drawOverlayBarGraph", $DataDescription ); 2007 $this->validateData ( "drawOverlayBarGraph", $Data ); 2008 2009 $LayerHeight = $this->GArea_Y2 - $this->GArea_Y1; 2010 2011 $GraphID = 0; 2012 foreach ( $DataDescription->values as $ColName ) { 2013 $ColorID = $DataDescription->getColumnIndex($ColName); 2014 2015 $XWidth = $this->DivisionWidth / 4; 2016 $XPos = $this->GAreaXOffset; 2017 $YZero = $LayerHeight - ((0 - $this->VMin) * $this->DivisionRatio); 2018 foreach (array_keys($Data) as $Key) { 2019 if (isset ( $Data [$Key] [$ColName] )) { 2020 $Value = $Data [$Key] [$ColName]; 2021 if (is_numeric ( $Value )) { 2022 $YPos = $LayerHeight - (($Value - $this->VMin) * $this->DivisionRatio); 2023 2024 $this->canvas->drawFilledRectangle(new Point(floor($XPos - $XWidth + $this->GArea_X1), 2025 floor($YPos + $this->GArea_Y1)), 2026 new Point(floor($XPos + $XWidth + $this->GArea_X1), 2027 floor($YZero + $this->GArea_Y1)), 2028 $this->palette->colors[$GraphID], 2029 ShadowProperties::NoShadow(), 2030 false, 2031 $Alpha); 2032 2033 $X1 = floor ( $XPos - $XWidth + $this->GArea_X1 ); 2034 $Y1 = floor ( $YPos + $this->GArea_Y1 ) + .2; 2035 $X2 = floor ( $XPos + $XWidth + $this->GArea_X1 ); 2036 $Y2 = $this->GArea_Y2 - ((0 - $this->VMin) * $this->DivisionRatio); 2037 if ($X1 <= $this->GArea_X1) { 2038 $X1 = $this->GArea_X1 + 1; 2039 } 2040 if ($X2 >= $this->GArea_X2) { 2041 $X2 = $this->GArea_X2 - 1; 2042 } 2043 2044 /* Save point into the image map if option activated */ 2045 if ($this->BuildMap) 2046 $this->addToImageMap ( $X1, min ( $Y1, $Y2 ), $X2, max ( $Y1, $Y2 ), $DataDescription->description[$ColName], $Data [$Key] [$ColName] . $DataDescription->getYUnit(), "oBar" ); 2047 2048 $this->canvas->drawLine(new Point($X1, 2049 $Y1), 2050 new Point($X2, 2051 $Y1), 2052 $this->palette->colors[$ColorID], 2053 $this->LineWidth, 2054 $this->LineDotSize, 2055 $this->shadowProperties, 2056 new Point($this->GArea_X1, 2057 $this->GArea_Y1), 2058 new Point($this->GArea_X2, 2059 $this->GArea_Y2)); 2060 } 2061 } 2062 $XPos = $XPos + $this->DivisionWidth; 2063 } 2064 2065 $GraphID ++; 2066 } 2067 } 2068 2069 /** 2070 * This function draw a bar graph 2071 */ 2072 function drawBarGraph($Data, $DataDescription, $Alpha = 100) { 2073 /* Validate the Data and DataDescription array */ 2074 $this->validateDataDescription ( "drawBarGraph", $DataDescription ); 2075 $this->validateData ( "drawBarGraph", $Data ); 2076 2077 $Series = count ( $DataDescription->values); 2078 $SeriesWidth = $this->DivisionWidth / ($Series + 1); 2079 $SerieXOffset = $this->DivisionWidth / 2 - $SeriesWidth / 2; 2080 2081 $YZero = $this->GArea_Y2 - ((0 - $this->VMin) * $this->DivisionRatio); 2082 if ($YZero > $this->GArea_Y2) { 2083 $YZero = $this->GArea_Y2; 2084 } 2085 2086 $SerieID = 0; 2087 2088 foreach ( $DataDescription->values as $ColName ) { 2089 $ColorID = $DataDescription->getColumnIndex($ColName); 2090 2091 $XPos = $this->GArea_X1 + $this->GAreaXOffset - $SerieXOffset + $SeriesWidth * $SerieID; 2092 foreach (array_keys($Data) as $Key) { 2093 if (isset ( $Data [$Key] [$ColName] )) { 2094 if (is_numeric ( $Data [$Key] [$ColName] )) { 2095 $Value = $Data [$Key] [$ColName]; 2096 $YPos = $this->GArea_Y2 - (($Value - $this->VMin) * $this->DivisionRatio); 2097 2098 /* Save point into the image map if option activated */ 2099 if ($this->BuildMap) { 2100 $this->addToImageMap ( $XPos + 1, min ( $YZero, $YPos ), $XPos + $SeriesWidth - 1, max ( $YZero, $YPos ), $DataDescription->description[$ColName], $Data [$Key] [$ColName] . $DataDescription->getYUnit(), "Bar" ); 2101 } 2102 2103 if ($Alpha == 100) { 2104 $this->canvas->drawRectangle(new Point($XPos + 1, $YZero), 2105 new Point($XPos + $SeriesWidth - 1, 2106 $YPos), 2107 new Color(25, 25, 25), 2108 $this->LineWidth, 2109 $this->LineDotSize, 2110 $this->shadowProperties); 2111 } 2112 2113 $this->canvas->drawFilledRectangle(new Point($XPos + 1, 2114 $YZero), 2115 new Point($XPos + $SeriesWidth - 1, 2116 $YPos), 2117 $this->palette->colors[$ColorID], 2118 $this->shadowProperties, 2119 TRUE, $Alpha); 2120 } 2121 } 2122 $XPos = $XPos + $this->DivisionWidth; 2123 } 2124 $SerieID ++; 2125 } 2126 } 2127 2128 /** 2129 * This function draw a stacked bar graph 2130 */ 2131 function drawStackedBarGraph($Data, $DataDescription, $Alpha = 50, $Contiguous = FALSE) { 2132 /* Validate the Data and DataDescription array */ 2133 $this->validateDataDescription ( "drawBarGraph", $DataDescription ); 2134 $this->validateData ( "drawBarGraph", $Data ); 2135 2136 if ($Contiguous) 2137 $SeriesWidth = $this->DivisionWidth; 2138 else 2139 $SeriesWidth = $this->DivisionWidth * .8; 2140 2141 $YZero = $this->GArea_Y2 - ((0 - $this->VMin) * $this->DivisionRatio); 2142 if ($YZero > $this->GArea_Y2) { 2143 $YZero = $this->GArea_Y2; 2144 } 2145 2146 $SerieID = 0; 2147 $LastValue = ""; 2148 foreach ( $DataDescription->values as $ColName ) { 2149 $ColorID = $DataDescription->getColumnIndex($ColName); 2150 2151 $XPos = $this->GArea_X1 + $this->GAreaXOffset - $SeriesWidth / 2; 2152 foreach (array_keys($Data) as $Key) { 2153 if (isset ( $Data [$Key] [$ColName] )) { 2154 if (is_numeric ( $Data [$Key] [$ColName] )) { 2155 $Value = $Data [$Key] [$ColName]; 2156 2157 if (isset ( $LastValue [$Key] )) { 2158 $YPos = $this->GArea_Y2 - ((($Value + $LastValue [$Key]) - $this->VMin) * $this->DivisionRatio); 2159 $YBottom = $this->GArea_Y2 - (($LastValue [$Key] - $this->VMin) * $this->DivisionRatio); 2160 $LastValue [$Key] += $Value; 2161 } else { 2162 $YPos = $this->GArea_Y2 - (($Value - $this->VMin) * $this->DivisionRatio); 2163 $YBottom = $YZero; 2164 $LastValue [$Key] = $Value; 2165 } 2166 2167 /* Save point into the image map if option activated */ 2168 if ($this->BuildMap) 2169 $this->addToImageMap ( $XPos + 1, min ( $YBottom, $YPos ), $XPos + $SeriesWidth - 1, max ( $YBottom, $YPos ), $DataDescription->description[$ColName], $Data [$Key] [$ColName] . $DataDescription->getYUnit(), "sBar" ); 2170 2171 $this->canvas->drawFilledRectangle(new Point($XPos + 1, 2172 $YBottom), 2173 new Point($XPos + $SeriesWidth - 1, 2174 $YPos), 2175 $this->palette->colors[$ColorID], 2176 $this->shadowProperties, 2177 TRUE, 2178 $Alpha); 2179 } 2180 } 2181 $XPos = $XPos + $this->DivisionWidth; 2182 } 2183 $SerieID ++; 2184 } 2185 } 2186 2187 /** 2188 * This function draw a limits bar graphs 2189 */ 2190 function drawLimitsGraph($Data, $DataDescription, Color $color = null) { 2191 if ($color == null) { 2192 $color = new Color(0, 0, 0); 2193 } 2194 2195 /* Validate the Data and DataDescription array */ 2196 $this->validateDataDescription ( "drawLimitsGraph", $DataDescription ); 2197 $this->validateData ( "drawLimitsGraph", $Data ); 2198 2199 $XWidth = $this->DivisionWidth / 4; 2200 $XPos = $this->GArea_X1 + $this->GAreaXOffset; 2201 2202 $graphAreaMin = new Point($this->GArea_X1, $this->GArea_Y1); 2203 $graphAreaMax = new Point($this->GArea_X2, $this->GArea_Y2); 2204 2205 foreach (array_keys($Data) as $Key) { 2206 $Min = $Data [$Key] [$DataDescription->values[0]]; 2207 $Max = $Data [$Key] [$DataDescription->values[0]]; 2208 $GraphID = 0; 2209 $MaxID = 0; 2210 $MinID = 0; 2211 foreach ( $DataDescription->values as $ColName ) { 2212 if (isset ( $Data [$Key] [$ColName] )) { 2213 if ($Data [$Key] [$ColName] > $Max && is_numeric ( $Data [$Key] [$ColName] )) { 2214 $Max = $Data [$Key] [$ColName]; 2215 $MaxID = $GraphID; 2216 } 2217 } 2218 if (isset ( $Data [$Key] [$ColName] ) && is_numeric ( $Data [$Key] [$ColName] )) { 2219 if ($Data [$Key] [$ColName] < $Min) { 2220 $Min = $Data [$Key] [$ColName]; 2221 $MinID = $GraphID; 2222 } 2223 $GraphID ++; 2224 } 2225 } 2226 2227 $YPos = $this->GArea_Y2 - (($Max - $this->VMin) * $this->DivisionRatio); 2228 $X1 = floor ( $XPos - $XWidth ); 2229 $Y1 = floor ( $YPos ) - .2; 2230 $X2 = floor ( $XPos + $XWidth ); 2231 if ($X1 <= $this->GArea_X1) { 2232 $X1 = $this->GArea_X1 + 1; 2233 } 2234 if ($X2 >= $this->GArea_X2) { 2235 $X2 = $this->GArea_X2 - 1; 2236 } 2237 2238 $YPos = $this->GArea_Y2 - (($Min - $this->VMin) * $this->DivisionRatio); 2239 $Y2 = floor ( $YPos ) + .2; 2240 2241 $this->canvas->drawLine(new Point(floor ( $XPos ) - .2, $Y1 + 1), 2242 new Point(floor ( $XPos ) - .2, $Y2 - 1), 2243 $color, 2244 $this->LineWidth, 2245 $this->LineDotSize, 2246 $this->shadowProperties, 2247 $graphAreaMin, 2248 $graphAreaMax); 2249 $this->canvas->drawLine(new Point(floor ( $XPos ) + .2, $Y1 + 1), 2250 new Point(floor ( $XPos ) + .2, $Y2 - 1), 2251 $color, 2252 $this->LineWidth, 2253 $this->LineDotSize, 2254 $this->shadowProperties, 2255 $graphAreaMin, 2256 $graphAreaMax); 2257 $this->canvas->drawLine(new Point($X1, 2258 $Y1), 2259 new Point($X2, 2260 $Y1), 2261 $this->palette->colors[$MaxID], 2262 $this->LineWidth, 2263 $this->LineDotSize, 2264 $this->shadowProperties); 2265 $this->canvas->drawLine(new Point($X1, 2266 $Y2), 2267 new Point($X2, 2268 $Y2), 2269 $this->palette->colors[$MinID], 2270 $this->LineWidth, 2271 $this->LineDotSize, 2272 $this->shadowProperties); 2273 2274 $XPos = $XPos + $this->DivisionWidth; 2275 } 2276 } 2277 2278 /** 2279 * This function draw radar axis centered on the graph area 2280 */ 2281 function drawRadarAxis($Data, $DataDescription, $Mosaic = TRUE, $BorderOffset = 10, Color $colorA = null, Color $colorS = null, $MaxValue = -1) { 2282 if ($colorA == null) { 2283 $colorA = new Color(60, 60, 60); 2284 } 2285 2286 if ($colorS == null) { 2287 $colorS = new Color(200, 200, 200); 2288 } 2289 2290 /* Validate the Data and DataDescription array */ 2291 $this->validateDataDescription ( "drawRadarAxis", $DataDescription ); 2292 $this->validateData ( "drawRadarAxis", $Data ); 2293 2294 /* Draw radar axis */ 2295 $Points = count ( $Data ); 2296 $Radius = ($this->GArea_Y2 - $this->GArea_Y1) / 2 - $BorderOffset; 2297 $XCenter = ($this->GArea_X2 - $this->GArea_X1) / 2 + $this->GArea_X1; 2298 $YCenter = ($this->GArea_Y2 - $this->GArea_Y1) / 2 + $this->GArea_Y1; 2299 2300 /* Search for the max value */ 2301 if ($MaxValue == - 1) { 2302 foreach ( $DataDescription->values as $ColName ) { 2303 foreach (array_keys($Data) as $Key) { 2304 if (isset ( $Data [$Key] [$ColName] )) 2305 if ($Data [$Key] [$ColName] > $MaxValue) { 2306 $MaxValue = $Data [$Key] [$ColName]; 2307 } 2308 } 2309 } 2310 } 2311 2312 /* Draw the mosaic */ 2313 if ($Mosaic) { 2314 $RadiusScale = $Radius / $MaxValue; 2315 for($t = 1; $t <= $MaxValue - 1; $t ++) { 2316 $TRadius = $RadiusScale * $t; 2317 $LastX1 = - 1; 2318 2319 for($i = 0; $i <= $Points; $i ++) { 2320 $Angle = - 90 + $i * 360 / $Points; 2321 $X1 = cos ( $Angle * M_PI / 180 ) * $TRadius + $XCenter; 2322 $Y1 = sin ( $Angle * M_PI / 180 ) * $TRadius + $YCenter; 2323 $X2 = cos ( $Angle * M_PI / 180 ) * ($TRadius + $RadiusScale) + $XCenter; 2324 $Y2 = sin ( $Angle * M_PI / 180 ) * ($TRadius + $RadiusScale) + $YCenter; 2325 2326 if ($t % 2 == 1 && $LastX1 != - 1) { 2327 $Plots = ""; 2328 $Plots [] = $X1; 2329 $Plots [] = $Y1; 2330 $Plots [] = $X2; 2331 $Plots [] = $Y2; 2332 $Plots [] = $LastX2; 2333 $Plots [] = $LastY2; 2334 $Plots [] = $LastX1; 2335 $Plots [] = $LastY1; 2336 2337 $this->canvas->drawFilledPolygon($Plots, 2338 (count ( $Plots ) + 1) / 2, 2339 new Color(250, 250, 250)); 2340 } 2341 2342 $LastX1 = $X1; 2343 $LastY1 = $Y1; 2344 $LastX2 = $X2; 2345 $LastY2 = $Y2; 2346 } 2347 } 2348 } 2349 2350 /* Draw the spider web */ 2351 for($t = 1; $t <= $MaxValue; $t ++) { 2352 $TRadius = ($Radius / $MaxValue) * $t; 2353 $LastX = - 1; 2354 2355 for($i = 0; $i <= $Points; $i ++) { 2356 $Angle = - 90 + $i * 360 / $Points; 2357 $X = cos ( $Angle * M_PI / 180 ) * $TRadius + $XCenter; 2358 $Y = sin ( $Angle * M_PI / 180 ) * $TRadius + $YCenter; 2359 2360 if ($LastX != - 1) 2361 $this->canvas->drawDottedLine(new Point($LastX, $LastY), 2362 new Point($X, $Y), 2363 4, 1, $colorS, 2364 $this->shadowProperties); 2365 2366 $LastX = $X; 2367 $LastY = $Y; 2368 } 2369 } 2370 2371 /* Draw the axis */ 2372 for($i = 0; $i <= $Points; $i ++) { 2373 $Angle = - 90 + $i * 360 / $Points; 2374 $X = cos ( $Angle * M_PI / 180 ) * $Radius + $XCenter; 2375 $Y = sin ( $Angle * M_PI / 180 ) * $Radius + $YCenter; 2376 2377 $this->canvas->drawLine(new Point($XCenter, $YCenter), 2378 new Point($X, $Y), 2379 $colorA, 2380 $this->LineWidth, 2381 $this->LineDotSize, 2382 $this->shadowProperties); 2383 2384 $XOffset = 0; 2385 $YOffset = 0; 2386 if (isset ( $Data [$i] [$DataDescription->getPosition()] )) { 2387 $Label = $Data [$i] [$DataDescription->getPosition()]; 2388 2389 $Positions = imagettfbbox ( $this->FontSize, 0, $this->FontName, $Label ); 2390 $Width = $Positions [2] - $Positions [6]; 2391 $Height = $Positions [3] - $Positions [7]; 2392 2393 if ($Angle >= 0 && $Angle <= 90) 2394 $YOffset = $Height; 2395 2396 if ($Angle > 90 && $Angle <= 180) { 2397 $YOffset = $Height; 2398 $XOffset = - $Width; 2399 } 2400 2401 if ($Angle > 180 && $Angle <= 270) { 2402 $XOffset = - $Width; 2403 } 2404 2405 $this->canvas->drawText($this->FontSize, 2406 0, 2407 new Point($X + $XOffset, $Y + $YOffset), 2408 $colorA, 2409 $this->FontName, 2410 $Label, 2411 ShadowProperties::NoShadow()); 2412 } 2413 } 2414 2415 /* Write the values */ 2416 for($t = 1; $t <= $MaxValue; $t ++) { 2417 $TRadius = ($Radius / $MaxValue) * $t; 2418 2419 $Angle = - 90 + 360 / $Points; 2420 $X1 = $XCenter; 2421 $Y1 = $YCenter - $TRadius; 2422 $X2 = cos ( $Angle * M_PI / 180 ) * $TRadius + $XCenter; 2423 $Y2 = sin ( $Angle * M_PI / 180 ) * $TRadius + $YCenter; 2424 2425 $XPos = floor ( ($X2 - $X1) / 2 ) + $X1; 2426 $YPos = floor ( ($Y2 - $Y1) / 2 ) + $Y1; 2427 2428 $Positions = imagettfbbox ( $this->FontSize, 0, $this->FontName, $t ); 2429 $X = $XPos - ($X + $Positions [2] - $X + $Positions [6]) / 2; 2430 $Y = $YPos + $this->FontSize; 2431 2432 $this->canvas->drawFilledRoundedRectangle(new Point($X + $Positions [6] - 2, 2433 $Y + $Positions [7] - 1), 2434 new Point($X + $Positions [2] + 4, 2435 $Y + $Positions [3] + 1), 2436 2, 2437 new Color(240, 240, 240), 2438 $this->LineWidth, 2439 $this->LineDotSize, 2440 $this->shadowProperties); 2441 2442 $this->canvas->drawRoundedRectangle(new Point($X + $Positions [6] - 2, 2443 $Y + $Positions [7] - 1), 2444 new Point($X + $Positions [2] + 4, 2445 $Y + $Positions [3] + 1), 2446 2, 2447 new Color(220, 220, 220), 2448 $this->LineWidth, 2449 $this->LineDotSize, 2450 $this->shadowProperties); 2451 $this->canvas->drawText($this->FontSize, 2452 0, 2453 new Point($X, $Y), 2454 $colorA, 2455 $this->FontName, 2456 $t, 2457 ShadowProperties::NoShadow()); 2458 } 2459 } 2460 2461 private function calculateMaxValue($Data, $DataDescription) { 2462 $MaxValue = -1; 2463 foreach ( $DataDescription->values as $ColName ) { 2464 foreach (array_keys($Data) as $Key) { 2465 if (isset ( $Data [$Key] [$ColName] )) 2466 if ($Data [$Key] [$ColName] > $MaxValue && is_numeric($Data[$Key][$ColName])) { 2467 $MaxValue = $Data [$Key] [$ColName]; 2468 } 2469 } 2470 } 2471 return $MaxValue; 2472 } 2473 2474 /** 2475 * This function draw a radar graph centered on the graph area 2476 */ 2477 function drawRadar($Data, $DataDescription, $BorderOffset = 10, $MaxValue = -1) { 2478 /* Validate the Data and DataDescription array */ 2479 $this->validateDataDescription ( "drawRadar", $DataDescription ); 2480 $this->validateData ( "drawRadar", $Data ); 2481 2482 $Points = count ( $Data ); 2483 $Radius = ($this->GArea_Y2 - $this->GArea_Y1) / 2 - $BorderOffset; 2484 $XCenter = ($this->GArea_X2 - $this->GArea_X1) / 2 + $this->GArea_X1; 2485 $YCenter = ($this->GArea_Y2 - $this->GArea_Y1) / 2 + $this->GArea_Y1; 2486 2487 /* Search for the max value */ 2488 if ($MaxValue == - 1) { 2489 $MaxValue = $this->calculateMaxValue($Data, $DataDescription); 2490 } 2491 2492 $GraphID = 0; 2493 foreach ( $DataDescription->values as $ColName ) { 2494 $ColorID = $DataDescription->getColumnIndex($ColName); 2495 2496 $Angle = - 90; 2497 $XLast = - 1; 2498 foreach (array_keys($Data) as $Key) { 2499 if (isset ( $Data [$Key] [$ColName] )) { 2500 $Value = $Data [$Key] [$ColName]; 2501 $Strength = ($Radius / $MaxValue) * $Value; 2502 2503 $XPos = cos ( $Angle * M_PI / 180 ) * $Strength + $XCenter; 2504 $YPos = sin ( $Angle * M_PI / 180 ) * $Strength + $YCenter; 2505 2506 if ($XLast != - 1) 2507 $this->canvas->drawLine(new Point($XLast, 2508 $YLast), 2509 new Point($XPos, 2510 $YPos), 2511 $this->palette->colors[$ColorID], 2512 $this->LineWidth, 2513 $this->LineDotSize, 2514 $this->shadowProperties); 2515 2516 if ($XLast == - 1) { 2517 $FirstX = $XPos; 2518 $FirstY = $YPos; 2519 } 2520 2521 $Angle = $Angle + (360 / $Points); 2522 $XLast = $XPos; 2523 $YLast = $YPos; 2524 } 2525 } 2526 $this->canvas->drawLine(new Point($XPos, 2527 $YPos), 2528 new Point($FirstX, 2529 $FirstY), 2530 $this->palette->colors[$ColorID], 2531 $this->LineWidth, 2532 $this->LineDotSize, 2533 $this->shadowProperties); 2534 $GraphID ++; 2535 } 2536 } 2537 2538 /** 2539 * This function draw a radar graph centered on the graph area 2540 */ 2541 function drawFilledRadar($Data, $DataDescription, $Alpha = 50, $BorderOffset = 10, $MaxValue = -1) { 2542 /* Validate the Data and DataDescription array */ 2543 $this->validateDataDescription ( "drawFilledRadar", $DataDescription ); 2544 $this->validateData ( "drawFilledRadar", $Data ); 2545 2546 $Points = count ( $Data ); 2547 $Radius = ($this->GArea_Y2 - $this->GArea_Y1) / 2 - $BorderOffset; 2548 $XCenter = ($this->GArea_X2 - $this->GArea_X1) / 2; 2549 $YCenter = ($this->GArea_Y2 - $this->GArea_Y1) / 2; 2550 2551 /* Search for the max value */ 2552 if ($MaxValue == - 1) { 2553 $MaxValue = $this->calculateMaxValue($Data, $DataDescription); 2554 } 2555 2556 $GraphID = 0; 2557 foreach ( $DataDescription->values as $ColName ) { 2558 $ColorID = $DataDescription->getColumnIndex($ColName); 2559 2560 $Angle = - 90; 2561 $XLast = - 1; 2562 $Plots = array(); 2563 foreach (array_keys($Data) as $Key) { 2564 if (isset ( $Data [$Key] [$ColName] )) { 2565 $Value = $Data [$Key] [$ColName]; 2566 if (! is_numeric ( $Value )) { 2567 $Value = 0; 2568 } 2569 $Strength = ($Radius / $MaxValue) * $Value; 2570 2571 $XPos = cos ( $Angle * M_PI / 180 ) * $Strength + $XCenter; 2572 $YPos = sin ( $Angle * M_PI / 180 ) * $Strength + $YCenter; 2573 2574 $Plots [] = $XPos + $this->GArea_X1; 2575 $Plots [] = $YPos + $this->GArea_Y1; 2576 2577 $Angle = $Angle + (360 / $Points); 2578 $XLast = $XPos; 2579 } 2580 } 2581 2582 if (isset ( $Plots [0] )) { 2583 $Plots [] = $Plots [0]; 2584 $Plots [] = $Plots [1]; 2585 2586 $this->canvas->drawFilledPolygon($Plots, 2587 (count ( $Plots ) + 1) / 2, 2588 $this->palette->colors[$ColorID], 2589 $Alpha); 2590 2591 for($i = 0; $i <= count ( $Plots ) - 4; $i = $i + 2) 2592 $this->canvas->drawLine(new Point($Plots [$i], 2593 $Plots [$i + 1]), 2594 new Point($Plots [$i + 2], 2595 $Plots [$i + 3]), 2596 $this->palette->colors[$ColorID], 2597 $this->LineWidth, 2598 $this->LineDotSize, 2599 $this->shadowProperties); 2600 } 2601 2602 $GraphID ++; 2603 } 2604 } 2605 2606 /** 2607 * This function can be used to set the background color 2608 */ 2609 function drawBackground(Color $color) { 2610 $C_Background = $this->canvas->allocateColor($color); 2611 imagefilledrectangle ( $this->canvas->getPicture(), 0, 0, $this->XSize, $this->YSize, $C_Background ); 2612 } 2613 2614 private function drawGradient(Point $point1, Point $point2, Color $color, $decay) { 2615 /* Positive gradient */ 2616 if ($decay > 0) { 2617 $YStep = ($point2->getY() - $point1->getY() - 2) / $decay; 2618 for($i = 0; $i <= $decay; $i ++) { 2619 $color = $color->addRGBIncrement(-1); 2620 $Yi1 = $point1->getY() + ($i * $YStep); 2621 $Yi2 = ceil ( $Yi1 + ($i * $YStep) + $YStep ); 2622 if ($Yi2 >= $Yi2) { 2623 $Yi2 = $point2->getY() - 1; 2624 } 2625 2626 $this->canvas->drawFilledRectangle(new Point($point1->getX(), $Yi1), 2627 new Point($point2->getX(), $Yi2), 2628 $color, 2629 ShadowProperties::NoShadow()); 2630 } 2631 } 2632 2633 /* Negative gradient */ 2634 if ($decay < 0) { 2635 $YStep = ($point2->getY() - $point1->getY() - 2) / - $decay; 2636 $Yi1 = $point1->getY(); 2637 $Yi2 = $point1->getY() + $YStep; 2638 for($i = - $decay; $i >= 0; $i --) { 2639 $color = $color->addRGBIncrement(1); 2640 2641 $this->canvas->drawFilledRectangle(new Point($point1->getX(), $Yi1), 2642 new Point($point2->getX(), $Yi2), 2643 $color, 2644 ShadowProperties::NoShadow()); 2645 2646 $Yi1 += $YStep; 2647 $Yi2 += $YStep; 2648 if ($Yi2 >= $Yi2) { 2649 $Yi2 = $point2->getY() - 1; 2650 } 2651 } 2652 } 2653 } 2654 2655 /** 2656 * This function can be used to set the background color 2657 */ 2658 private function drawGraphAreaGradient(BackgroundStyle $style) { 2659 if (!$style->useGradient()) { 2660 return; 2661 } 2662 2663 $this->drawGradient(new Point($this->GArea_X1 + 1, $this->GArea_Y1 + 1), 2664 new Point($this->GArea_X2 - 1, $this->GArea_Y2), 2665 $style->getGradientStartColor(), 2666 $style->getGradientDecay()); 2667 } 2668 2669 public function drawBackgroundGradient(Color $color, $decay) { 2670 $this->drawGradient(new Point(0, 0), 2671 new Point($this->XSize, $this->YSize), 2672 $color, 2673 $decay); 2674 } 2675 2676 /** 2677 * This function will draw an ellipse 2678 */ 2679 function drawEllipse($Xc, $Yc, $Height, $Width, Color $color) { 2680 $this->canvas->drawCircle(new Point($Xc, $Yc), $Height, $color, $this->shadowProperties, $Width ); 2681 } 2682 2683 /** 2684 * This function will draw a filled ellipse 2685 */ 2686 function drawFilledEllipse($Xc, $Yc, $Height, $Width, Color $color) { 2687 $this->canvas->drawFilledCircle(new Point($Xc, $Yc), $Height, $color, $this->shadowProperties, $Width ); 2688 } 2689 2690 /** 2691 * Load a PNG file and draw it over the chart 2692 */ 2693 function drawFromPNG($FileName, $X, $Y, $Alpha = 100) { 2694 $this->drawFromPicture ( 1, $FileName, $X, $Y, $Alpha ); 2695 } 2696 2697 /** 2698 * Load a GIF file and draw it over the chart 2699 */ 2700 function drawFromGIF($FileName, $X, $Y, $Alpha = 100) { 2701 $this->drawFromPicture ( 2, $FileName, $X, $Y, $Alpha ); 2702 } 2703 2704 /** 2705 * Load a JPEG file and draw it over the chart 2706 */ 2707 function drawFromJPG($FileName, $X, $Y, $Alpha = 100) { 2708 $this->drawFromPicture ( 3, $FileName, $X, $Y, $Alpha ); 2709 } 2710 2711 /** 2712 * Generic loader function for external pictures 2713 */ 2714 function drawFromPicture($PicType, $FileName, $X, $Y, $Alpha = 100) { 2715 if (file_exists ( $FileName )) { 2716 $Infos = getimagesize ( $FileName ); 2717 $Width = $Infos [0]; 2718 $Height = $Infos [1]; 2719 if ($PicType == 1) { 2720 $Raster = imagecreatefrompng ( $FileName ); 2721 } 2722 if ($PicType == 2) { 2723 $Raster = imagecreatefromgif ( $FileName ); 2724 } 2725 if ($PicType == 3) { 2726 $Raster = imagecreatefromjpeg ( $FileName ); 2727 } 2728 2729 imagecopymerge ( $this->canvas->getPicture(), $Raster, $X, $Y, 0, 0, $Width, $Height, $Alpha ); 2730 imagedestroy ( $Raster ); 2731 } 2732 } 2733 2734 /** 2735 * Add a border to the picture 2736 * 2737 * @todo This hasn't been updated to the new API yet 2738 */ 2739 function addBorder($Size = 3, $R = 0, $G = 0, $B = 0) { 2740 $Width = $this->XSize + 2 * $Size; 2741 $Height = $this->YSize + 2 * $Size; 2742 2743 $Resampled = imagecreatetruecolor ( $Width, $Height ); 2744 $C_Background = imagecolorallocate($Resampled, $R, $G, $B); 2745 imagefilledrectangle ( $Resampled, 0, 0, $Width, $Height, $C_Background ); 2746 2747 imagecopy ( $Resampled, $this->canvas->getPicture(), $Size, $Size, 0, 0, $this->XSize, $this->YSize ); 2748 imagedestroy ( $this->canvas->getPicture() ); 2749 2750 $this->XSize = $Width; 2751 $this->YSize = $Height; 2752 2753 $this->canvas->setPicture(imagecreatetruecolor ( $this->XSize, $this->YSize )); 2754 $C_White = $this->canvas->allocate(new Color(255, 255, 255)); 2755 imagefilledrectangle ( $this->canvas->getPicture(), 0, 0, $this->XSize, $this->YSize, $C_White ); 2756 imagecolortransparent ( $this->canvas->getPicture(), $C_White ); 2757 imagecopy ( $this->canvas->getPicture(), $Resampled, 0, 0, 0, 0, $this->XSize, $this->YSize ); 2758 } 2759 2760 /** 2761 * Render the current picture to a file 2762 */ 2763 function Render($FileName) { 2764 if ($this->ErrorReporting) 2765 $this->printErrors ( $this->ErrorInterface ); 2766 2767 /* Save image map if requested */ 2768 if ($this->BuildMap) 2769 $this->SaveImageMap (); 2770 2771 imagepng ( $this->canvas->getPicture(), $FileName ); 2772 } 2773 2774 /** 2775 * Render the current picture to STDOUT 2776 */ 2777 function Stroke() { 2778 if ($this->ErrorReporting) 2779 $this->printErrors ( "GD" ); 2780 2781 /* Save image map if requested */ 2782 if ($this->BuildMap) 2783 $this->SaveImageMap (); 2784 2785 header ( 'Content-type: image/png' ); 2786 imagepng ( $this->canvas->getPicture() ); 2787 } 2788 2789 /** 2790 * Validate data contained in the description array 2791 * 2792 * @todo Should this be a method on DataDescription? 2793 */ 2794 protected function validateDataDescription($FunctionName, DataDescription &$DataDescription, $DescriptionRequired = TRUE) { 2795 if ($DataDescription->getPosition() == '') { 2796 $this->Errors [] = "[Warning] " . $FunctionName . " - Y Labels are not set."; 2797 $DataDescription->setPosition("Name"); 2798 } 2799 2800 if ($DescriptionRequired) { 2801 if (! isset ( $DataDescription->description)) { 2802 $this->Errors [] = "[Warning] " . $FunctionName . " - Series descriptions are not set."; 2803 foreach ( $DataDescription->values as $key => $Value ) { 2804 $DataDescription->description[$Value] = $Value; 2805 } 2806 } 2807 2808 if (count ( $DataDescription->description) < count ( $DataDescription->values )) { 2809 $this->Errors [] = "[Warning] " . $FunctionName . " - Some series descriptions are not set."; 2810 foreach ( $DataDescription->values as $key => $Value ) { 2811 if (! isset ( $DataDescription->description[$Value] )) 2812 $DataDescription->description[$Value] = $Value; 2813 } 2814 } 2815 } 2816 } 2817 2818 /** 2819 * Validate data contained in the data array 2820 */ 2821 protected function validateData($FunctionName, &$Data) { 2822 $DataSummary = array (); 2823 2824 foreach ( $Data as $key => $Values ) { 2825 foreach ( $Values as $key2 => $Value ) { 2826 if (! isset ( $DataSummary [$key2] )) 2827 $DataSummary [$key2] = 1; 2828 else 2829 $DataSummary [$key2] ++; 2830 } 2831 } 2832 2833 if (empty($DataSummary)) 2834 $this->Errors [] = "[Warning] " . $FunctionName . " - No data set."; 2835 2836 foreach ( $DataSummary as $key => $Value ) { 2837 if ($Value < max ( $DataSummary )) { 2838 $this->Errors [] = "[Warning] " . $FunctionName . " - Missing data in serie " . $key . "."; 2839 } 2840 } 2841 } 2842 2843 /** 2844 * Print all error messages on the CLI or graphically 2845 */ 2846 function printErrors($Mode = "CLI") { 2847 if (count ( $this->Errors ) == 0) 2848 return (0); 2849 2850 if ($Mode == "CLI") { 2851 foreach ( $this->Errors as $key => $Value ) 2852 echo $Value . "\r\n"; 2853 } elseif ($Mode == "GD") { 2854 $MaxWidth = 0; 2855 foreach ( $this->Errors as $key => $Value ) { 2856 $Position = imageftbbox ( $this->ErrorFontSize, 0, $this->ErrorFontName, $Value ); 2857 $TextWidth = $Position [2] - $Position [0]; 2858 if ($TextWidth > $MaxWidth) { 2859 $MaxWidth = $TextWidth; 2860 } 2861 } 2862 $this->canvas->drawFilledRoundedRectangle(new Point($this->XSize - ($MaxWidth + 20), 2863 $this->YSize - (20 + (($this->ErrorFontSize + 4) * count ( $this->Errors )))), 2864 new Point($this->XSize - 10, 2865 $this->YSize - 10), 2866 6, 2867 new Color(233, 185, 185), 2868 $this->lineWidth, 2869 $this->lineDotSize, 2870 $this->shadowProperties); 2871 2872 $this->canvas->drawRoundedRectangle(new Point($this->XSize - ($MaxWidth + 20), 2873 $this->YSize - (20 + (($this->ErrorFontSize + 4) * count ( $this->Errors )))), 2874 new Point($this->XSize - 10, 2875 $this->YSize - 10), 2876 6, 2877 new Color(193, 145, 145), 2878 $this->LineWidth, 2879 $this->LineDotSize, 2880 $this->shadowProperties); 2881 2882 $YPos = $this->YSize - (18 + (count ( $this->Errors ) - 1) * ($this->ErrorFontSize + 4)); 2883 foreach ( $this->Errors as $key => $Value ) { 2884 $this->canvas->drawText($this->ErrorFontSize, 2885 0, 2886 new Point($this->XSize - ($MaxWidth + 15), 2887 $YPos), 2888 new Color(133, 85, 85), 2889 $this->ErrorFontName, 2890 $Value, 2891 ShadowProperties::NoShadow()); 2892 $YPos = $YPos + ($this->ErrorFontSize + 4); 2893 } 2894 } 2895 } 2896 2897 /** 2898 * Activate the image map creation process 2899 */ 2900 function setImageMap($Mode = TRUE, $GraphID = "MyGraph") { 2901 $this->BuildMap = $Mode; 2902 $this->MapID = $GraphID; 2903 } 2904 2905 /** 2906 * Add a box into the image map 2907 */ 2908 function addToImageMap($X1, $Y1, $X2, $Y2, $SerieName, $Value, $CallerFunction) { 2909 if ($this->MapFunction == NULL || $this->MapFunction == $CallerFunction) { 2910 $this->ImageMap [] = round ( $X1 ) . "," . round ( $Y1 ) . "," . round ( $X2 ) . "," . round ( $Y2 ) . "," . $SerieName . "," . $Value; 2911 $this->MapFunction = $CallerFunction; 2912 } 2913 } 2914 2915 /** 2916 * Load and cleanup the image map from disk 2917 */ 2918 function getImageMap($MapName, $Flush = TRUE) { 2919 /* Strip HTML query strings */ 2920 $Values = $this->tmpFolder . $MapName; 2921 $Value = explode ( "\?", $Values ); 2922 $FileName = $Value [0]; 2923 2924 if (file_exists ( $FileName )) { 2925 $Handle = fopen ( $FileName, "r" ); 2926 $MapContent = fread ( $Handle, filesize ( $FileName ) ); 2927 fclose ( $Handle ); 2928 echo $MapContent; 2929 2930 if ($Flush) 2931 unlink ( $FileName ); 2932 2933 exit (); 2934 } else { 2935 header ( "HTTP/1.0 404 Not Found" ); 2936 exit (); 2937 } 2938 } 2939 2940 /** 2941 * Save the image map to the disk 2942 */ 2943 function SaveImageMap() { 2944 if (! $this->BuildMap) { 2945 return (- 1); 2946 } 2947 2948 if ($this->ImageMap == NULL) { 2949 $this->Errors [] = "[Warning] SaveImageMap - Image map is empty."; 2950 return (- 1); 2951 } 2952 2953 $Handle = fopen ( $this->tmpFolder . $this->MapID, 'w' ); 2954 if (! $Handle) { 2955 $this->Errors [] = "[Warning] SaveImageMap - Cannot save the image map."; 2956 return (- 1); 2957 } else { 2958 foreach ( $this->ImageMap as $Value ) 2959 fwrite ( $Handle, htmlentities ( $Value ) . "\r" ); 2960 } 2961 fclose ( $Handle ); 2962 } 2963 2964 /** 2965 * Set date format for axis labels 2966 */ 2967 function setDateFormat($Format) { 2968 $this->DateFormat = $Format; 2969 } 2970 2971 /** 2972 * Convert TS to a date format string 2973 */ 2974 function ToDate($Value) { 2975 return (date ( $this->DateFormat, $Value )); 2976 } 2977 2978 /** 2979 * Check if a number is a full integer (for scaling) 2980 */ 2981 static private function isRealInt($Value) { 2982 if ($Value == floor ( $Value )) 2983 return (TRUE); 2984 return (FALSE); 2985 } 2986 2987 /** 2988 * @todo I don't know what this does yet, I'm refactoring... 2989 */ 2990 public function calculateScales(& $Scale, & $Divisions) { 2991 /* Compute automatic scaling */ 2992 $ScaleOk = FALSE; 2993 $Factor = 1; 2994 $MinDivHeight = 25; 2995 $MaxDivs = ($this->GArea_Y2 - $this->GArea_Y1) / $MinDivHeight; 2996 2997 if ($this->VMax <= $this->VMin) { 2998 throw new Exception("Impossible to calculate scales when VMax <= VMin"); 2999 } 3000 3001 if ($this->VMin == 0 && $this->VMax == 0) { 3002 $this->VMin = 0; 3003 $this->VMax = 2; 3004 $Scale = 1; 3005 $Divisions = 2; 3006 } elseif ($MaxDivs > 1) { 3007 while ( ! $ScaleOk ) { 3008 $Scale1 = ($this->VMax - $this->VMin) / $Factor; 3009 $Scale2 = ($this->VMax - $this->VMin) / $Factor / 2; 3010 3011 if ($Scale1 > 1 && $Scale1 <= $MaxDivs && ! $ScaleOk) { 3012 $ScaleOk = TRUE; 3013 $Divisions = floor ( $Scale1 ); 3014 $Scale = 1; 3015 } 3016 if ($Scale2 > 1 && $Scale2 <= $MaxDivs && ! $ScaleOk) { 3017 $ScaleOk = TRUE; 3018 $Divisions = floor ( $Scale2 ); 3019 $Scale = 2; 3020 } 3021 if (! $ScaleOk) { 3022 if ($Scale2 > 1) { 3023 $Factor = $Factor * 10; 3024 } 3025 if ($Scale2 < 1) { 3026 $Factor = $Factor / 10; 3027 } 3028 } 3029 } 3030 3031 if (floor ( $this->VMax / $Scale / $Factor ) != $this->VMax / $Scale / $Factor) { 3032 $GridID = floor ( $this->VMax / $Scale / $Factor ) + 1; 3033 $this->VMax = $GridID * $Scale * $Factor; 3034 $Divisions ++; 3035 } 3036 3037 if (floor ( $this->VMin / $Scale / $Factor ) != $this->VMin / $Scale / $Factor) { 3038 $GridID = floor ( $this->VMin / $Scale / $Factor ); 3039 $this->VMin = $GridID * $Scale * $Factor; 3040 $Divisions ++; 3041 } 3042 } else /* Can occur for small graphs */ 3043 $Scale = 1; 3044 } 3045 3046 /* 3047 * Returns the resource 3048 */ 3049 public function getPicture() { 3050 return $this->canvas->getPicture(); 3051 } 3052 3053 static private function computeAutomaticScaling($minCoord, $maxCoord, &$minVal, &$maxVal, &$Divisions) { 3054 $ScaleOk = FALSE; 3055 $Factor = 1.0; 3056 $MinDivHeight = 25; 3057 $MaxDivs = ($maxCoord - $minCoord) / $MinDivHeight; 3058 3059 if ($minVal == 0 && $maxVal == 0){ 3060 $minVal = 0; 3061 $maxVal = 2; 3062 $Scale = 1; 3063 $Divisions = 2; 3064 }elseif ($minVal == $maxVal){ 3065 $minVal = 0; 3066 $maxVal = $maxVal*2; 3067 $Scale = 1; 3068 $Divisions = 2; 3069 } elseif ($MaxDivs > 1) { 3070 while ( ! $ScaleOk ) { 3071 if($Factor == 0) { die(); } 3072 $Scale1 = ($maxVal - $minVal) / $Factor; 3073 $Scale2 = ($maxVal - $minVal) / $Factor / 2; 3074 3075 if ($Scale1 > 1 && $Scale1 <= $MaxDivs && ! $ScaleOk) { 3076 $ScaleOk = TRUE; 3077 $Divisions = floor ( $Scale1 ); 3078 $Scale = 1; 3079 } 3080 if ($Scale2 > 1 && $Scale2 <= $MaxDivs && ! $ScaleOk) { 3081 $ScaleOk = TRUE; 3082 $Divisions = floor ( $Scale2 ); 3083 $Scale = 2; 3084 } 3085 if (! $ScaleOk) { 3086 if ($Scale2 > 1) { 3087 $Factor = $Factor * 10; 3088 } 3089 if ($Scale2 < 1) { 3090 $Factor = $Factor / 10; 3091 } 3092 } 3093 } 3094 3095 if (floor ( $maxVal / $Scale / $Factor ) != $maxVal / $Scale / $Factor) { 3096 $GridID = floor ( $maxVal / $Scale / $Factor ) + 1; 3097 $maxVal = $GridID * $Scale * $Factor; 3098 $Divisions ++; 3099 } 3100 3101 if (floor ( $minVal / $Scale / $Factor ) != $minVal / $Scale / $Factor) { 3102 $GridID = floor ( $minVal / $Scale / $Factor ); 3103 $minVal = $GridID * $Scale * $Factor; 3104 $Divisions ++; 3105 } 3106 } else /* Can occurs for small graphs */ 3107 $Scale = 1; 3108 3109 if (! isset ( $Divisions )) 3110 $Divisions = 2; 3111 3112 if (self::isRealInt ( ($maxVal - $minVal) / ($Divisions - 1) )) 3113 $Divisions --; 3114 elseif (self::isRealInt ( ($maxVal - $minVal) / ($Divisions + 1) )) 3115 $Divisions ++; 3116 } 3117 3118 static private function convertValueForDisplay($value, $format, $unit) { 3119 if ($format == "number") 3120 return $value . $unit; 3121 if ($format == "time") 3122 return ConversionHelpers::ToTime ( $value ); 3123 if ($format == "date") 3124 return $this->ToDate ( $value ); 3125 if ($format == "metric") 3126 return ConversionHelpers::ToMetric ( $value ); 3127 if ($format == "currency") 3128 return ConversionHelpers::ToCurrency ( $value ); 3129 } 3130} 3131 3132/** 3133 * 3134 * @param $Message 3135 */ 3136function RaiseFatal($Message) { 3137 echo "[FATAL] " . $Message . "\r\n"; 3138 exit (); 3139} 3140?> 3141