| [ Index ] |
PHP Cross Reference of MantisBT |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * File containing the abstract ezcGraphChartElementNumericAxis 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 a numeric 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 * The axis tries to calculate "nice" start and end values for the axis scale. 25 * The used interval is considered as nice, if it is equal to [1,2,5] * 10^x 26 * with x in [.., -1, 0, 1, ..]. 27 * 28 * The start and end value are the next bigger / smaller multiple of the 29 * intervall compared to the maximum / minimum axis value. 30 * 31 * You may specify custom step sizes using the properties $majorStep and 32 * $minorStep. The minimum and maximum values for the axis labels can be 33 * defined using the $min and $max properties. You should be able to set any 34 * subset of these values, and all values not explicitely set will be 35 * calculated automatically. 36 * 37 * This axis should be used for all numeric values except dates. If your data 38 * does span very big number intervals you might want to consider using the 39 * logrithmic axis instead. 40 * 41 * The numeric axis may be used like: 42 * 43 * <code> 44 * $graph = new ezcGraphLineChart(); 45 * $graph->title = 'Some random data'; 46 * $graph->legend = false; 47 * 48 * $graph->xAxis = new ezcGraphChartElementNumericAxis(); 49 * // The y axis is numeric by default. 50 * 51 * $graph->xAxis->min = -15; 52 * $graph->xAxis->max = 15; 53 * $graph->xAxis->majorStep = 5; 54 * 55 * $data = array( 56 * array(), 57 * array() 58 * ); 59 * for ( $i = -10; $i <= 10; $i++ ) 60 * { 61 * $data[0][$i] = mt_rand( -23, 59 ); 62 * $data[1][$i] = mt_rand( -23, 59 ); 63 * } 64 * 65 * // Add data 66 * $graph->data['random blue'] = new ezcGraphArrayDataSet( $data[0] ); 67 * $graph->data['random green'] = new ezcGraphArrayDataSet( $data[1] ); 68 * 69 * $graph->render( 400, 150, 'tutorial_axis_numeric.svg' ); 70 * </code> 71 * 72 * @property float $min 73 * Minimum value of displayed scale on axis. 74 * @property float $max 75 * Maximum value of displayed scale on axis. 76 * @property mixed $majorStep 77 * Labeled major steps displayed on the axis. 78 * @property mixed $minorStep 79 * Non labeled minor steps on the axis. 80 * @property-read float $minValue 81 * Minimum Value to display on this axis. 82 * @property-read float $maxValue 83 * Maximum value to display on this axis. 84 * 85 * @version 1.5 86 * @package Graph 87 * @mainclass 88 */ 89 class ezcGraphChartElementNumericAxis extends ezcGraphChartElementAxis 90 { 91 92 /** 93 * Constant used for calculation of automatic definition of major scaling 94 * steps 95 */ 96 const MIN_MAJOR_COUNT = 5; 97 98 /** 99 * Constant used for automatic calculation of minor steps from given major 100 * steps 101 */ 102 const MIN_MINOR_COUNT = 8; 103 104 /** 105 * Constructor 106 * 107 * @param array $options Default option array 108 * @return void 109 * @ignore 110 */ 111 public function __construct( array $options = array() ) 112 { 113 $this->properties['min'] = null; 114 $this->properties['max'] = null; 115 $this->properties['minValue'] = null; 116 $this->properties['maxValue'] = null; 117 118 parent::__construct( $options ); 119 } 120 121 /** 122 * __set 123 * 124 * @param mixed $propertyName 125 * @param mixed $propertyValue 126 * @throws ezcBaseValueException 127 * If a submitted parameter was out of range or type. 128 * @throws ezcBasePropertyNotFoundException 129 * If a the value for the property options is not an instance of 130 * @return void 131 * @ignore 132 */ 133 public function __set( $propertyName, $propertyValue ) 134 { 135 switch ( $propertyName ) 136 { 137 case 'min': 138 if ( !is_numeric( $propertyValue ) ) 139 { 140 throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' ); 141 } 142 143 $this->properties['min'] = (float) $propertyValue; 144 $this->properties['initialized'] = true; 145 break; 146 case 'max': 147 if ( !is_numeric( $propertyValue ) ) 148 { 149 throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' ); 150 } 151 152 $this->properties['max'] = (float) $propertyValue; 153 $this->properties['initialized'] = true; 154 break; 155 default: 156 parent::__set( $propertyName, $propertyValue ); 157 break; 158 } 159 } 160 161 /** 162 * Returns a "nice" number for a given floating point number. 163 * 164 * Nice numbers are steps on a scale which are easily recognized by humans 165 * like 0.5, 25, 1000 etc. 166 * 167 * @param float $float Number to be altered 168 * @return float Nice number 169 */ 170 protected function getNiceNumber( $float ) 171 { 172 // Get absolute value and save sign 173 $abs = abs( $float ); 174 $sign = $float / $abs; 175 176 // Normalize number to a range between 1 and 10 177 $log = (int) round( log10( $abs ), 0 ); 178 $abs /= pow( 10, $log ); 179 180 181 // find next nice number 182 if ( $abs > 5 ) 183 { 184 $abs = 10.; 185 } 186 elseif ( $abs > 2.5 ) 187 { 188 $abs = 5.; 189 } 190 elseif ( $abs > 1 ) 191 { 192 $abs = 2.5; 193 } 194 else 195 { 196 $abs = 1; 197 } 198 199 // unnormalize number to original values 200 return $abs * pow( 10, $log ) * $sign; 201 } 202 203 /** 204 * Calculate minimum value for displayed axe basing on real minimum and 205 * major step size 206 * 207 * @param float $min Real data minimum 208 * @param float $max Real data maximum 209 * @return void 210 */ 211 protected function calculateMinimum( $min, $max ) 212 { 213 if ( $this->properties['max'] === null ) 214 { 215 $this->properties['min'] = floor( $min / $this->properties['majorStep'] ) * $this->properties['majorStep']; 216 } 217 else 218 { 219 $calculatedMin = $this->properties['max']; 220 221 do { 222 $calculatedMin -= $this->properties['majorStep']; 223 } while ( $calculatedMin > $min ); 224 225 $this->properties['min'] = $calculatedMin; 226 } 227 } 228 229 /** 230 * Calculate maximum value for displayed axe basing on real maximum and 231 * major step size 232 * 233 * @param float $min Real data minimum 234 * @param float $max Real data maximum 235 * @return void 236 */ 237 protected function calculateMaximum( $min, $max ) 238 { 239 $calculatedMax = $this->properties['min']; 240 241 do { 242 $calculatedMax += $this->properties['majorStep']; 243 } while ( $calculatedMax < $max ); 244 245 $this->properties['max'] = $calculatedMax; 246 } 247 248 /** 249 * Calculate size of minor steps based on the size of the major step size 250 * 251 * @param float $min Real data minimum 252 * @param float $max Real data maximum 253 * @return void 254 */ 255 protected function calculateMinorStep( $min, $max ) 256 { 257 $stepSize = $this->properties['majorStep'] / self::MIN_MINOR_COUNT; 258 $this->properties['minorStep'] = $this->getNiceNumber( $stepSize ); 259 } 260 261 /** 262 * Calculate size of major step based on the span to be displayed and the 263 * defined MIN_MAJOR_COUNT constant. 264 * 265 * @param float $min Real data minimum 266 * @param float $max Real data maximum 267 * @return void 268 */ 269 protected function calculateMajorStep( $min, $max ) 270 { 271 $span = $max - $min; 272 $stepSize = $span / self::MIN_MAJOR_COUNT; 273 $this->properties['majorStep'] = $this->getNiceNumber( $stepSize ); 274 } 275 276 /** 277 * Add data for this axis 278 * 279 * @param array $values Value which will be displayed on this axis 280 * @return void 281 */ 282 public function addData( array $values ) 283 { 284 foreach ( $values as $value ) 285 { 286 if ( $this->properties['minValue'] === null || 287 $value < $this->properties['minValue'] ) 288 { 289 $this->properties['minValue'] = $value; 290 } 291 292 if ( $this->properties['maxValue'] === null || 293 $value > $this->properties['maxValue'] ) 294 { 295 $this->properties['maxValue'] = $value; 296 } 297 } 298 299 $this->properties['initialized'] = true; 300 } 301 302 /** 303 * Calculate axis bounding values on base of the assigned values 304 * 305 * @abstract 306 * @access public 307 * @return void 308 */ 309 public function calculateAxisBoundings() 310 { 311 // Prevent division by zero, when min == max 312 if ( $this->properties['minValue'] == $this->properties['maxValue'] ) 313 { 314 if ( $this->properties['minValue'] == 0 ) 315 { 316 $this->properties['maxValue'] = 1; 317 } 318 else 319 { 320 if ( $this->properties['majorStep'] !== null ) 321 { 322 $this->properties['minValue'] -= $this->properties['majorStep']; 323 $this->properties['maxValue'] += $this->properties['majorStep']; 324 } 325 else 326 { 327 $this->properties['minValue'] -= ( $this->properties['minValue'] * .1 ); 328 $this->properties['maxValue'] += ( $this->properties['maxValue'] * .1 ); 329 } 330 } 331 } 332 333 // Use custom minimum and maximum if available 334 if ( $this->properties['min'] !== null ) 335 { 336 $this->properties['minValue'] = $this->properties['min']; 337 } 338 339 if ( $this->properties['max'] !== null ) 340 { 341 $this->properties['maxValue'] = $this->properties['max']; 342 } 343 344 // If min and max values are forced, we may not be able to find a 345 // "nice" number for the steps. Try to find such a nice step size, or 346 // fall back to a step size, which is just the span divided by 5. 347 if ( ( $this->properties['min'] !== null ) && 348 ( $this->properties['max'] !== null ) && 349 ( $this->properties['majorStep'] === null ) ) 350 { 351 $diff = $this->properties['max'] - $this->properties['min']; 352 $this->calculateMajorStep( $this->properties['minValue'], $this->properties['maxValue'] ); 353 $stepInvariance = $diff / $this->properties['majorStep']; 354 if ( ( $stepInvariance - floor( $stepInvariance ) ) > .0000001 ) 355 { 356 // For too big step invariances calculate the step size just 357 // from the given difference between min and max value. 358 $this->properties['majorStep'] = ( $this->properties['max'] - $this->properties['min'] ) / self::MIN_MAJOR_COUNT; 359 $this->properties['minorStep'] = $this->properties['majorStep'] / self::MIN_MAJOR_COUNT; 360 } 361 } 362 363 // Calculate "nice" values for scaling parameters 364 if ( $this->properties['majorStep'] === null ) 365 { 366 $this->calculateMajorStep( $this->properties['minValue'], $this->properties['maxValue'] ); 367 } 368 369 if ( $this->properties['minorStep'] === null ) 370 { 371 $this->calculateMinorStep( $this->properties['minValue'], $this->properties['maxValue'] ); 372 } 373 374 if ( $this->properties['min'] === null ) 375 { 376 $this->calculateMinimum( $this->properties['minValue'], $this->properties['maxValue'] ); 377 } 378 379 if ( $this->properties['max'] === null ) 380 { 381 $this->calculateMaximum( $this->properties['minValue'], $this->properties['maxValue'] ); 382 } 383 384 // Check that the major step size matches up with the min and max 385 // values on the axis. 386 $quotient = ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['majorStep']; 387 $quotient = abs( $quotient - floor( $quotient ) ); 388 if ( ( $quotient >= .00001 ) && 389 ( abs( $quotient - 1 ) >= .00001 ) ) 390 { 391 throw new ezcGraphInvalidStepSizeException( "The difference between minimum and maximum value is not a multiplier of the major step size." ); 392 } 393 394 // Check that the minor step size matches up with major step size on 395 // the axis. 396 $quotient = $this->properties['majorStep'] / $this->properties['minorStep']; 397 $quotient = abs( $quotient - floor( $quotient ) ); 398 if ( ( $quotient >= .00001 ) && 399 ( abs( $quotient - 1 ) >= .00001 ) ) 400 { 401 throw new ezcGraphInvalidStepSizeException( "The major step size value is not a multiplier of the minor step size." ); 402 } 403 } 404 405 /** 406 * Get coordinate for a dedicated value on the chart 407 * 408 * @param float $value Value to determine position for 409 * @return float Position on chart 410 */ 411 public function getCoordinate( $value ) 412 { 413 // Force typecast, because ( false < -100 ) results in (bool) true 414 $floatValue = (float) $value; 415 416 if ( ( $value === false ) && 417 ( ( $floatValue < $this->properties['min'] ) || ( $floatValue > $this->properties['max'] ) ) ) 418 { 419 switch ( $this->position ) 420 { 421 case ezcGraph::LEFT: 422 case ezcGraph::TOP: 423 return 0.; 424 case ezcGraph::RIGHT: 425 case ezcGraph::BOTTOM: 426 return 1.; 427 } 428 } 429 else 430 { 431 switch ( $this->position ) 432 { 433 case ezcGraph::LEFT: 434 case ezcGraph::TOP: 435 return ( $value - $this->properties['min'] ) / ( $this->properties['max'] - $this->properties['min'] ); 436 case ezcGraph::RIGHT: 437 case ezcGraph::BOTTOM: 438 return 1 - ( $value - $this->properties['min'] ) / ( $this->properties['max'] - $this->properties['min'] ); 439 } 440 } 441 } 442 443 /** 444 * Return count of minor steps 445 * 446 * @return integer Count of minor steps 447 */ 448 public function getMinorStepCount() 449 { 450 return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['minorStep'] ); 451 } 452 453 /** 454 * Return count of major steps 455 * 456 * @return integer Count of major steps 457 */ 458 public function getMajorStepCount() 459 { 460 return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['majorStep'] ); 461 } 462 463 /** 464 * Get label for a dedicated step on the axis 465 * 466 * @param integer $step Number of step 467 * @return string label 468 */ 469 public function getLabel( $step ) 470 { 471 if ( $this->properties['labelCallback'] !== null ) 472 { 473 return call_user_func_array( 474 $this->properties['labelCallback'], 475 array( 476 $this->properties['min'] + ( $step * $this->properties['majorStep'] ), 477 $step, 478 ) 479 ); 480 } 481 elseif ( $this->properties['formatString'] !== null ) 482 { 483 return sprintf( $this->properties['formatString'], $this->properties['min'] + ( $step * $this->properties['majorStep'] ) ); 484 } 485 else 486 { 487 return $this->properties['min'] + ( $step * $this->properties['majorStep'] ); 488 } 489 } 490 491 /** 492 * Is zero step 493 * 494 * Returns true if the given step is the one on the initial axis position 495 * 496 * @param int $step Number of step 497 * @return bool Status If given step is initial axis position 498 */ 499 public function isZeroStep( $step ) 500 { 501 return ( $this->getLabel( $step ) == 0 ); 502 } 503 } 504 505 ?>
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 |