diff options
author | Kore Nordmann <github@kore-nordmann.de> | 2006-08-09 08:08:43 +0000 |
---|---|---|
committer | Kore Nordmann <github@kore-nordmann.de> | 2006-08-09 08:08:43 +0000 |
commit | 7590db4ae7a46bf9fdf71813a4e00ce3eb1c67f9 (patch) | |
tree | 6368608582d5932a1dc8a8568cebc8a7fdb52471 | |
parent | 63b5d5f3f2dee22c890d219aeb8e4cc7fd228f41 (diff) | |
download | zetacomponents-graph-7590db4ae7a46bf9fdf71813a4e00ce3eb1c67f9.zip zetacomponents-graph-7590db4ae7a46bf9fdf71813a4e00ce3eb1c67f9.tar.gz |
- Added date axis
-rw-r--r-- | src/axis/date.php | 385 | ||||
-rw-r--r-- | src/graph_autoload.php | 1 | ||||
-rw-r--r-- | tests/date_axis_test.php | 354 | ||||
-rw-r--r-- | tests/suite.php | 2 |
4 files changed, 742 insertions, 0 deletions
diff --git a/src/axis/date.php b/src/axis/date.php new file mode 100644 index 0000000..5f58a32 --- /dev/null +++ b/src/axis/date.php @@ -0,0 +1,385 @@ +<?php +/** + * File containing the abstract ezcGraphChartElementDateAxis class + * + * @package Graph + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ +/** + * Class to represent a axe as a chart element + * + * @package Graph + */ +class ezcGraphChartElementDateAxis extends ezcGraphChartElementAxis +{ + + /** + * Minimum inserted date + * + * @var int + */ + protected $minValue = false; + + /** + * Maximum inserted date + * + * @var int + */ + protected $maxValue = false; + + /** + * Starting date used to display on axis + * + * @var float + */ + protected $startDate = false; + + /** + * End date used to display on axis + * + * @var float + */ + protected $endDate = false; + + /** + * Time interval between steps on axis + * + * @var float + */ + protected $interval = false; + + /** + * Format of date string + * + * Like http://php.net/date + * + * @var string + */ + protected $dateFormat = false; + + /** + * Nice time intervals to used if there is no user defined interval + * + * @var array + */ + protected $predefinedIntervals = array( + // Second + 1 => 'H:i.s', + // Ten seconds + 10 => 'H:i.s', + // Thirty seconds + 30 => 'H:i.s', + // Minute + 60 => 'H:i', + // Ten minutes + 600 => 'H:i', + // Half an hour + 1800 => 'H:i', + // Hour + 3600 => 'H:i', + // Four hours + 14400 => 'H:i', + // Six hours + 21600 => 'H:i', + // Half a day + 43200 => 'd.m a', + // Day + 86400 => 'd.m', + // Week + 604800 => 'W', + // Month + 2629800 => 'M y', + // Year + 31536000 => 'Y', + // Decade + 315360000 => 'Y', + ); + + /** + * Constant used for calculation of automatic definition of major scaling + * steps + */ + const MAJOR_COUNT = 10; + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'startDate': + $this->startDate = (int) $propertyValue; + break; + case 'endDate': + $this->endDate = (int) $propertyValue; + break; + case 'interval': + $this->interval = (int) $propertyValue; + break; + case 'dateFormat': + $this->dateFormat = (string) $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } + + /** + * Add data for this axis + * + * @param mixed $value Value which will be displayed on this axis + * @return void + */ + public function addData( array $values ) + { + foreach ( $values as $value ) + { + if ( is_numeric( $value ) ) + { + $value = (int) $value; + } + elseif ( ( $value = strtotime( $value ) ) === false ) + { + throw new ezcGraphErrorParsingDateException( $value ); + } + + if ( $this->minValue === false || + $value < $this->minValue ) + { + $this->minValue = $value; + } + + if ( $this->maxValue === false || + $value > $this->maxValue ) + { + $this->maxValue = $value; + } + } + } + + /** + * Calculate nice time interval + * + * Use the best fitting time interval defined in class property array + * predefinedIntervals. + * + * @param int $min Start time + * @param int $max End time + * @return void + */ + protected function calculateInterval( $min, $max ) + { + $diff = $max - $min; + + foreach ( $this->predefinedIntervals as $interval => $format ) + { + if ( ( $diff / $interval ) <= self::MAJOR_COUNT ) + { + break; + } + } + + $this->interval = $interval; + } + + protected function calculateLowerNiceDate( $min, $interval ) + { + $dateSteps = array( 60, 60, 24, 7, 52 ); + + $date = array( + (int) date( 's', $min ), + (int) date( 'i', $min ), + (int) date( 'H', $min ), + (int) date( 'd', $min ), + (int) date( 'm', $min ), + (int) date( 'Y', $min ), + ); + + $element = 0; + while ( ( $step = array_shift( $dateSteps ) ) && + ( $interval > $step ) ) + { + $interval /= $step; + $date[$element++] = (int) ( $element > 2 ); + } + + $date[$element] -= $date[$element] % $interval; + + return mktime( + $date[2], + $date[1], + $date[0], + $date[4], + $date[3], + $date[5] + ); + } + + public function calculateMinimum( $min, $max ) + { + $this->startDate = $this->calculateLowerNiceDate( $min, $this->interval ); + } + + public function calculateMaximum( $min, $max ) + { + $this->endDate = $this->calculateLowerNiceDate( $max, $this->interval ); + + while ( $this->endDate < $max ) + { + $this->endDate += $this->interval; + } + } + + /** + * Calculate axis bounding values on base of the assigned values + * + * @abstract + * @access public + * @return void + */ + public function calculateAxisBoundings() + { + // Prevent division by zero, when min == max + if ( $this->minValue == $this->maxValue ) + { + if ( $this->minValue == 0 ) + { + $this->maxValue = 1; + } + else + { + $this->minValue -= ( $this->minValue * .1 ); + $this->maxValue += ( $this->maxValue * .1 ); + } + } + + // Use custom minimum and maximum if available + if ( $this->startDate !== false ) + { + $this->minValue = $this->startDate; + } + + if ( $this->endDate !== false ) + { + $this->maxValue = $this->endDate; + } + + // Calculate "nice" values for scaling parameters + if ( $this->interval === false ) + { + $this->calculateInterval( $this->minValue, $this->maxValue ); + } + + if ( $this->dateFormat === false && isset( $this->predefinedIntervals[$this->interval] ) ) + { + $this->dateFormat = $this->predefinedIntervals[$this->interval]; + } + + if ( $this->startDate === false ) + { + $this->calculateMinimum( $this->minValue, $this->maxValue ); + } + + if ( $this->endDate === false ) + { + $this->calculateMaximum( $this->minValue, $this->maxValue ); + } + } + + /** + * Get coordinate for a dedicated value on the chart + * + * @param ezcGraphBounding $boundings + * @param float $value Value to determine position for + * @return float Position on chart + */ + public function getCoordinate( $value ) + { + // Force typecast, because ( false < -100 ) results in (bool) true + $floatValue = (float) $value; + + if ( ( $value === false ) && + ( ( $floatValue < $this->startDate ) || ( $floatValue > $this->endDate ) ) ) + { + switch ( $this->position ) + { + case ezcGraph::LEFT: + case ezcGraph::TOP: + return 0.; + case ezcGraph::RIGHT: + case ezcGraph::BOTTOM: + return 1.; + } + } + else + { + switch ( $this->position ) + { + case ezcGraph::LEFT: + case ezcGraph::TOP: + return ( $value - $this->startDate ) / ( $this->endDate - $this->startDate ); + case ezcGraph::RIGHT: + case ezcGraph::BOTTOM: + return 1 - ( $value - $this->startDate ) / ( $this->endDate - $this->startDate ); + } + } + } + + /** + * Return count of minor steps + * + * @return integer Count of minor steps + */ + public function getMinorStepCount() + { + return false; + } + + /** + * Return count of major steps + * + * @return integer Count of major steps + */ + public function getMajorStepCount() + { + return (int) ( ( $this->endDate - $this->startDate ) / $this->interval ); + } + + /** + * Get label for a dedicated step on the axis + * + * @param integer $step Number of step + * @return string label + */ + public function getLabel( $step ) + { + return date( $this->dateFormat, $this->startDate + ( $step * $this->interval ) ); + } + + /** + * Is zero step + * + * Returns true if the given step is the one on the initial axis position + * + * @param int $step Number of step + * @return bool Status If given step is initial axis position + */ + public function isZeroStep( $step ) + { + return ( $step == 0 ); + } +} + +?> diff --git a/src/graph_autoload.php b/src/graph_autoload.php index 6848661..80502c5 100644 --- a/src/graph_autoload.php +++ b/src/graph_autoload.php @@ -60,6 +60,7 @@ return array( 'ezcGraphChartElementLegend' => 'Graph/element/legend.php', 'ezcGraphChartElementBackgroundImage' => 'Graph/element/background.php', 'ezcGraphChartElementAxis' => 'Graph/element/axis.php', + 'ezcGraphChartElementDateAxis' => 'Graph/axis/date.php', 'ezcGraphChartElementNumericAxis' => 'Graph/axis/numeric.php', 'ezcGraphChartElementLabeledAxis' => 'Graph/axis/labeled.php', diff --git a/tests/date_axis_test.php b/tests/date_axis_test.php new file mode 100644 index 0000000..a78f1cc --- /dev/null +++ b/tests/date_axis_test.php @@ -0,0 +1,354 @@ +<?php +/** + * ezcGraphDateAxisTest + * + * @package Graph + * @version //autogen// + * @subpackage Tests + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ + +/** + * Tests for ezcGraph class. + * + * @package ImageAnalysis + * @subpackage Tests + */ +class ezcGraphDateAxisTest extends ezcTestCase +{ + + protected $chart; + + public static function suite() + { + return new ezcTestSuite( "ezcGraphDateAxisTest" ); + } + + /** + * setUp + * + * @access public + */ + public function setUp() + { + date_default_timezone_set( 'Europe/Berlin' ); + + $this->chart = new ezcGraphLineChart(); + $this->chart->xAxis = new ezcGraphChartElementDateAxis(); + } + + /** + * tearDown + * + * @access public + */ + public function tearDown() + { + unset( $this->chart ); + } + + public function testManualScaling() + { + $this->chart->xAxis->startDate = 0; + $this->chart->xAxis->endDate = 100; + $this->chart->xAxis->interval = 10; + + $this->chart['some data'] = array( 10 => 12, 37 => 235, 43 => 17, 114 => 39 ); + + $this->chart->render( 500, 200 ); + + $this->assertEquals( + 0, + $this->getNonPublicProperty( $this->chart->xAxis, 'startDate' ), + 'Wrong starting date. ' + ); + + $this->assertEquals( + 100, + $this->getNonPublicProperty( $this->chart->xAxis, 'endDate' ), + 'Wrong end date. ' + ); + + $this->assertEquals( + 10, + $this->getNonPublicProperty( $this->chart->xAxis, 'interval' ), + 'Wrong interval. ' + ); + } + + public function testManualBoundingsForScaling() + { + $this->chart->xAxis->startDate = 0; + $this->chart->xAxis->endDate = 100; + + $this->chart['some data'] = array( 10 => 12, 37 => 235, 43 => 17, 114 => 39 ); + + $this->chart->render( 500, 200 ); + + $this->assertEquals( + 0, + $this->getNonPublicProperty( $this->chart->xAxis, 'startDate' ), + 'Wrong starting date. ' + ); + + $this->assertEquals( + 100, + $this->getNonPublicProperty( $this->chart->xAxis, 'endDate' ), + 'Wrong end date. ' + ); + + $this->assertEquals( + 10, + $this->getNonPublicProperty( $this->chart->xAxis, 'interval' ), + 'Wrong interval. ' + ); + } + + public function testManualIntervalForScaling() + { + $this->chart->xAxis->interval = 10; + + $this->chart['some data'] = array( 10 => 12, 37 => 235, 43 => 17, 114 => 39 ); + + $this->chart->render( 500, 200 ); + + $this->assertEquals( + 10, + $this->getNonPublicProperty( $this->chart->xAxis, 'startDate' ), + 'Wrong starting date. ' + ); + + $this->assertEquals( + 120, + $this->getNonPublicProperty( $this->chart->xAxis, 'endDate' ), + 'Wrong end date. ' + ); + + $this->assertEquals( + 10, + $this->getNonPublicProperty( $this->chart->xAxis, 'interval' ), + 'Wrong interval. ' + ); + } + + public function testAutomagicScalingSingle1() + { + $this->chart['some data'] = array( 10 => 12, 37 => 235, 43 => 17, 114 => 39 ); + $this->chart->render( 500, 200 ); + + $this->assertEquals( + 0, + $this->getNonPublicProperty( $this->chart->xAxis, 'startDate' ), + 'Wrong starting date. ' + ); + + $this->assertEquals( + 120, + $this->getNonPublicProperty( $this->chart->xAxis, 'endDate' ), + 'Wrong end date. ' + ); + + $this->assertEquals( + 30, + $this->getNonPublicProperty( $this->chart->xAxis, 'interval' ), + 'Wrong interval. ' + ); + } + + public function testAutomagicScalingSingle2() + { + $this->chart['some data'] = array( 30010 => 12, 30037 => 235, 30043 => 17, 30114 => 39 ); + $this->chart->render( 500, 200 ); + + $this->assertEquals( + 30000, + $this->getNonPublicProperty( $this->chart->xAxis, 'startDate' ), + 'Wrong starting date. ' + ); + + $this->assertEquals( + 30120, + $this->getNonPublicProperty( $this->chart->xAxis, 'endDate' ), + 'Wrong end date. ' + ); + + $this->assertEquals( + 30, + $this->getNonPublicProperty( $this->chart->xAxis, 'interval' ), + 'Wrong interval. ' + ); + } + + public function testAutomagicScalingSingle3() + { + $this->chart['some data'] = array( + mktime( 10, 13, 57, 5, 7, 2006 ) => 324, + mktime( 10, 46, 13, 5, 7, 2006 ) => 324, + mktime( 11, 15, 45, 5, 7, 2006 ) => 324, + mktime( 12, 32, 01, 5, 7, 2006 ) => 324, + ); + $this->chart->render( 500, 200 ); + + $this->assertEquals( + 'Sun, 07 May 2006 10:00:00 +0200', + date( 'r', $this->getNonPublicProperty( $this->chart->xAxis, 'startDate' ) ), + 'Wrong starting date. ' + ); + + $this->assertEquals( + 'Sun, 07 May 2006 13:00:00 +0200', + date( 'r', $this->getNonPublicProperty( $this->chart->xAxis, 'endDate' ) ), + 'Wrong end date. ' + ); + + $this->assertEquals( + 1800, + $this->getNonPublicProperty( $this->chart->xAxis, 'interval' ), + 'Wrong interval. ' + ); + } + + public function testAutomagicScalingSingle4() + { + $this->chart['some data'] = array( + mktime( 10, 13, 57, 5, 7, 2006 ) => 324, + mktime( 17, 46, 13, 5, 7, 2006 ) => 324, + mktime( 11, 15, 45, 5, 8, 2006 ) => 324, + mktime( 20, 32, 1, 5, 8, 2006 ) => 324, + mktime( 8, 43, 19, 5, 9, 2006 ) => 324, + ); + $this->chart->render( 500, 200 ); + + $this->assertEquals( + 'Sun, 07 May 2006 06:00:00 +0200', + date( 'r', $this->getNonPublicProperty( $this->chart->xAxis, 'startDate' ) ), + 'Wrong starting date. ' + ); + + $this->assertEquals( + 'Tue, 09 May 2006 12:00:00 +0200', + date( 'r', $this->getNonPublicProperty( $this->chart->xAxis, 'endDate' ) ), + 'Wrong end date. ' + ); + + $this->assertEquals( + 21600, + $this->getNonPublicProperty( $this->chart->xAxis, 'interval' ), + 'Wrong interval. ' + ); + } + + public function testAutomagicScalingSingle5() + { + $this->chart['some data'] = array( + mktime( 1, 0, 0, 1, 1, 2001 ) => 324, + mktime( 1, 0, 0, 1, 1, 2002 ) => 324, + mktime( 1, 0, 0, 1, 1, 2003 ) => 324, + mktime( 1, 0, 0, 1, 1, 2004 ) => 324, + ); + $this->chart->render( 500, 200 ); + + $this->assertEquals( + 'Mon, 01 Jan 2001 01:00:00 +0100', + date( 'r', $this->getNonPublicProperty( $this->chart->xAxis, 'startDate' ) ), + 'Wrong starting date. ' + ); + + $this->assertEquals( + 'Thu, 01 Jan 2004 01:00:00 +0100', + date( 'r', $this->getNonPublicProperty( $this->chart->xAxis, 'endDate' ) ), + 'Wrong end date. ' + ); + + $this->assertEquals( + 31536000, + $this->getNonPublicProperty( $this->chart->xAxis, 'interval' ), + 'Wrong interval. ' + ); + } + + public function testPositionLeft() + { + $this->chart['some data'] = array( + mktime( 10, 13, 57, 5, 7, 2006 ) => 324, + mktime( 17, 46, 13, 5, 7, 2006 ) => 324, + mktime( 11, 15, 45, 5, 8, 2006 ) => 324, + mktime( 20, 32, 1, 5, 8, 2006 ) => 324, + mktime( 8, 43, 19, 5, 9, 2006 ) => 324, + ); + $this->chart->xAxis->position = ezcGraph::LEFT; + $this->chart->render( 500, 200 ); + + $this->assertEquals( + 0., + $this->chart->xAxis->getCoordinate( false ), + 'Wrong initial axis position. ', + .05 + ); + + $this->assertEquals( + 0., + $this->chart->xAxis->getCoordinate( mktime( 6, 0, 0, 5, 7, 2006 ) ), + 'Wrong minimal value. ', + .05 + ); + + $this->assertEquals( + .575, + $this->chart->xAxis->getCoordinate( mktime( 13, 1, 34, 5, 8, 2006 ) ), + 'Wrong mid value. ', + .05 + ); + + $this->assertEquals( + 1., + $this->chart->xAxis->getCoordinate( mktime( 12, 0, 0, 5, 9, 2006 ) ), + 'Wrong maximum value. ', + .05 + ); + } + + public function testPositionRight() + { + $this->chart['some data'] = array( + mktime( 10, 13, 57, 5, 7, 2006 ) => 324, + mktime( 17, 46, 13, 5, 7, 2006 ) => 324, + mktime( 11, 15, 45, 5, 8, 2006 ) => 324, + mktime( 20, 32, 1, 5, 8, 2006 ) => 324, + mktime( 8, 43, 19, 5, 9, 2006 ) => 324, + ); + $this->chart->xAxis->position = ezcGraph::RIGHT; + $this->chart->render( 500, 200 ); + + $this->assertEquals( + 1., + $this->chart->xAxis->getCoordinate( false ), + 'Wrong initial axis position. ', + .05 + ); + + $this->assertEquals( + 1., + $this->chart->xAxis->getCoordinate( mktime( 6, 0, 0, 5, 7, 2006 ) ), + 'Wrong minimal value. ', + .05 + ); + + $this->assertEquals( + .425, + $this->chart->xAxis->getCoordinate( mktime( 13, 1, 34, 5, 8, 2006 ) ), + 'Wrong mid value. ', + .05 + ); + + $this->assertEquals( + 0., + $this->chart->xAxis->getCoordinate( mktime( 12, 0, 0, 5, 9, 2006 ) ), + 'Wrong maximum value. ', + .05 + ); + } +} + +?> diff --git a/tests/suite.php b/tests/suite.php index ea08751..71c4aca 100644 --- a/tests/suite.php +++ b/tests/suite.php @@ -21,6 +21,7 @@ require_once 'legend_test.php'; require_once 'text_test.php'; require_once 'numeric_axis_test.php'; require_once 'labeled_axis_test.php'; +require_once 'date_axis_test.php'; require_once 'renderer_2d_test.php'; require_once 'renderer_3d_test.php'; require_once 'axis_exact_renderer_test.php'; @@ -51,6 +52,7 @@ class ezcGraphSuite extends ezcTestSuite $this->addTest( ezcGraphLegendTest::suite() ); $this->addTest( ezcGraphNumericAxisTest::suite() ); $this->addTest( ezcGraphLabeledAxisTest::suite() ); + $this->addTest( ezcGraphDateAxisTest::suite() ); $this->addTest( ezcGraphRenderer2dTest::suite() ); $this->addTest( ezcGraphRenderer3dTest::suite() ); $this->addTest( ezcGraphAxisExactRendererTest::suite() ); |