blob: cd3f0d49e6f7b486482cb36555b6d1a28a4a8235 [file] [log] [blame]
<?php
/**
* File containing the abstract ezcGraphChartElementAxis class
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @package Graph
* @version //autogentag//
* @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
*/
/**
* Class to represent an axis as a chart element
*
* Chart elements can be understood as widgets or layout container inside the
* chart. The actual transformation to images happens inside the renderers.
* They represent all elements inside the chart and contain mostly general
* formatting options, while the renderer itself might define additional
* formatting options for some chart elments. You can find more about the
* general formatting options for chart elements in the base class
* ezcGraphChartElement.
*
* The axis elements are special elements, as the border and background
* settings do not apply directly as axis axis are not put inside any boxes.
* The border value is reused for the color of the axis itself.
*
* Generally you should select the axis which matches your data best. By
* default a labeled x axis and a numeric y axis are used. If you are using
* date or time values on either axis, you should for example use a
* ezcGraphChartElementDateAxis. The currently available axis types are:
*
* - ezcGraphChartElementDateAxis
* - ezcGraphChartElementLabeledAxis
* - ezcGraphChartElementLogarithmicalAxis
* - ezcGraphChartElementNumericAxis
*
* Beside this there are several option to define the general layout of the
* axis labels. The $formatString option may be used to add additional text to
* each label on the axis, like a percent sign on the y axis:
*
* <code>
* $chart->xAxis->formatString = '%s %%';
* </code>
*
* For more complex formatting operations for the label you may assign a custom
* formatter function to the property $labelCallback.
*
* The orientation of labels and their position relatively to the axis ticks is
* calcualted and rendered by the ezcGraphAxisLabelRenderer classes. You can
* choose between different axis label renderer, or create you own, and assign
* an instance of one to the property $axisLabelRenderer. Currently the
* available axis label renderers are:
*
* - ezcGraphAxisBoxedLabelRenderer
*
* Renders grid and labels like commonly used in bar charts, with the label
* between two grid lines.
*
* - ezcGraphAxisCenteredLabelRenderer
*
* Centers the label right next to a tick. Commonly used for labeled axis.
*
* - ezcGraphAxisExactLabelRenderer
*
* Put the label next to each tick. Commonly used for numeric axis.
*
* - ezcGraphAxisNoLabelRenderer
*
* Renders no labels.
*
* - ezcGraphAxisRadarLabelRenderer
*
* Special label renderer for radar charts.
*
* - ezcGraphAxisRotatedLabelRenderer
*
* Accepts a rotation angle for the texts put at some axis, which might be
* useful for longer textual labels on the axis.
*
* The label renderer used by default is different depending on the axis type.
*
* @property float $nullPosition
* The position of the null value.
* @property float $axisSpace
* Percent of the chart space used to display axis labels and
* arrowheads instead of data values.
* @property float $outerAxisSpace
* Percent of the chart space used to display axis arrow at the outer
* side of the axis. If set to null, the axisSpace will be used here.
* @property ezcGraphColor $majorGrid
* Color of major majorGrid.
* @property ezcGraphColor $minorGrid
* Color of minor majorGrid.
* @property mixed $majorStep
* Labeled major steps displayed on the axis. @TODO: Should be moved
* to numeric axis.
* @property mixed $minorStep
* Non labeled minor steps on the axis. @TODO: Should be moved to
* numeric axis.
* @property string $formatString
* Formatstring to use for labeling of the axis.
* @property string $label
* Axis label
* @property int $labelSize
* Size of axis label
* @property int $labelMargin
* Distance between label an axis
* @property int $minArrowHeadSize
* Minimum Size used to draw arrow heads.
* @property int $maxArrowHeadSize
* Maximum Size used to draw arrow heads.
* @property ezcGraphAxisLabelRenderer $axisLabelRenderer
* AxisLabelRenderer used to render labels and grid on this axis.
* @property callback $labelCallback
* Callback function to format chart labels.
* Function will receive two parameters and should return a
* reformatted label.
* string function( label, step )
* @property float $chartPosition
* Position of the axis in the chart. Only useful for additional
* axis. The basic chart axis will be automatically positioned.
* @property-read bool $initialized
* Property indicating if some values were associated with axis, or a
* scaling has been set manually.
* @property float $labelRotation
* Rotation of the axis label in degree
*
* @version //autogentag//
* @package Graph
*/
abstract class ezcGraphChartElementAxis extends ezcGraphChartElement
{
/**
* Axis label renderer class
*
* @var ezcGraphAxisLabelRenderer
*/
protected $axisLabelRenderer;
/**
* Optionally set inner boundings. May be null depending on the used chart
* implementation.
*
* @var ezcGraphBoundings
*/
protected $innerBoundings;
/**
* Constructor
*
* @param array $options Default option array
* @return void
* @ignore
*/
public function __construct( array $options = array() )
{
$this->properties['nullPosition'] = false;
$this->properties['axisSpace'] = .1;
$this->properties['outerAxisSpace'] = null;
$this->properties['majorGrid'] = false;
$this->properties['minorGrid'] = false;
$this->properties['majorStep'] = null;
$this->properties['minorStep'] = null;
$this->properties['formatString'] = '%s';
$this->properties['label'] = false;
$this->properties['labelSize'] = 14;
$this->properties['labelMargin'] = 2;
$this->properties['minArrowHeadSize'] = 4;
$this->properties['maxArrowHeadSize'] = 8;
$this->properties['labelCallback'] = null;
$this->properties['chartPosition'] = null;
$this->properties['initialized'] = false;
$this->properties['labelRotation'] = 0.;
parent::__construct( $options );
if ( !isset( $this->axisLabelRenderer ) )
{
$this->axisLabelRenderer = new ezcGraphAxisExactLabelRenderer();
}
}
/**
* Set colors and border fro this element
*
* @param ezcGraphPalette $palette Palette
* @return void
*/
public function setFromPalette( ezcGraphPalette $palette )
{
$this->border = $palette->axisColor;
$this->padding = $palette->padding;
$this->margin = $palette->margin;
$this->majorGrid = $palette->majorGridColor;
$this->minorGrid = $palette->minorGridColor;
}
/**
* __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 'nullPosition':
$this->properties['nullPosition'] = (float) $propertyValue;
break;
case 'axisSpace':
if ( !is_numeric( $propertyValue ) ||
( $propertyValue < 0 ) ||
( $propertyValue >= 1 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float < 1' );
}
$this->properties['axisSpace'] = (float) $propertyValue;
break;
case 'outerAxisSpace':
if ( !is_null( $propertyValue ) &&
( !is_numeric( $propertyValue ) ||
( $propertyValue < 0 ) ||
( $propertyValue >= 1 ) ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'null, or 0 <= float < 1' );
}
$this->properties['outerAxisSpace'] = $propertyValue;
break;
case 'majorGrid':
$this->properties['majorGrid'] = ezcGraphColor::create( $propertyValue );
break;
case 'minorGrid':
$this->properties['minorGrid'] = ezcGraphColor::create( $propertyValue );
break;
case 'majorStep':
if ( !is_numeric( $propertyValue ) ||
( $propertyValue <= 0 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' );
}
$this->properties['majorStep'] = (float) $propertyValue;
break;
case 'minorStep':
if ( !is_numeric( $propertyValue ) ||
( $propertyValue <= 0 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' );
}
$this->properties['minorStep'] = (float) $propertyValue;
break;
case 'formatString':
$this->properties['formatString'] = (string) $propertyValue;
break;
case 'label':
$this->properties['label'] = (string) $propertyValue;
break;
case 'labelSize':
if ( !is_numeric( $propertyValue ) ||
( $propertyValue <= 6 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 6' );
}
$this->properties['labelSize'] = (int) $propertyValue;
break;
case 'labelMargin':
if ( !is_numeric( $propertyValue ) ||
( $propertyValue <= 0 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
}
$this->properties['labelMargin'] = (int) $propertyValue;
break;
case 'maxArrowHeadSize':
if ( !is_numeric( $propertyValue ) ||
( $propertyValue <= 0 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
}
$this->properties['maxArrowHeadSize'] = (int) $propertyValue;
break;
case 'minArrowHeadSize':
if ( !is_numeric( $propertyValue ) ||
( $propertyValue <= 0 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
}
$this->properties['minArrowHeadSize'] = (int) $propertyValue;
break;
case 'axisLabelRenderer':
if ( $propertyValue instanceof ezcGraphAxisLabelRenderer )
{
$this->axisLabelRenderer = $propertyValue;
}
else
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphAxisLabelRenderer' );
}
break;
case 'labelCallback':
if ( is_callable( $propertyValue ) )
{
$this->properties['labelCallback'] = $propertyValue;
}
else
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'callback function' );
}
break;
case 'chartPosition':
if ( !is_numeric( $propertyValue ) ||
( $propertyValue < 0 ) ||
( $propertyValue > 1 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
}
$this->properties['chartPosition'] = (float) $propertyValue;
break;
case 'labelRotation':
if ( !is_numeric( $propertyValue ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' );
}
$this->properties['labelRotation'] = fmod( (float) $propertyValue, 360. );
break;
default:
parent::__set( $propertyName, $propertyValue );
break;
}
}
/**
* __get
*
* @param mixed $propertyName
* @throws ezcBasePropertyNotFoundException
* If a the value for the property options is not an instance of
* @return mixed
* @ignore
*/
public function __get( $propertyName )
{
switch ( $propertyName )
{
case 'axisLabelRenderer':
return $this->axisLabelRenderer;
default:
return parent::__get( $propertyName );
}
}
/**
* Get coordinate for a dedicated value on the chart
*
* @param float $value Value to determine position for
* @return float Position on chart
*/
abstract public function getCoordinate( $value );
/**
* Return count of minor steps
*
* @return integer Count of minor steps
*/
abstract public function getMinorStepCount();
/**
* Return count of major steps
*
* @return integer Count of major steps
*/
abstract public function getMajorStepCount();
/**
* Get label for a dedicated step on the axis
*
* @param integer $step Number of step
* @return string label
*/
abstract public function getLabel( $step );
/**
* Return array of steps on this axis
*
* @return array( ezcGraphAxisStep )
*/
public function getSteps()
{
$majorSteps = $this->getMajorStepCount();
$minorStepsPerMajorStepCount = ( $this->getMinorStepCount() / $majorSteps );
$majorStepSize = 1 / $majorSteps;
$minorStepSize = ( $minorStepsPerMajorStepCount > 0 ? $majorStepSize / $minorStepsPerMajorStepCount : 0 );
$steps = array();
for ( $major = 0; $major <= $majorSteps; ++$major )
{
$majorStep = new ezcGraphAxisStep(
$majorStepSize * $major,
$majorStepSize,
$this->getLabel( $major ),
array(),
$this->isZeroStep( $major ),
( $major === $majorSteps )
);
if ( ( $minorStepsPerMajorStepCount > 0 ) &&
( $major < $majorSteps ) )
{
// Do not add minor steps at major steps positions
for( $minor = 1; $minor < $minorStepsPerMajorStepCount; ++$minor )
{
$majorStep->childs[] = new ezcGraphAxisStep(
( $majorStepSize * $major ) + ( $minorStepSize * $minor ),
$minorStepSize
);
}
}
$steps[] = $majorStep;
}
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
*/
abstract public function isZeroStep( $step );
/**
* Add data for this axis
*
* @param array $values
* @return void
*/
abstract public function addData( array $values );
/**
* Calculate axis bounding values on base of the assigned values
*
* @abstract
* @access public
* @return void
*/
abstract public function calculateAxisBoundings();
/**
* Render the axis
*
* @param ezcGraphRenderer $renderer Renderer
* @param ezcGraphBoundings $boundings Boundings for the axis
* @return ezcGraphBoundings Remaining boundings
*/
public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphBoundings $innerBoundings = null )
{
$this->innerBoundings = $innerBoundings;
$startSpace = $this->axisSpace;
$endSpace = $this->outerAxisSpace === null ? $this->axisSpace : $this->outerAxisSpace;
switch ( $this->position )
{
case ezcGraph::TOP:
$start = new ezcGraphCoordinate(
$boundings->width * $startSpace +
$this->nullPosition * $boundings->width * ( 1 - ( $startSpace + $endSpace ) ),
0
);
$end = new ezcGraphCoordinate(
$boundings->width * $startSpace +
$this->nullPosition * $boundings->width * ( 1 - ( $startSpace + $endSpace ) ),
$boundings->height
);
break;
case ezcGraph::BOTTOM:
$start = new ezcGraphCoordinate(
$boundings->width * $startSpace +
$this->nullPosition * $boundings->width * ( 1 - ( $startSpace + $endSpace ) ),
$boundings->height
);
$end = new ezcGraphCoordinate(
$boundings->width * $startSpace +
$this->nullPosition * $boundings->width * ( 1 - ( $startSpace + $endSpace ) ),
0
);
break;
case ezcGraph::LEFT:
$start = new ezcGraphCoordinate(
0,
$boundings->height * $endSpace +
$this->nullPosition * $boundings->height * ( 1 - ( $startSpace + $endSpace ) )
);
$end = new ezcGraphCoordinate(
$boundings->width,
$boundings->height * $endSpace +
$this->nullPosition * $boundings->height * ( 1 - ( $startSpace + $endSpace ) )
);
break;
case ezcGraph::RIGHT:
$start = new ezcGraphCoordinate(
$boundings->width,
$boundings->height * $endSpace +
$this->nullPosition * $boundings->height * ( 1 - ( $startSpace + $endSpace ) )
);
$end = new ezcGraphCoordinate(
0,
$boundings->height * $endSpace +
$this->nullPosition * $boundings->height * ( 1 - ( $startSpace + $endSpace ) )
);
break;
}
$renderer->drawAxis(
$boundings,
$start,
$end,
$this,
$this->axisLabelRenderer,
$innerBoundings
);
return $boundings;
}
}
?>