fae6352bf2
Removed PHP_Compat, and references to it. Removed ionCube/Zend/mmCache compatibility checks in test.php script. Changed minimum PHP requirement to 5.0 in test.php script.
623 lines
22 KiB
PHP
623 lines
22 KiB
PHP
<?php
|
|
|
|
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
|
|
|
/**
|
|
* Image_Graph - PEAR PHP OO Graph Rendering Utility.
|
|
*
|
|
* PHP versions 4 and 5
|
|
*
|
|
* LICENSE: This library is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or (at your
|
|
* option) any later version. This library is distributed in the hope that it
|
|
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
|
|
* General Public License for more details. You should have received a copy of
|
|
* the GNU Lesser General Public License along with this library; if not, write
|
|
* to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
* 02111-1307 USA
|
|
*
|
|
* @category Images
|
|
* @package Image_Graph
|
|
* @subpackage Plot
|
|
* @author Jesper Veggerby <pear.nosey@veggerby.dk>
|
|
* @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
|
|
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
|
* @version CVS: $Id: Pie.php,v 1.19 2005/11/27 22:21:16 nosey Exp $
|
|
* @link http://pear.php.net/package/Image_Graph
|
|
*/
|
|
|
|
/**
|
|
* Include file Image/Graph/Plot.php
|
|
*/
|
|
require_once 'Image/Graph/Plot.php';
|
|
|
|
/**
|
|
* 2D Piechart.
|
|
*
|
|
* @category Images
|
|
* @package Image_Graph
|
|
* @subpackage Plot
|
|
* @author Jesper Veggerby <pear.nosey@veggerby.dk>
|
|
* @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
|
|
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
|
* @version Release: @package_version@
|
|
* @link http://pear.php.net/package/Image_Graph
|
|
*/
|
|
class Image_Graph_Plot_Pie extends Image_Graph_Plot
|
|
{
|
|
|
|
/**
|
|
* The radius of the 'pie' spacing
|
|
* @access private
|
|
* @var int
|
|
*/
|
|
var $_radius = 3;
|
|
|
|
/**
|
|
* Explode pie slices.
|
|
* @access private
|
|
* @var mixed
|
|
*/
|
|
var $_explode = false;
|
|
|
|
/**
|
|
* The starting angle of the plot
|
|
* @access private
|
|
* @var int
|
|
*/
|
|
var $_startingAngle = 0;
|
|
|
|
/**
|
|
* The angle direction (1 = CCW, -1 = CW)
|
|
* @access private
|
|
* @var int
|
|
*/
|
|
var $_angleDirection = 1;
|
|
|
|
/**
|
|
* The diameter of the pie plot
|
|
* @access private
|
|
* @var int
|
|
*/
|
|
var $_diameter = false;
|
|
|
|
/**
|
|
* Group items below this limit together as "the rest"
|
|
* @access private
|
|
* @var double
|
|
*/
|
|
var $_restGroupLimit = false;
|
|
|
|
/**
|
|
* Rest group title
|
|
* @access private
|
|
* @var string
|
|
*/
|
|
var $_restGroupTitle = 'The rest';
|
|
|
|
/**
|
|
* Perform the actual drawing on the legend.
|
|
*
|
|
* @param int $x0 The top-left x-coordinate
|
|
* @param int $y0 The top-left y-coordinate
|
|
* @param int $x1 The bottom-right x-coordinate
|
|
* @param int $y1 The bottom-right y-coordinate
|
|
* @access private
|
|
*/
|
|
function _drawLegendSample($x0, $y0, $x1, $y1)
|
|
{
|
|
$y = ($y0 + $y1) / 2;
|
|
$this->_canvas->pieslice(
|
|
array(
|
|
'x' => $x1,
|
|
'y' => $y,
|
|
'rx' => abs($x1 - $x0) / 2,
|
|
'ry' => abs($y1 - $y0) / 2,
|
|
'v1' => 45,
|
|
'v2' => 315
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Calculate marker point data
|
|
*
|
|
* @param array $point The point to calculate data for
|
|
* @param array $nextPoint The next point
|
|
* @param array $prevPoint The previous point
|
|
* @param array $totals The pre-calculated totals, if needed
|
|
* @return array An array containing marker point data
|
|
* @access private
|
|
*/
|
|
function _getMarkerData($point, $nextPoint, $prevPoint, &$totals)
|
|
{
|
|
$point = parent::_getMarkerData($point, $nextPoint, $prevPoint, $totals);
|
|
|
|
$y = $totals['CURRENT_Y'];
|
|
|
|
if ($this->_angleDirection < 0) {
|
|
$y = $totals['ALL_SUM_Y'] - $y;
|
|
}
|
|
|
|
$point['ANGLE'] = 360 * (($y + ($point['Y'] / 2)) / $totals['ALL_SUM_Y']) + $this->_startingAngle;
|
|
$totals['CURRENT_Y'] += $point['Y'];
|
|
|
|
$point['ANG_X'] = cos(deg2rad($point['ANGLE']));
|
|
$point['ANG_Y'] = sin(deg2rad($point['ANGLE']));
|
|
|
|
$point['AX'] = -10 * $point['ANG_X'];
|
|
$point['AY'] = -10 * $point['ANG_Y'];
|
|
|
|
if ((isset($totals['ALL_SUM_Y'])) && ($totals['ALL_SUM_Y'] != 0)) {
|
|
$point['PCT_MIN_Y'] = $point['PCT_MAX_Y'] = (100 * $point['Y'] / $totals['ALL_SUM_Y']);
|
|
}
|
|
|
|
$point['LENGTH'] = 10; //$radius;
|
|
|
|
$x = $point['X'];
|
|
$explodeRadius = 0;
|
|
if ((is_array($this->_explode)) && (isset($this->_explode[$x]))) {
|
|
$explodeRadius = $this->_explode[$x];
|
|
} elseif (is_numeric($this->_explode)) {
|
|
$explodeRadius = $this->_explode;
|
|
}
|
|
|
|
$point['MARKER_X'] = $totals['CENTER_X'] +
|
|
($totals['RADIUS'] + $explodeRadius) * $point['ANG_X'];
|
|
$point['MARKER_Y'] = $totals['CENTER_Y'] +
|
|
($totals['RADIUS'] + $explodeRadius) * $point['ANG_Y'];
|
|
|
|
return $point;
|
|
}
|
|
|
|
/**
|
|
* Draws markers on the canvas
|
|
*
|
|
* @access private
|
|
*/
|
|
function _drawMarker()
|
|
{
|
|
|
|
if ($this->_marker) {
|
|
$totals = $this->_getTotals();
|
|
|
|
$totals['CENTER_X'] = (int) (($this->_left + $this->_right) / 2);
|
|
$totals['CENTER_Y'] = (int) (($this->_top + $this->_bottom) / 2);
|
|
|
|
$totals['CURRENT_Y'] = 0;
|
|
$number = 0;
|
|
|
|
$diameter = $this->_getDiameter();
|
|
|
|
$keys = array_keys($this->_dataset);
|
|
foreach ($keys as $key) {
|
|
$dataset =& $this->_dataset[$key];
|
|
|
|
if (count($this->_dataset) == 1) {
|
|
$totals['RADIUS0'] = false;
|
|
$totals['RADIUS'] = $diameter / 2;
|
|
} else {
|
|
$dr = $diameter / (2 * count($this->_dataset));
|
|
|
|
$totals['RADIUS0'] = $number * $dr + ($number > 0 ? $this->_radius : 0);
|
|
$totals['RADIUS'] = ($number + 1) * $dr;
|
|
}
|
|
|
|
$totals['ALL_SUM_Y'] = 0;
|
|
$totals['CURRENT_Y'] = 0;
|
|
$dataset->_reset();
|
|
while ($point = $dataset->_next()) {
|
|
$totals['ALL_SUM_Y'] += $point['Y'];
|
|
}
|
|
|
|
$dataset->_reset();
|
|
$currentY = 0;
|
|
$the_rest = 0;
|
|
while ($point = $dataset->_next()) {
|
|
if (($this->_restGroupLimit !== false) && ($point['Y'] <= $this->_restGroupLimit)) {
|
|
$the_rest += $point['Y'];
|
|
}
|
|
else {
|
|
if ((!is_object($this->_dataSelector)) ||
|
|
($this->_dataSelector->select($point))
|
|
) {
|
|
$point = $this->_getMarkerData(
|
|
$point,
|
|
false,
|
|
false,
|
|
$totals
|
|
);
|
|
if (is_array($point)) {
|
|
$this->_marker->_drawMarker(
|
|
$point['MARKER_X'],
|
|
$point['MARKER_Y'],
|
|
$point
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ($the_rest > 0) {
|
|
$point = array('X' => $this->_restGroupTitle, 'Y' => $the_rest);
|
|
$point = $this->_getMarkerData(
|
|
$point,
|
|
false,
|
|
false,
|
|
$totals
|
|
);
|
|
if (is_array($point)) {
|
|
$this->_marker->_drawMarker(
|
|
$point['MARKER_X'],
|
|
$point['MARKER_Y'],
|
|
$point
|
|
);
|
|
}
|
|
}
|
|
$number++;
|
|
}
|
|
unset($keys);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Explodes a piece of this pie chart
|
|
*
|
|
* @param int $explode Radius to explode with (or an array)
|
|
* @param string $x The 'x' value to explode or omitted
|
|
*/
|
|
function explode($explode, $x = false)
|
|
{
|
|
if ($x === false) {
|
|
$this->_explode = $explode;
|
|
} else {
|
|
$this->_explode[$x] = $explode;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the starting angle of the plot
|
|
*
|
|
* East is 0 degrees
|
|
* South is 90 degrees
|
|
* West is 180 degrees
|
|
* North is 270 degrees
|
|
*
|
|
* It is also possible to specify the direction of the plot angles (i.e. clockwise 'cw' or
|
|
* counterclockwise 'ccw')
|
|
*/
|
|
function setStartingAngle($angle = 0, $direction = 'ccw')
|
|
{
|
|
$this->_startingAngle = ($angle % 360);
|
|
$this->_angleDirection = ($direction == 'ccw' ? 1 : -1);
|
|
}
|
|
|
|
/**
|
|
* Set the diameter of the pie plot (i.e. the number of pixels the
|
|
* pie plot should be across)
|
|
*
|
|
* Use 'max' for the maximum possible diameter
|
|
*
|
|
* Use negative values for the maximum possible - minus this value (fx -2
|
|
* to leave 1 pixel at each side)
|
|
*
|
|
* @param mixed @diameter The number of pixels
|
|
*/
|
|
function setDiameter($diameter)
|
|
{
|
|
$this->_diameter = $diameter;
|
|
}
|
|
|
|
/**
|
|
* Set the limit for the y-value, where values below are grouped together
|
|
* as "the rest"
|
|
*
|
|
* @param double $limit The limit
|
|
* @param string $title The title to display in the legends (default 'The
|
|
* rest')
|
|
*/
|
|
function setRestGroup($limit, $title = 'The rest')
|
|
{
|
|
$this->_restGroupLimit = $limit;
|
|
$this->_restGroupTitle = $title;
|
|
}
|
|
|
|
/**
|
|
* Get the diameter of the plot
|
|
* @return int The number of pixels the diameter is
|
|
* @access private
|
|
*/
|
|
function _getDiameter()
|
|
{
|
|
$diameter = 0;
|
|
if ($this->_diameter === false) {
|
|
$diameter = min($this->height(), $this->width()) * 0.75;
|
|
}
|
|
else {
|
|
if ($this->_diameter === 'max') {
|
|
$diameter = min($this->height(), $this->width());
|
|
}
|
|
elseif ($this->_diameter < 0) {
|
|
$diameter = min($this->height(), $this->width()) + $this->_diameter;
|
|
} else {
|
|
$diameter = $this->_diameter;
|
|
}
|
|
}
|
|
return $diameter;
|
|
}
|
|
|
|
|
|
/**
|
|
* Output the plot
|
|
*
|
|
* @return bool Was the output 'good' (true) or 'bad' (false).
|
|
* @access private
|
|
*/
|
|
function _done()
|
|
{
|
|
if (parent::_done() === false) {
|
|
return false;
|
|
}
|
|
|
|
$number = 0;
|
|
|
|
$keys = array_keys($this->_dataset);
|
|
foreach ($keys as $key) {
|
|
$dataset =& $this->_dataset[$key];
|
|
|
|
$totalY = 0;
|
|
$dataset->_reset();
|
|
while ($point = $dataset->_next()) {
|
|
$totalY += $point['Y'];
|
|
}
|
|
|
|
$centerX = (int) (($this->_left + $this->_right) / 2);
|
|
$centerY = (int) (($this->_top + $this->_bottom) / 2);
|
|
$diameter = $this->_getDiameter();
|
|
if ($this->_angleDirection < 0) {
|
|
$currentY = $totalY;
|
|
} else {
|
|
$currentY = 0; //rand(0, 100)*$totalY/100;
|
|
}
|
|
$dataset->_reset();
|
|
|
|
if (count($this->_dataset) == 1) {
|
|
$radius0 = false;
|
|
$radius1 = $diameter / 2;
|
|
} else {
|
|
$dr = $diameter / (2 * count($this->_dataset));
|
|
|
|
$radius0 = $number * $dr + ($number > 0 ? $this->_radius : 0);
|
|
$radius1 = ($number + 1) * $dr;
|
|
}
|
|
|
|
$the_rest = 0;
|
|
while ($point = $dataset->_next()) {
|
|
if (($this->_restGroupLimit !== false) && ($point['Y'] <= $this->_restGroupLimit)) {
|
|
$the_rest += $point['Y'];
|
|
}
|
|
else {
|
|
$angle1 = 360 * ($currentY / $totalY) + $this->_startingAngle;
|
|
$currentY += $this->_angleDirection * $point['Y'];
|
|
$angle2 = 360 * ($currentY / $totalY) + $this->_startingAngle;
|
|
|
|
$x = $point['X'];
|
|
$id = $point['ID'];
|
|
|
|
$dX = 0;
|
|
$dY = 0;
|
|
$explodeRadius = 0;
|
|
if ((is_array($this->_explode)) && (isset($this->_explode[$x]))) {
|
|
$explodeRadius = $this->_explode[$x];
|
|
} elseif (is_numeric($this->_explode)) {
|
|
$explodeRadius = $this->_explode;
|
|
}
|
|
|
|
if ($explodeRadius > 0) {
|
|
$dX = $explodeRadius * cos(deg2rad(($angle1 + $angle2) / 2));
|
|
$dY = $explodeRadius * sin(deg2rad(($angle1 + $angle2) / 2));
|
|
}
|
|
|
|
$ID = $point['ID'];
|
|
$this->_getFillStyle($ID);
|
|
$this->_getLineStyle($ID);
|
|
$this->_canvas->pieslice(
|
|
$this->_mergeData(
|
|
$point,
|
|
array(
|
|
'x' => $centerX + $dX,
|
|
'y' => $centerY + $dY,
|
|
'rx' => $radius1,
|
|
'ry' => $radius1,
|
|
'v1' => $angle1,
|
|
'v2' => $angle2,
|
|
'srx' => $radius0,
|
|
'sry' => $radius0
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
if ($the_rest > 0) {
|
|
$angle1 = 360 * ($currentY / $totalY) + $this->_startingAngle;
|
|
$currentY += $this->_angleDirection * $the_rest;
|
|
$angle2 = 360 * ($currentY / $totalY) + $this->_startingAngle;
|
|
|
|
$x = 'rest';
|
|
$id = 'rest';
|
|
|
|
$dX = 0;
|
|
$dY = 0;
|
|
$explodeRadius = 0;
|
|
if ((is_array($this->_explode)) && (isset($this->_explode[$x]))) {
|
|
$explodeRadius = $this->_explode[$x];
|
|
} elseif (is_numeric($this->_explode)) {
|
|
$explodeRadius = $this->_explode;
|
|
}
|
|
|
|
if ($explodeRadius > 0) {
|
|
$dX = $explodeRadius * cos(deg2rad(($angle1 + $angle2) / 2));
|
|
$dY = $explodeRadius * sin(deg2rad(($angle1 + $angle2) / 2));
|
|
}
|
|
|
|
$ID = $id;
|
|
$this->_getFillStyle($ID);
|
|
$this->_getLineStyle($ID);
|
|
$this->_canvas->pieslice(
|
|
$this->_mergeData(
|
|
$point,
|
|
array(
|
|
'x' => $centerX + $dX,
|
|
'y' => $centerY + $dY,
|
|
'rx' => $radius1,
|
|
'ry' => $radius1,
|
|
'v1' => $angle1,
|
|
'v2' => $angle2,
|
|
'srx' => $radius0,
|
|
'sry' => $radius0
|
|
)
|
|
)
|
|
);
|
|
}
|
|
$number++;
|
|
}
|
|
unset($keys);
|
|
$this->_drawMarker();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Draw a sample for use with legend
|
|
*
|
|
* @param array $param The parameters for the legend
|
|
* @access private
|
|
*/
|
|
function _legendSample(&$param)
|
|
{
|
|
if (is_array($this->_dataset)) {
|
|
|
|
$this->_canvas->startGroup(get_class($this) . '_' . $this->_title);
|
|
$this->_clip(true);
|
|
|
|
$totals = $this->_getTotals();
|
|
$totals['CENTER_X'] = (int) (($this->_left + $this->_right) / 2);
|
|
$totals['CENTER_Y'] = (int) (($this->_top + $this->_bottom) / 2);
|
|
$totals['RADIUS'] = min($this->height(), $this->width()) * 0.75 * 0.5;
|
|
$totals['CURRENT_Y'] = 0;
|
|
|
|
if (is_a($this->_fillStyle, "Image_Graph_Fill")) {
|
|
$this->_fillStyle->_reset();
|
|
}
|
|
|
|
$count = 0;
|
|
$keys = array_keys($this->_dataset);
|
|
foreach ($keys as $key) {
|
|
$dataset =& $this->_dataset[$key];
|
|
$count++;
|
|
|
|
$dataset->_reset();
|
|
$the_rest = 0;
|
|
while ($point = $dataset->_next()) {
|
|
$caption = $point['X'];
|
|
if (($this->_restGroupLimit !== false) && ($point['Y'] <= $this->_restGroupLimit)) {
|
|
$the_rest += $point['Y'];
|
|
}
|
|
else {
|
|
$this->_canvas->setFont($param['font']);
|
|
$width = 20 + $param['width'] + $this->_canvas->textWidth($caption);
|
|
$param['maxwidth'] = max($param['maxwidth'], $width);
|
|
$x2 = $param['x'] + $width;
|
|
$y2 = $param['y'] + $param['height']+5;
|
|
|
|
if ((($param['align'] & IMAGE_GRAPH_ALIGN_VERTICAL) != 0) && ($y2 > $param['bottom'])) {
|
|
$param['y'] = $param['top'];
|
|
$param['x'] = $x2;
|
|
$y2 = $param['y'] + $param['height'];
|
|
} elseif ((($param['align'] & IMAGE_GRAPH_ALIGN_VERTICAL) == 0) && ($x2 > $param['right'])) {
|
|
$param['x'] = $param['left'];
|
|
$param['y'] = $y2;
|
|
$x2 = $param['x'] + 20 + $param['width'] + $this->_canvas->textWidth($caption);
|
|
}
|
|
|
|
$x = $x0 = $param['x'];
|
|
$y = $param['y'];
|
|
$y0 = $param['y'] - $param['height']/2;
|
|
$x1 = $param['x'] + $param['width'];
|
|
$y1 = $param['y'] + $param['height']/2;
|
|
|
|
if (!isset($param['simulate'])) {
|
|
$this->_getFillStyle($point['ID']);
|
|
$this->_getLineStyle($point['ID']);
|
|
$this->_drawLegendSample($x0, $y0, $x1, $y1);
|
|
|
|
if (($this->_marker) && ($dataset) && ($param['show_marker'])) {
|
|
$prevPoint = $dataset->_nearby(-2);
|
|
$nextPoint = $dataset->_nearby();
|
|
|
|
$p = $this->_getMarkerData($point, $nextPoint, $prevPoint, $totals);
|
|
if (is_array($point)) {
|
|
$p['MARKER_X'] = $x+$param['width']/2;
|
|
$p['MARKER_Y'] = $y;
|
|
unset ($p['AVERAGE_Y']);
|
|
$this->_marker->_drawMarker($p['MARKER_X'], $p['MARKER_Y'], $p);
|
|
}
|
|
}
|
|
$this->write($x + $param['width'] +10, $y, $caption, IMAGE_GRAPH_ALIGN_CENTER_Y | IMAGE_GRAPH_ALIGN_LEFT, $param['font']);
|
|
}
|
|
|
|
if (($param['align'] & IMAGE_GRAPH_ALIGN_VERTICAL) != 0) {
|
|
$param['y'] = $y2;
|
|
} else {
|
|
$param['x'] = $x2;
|
|
}
|
|
}
|
|
}
|
|
if ($the_rest > 0) {
|
|
$this->_canvas->setFont($param['font']);
|
|
$width = 20 + $param['width'] + $this->_canvas->textWidth($this->_restGroupTitle);
|
|
$param['maxwidth'] = max($param['maxwidth'], $width);
|
|
$x2 = $param['x'] + $width;
|
|
$y2 = $param['y'] + $param['height']+5;
|
|
|
|
if ((($param['align'] & IMAGE_GRAPH_ALIGN_VERTICAL) != 0) && ($y2 > $param['bottom'])) {
|
|
$param['y'] = $param['top'];
|
|
$param['x'] = $x2;
|
|
$y2 = $param['y'] + $param['height'];
|
|
} elseif ((($param['align'] & IMAGE_GRAPH_ALIGN_VERTICAL) == 0) && ($x2 > $param['right'])) {
|
|
$param['x'] = $param['left'];
|
|
$param['y'] = $y2;
|
|
$x2 = $param['x'] + 20 + $param['width'] + $this->_canvas->textWidth($this->_restGroupTitle);
|
|
}
|
|
|
|
$x = $x0 = $param['x'];
|
|
$y = $param['y'];
|
|
$y0 = $param['y'] - $param['height']/2;
|
|
$x1 = $param['x'] + $param['width'];
|
|
$y1 = $param['y'] + $param['height']/2;
|
|
|
|
if (!isset($param['simulate'])) {
|
|
$this->_getFillStyle('rest');
|
|
$this->_getLineStyle('rest');
|
|
$this->_drawLegendSample($x0, $y0, $x1, $y1);
|
|
|
|
$this->write($x + $param['width'] + 10, $y, $this->_restGroupTitle, IMAGE_GRAPH_ALIGN_CENTER_Y | IMAGE_GRAPH_ALIGN_LEFT, $param['font']);
|
|
}
|
|
|
|
if (($param['align'] & IMAGE_GRAPH_ALIGN_VERTICAL) != 0) {
|
|
$param['y'] = $y2;
|
|
} else {
|
|
$param['x'] = $x2;
|
|
}
|
|
}
|
|
}
|
|
unset($keys);
|
|
$this->_clip(false);
|
|
$this->_canvas->endGroup();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
?>
|