| [ Index ] |
PHP Cross Reference of MantisBT |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * File containing the ezcGraphChartElementDateAxis class 4 * 5 * @package Graph 6 * @version 1.5 7 * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. 8 * @license http://ez.no/licenses/new_bsd New BSD License 9 */ 10 /** 11 * Class to represent date axis. 12 * 13 * Axis elements represent the axis in a bar, line or radar chart. They are 14 * chart elements (ezcGraphChartElement) extending from 15 * ezcGraphChartElementAxis, where additional formatting options can be found. 16 * You should generally use the axis, which matches your input data best, so 17 * that the automatic chart layouting works best. Aavailable axis types are: 18 * 19 * - ezcGraphChartElementDateAxis 20 * - ezcGraphChartElementLabeledAxis 21 * - ezcGraphChartElementLogarithmicalAxis 22 * - ezcGraphChartElementNumericAxis 23 * 24 * Date axis will try to find a "nice" interval based on the values on the x 25 * axis. If non numeric values are given, ezcGraphChartElementDateAxis will 26 * convert them to timestamps using PHPs strtotime function. 27 * 28 * It is always possible to set start date, end date and the interval manually 29 * by yourself. 30 * 31 * The $dateFormat option provides an additional way of formatting the labels 32 * used on the axis. The options from the parent class $formatString and 33 * $labelCallback do still apply. 34 * 35 * You may use a date axis like in the following example: 36 * 37 * <code> 38 * $graph = new ezcGraphLineChart(); 39 * $graph->options->fillLines = 210; 40 * $graph->title = 'Concurrent requests'; 41 * $graph->legend = false; 42 * 43 * $graph->xAxis = new ezcGraphChartElementDateAxis(); 44 * 45 * // Add data 46 * $graph->data['Machine 1'] = new ezcGraphArrayDataSet( array( 47 * '8:00' => 3241, 48 * '8:13' => 934, 49 * '8:24' => 1201, 50 * '8:27' => 1752, 51 * '8:51' => 123, 52 * ) ); 53 * $graph->data['Machine 2'] = new ezcGraphArrayDataSet( array( 54 * '8:05' => 623, 55 * '8:12' => 2103, 56 * '8:33' => 543, 57 * '8:43' => 2034, 58 * '8:59' => 3410, 59 * ) ); 60 * 61 * $graph->data['Machine 1']->symbol = ezcGraph::BULLET; 62 * $graph->data['Machine 2']->symbol = ezcGraph::BULLET; 63 * 64 * $graph->render( 400, 150, 'tutorial_axis_datetime.svg' ); 65 * </code> 66 * 67 * @property float $startDate 68 * Starting date used to display on axis. 69 * @property float $endDate 70 * End date used to display on axis. 71 * @property float $interval 72 * Time interval between steps on axis. 73 * @property string $dateFormat 74 * Format of date string 75 * Like http://php.net/date 76 * 77 * @version 1.5 78 * @package Graph 79 * @mainclass 80 */ 81 class ezcGraphChartElementDateAxis extends ezcGraphChartElementAxis 82 { 83 84 const MONTH = 2629800; 85 86 const YEAR = 31536000; 87 88 const DECADE = 315360000; 89 90 /** 91 * Minimum inserted date 92 * 93 * @var int 94 */ 95 protected $minValue = false; 96 97 /** 98 * Maximum inserted date 99 * 100 * @var int 101 */ 102 protected $maxValue = false; 103 104 /** 105 * Nice time intervals to used if there is no user defined interval 106 * 107 * @var array 108 */ 109 protected $predefinedIntervals = array( 110 // Second 111 1 => 'H:i.s', 112 // Ten seconds 113 10 => 'H:i.s', 114 // Thirty seconds 115 30 => 'H:i.s', 116 // Minute 117 60 => 'H:i', 118 // Ten minutes 119 600 => 'H:i', 120 // Half an hour 121 1800 => 'H:i', 122 // Hour 123 3600 => 'H:i', 124 // Four hours 125 14400 => 'H:i', 126 // Six hours 127 21600 => 'H:i', 128 // Half a day 129 43200 => 'd.m a', 130 // Day 131 86400 => 'd.m', 132 // Week 133 604800 => 'W', 134 // Month 135 self::MONTH => 'M y', 136 // Year 137 self::YEAR => 'Y', 138 // Decade 139 self::DECADE => 'Y', 140 ); 141 142 /** 143 * Constant used for calculation of automatic definition of major scaling 144 * steps 145 */ 146 const MAJOR_COUNT = 10; 147 148 /** 149 * Constructor 150 * 151 * @param array $options Default option array 152 * @return void 153 * @ignore 154 */ 155 public function __construct( array $options = array() ) 156 { 157 $this->properties['startDate'] = false; 158 $this->properties['endDate'] = false; 159 $this->properties['interval'] = false; 160 $this->properties['dateFormat'] = false; 161 162 parent::__construct( $options ); 163 } 164 165 /** 166 * __set 167 * 168 * @param mixed $propertyName 169 * @param mixed $propertyValue 170 * @throws ezcBaseValueException 171 * If a submitted parameter was out of range or type. 172 * @throws ezcBasePropertyNotFoundException 173 * If a the value for the property options is not an instance of 174 * @return void 175 * @ignore 176 */ 177 public function __set( $propertyName, $propertyValue ) 178 { 179 switch ( $propertyName ) 180 { 181 case 'startDate': 182 $this->properties['startDate'] = (int) $propertyValue; 183 break; 184 case 'endDate': 185 $this->properties['endDate'] = (int) $propertyValue; 186 break; 187 case 'interval': 188 $this->properties['interval'] = (int) $propertyValue; 189 $this->properties['initialized'] = true; 190 break; 191 case 'dateFormat': 192 $this->properties['dateFormat'] = (string) $propertyValue; 193 break; 194 default: 195 parent::__set( $propertyName, $propertyValue ); 196 break; 197 } 198 } 199 200 /** 201 * Ensure proper timestamp 202 * 203 * Takes a mixed value from datasets, like timestamps, or strings 204 * describing some time and converts it to a timestamp. 205 * 206 * @param mixed $value 207 * @return int 208 */ 209 protected static function ensureTimestamp( $value ) 210 { 211 if ( is_numeric( $value ) ) 212 { 213 $timestamp = (int) $value; 214 } 215 elseif ( ( $timestamp = strtotime( $value ) ) === false ) 216 { 217 throw new ezcGraphErrorParsingDateException( $value ); 218 } 219 220 return $timestamp; 221 } 222 223 /** 224 * Add data for this axis 225 * 226 * @param array $values Value which will be displayed on this axis 227 * @return void 228 */ 229 public function addData( array $values ) 230 { 231 foreach ( $values as $nr => $value ) 232 { 233 $value = self::ensureTimestamp( $value ); 234 235 if ( $this->minValue === false || 236 $value < $this->minValue ) 237 { 238 $this->minValue = $value; 239 } 240 241 if ( $this->maxValue === false || 242 $value > $this->maxValue ) 243 { 244 $this->maxValue = $value; 245 } 246 } 247 248 $this->properties['initialized'] = true; 249 } 250 251 /** 252 * Calculate nice time interval 253 * 254 * Use the best fitting time interval defined in class property array 255 * predefinedIntervals. 256 * 257 * @param int $min Start time 258 * @param int $max End time 259 * @return void 260 */ 261 protected function calculateInterval( $min, $max ) 262 { 263 $diff = $max - $min; 264 265 foreach ( $this->predefinedIntervals as $interval => $format ) 266 { 267 if ( ( $diff / $interval ) <= self::MAJOR_COUNT ) 268 { 269 break; 270 } 271 } 272 273 if ( ( $this->properties['startDate'] !== false ) && 274 ( $this->properties['endDate'] !== false ) ) 275 { 276 // Use interval between defined borders 277 if ( ( $diff % $interval ) > 0 ) 278 { 279 // Stil use predefined date format from old interval if not set 280 if ( $this->properties['dateFormat'] === false ) 281 { 282 $this->properties['dateFormat'] = $this->predefinedIntervals[$interval]; 283 } 284 285 $count = ceil( $diff / $interval ); 286 $interval = round( $diff / $count, 0 ); 287 } 288 } 289 290 $this->properties['interval'] = $interval; 291 } 292 293 /** 294 * Calculate lower nice date 295 * 296 * Calculates a date which is earlier or equal to the given date, and is 297 * divisible by the given interval. 298 * 299 * @param int $min Date 300 * @param int $interval Interval 301 * @return int Earlier date 302 */ 303 protected function calculateLowerNiceDate( $min, $interval ) 304 { 305 switch ( $interval ) 306 { 307 case self::MONTH: 308 // Special handling for months - not covered by the default 309 // algorithm 310 return mktime( 311 1, 312 0, 313 0, 314 (int) date( 'm', $min ), 315 1, 316 (int) date( 'Y', $min ) 317 ); 318 default: 319 $dateSteps = array( 60, 60, 24, 7, 52 ); 320 321 $date = array( 322 (int) date( 's', $min ), 323 (int) date( 'i', $min ), 324 (int) date( 'H', $min ), 325 (int) date( 'd', $min ), 326 (int) date( 'm', $min ), 327 (int) date( 'Y', $min ), 328 ); 329 330 $element = 0; 331 while ( ( $step = array_shift( $dateSteps ) ) && 332 ( $interval > $step ) ) 333 { 334 $interval /= $step; 335 $date[$element++] = (int) ( $element > 2 ); 336 } 337 338 $date[$element] -= $date[$element] % $interval; 339 340 return mktime( 341 $date[2], 342 $date[1], 343 $date[0], 344 $date[4], 345 $date[3], 346 $date[5] 347 ); 348 } 349 } 350 351 /** 352 * Calculate start date 353 * 354 * Use calculateLowerNiceDate to get a date earlier or equal date then the 355 * minimum date to use it as the start date for the axis depending on the 356 * selected interval. 357 * 358 * @param mixed $min Minimum date 359 * @param mixed $max Maximum date 360 * @return void 361 */ 362 public function calculateMinimum( $min, $max ) 363 { 364 if ( $this->properties['endDate'] === false ) 365 { 366 $this->properties['startDate'] = $this->calculateLowerNiceDate( $min, $this->interval ); 367 } 368 else 369 { 370 $this->properties['startDate'] = $this->properties['endDate']; 371 372 while ( $this->properties['startDate'] > $min ) 373 { 374 switch ( $this->interval ) 375 { 376 case self::MONTH: 377 $this->properties['startDate'] = strtotime( '-1 month', $this->properties['startDate'] ); 378 break; 379 case self::YEAR: 380 $this->properties['startDate'] = strtotime( '-1 year', $this->properties['startDate'] ); 381 break; 382 case self::DECADE: 383 $this->properties['startDate'] = strtotime( '-10 years', $this->properties['startDate'] ); 384 break; 385 default: 386 $this->properties['startDate'] -= $this->interval; 387 } 388 } 389 } 390 } 391 392 /** 393 * Calculate end date 394 * 395 * Use calculateLowerNiceDate to get a date later or equal date then the 396 * maximum date to use it as the end date for the axis depending on the 397 * selected interval. 398 * 399 * @param mixed $min Minimum date 400 * @param mixed $max Maximum date 401 * @return void 402 */ 403 public function calculateMaximum( $min, $max ) 404 { 405 $this->properties['endDate'] = $this->properties['startDate']; 406 407 while ( $this->properties['endDate'] < $max ) 408 { 409 switch ( $this->interval ) 410 { 411 case self::MONTH: 412 $this->properties['endDate'] = strtotime( '+1 month', $this->properties['endDate'] ); 413 break; 414 case self::YEAR: 415 $this->properties['endDate'] = strtotime( '+1 year', $this->properties['endDate'] ); 416 break; 417 case self::DECADE: 418 $this->properties['endDate'] = strtotime( '+10 years', $this->properties['endDate'] ); 419 break; 420 default: 421 $this->properties['endDate'] += $this->interval; 422 } 423 } 424 } 425 426 /** 427 * Calculate axis bounding values on base of the assigned values 428 * 429 * @return void 430 */ 431 public function calculateAxisBoundings() 432 { 433 // Prevent division by zero, when min == max 434 if ( $this->minValue == $this->maxValue ) 435 { 436 if ( $this->minValue == 0 ) 437 { 438 $this->maxValue = 1; 439 } 440 else 441 { 442 $this->minValue -= ( $this->minValue * .1 ); 443 $this->maxValue += ( $this->maxValue * .1 ); 444 } 445 } 446 447 // Use custom minimum and maximum if available 448 if ( $this->properties['startDate'] !== false ) 449 { 450 $this->minValue = $this->properties['startDate']; 451 } 452 453 if ( $this->properties['endDate'] !== false ) 454 { 455 $this->maxValue = $this->properties['endDate']; 456 } 457 458 // Calculate "nice" values for scaling parameters 459 if ( $this->properties['interval'] === false ) 460 { 461 $this->calculateInterval( $this->minValue, $this->maxValue ); 462 } 463 464 if ( $this->properties['dateFormat'] === false && isset( $this->predefinedIntervals[$this->interval] ) ) 465 { 466 $this->properties['dateFormat'] = $this->predefinedIntervals[$this->interval]; 467 } 468 469 if ( $this->properties['startDate'] === false ) 470 { 471 $this->calculateMinimum( $this->minValue, $this->maxValue ); 472 } 473 474 if ( $this->properties['endDate'] === false ) 475 { 476 $this->calculateMaximum( $this->minValue, $this->maxValue ); 477 } 478 } 479 480 /** 481 * Get coordinate for a dedicated value on the chart 482 * 483 * @param float $value Value to determine position for 484 * @return float Position on chart 485 */ 486 public function getCoordinate( $value ) 487 { 488 // Force typecast, because ( false < -100 ) results in (bool) true 489 $intValue = ( $value === false ? false : self::ensureTimestamp( $value ) ); 490 491 if ( ( $value === false ) && 492 ( ( $intValue < $this->startDate ) || ( $intValue > $this->endDate ) ) ) 493 { 494 switch ( $this->position ) 495 { 496 case ezcGraph::LEFT: 497 case ezcGraph::TOP: 498 return 0.; 499 case ezcGraph::RIGHT: 500 case ezcGraph::BOTTOM: 501 return 1.; 502 } 503 } 504 else 505 { 506 switch ( $this->position ) 507 { 508 case ezcGraph::LEFT: 509 case ezcGraph::TOP: 510 return ( $intValue - $this->startDate ) / ( $this->endDate - $this->startDate ); 511 case ezcGraph::RIGHT: 512 case ezcGraph::BOTTOM: 513 return 1 - ( $intValue - $this->startDate ) / ( $this->endDate - $this->startDate ); 514 } 515 } 516 } 517 518 /** 519 * Return count of minor steps 520 * 521 * @return integer Count of minor steps 522 */ 523 public function getMinorStepCount() 524 { 525 return false; 526 } 527 528 /** 529 * Return count of major steps 530 * 531 * @return integer Count of major steps 532 */ 533 public function getMajorStepCount() 534 { 535 return (int) ceil( ( $this->properties['endDate'] - $this->startDate ) / $this->interval ); 536 } 537 538 /** 539 * Get label for a dedicated step on the axis 540 * 541 * @param integer $step Number of step 542 * @return string label 543 */ 544 public function getLabel( $step ) 545 { 546 return $this->getLabelFromTimestamp( $this->startDate + ( $step * $this->interval ), $step ); 547 } 548 549 /** 550 * Get label for timestamp 551 * 552 * @param int $time 553 * @param int $step 554 * @return string 555 */ 556 protected function getLabelFromTimestamp( $time, $step ) 557 { 558 if ( $this->properties['labelCallback'] !== null ) 559 { 560 return call_user_func_array( 561 $this->properties['labelCallback'], 562 array( 563 date( $this->properties['dateFormat'], $time ), 564 $step, 565 ) 566 ); 567 } 568 else 569 { 570 return date( $this->properties['dateFormat'], $time ); 571 } 572 } 573 574 /** 575 * Return array of steps on this axis 576 * 577 * @return array( ezcGraphAxisStep ) 578 */ 579 public function getSteps() 580 { 581 $steps = array(); 582 583 $start = $this->properties['startDate']; 584 $end = $this->properties['endDate']; 585 $distance = $end - $start; 586 587 $step = 0; 588 for ( $time = $start; $time <= $end; ) 589 { 590 $steps[] = new ezcGraphAxisStep( 591 ( $time - $start ) / $distance, 592 $this->interval / $distance, 593 $this->getLabelFromTimestamp( $time, $step++ ), 594 array(), 595 $step === 1, 596 $time >= $end 597 ); 598 599 switch ( $this->interval ) 600 { 601 case self::MONTH: 602 $time = strtotime( '+1 month', $time ); 603 break; 604 case self::YEAR: 605 $time = strtotime( '+1 year', $time ); 606 break; 607 case self::DECADE: 608 $time = strtotime( '+10 years', $time ); 609 break; 610 default: 611 $time += $this->interval; 612 break; 613 } 614 } 615 616 return $steps; 617 } 618 619 /** 620 * Is zero step 621 * 622 * Returns true if the given step is the one on the initial axis position 623 * 624 * @param int $step Number of step 625 * @return bool Status If given step is initial axis position 626 */ 627 public function isZeroStep( $step ) 628 { 629 return ( $step == 0 ); 630 } 631 } 632 633 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Jul 28 15:48:31 2011 | Cross-referenced by PHPXref 0.7 |