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