mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-06-03 21:29:28 +00:00
586 lines
16 KiB
PHP
586 lines
16 KiB
PHP
<?php
|
|
/**
|
|
* File containing the ezcGraphChartElementDateAxis class
|
|
*
|
|
* @package Graph
|
|
* @version 1.3
|
|
* @copyright Copyright (C) 2005-2008 eZ systems as. All rights reserved.
|
|
* @license http://ez.no/licenses/new_bsd New BSD License
|
|
*/
|
|
/**
|
|
* Class to represent date axis. Date axis will try to find a "nice" interval
|
|
* based on the values on the x axis. If non numeric values are given,
|
|
* ezcGraphChartElementDateAxis will convert them to timestamps using PHPs
|
|
* strtotime function.
|
|
*
|
|
* It is always possible to set start date, end date and the interval manually
|
|
* by yourself.
|
|
*
|
|
* @property float $startDate
|
|
* Starting date used to display on axis.
|
|
* @property float $endDate
|
|
* End date used to display on axis.
|
|
* @property float $interval
|
|
* Time interval between steps on axis.
|
|
* @property string $dateFormat
|
|
* Format of date string
|
|
* Like http://php.net/date
|
|
*
|
|
* @version 1.3
|
|
* @package Graph
|
|
* @mainclass
|
|
*/
|
|
class ezcGraphChartElementDateAxis extends ezcGraphChartElementAxis
|
|
{
|
|
|
|
const MONTH = 2629800;
|
|
|
|
const YEAR = 31536000;
|
|
|
|
const DECADE = 315360000;
|
|
|
|
/**
|
|
* Minimum inserted date
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $minValue = false;
|
|
|
|
/**
|
|
* Maximum inserted date
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $maxValue = 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
|
|
self::MONTH => 'M y',
|
|
// Year
|
|
self::YEAR => 'Y',
|
|
// Decade
|
|
self::DECADE => 'Y',
|
|
);
|
|
|
|
/**
|
|
* Constant used for calculation of automatic definition of major scaling
|
|
* steps
|
|
*/
|
|
const MAJOR_COUNT = 10;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param array $options Default option array
|
|
* @return void
|
|
* @ignore
|
|
*/
|
|
public function __construct( array $options = array() )
|
|
{
|
|
$this->properties['startDate'] = false;
|
|
$this->properties['endDate'] = false;
|
|
$this->properties['interval'] = false;
|
|
$this->properties['dateFormat'] = false;
|
|
|
|
parent::__construct( $options );
|
|
}
|
|
|
|
/**
|
|
* __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
|
|
* @ignore
|
|
*/
|
|
public function __set( $propertyName, $propertyValue )
|
|
{
|
|
switch ( $propertyName )
|
|
{
|
|
case 'startDate':
|
|
$this->properties['startDate'] = (int) $propertyValue;
|
|
break;
|
|
case 'endDate':
|
|
$this->properties['endDate'] = (int) $propertyValue;
|
|
break;
|
|
case 'interval':
|
|
$this->properties['interval'] = (int) $propertyValue;
|
|
$this->properties['initialized'] = true;
|
|
break;
|
|
case 'dateFormat':
|
|
$this->properties['dateFormat'] = (string) $propertyValue;
|
|
break;
|
|
default:
|
|
parent::__set( $propertyName, $propertyValue );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensure proper timestamp
|
|
*
|
|
* Takes a mixed value from datasets, like timestamps, or strings
|
|
* describing some time and converts it to a timestamp.
|
|
*
|
|
* @param mixed $value
|
|
* @return int
|
|
*/
|
|
protected static function ensureTimestamp( $value )
|
|
{
|
|
if ( is_numeric( $value ) )
|
|
{
|
|
$timestamp = (int) $value;
|
|
}
|
|
elseif ( ( $timestamp = strtotime( $value ) ) === false )
|
|
{
|
|
throw new ezcGraphErrorParsingDateException( $value );
|
|
}
|
|
|
|
return $timestamp;
|
|
}
|
|
|
|
/**
|
|
* Add data for this axis
|
|
*
|
|
* @param array $values Value which will be displayed on this axis
|
|
* @return void
|
|
*/
|
|
public function addData( array $values )
|
|
{
|
|
foreach ( $values as $nr => $value )
|
|
{
|
|
$value = self::ensureTimestamp( $value );
|
|
|
|
if ( $this->minValue === false ||
|
|
$value < $this->minValue )
|
|
{
|
|
$this->minValue = $value;
|
|
}
|
|
|
|
if ( $this->maxValue === false ||
|
|
$value > $this->maxValue )
|
|
{
|
|
$this->maxValue = $value;
|
|
}
|
|
}
|
|
|
|
$this->properties['initialized'] = true;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
}
|
|
|
|
if ( ( $this->properties['startDate'] !== false ) &&
|
|
( $this->properties['endDate'] !== false ) )
|
|
{
|
|
// Use interval between defined borders
|
|
if ( ( $diff % $interval ) > 0 )
|
|
{
|
|
// Stil use predefined date format from old interval if not set
|
|
if ( $this->properties['dateFormat'] === false )
|
|
{
|
|
$this->properties['dateFormat'] = $this->predefinedIntervals[$interval];
|
|
}
|
|
|
|
$count = ceil( $diff / $interval );
|
|
$interval = round( $diff / $count, 0 );
|
|
}
|
|
}
|
|
|
|
$this->properties['interval'] = $interval;
|
|
}
|
|
|
|
/**
|
|
* Calculate lower nice date
|
|
*
|
|
* Calculates a date which is earlier or equal to the given date, and is
|
|
* divisible by the given interval.
|
|
*
|
|
* @param int $min Date
|
|
* @param int $interval Interval
|
|
* @return int Earlier date
|
|
*/
|
|
protected function calculateLowerNiceDate( $min, $interval )
|
|
{
|
|
switch ( $interval )
|
|
{
|
|
case self::MONTH:
|
|
// Special handling for months - not covered by the default
|
|
// algorithm
|
|
return mktime(
|
|
1,
|
|
0,
|
|
0,
|
|
(int) date( 'm', $min ),
|
|
1,
|
|
(int) date( 'Y', $min )
|
|
);
|
|
default:
|
|
$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]
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate start date
|
|
*
|
|
* Use calculateLowerNiceDate to get a date earlier or equal date then the
|
|
* minimum date to use it as the start date for the axis depending on the
|
|
* selected interval.
|
|
*
|
|
* @param mixed $min Minimum date
|
|
* @param mixed $max Maximum date
|
|
* @return void
|
|
*/
|
|
public function calculateMinimum( $min, $max )
|
|
{
|
|
if ( $this->properties['endDate'] === false )
|
|
{
|
|
$this->properties['startDate'] = $this->calculateLowerNiceDate( $min, $this->interval );
|
|
}
|
|
else
|
|
{
|
|
$this->properties['startDate'] = $this->properties['endDate'];
|
|
|
|
while ( $this->properties['startDate'] > $min )
|
|
{
|
|
switch ( $this->interval )
|
|
{
|
|
case self::MONTH:
|
|
$this->properties['startDate'] = strtotime( '-1 month', $this->properties['startDate'] );
|
|
break;
|
|
case self::YEAR:
|
|
$this->properties['startDate'] = strtotime( '-1 year', $this->properties['startDate'] );
|
|
break;
|
|
case self::DECADE:
|
|
$this->properties['startDate'] = strtotime( '-10 years', $this->properties['startDate'] );
|
|
break;
|
|
default:
|
|
$this->properties['startDate'] -= $this->interval;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate end date
|
|
*
|
|
* Use calculateLowerNiceDate to get a date later or equal date then the
|
|
* maximum date to use it as the end date for the axis depending on the
|
|
* selected interval.
|
|
*
|
|
* @param mixed $min Minimum date
|
|
* @param mixed $max Maximum date
|
|
* @return void
|
|
*/
|
|
public function calculateMaximum( $min, $max )
|
|
{
|
|
$this->properties['endDate'] = $this->properties['startDate'];
|
|
|
|
while ( $this->properties['endDate'] < $max )
|
|
{
|
|
switch ( $this->interval )
|
|
{
|
|
case self::MONTH:
|
|
$this->properties['endDate'] = strtotime( '+1 month', $this->properties['endDate'] );
|
|
break;
|
|
case self::YEAR:
|
|
$this->properties['endDate'] = strtotime( '+1 year', $this->properties['endDate'] );
|
|
break;
|
|
case self::DECADE:
|
|
$this->properties['endDate'] = strtotime( '+10 years', $this->properties['endDate'] );
|
|
break;
|
|
default:
|
|
$this->properties['endDate'] += $this->interval;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate axis bounding values on base of the assigned values
|
|
*
|
|
* @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->properties['startDate'] !== false )
|
|
{
|
|
$this->minValue = $this->properties['startDate'];
|
|
}
|
|
|
|
if ( $this->properties['endDate'] !== false )
|
|
{
|
|
$this->maxValue = $this->properties['endDate'];
|
|
}
|
|
|
|
// Calculate "nice" values for scaling parameters
|
|
if ( $this->properties['interval'] === false )
|
|
{
|
|
$this->calculateInterval( $this->minValue, $this->maxValue );
|
|
}
|
|
|
|
if ( $this->properties['dateFormat'] === false && isset( $this->predefinedIntervals[$this->interval] ) )
|
|
{
|
|
$this->properties['dateFormat'] = $this->predefinedIntervals[$this->interval];
|
|
}
|
|
|
|
if ( $this->properties['startDate'] === false )
|
|
{
|
|
$this->calculateMinimum( $this->minValue, $this->maxValue );
|
|
}
|
|
|
|
if ( $this->properties['endDate'] === false )
|
|
{
|
|
$this->calculateMaximum( $this->minValue, $this->maxValue );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get coordinate for a dedicated value on the chart
|
|
*
|
|
* @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
|
|
$intValue = ( $value === false ? false : self::ensureTimestamp( $value ) );
|
|
|
|
if ( ( $value === false ) &&
|
|
( ( $intValue < $this->startDate ) || ( $intValue > $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 ( $intValue - $this->startDate ) / ( $this->endDate - $this->startDate );
|
|
case ezcGraph::RIGHT:
|
|
case ezcGraph::BOTTOM:
|
|
return 1 - ( $intValue - $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) ceil( ( $this->properties['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 $this->getLabelFromTimestamp( $this->startDate + ( $step * $this->interval ), $step );
|
|
}
|
|
|
|
/**
|
|
* Get label for timestamp
|
|
*
|
|
* @param int $time
|
|
* @param int $step
|
|
* @return string
|
|
*/
|
|
protected function getLabelFromTimestamp( $time, $step )
|
|
{
|
|
if ( $this->properties['labelCallback'] !== null )
|
|
{
|
|
return call_user_func_array(
|
|
$this->properties['labelCallback'],
|
|
array(
|
|
date( $this->properties['dateFormat'], $time ),
|
|
$step,
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return date( $this->properties['dateFormat'], $time );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return array of steps on this axis
|
|
*
|
|
* @return array( ezcGraphAxisStep )
|
|
*/
|
|
public function getSteps()
|
|
{
|
|
$steps = array();
|
|
|
|
$start = $this->properties['startDate'];
|
|
$end = $this->properties['endDate'];
|
|
$distance = $end - $start;
|
|
|
|
$step = 0;
|
|
for ( $time = $start; $time <= $end; )
|
|
{
|
|
$steps[] = new ezcGraphAxisStep(
|
|
( $time - $start ) / $distance,
|
|
$this->interval / $distance,
|
|
$this->getLabelFromTimestamp( $time, $step++ ),
|
|
array(),
|
|
$step === 1,
|
|
$time >= $end
|
|
);
|
|
|
|
switch ( $this->interval )
|
|
{
|
|
case self::MONTH:
|
|
$time = strtotime( '+1 month', $time );
|
|
break;
|
|
case self::YEAR:
|
|
$time = strtotime( '+1 year', $time );
|
|
break;
|
|
case self::DECADE:
|
|
$time = strtotime( '+10 years', $time );
|
|
break;
|
|
default:
|
|
$time += $this->interval;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $steps;
|
|
}
|
|
|
|
/**
|
|
* 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 );
|
|
}
|
|
}
|
|
|
|
?>
|