You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
827 lines
20 KiB
827 lines
20 KiB
1 year ago
|
<?php
|
||
|
/*
|
||
|
** Zabbix
|
||
|
** Copyright (C) 2001-2023 Zabbix SIA
|
||
|
**
|
||
|
** This program is free software; you can redistribute it and/or modify
|
||
|
** it under the terms of the GNU General Public License as published by
|
||
|
** the Free Software Foundation; either version 2 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** This program 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 General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program; if not, write to the Free Software
|
||
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
**/
|
||
|
|
||
|
|
||
|
class CPieGraphDraw extends CGraphDraw {
|
||
|
|
||
|
const DEFAULT_HEADER_PADDING_TOP = 30;
|
||
|
|
||
|
const GRAPH_WIDTH_MIN = 20;
|
||
|
const GRAPH_HEIGHT_MIN = 20;
|
||
|
|
||
|
private $background;
|
||
|
private $sum;
|
||
|
private $exploderad;
|
||
|
private $exploderad3d;
|
||
|
private $graphheight3d;
|
||
|
private $shiftlegendright;
|
||
|
private $dataFrom;
|
||
|
private $shiftYLegend;
|
||
|
|
||
|
public function __construct($type = GRAPH_TYPE_PIE) {
|
||
|
parent::__construct($type);
|
||
|
$this->background = false;
|
||
|
$this->sum = false;
|
||
|
$this->exploderad = 1;
|
||
|
$this->exploderad3d = 3;
|
||
|
$this->graphheight3d = 12;
|
||
|
$this->shiftlegendright = 17 * 7 + 7 + 10; // count of static chars * px/char + for color rectangle + space
|
||
|
}
|
||
|
|
||
|
/********************************************************************************************************/
|
||
|
/* PRE CONFIG: ADD / SET / APPLY
|
||
|
/********************************************************************************************************/
|
||
|
public function addItem($itemid, $calc_fnc = CALC_FNC_AVG, $color = null, $type = null) {
|
||
|
$items = API::Item()->get([
|
||
|
'output' => ['itemid', 'hostid', 'name', 'key_', 'units', 'value_type', 'valuemapid', 'history', 'trends'],
|
||
|
'itemids' => [$itemid],
|
||
|
'webitems' => true
|
||
|
]);
|
||
|
|
||
|
if (!$items) {
|
||
|
$items = API::ItemPrototype()->get([
|
||
|
'output' => ['itemid', 'hostid', 'name', 'key_', 'units', 'value_type', 'valuemapid', 'history',
|
||
|
'trends'
|
||
|
],
|
||
|
'itemids' => [$itemid]
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
$this->items[$this->num] = reset($items);
|
||
|
|
||
|
$host = get_host_by_hostid($this->items[$this->num]['hostid']);
|
||
|
|
||
|
$this->items[$this->num]['host'] = $host['host'];
|
||
|
$this->items[$this->num]['hostname'] = $host['name'];
|
||
|
$this->items[$this->num]['color'] = is_null($color) ? 'Dark Green' : $color;
|
||
|
$this->items[$this->num]['calc_fnc'] = is_null($calc_fnc) ? CALC_FNC_AVG : $calc_fnc;
|
||
|
$this->items[$this->num]['calc_type'] = is_null($type) ? GRAPH_ITEM_SIMPLE : $type;
|
||
|
|
||
|
$this->num++;
|
||
|
}
|
||
|
|
||
|
public function switchPie3D($type = false) {
|
||
|
if ($type) {
|
||
|
$this->type = $type;
|
||
|
}
|
||
|
else {
|
||
|
switch ($this->type) {
|
||
|
case GRAPH_TYPE_EXPLODED:
|
||
|
$this->type = GRAPH_TYPE_3D_EXPLODED;
|
||
|
break;
|
||
|
case GRAPH_TYPE_3D_EXPLODED:
|
||
|
$this->type = GRAPH_TYPE_EXPLODED;
|
||
|
break;
|
||
|
case GRAPH_TYPE_3D:
|
||
|
$this->type = GRAPH_TYPE_PIE;
|
||
|
break;
|
||
|
case GRAPH_TYPE_PIE:
|
||
|
$this->type = GRAPH_TYPE_3D;
|
||
|
break;
|
||
|
default:
|
||
|
$this->type = GRAPH_TYPE_PIE;
|
||
|
}
|
||
|
}
|
||
|
return $this->type;
|
||
|
}
|
||
|
|
||
|
protected function calc3dheight($height) {
|
||
|
$this->graphheight3d = (int) ($height / 20);
|
||
|
}
|
||
|
|
||
|
protected function calcExplodedCenter($anglestart, $angleend, $x, $y, $count) {
|
||
|
$count *= $this->exploderad;
|
||
|
$anglemid = (int) (($anglestart + $angleend) / 2);
|
||
|
|
||
|
$y += round($count * sin(deg2rad($anglemid)));
|
||
|
$x += round($count * cos(deg2rad($anglemid)));
|
||
|
|
||
|
return [(int) $x, (int) $y];
|
||
|
}
|
||
|
|
||
|
protected function calcExplodedRadius($sizeX, $sizeY, $count) {
|
||
|
$count *= $this->exploderad * 2;
|
||
|
$sizeX -= $count;
|
||
|
$sizeY -= $count;
|
||
|
|
||
|
return [(int) $sizeX, (int) $sizeY];
|
||
|
}
|
||
|
|
||
|
protected function calc3DAngle($sizeX, $sizeY) {
|
||
|
$sizeY *= GRAPH_3D_ANGLE / 90;
|
||
|
|
||
|
return [$sizeX, (int) round($sizeY)];
|
||
|
}
|
||
|
|
||
|
protected function selectData() {
|
||
|
$this->data = [];
|
||
|
$now = time();
|
||
|
|
||
|
if (isset($this->stime)) {
|
||
|
$this->from_time = $this->stime;
|
||
|
$this->to_time = $this->stime + $this->period;
|
||
|
}
|
||
|
else {
|
||
|
$this->to_time = $now;
|
||
|
$this->from_time = $this->to_time - $this->period;
|
||
|
}
|
||
|
|
||
|
$strvaluelength = 0; // we need to know how long in px will be our legend
|
||
|
|
||
|
// fetch values for items with the "last" function
|
||
|
$lastValueItems = [];
|
||
|
foreach ($this->items as $item) {
|
||
|
if ($item['calc_fnc'] == CALC_FNC_LST) {
|
||
|
$lastValueItems[] = $item;
|
||
|
}
|
||
|
}
|
||
|
if ($lastValueItems) {
|
||
|
$history = Manager::History()->getLastValues($lastValueItems);
|
||
|
}
|
||
|
|
||
|
$items = [];
|
||
|
|
||
|
for ($i = 0; $i < $this->num; $i++) {
|
||
|
$item = $this->items[$i];
|
||
|
|
||
|
$from_time = $this->from_time;
|
||
|
$to_time = $this->to_time;
|
||
|
|
||
|
$to_resolve = [];
|
||
|
|
||
|
// Override item history setting with housekeeping settings, if they are enabled in config.
|
||
|
if (CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_GLOBAL)) {
|
||
|
$item['history'] = timeUnitToSeconds(CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY));
|
||
|
}
|
||
|
else {
|
||
|
$to_resolve[] = 'history';
|
||
|
}
|
||
|
|
||
|
if (CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_GLOBAL)) {
|
||
|
$item['trends'] = timeUnitToSeconds(CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS));
|
||
|
}
|
||
|
else {
|
||
|
$to_resolve[] = 'trends';
|
||
|
}
|
||
|
|
||
|
// Otherwise, resolve user macro and parse the string. If successful, convert to seconds.
|
||
|
if ($to_resolve) {
|
||
|
$item = CMacrosResolverHelper::resolveTimeUnitMacros([$item], $to_resolve)[0];
|
||
|
|
||
|
$simple_interval_parser = new CSimpleIntervalParser();
|
||
|
|
||
|
if (!CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_GLOBAL)) {
|
||
|
if ($simple_interval_parser->parse($item['history']) != CParser::PARSE_SUCCESS) {
|
||
|
show_error_message(_s('Incorrect value for field "%1$s": %2$s.', 'history',
|
||
|
_('invalid history storage period')
|
||
|
));
|
||
|
exit;
|
||
|
}
|
||
|
$item['history'] = timeUnitToSeconds($item['history']);
|
||
|
}
|
||
|
|
||
|
if (!CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_GLOBAL)) {
|
||
|
if ($simple_interval_parser->parse($item['trends']) != CParser::PARSE_SUCCESS) {
|
||
|
show_error_message(_s('Incorrect value for field "%1$s": %2$s.', 'trends',
|
||
|
_('invalid trend storage period')
|
||
|
));
|
||
|
exit;
|
||
|
}
|
||
|
$item['trends'] = timeUnitToSeconds($item['trends']);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$this->data[$this->items[$i]['itemid']]['last'] = isset($history[$item['itemid']])
|
||
|
? $history[$item['itemid']][0]['value'] : null;
|
||
|
$this->data[$this->items[$i]['itemid']]['shift_min'] = 0;
|
||
|
$this->data[$this->items[$i]['itemid']]['shift_max'] = 0;
|
||
|
$this->data[$this->items[$i]['itemid']]['shift_avg'] = 0;
|
||
|
|
||
|
$item['source'] = ($item['trends'] == 0 || ($item['history'] > time() - ($from_time + $this->period / 2)))
|
||
|
? 'history'
|
||
|
: 'trends';
|
||
|
$items[] = $item;
|
||
|
}
|
||
|
|
||
|
$results = Manager::History()->getGraphAggregationByWidth($items, $from_time, $to_time);
|
||
|
$i = 0;
|
||
|
|
||
|
foreach ($items as $item) {
|
||
|
if (array_key_exists($item['itemid'], $results)) {
|
||
|
$result = $results[$item['itemid']];
|
||
|
$this->dataFrom = $result['source'];
|
||
|
|
||
|
foreach ($result['data'] as $row) {
|
||
|
$this->data[$item['itemid']]['min'] = $row['min'];
|
||
|
$this->data[$item['itemid']]['max'] = $row['max'];
|
||
|
$this->data[$item['itemid']]['avg'] = $row['avg'];
|
||
|
$this->data[$item['itemid']]['clock'] = $row['clock'];
|
||
|
}
|
||
|
unset($result);
|
||
|
}
|
||
|
else {
|
||
|
$this->dataFrom = $item['source'];
|
||
|
}
|
||
|
|
||
|
switch ($item['calc_fnc']) {
|
||
|
case CALC_FNC_MIN:
|
||
|
$fncName = 'min';
|
||
|
break;
|
||
|
case CALC_FNC_MAX:
|
||
|
$fncName = 'max';
|
||
|
break;
|
||
|
case CALC_FNC_LST:
|
||
|
$fncName = 'last';
|
||
|
break;
|
||
|
case CALC_FNC_AVG:
|
||
|
default:
|
||
|
$fncName = 'avg';
|
||
|
}
|
||
|
|
||
|
$item_value = empty($this->data[$item['itemid']][$fncName])
|
||
|
? 0
|
||
|
: abs($this->data[$item['itemid']][$fncName]);
|
||
|
|
||
|
if ($item['calc_type'] == GRAPH_ITEM_SUM) {
|
||
|
$this->background = $i;
|
||
|
$graph_sum = $item_value;
|
||
|
}
|
||
|
|
||
|
$this->sum += $item_value;
|
||
|
|
||
|
$convertedUnit = strlen(convertUnits([
|
||
|
'value' => $item_value,
|
||
|
'units' => $item['units']
|
||
|
]));
|
||
|
$strvaluelength = max($strvaluelength, $convertedUnit);
|
||
|
$i++;
|
||
|
}
|
||
|
|
||
|
if (isset($graph_sum)) {
|
||
|
$this->sum = $graph_sum;
|
||
|
}
|
||
|
$this->shiftlegendright += $strvaluelength * 7;
|
||
|
}
|
||
|
|
||
|
protected function drawLegend() {
|
||
|
$shiftY = $this->shiftY + $this->shiftYLegend;
|
||
|
$fontSize = 8;
|
||
|
|
||
|
// check if host name will be displayed
|
||
|
$displayHostName = (count(array_unique(zbx_objectValues($this->items, 'hostname'))) > 1);
|
||
|
|
||
|
// calculate function name X shift
|
||
|
$functionNameXShift = 0;
|
||
|
|
||
|
foreach ($this->items as $item) {
|
||
|
$name = $displayHostName ? $item['hostname'].NAME_DELIMITER.$item['name'] : $item['name'];
|
||
|
$dims = imageTextSize($fontSize, 0, $name);
|
||
|
|
||
|
if ($dims['width'] > $functionNameXShift) {
|
||
|
$functionNameXShift = $dims['width'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// display items
|
||
|
$i = 0;
|
||
|
$top_padding = $this->with_vertical_padding ? 10 : -(static::DEFAULT_TOP_BOTTOM_PADDING / 2);
|
||
|
|
||
|
foreach ($this->items as $item) {
|
||
|
$color = $this->getColor($item['color'], 0);
|
||
|
|
||
|
// function name
|
||
|
switch ($item['calc_fnc']) {
|
||
|
case CALC_FNC_MIN:
|
||
|
$fncName = 'min';
|
||
|
$fncRealName = _('min');
|
||
|
break;
|
||
|
case CALC_FNC_MAX:
|
||
|
$fncName = 'max';
|
||
|
$fncRealName = _('max');
|
||
|
break;
|
||
|
case CALC_FNC_LST:
|
||
|
$fncName = 'last';
|
||
|
$fncRealName = _('last');
|
||
|
break;
|
||
|
case CALC_FNC_AVG:
|
||
|
default:
|
||
|
$fncName = 'avg';
|
||
|
$fncRealName = _('avg');
|
||
|
}
|
||
|
|
||
|
if (isset($this->data[$item['itemid']])
|
||
|
&& isset($this->data[$item['itemid']][$fncName])) {
|
||
|
$dataValue = $this->data[$item['itemid']][$fncName];
|
||
|
$proc = ($this->sum == 0) ? 0 : ($dataValue * 100) / $this->sum;
|
||
|
|
||
|
$strValue = sprintf(_('Value').': %s ('.(round($proc) != round($proc, 2) ? '%0.2f' : '%0.0f').'%%)',
|
||
|
convertUnits([
|
||
|
'value' => $dataValue,
|
||
|
'units' => $this->items[$i]['units']
|
||
|
]),
|
||
|
$proc
|
||
|
);
|
||
|
|
||
|
$str = '['.$fncRealName.']';
|
||
|
}
|
||
|
else {
|
||
|
$strValue = _('Value: no data');
|
||
|
|
||
|
$str = '['._('no data').']';
|
||
|
}
|
||
|
|
||
|
// item name
|
||
|
imageText(
|
||
|
$this->im,
|
||
|
$fontSize,
|
||
|
0,
|
||
|
$this->shiftXleft + 15,
|
||
|
$this->sizeY + $shiftY + 14 * $i + 5,
|
||
|
$this->getColor($this->graphtheme['textcolor'], 0),
|
||
|
$displayHostName ? $item['hostname'].NAME_DELIMITER.$item['name'] : $item['name']
|
||
|
);
|
||
|
|
||
|
// function name
|
||
|
imageText(
|
||
|
$this->im,
|
||
|
$fontSize,
|
||
|
0,
|
||
|
$this->shiftXleft + $functionNameXShift + 30,
|
||
|
$this->sizeY + $shiftY + 14 * $i + 5,
|
||
|
$this->getColor($this->graphtheme['textcolor'], 0),
|
||
|
$str
|
||
|
);
|
||
|
|
||
|
// left square fill
|
||
|
imagefilledrectangle(
|
||
|
$this->im,
|
||
|
$this->shiftXleft,
|
||
|
$this->sizeY + $shiftY + 14 * $i - 5,
|
||
|
$this->shiftXleft + 10,
|
||
|
$this->sizeY + $shiftY + 5 + 14 * $i,
|
||
|
$color
|
||
|
);
|
||
|
|
||
|
// left square frame
|
||
|
imagerectangle(
|
||
|
$this->im,
|
||
|
$this->shiftXleft,
|
||
|
$this->sizeY + $shiftY + 14 * $i - 5,
|
||
|
$this->shiftXleft + 10,
|
||
|
$this->sizeY + $shiftY + 5 + 14 * $i,
|
||
|
$this->getColor('Black No Alpha')
|
||
|
);
|
||
|
|
||
|
$shiftX = $this->fullSizeX - $this->shiftlegendright - $this->shiftXright + 25;
|
||
|
|
||
|
// right square fill
|
||
|
imagefilledrectangle(
|
||
|
$this->im,
|
||
|
$shiftX - 10,
|
||
|
$this->shiftY + $top_padding + 14 * $i,
|
||
|
$shiftX,
|
||
|
$this->shiftY + $top_padding + 10 + 14 * $i,
|
||
|
$color
|
||
|
);
|
||
|
|
||
|
// right square frame
|
||
|
imagerectangle(
|
||
|
$this->im,
|
||
|
$shiftX - 10,
|
||
|
$this->shiftY + $top_padding + 14 * $i,
|
||
|
$shiftX,
|
||
|
$this->shiftY + $top_padding + 10 + 14 * $i,
|
||
|
$this->GetColor('Black No Alpha')
|
||
|
);
|
||
|
|
||
|
// item value
|
||
|
imagetext(
|
||
|
$this->im,
|
||
|
$fontSize,
|
||
|
0,
|
||
|
$shiftX + 5,
|
||
|
$this->shiftY + $top_padding + 14 * $i + 10,
|
||
|
$this->getColor($this->graphtheme['textcolor'], 0),
|
||
|
$strValue
|
||
|
);
|
||
|
|
||
|
$i++;
|
||
|
}
|
||
|
|
||
|
if ($this->sizeY < 120) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected function drawElementPie($values) {
|
||
|
$sum = $this->sum;
|
||
|
|
||
|
if ($this->background !== false) {
|
||
|
$least = 0;
|
||
|
foreach ($values as $item => $value) {
|
||
|
if ($item != $this->background) {
|
||
|
$least += $value;
|
||
|
}
|
||
|
}
|
||
|
$values[$this->background] -= $least;
|
||
|
}
|
||
|
|
||
|
if ($sum <= 0) {
|
||
|
$values = [0 => 1];
|
||
|
$sum = 1;
|
||
|
$isEmptyData = true;
|
||
|
}
|
||
|
else {
|
||
|
$isEmptyData = false;
|
||
|
}
|
||
|
|
||
|
$sizeX = $this->sizeX;
|
||
|
$sizeY = $this->sizeY;
|
||
|
|
||
|
if ($this->type == GRAPH_TYPE_EXPLODED) {
|
||
|
list($sizeX, $sizeY) = $this->calcExplodedRadius($sizeX, $sizeY, count($values));
|
||
|
}
|
||
|
|
||
|
$xc = $x = (int) ($this->sizeX / 2) + $this->shiftXleft;
|
||
|
$yc = $y = (int) ($this->sizeY / 2) + $this->shiftY;
|
||
|
|
||
|
$anglestart = 0;
|
||
|
$angleend = 0;
|
||
|
|
||
|
foreach ($values as $item => $value) {
|
||
|
if ($value == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$angleend += (int) (360 * $value / $sum) + 1;
|
||
|
$angleend = ($angleend > 360) ? 360 : $angleend;
|
||
|
|
||
|
if (($angleend - $anglestart) < 1) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ($this->type == GRAPH_TYPE_EXPLODED) {
|
||
|
list($x, $y) = $this->calcExplodedCenter($anglestart, $angleend, $xc, $yc, count($values));
|
||
|
}
|
||
|
|
||
|
imagefilledarc(
|
||
|
$this->im,
|
||
|
$x,
|
||
|
$y,
|
||
|
$sizeX,
|
||
|
$sizeY,
|
||
|
$anglestart,
|
||
|
$angleend,
|
||
|
$this->getColor((!$isEmptyData ? $this->items[$item]['color'] : 'FFFFFF'), 0),
|
||
|
IMG_ARC_PIE
|
||
|
);
|
||
|
imagefilledarc(
|
||
|
$this->im,
|
||
|
$x,
|
||
|
$y,
|
||
|
$sizeX,
|
||
|
$sizeY,
|
||
|
$anglestart,
|
||
|
$angleend,
|
||
|
$this->getColor('Black'),
|
||
|
IMG_ARC_PIE | IMG_ARC_EDGED | IMG_ARC_NOFILL
|
||
|
);
|
||
|
$anglestart = $angleend;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected function drawElementPie3D($values) {
|
||
|
$sum = $this->sum;
|
||
|
|
||
|
if ($this->background !== false) {
|
||
|
$least = 0;
|
||
|
foreach ($values as $item => $value) {
|
||
|
if ($item != $this->background) {
|
||
|
$least += $value;
|
||
|
}
|
||
|
}
|
||
|
$values[$this->background] -= $least;
|
||
|
}
|
||
|
|
||
|
if ($sum <= 0) {
|
||
|
$values = [0 => 1];
|
||
|
$sum = 1;
|
||
|
$isEmptyData = true;
|
||
|
}
|
||
|
else {
|
||
|
$isEmptyData = false;
|
||
|
}
|
||
|
|
||
|
$sizeX = $this->sizeX;
|
||
|
$sizeY = $this->sizeY;
|
||
|
|
||
|
$this->exploderad = $this->exploderad3d;
|
||
|
|
||
|
if ($this->type == GRAPH_TYPE_3D_EXPLODED) {
|
||
|
list($sizeX, $sizeY) = $this->calcExplodedRadius($sizeX, $sizeY, count($values));
|
||
|
}
|
||
|
|
||
|
list($sizeX, $sizeY) = $this->calc3DAngle($sizeX, $sizeY);
|
||
|
|
||
|
$xc = $x = (int) ($this->sizeX / 2) + $this->shiftXleft;
|
||
|
$yc = $y = (int) ($this->sizeY / 2) + $this->shiftY;
|
||
|
|
||
|
// bottom angle line
|
||
|
$anglestart = 0;
|
||
|
$angleend = 0;
|
||
|
|
||
|
foreach ($values as $item => $value) {
|
||
|
if ($value == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$angleend += (int) (360 * $value / $sum) + 1;
|
||
|
$angleend = ($angleend > 360) ? 360 : $angleend;
|
||
|
|
||
|
if (($angleend - $anglestart) < 1) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ($this->type == GRAPH_TYPE_3D_EXPLODED) {
|
||
|
list($x, $y) = $this->calcExplodedCenter($anglestart, $angleend, $xc, $yc, count($values));
|
||
|
}
|
||
|
|
||
|
imagefilledarc(
|
||
|
$this->im,
|
||
|
$x,
|
||
|
$y + $this->graphheight3d + 1,
|
||
|
$sizeX,
|
||
|
$sizeY,
|
||
|
$anglestart,
|
||
|
$angleend,
|
||
|
$this->getShadow((!$isEmptyData ? $this->items[$item]['color'] : 'FFFFFF'), 0),
|
||
|
IMG_ARC_PIE
|
||
|
);
|
||
|
imagefilledarc(
|
||
|
$this->im,
|
||
|
$x,
|
||
|
$y + $this->graphheight3d + 1,
|
||
|
$sizeX,
|
||
|
$sizeY,
|
||
|
$anglestart,
|
||
|
$angleend,
|
||
|
$this->getColor('Black'),
|
||
|
IMG_ARC_PIE | IMG_ARC_EDGED | IMG_ARC_NOFILL
|
||
|
);
|
||
|
$anglestart = $angleend;
|
||
|
}
|
||
|
|
||
|
// 3d effect
|
||
|
for ($i = $this->graphheight3d; $i > 0; $i--) {
|
||
|
$anglestart = 0;
|
||
|
$angleend = 0;
|
||
|
|
||
|
foreach ($values as $item => $value) {
|
||
|
if ($value == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$angleend += (int) (360 * $value / $sum) + 1;
|
||
|
$angleend = ($angleend > 360) ? 360 : $angleend;
|
||
|
|
||
|
if (($angleend - $anglestart) < 1) {
|
||
|
continue;
|
||
|
}
|
||
|
elseif ($this->sum == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ($this->type == GRAPH_TYPE_3D_EXPLODED) {
|
||
|
list($x, $y) = $this->calcExplodedCenter($anglestart, $angleend, $xc, $yc, count($values));
|
||
|
}
|
||
|
|
||
|
imagefilledarc(
|
||
|
$this->im,
|
||
|
$x,
|
||
|
$y + $i,
|
||
|
$sizeX,
|
||
|
$sizeY,
|
||
|
$anglestart,
|
||
|
$angleend,
|
||
|
$this->getShadow((!$isEmptyData ? $this->items[$item]['color'] : 'FFFFFF'), 0),
|
||
|
IMG_ARC_PIE
|
||
|
);
|
||
|
$anglestart = $angleend;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$anglestart = 0;
|
||
|
$angleend = 0;
|
||
|
|
||
|
foreach ($values as $item => $value) {
|
||
|
if ($value == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$angleend += (int) (360 * $value / $sum) + 1;
|
||
|
$angleend = ($angleend > 360) ? 360 : $angleend;
|
||
|
|
||
|
if (($angleend - $anglestart) < 1) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ($this->type == GRAPH_TYPE_3D_EXPLODED) {
|
||
|
list($x, $y) = $this->calcExplodedCenter($anglestart, $angleend, $xc, $yc, count($values));
|
||
|
}
|
||
|
|
||
|
imagefilledarc(
|
||
|
$this->im,
|
||
|
$x,
|
||
|
$y,
|
||
|
$sizeX,
|
||
|
$sizeY,
|
||
|
$anglestart,
|
||
|
$angleend,
|
||
|
$this->getColor((!$isEmptyData ? $this->items[$item]['color'] : 'FFFFFF'), 0),
|
||
|
IMG_ARC_PIE
|
||
|
);
|
||
|
imagefilledarc(
|
||
|
$this->im,
|
||
|
$x,
|
||
|
$y,
|
||
|
$sizeX,
|
||
|
$sizeY,
|
||
|
$anglestart,
|
||
|
$angleend,
|
||
|
$this->getColor('Black'),
|
||
|
IMG_ARC_PIE | IMG_ARC_EDGED | IMG_ARC_NOFILL
|
||
|
);
|
||
|
$anglestart = $angleend;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function draw() {
|
||
|
$debug_mode = CWebUser::getDebugMode();
|
||
|
if ($debug_mode) {
|
||
|
$start_time = microtime(true);
|
||
|
}
|
||
|
set_image_header();
|
||
|
$this->calculateTopPadding();
|
||
|
|
||
|
$this->selectData();
|
||
|
if (hasErrorMessages()) {
|
||
|
show_messages();
|
||
|
}
|
||
|
|
||
|
$this->shiftYLegend = 20;
|
||
|
$this->shiftXleft = 10;
|
||
|
$this->shiftXright = 0;
|
||
|
$this->fullSizeX = $this->sizeX;
|
||
|
$this->fullSizeY = $this->sizeY;
|
||
|
|
||
|
if ($this->sizeX < 300 || $this->sizeY < 200) {
|
||
|
$this->showLegend(0);
|
||
|
}
|
||
|
|
||
|
if ($this->drawLegend == 1) {
|
||
|
$this->sizeX -= $this->shiftXleft + $this->shiftXright + $this->shiftlegendright;
|
||
|
$this->sizeY -= $this->shiftY + $this->shiftYLegend + 14 * $this->num + 8;
|
||
|
}
|
||
|
elseif ($this->with_vertical_padding) {
|
||
|
$this->sizeX -= $this->shiftXleft * 2;
|
||
|
$this->sizeY -= $this->shiftY * 2;
|
||
|
}
|
||
|
|
||
|
if (!$this->with_vertical_padding) {
|
||
|
if ($this->drawLegend == 1) {
|
||
|
// Increase size of graph by sum of: 8px legend font size and 5px legend item bottom shift.
|
||
|
$this->sizeY += 13;
|
||
|
}
|
||
|
else {
|
||
|
// Remove y shift if only graph is rendered (no labels, header, vertical paddings).
|
||
|
$this->shiftY = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$this->sizeX = min($this->sizeX, $this->sizeY);
|
||
|
$this->sizeY = min($this->sizeX, $this->sizeY);
|
||
|
|
||
|
if ($this->sizeX + $this->shiftXleft > $this->fullSizeX) {
|
||
|
$this->sizeX = $this->fullSizeX - $this->shiftXleft - $this->shiftXleft;
|
||
|
$this->sizeY = min($this->sizeX, $this->sizeY);
|
||
|
}
|
||
|
|
||
|
$this->calc3dheight($this->sizeY);
|
||
|
|
||
|
$this->exploderad = (int) $this->sizeX / 100;
|
||
|
$this->exploderad3d = (int) $this->sizeX / 60;
|
||
|
|
||
|
$this->im = imagecreatetruecolor($this->fullSizeX, $this->fullSizeY);
|
||
|
|
||
|
$this->initColors();
|
||
|
$this->drawRectangle();
|
||
|
$this->drawHeader();
|
||
|
|
||
|
// for each metric
|
||
|
$values = [];
|
||
|
for ($i = 0; $i < $this->num; $i++) {
|
||
|
$data = &$this->data[$this->items[$i]['itemid']];
|
||
|
|
||
|
if (!isset($data)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
switch ($this->items[$i]['calc_fnc']) {
|
||
|
case CALC_FNC_MIN:
|
||
|
$fncName = 'min';
|
||
|
break;
|
||
|
case CALC_FNC_MAX:
|
||
|
$fncName = 'max';
|
||
|
break;
|
||
|
case CALC_FNC_LST:
|
||
|
$fncName = 'last';
|
||
|
break;
|
||
|
case CALC_FNC_AVG:
|
||
|
default:
|
||
|
$fncName = 'avg';
|
||
|
}
|
||
|
|
||
|
$values[$i] = empty($this->data[$this->items[$i]['itemid']][$fncName])
|
||
|
? 0
|
||
|
: abs($this->data[$this->items[$i]['itemid']][$fncName]);
|
||
|
}
|
||
|
|
||
|
switch ($this->type) {
|
||
|
case GRAPH_TYPE_EXPLODED:
|
||
|
$this->drawElementPie($values);
|
||
|
break;
|
||
|
case GRAPH_TYPE_3D:
|
||
|
$this->drawElementPie3D($values);
|
||
|
break;
|
||
|
case GRAPH_TYPE_3D_EXPLODED:
|
||
|
$this->drawElementPie3D($values);
|
||
|
break;
|
||
|
default:
|
||
|
$this->drawElementPie($values);
|
||
|
}
|
||
|
|
||
|
if ($this->drawLegend == 1) {
|
||
|
$this->drawLegend();
|
||
|
}
|
||
|
|
||
|
if ($debug_mode) {
|
||
|
$str = sprintf('%0.2f', microtime(true) - $start_time);
|
||
|
imageText(
|
||
|
$this->im,
|
||
|
6,
|
||
|
90,
|
||
|
$this->fullSizeX - 2,
|
||
|
$this->fullSizeY - 5,
|
||
|
$this->getColor('Gray'),
|
||
|
_s('Data from %1$s. Generated in %2$s sec.', $this->dataFrom, $str)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
unset($this->items, $this->data);
|
||
|
|
||
|
imageOut($this->im);
|
||
|
}
|
||
|
|
||
|
private function getShadow($color, $alpha = 0) {
|
||
|
if (isset($this->colorsrgb[$color])) {
|
||
|
$red = $this->colorsrgb[$color][0];
|
||
|
$green = $this->colorsrgb[$color][1];
|
||
|
$blue = $this->colorsrgb[$color][2];
|
||
|
}
|
||
|
else {
|
||
|
list($red, $green, $blue) = hex2rgb($color);
|
||
|
}
|
||
|
|
||
|
if ($this->sum > 0) {
|
||
|
$red = (int) ($red * 0.6);
|
||
|
$green = (int) ($green * 0.6);
|
||
|
$blue = (int) ($blue * 0.6);
|
||
|
}
|
||
|
|
||
|
$RGB = [$red, $green, $blue];
|
||
|
|
||
|
if ($alpha != 0) {
|
||
|
return imagecolorexactalpha($this->im, $RGB[0], $RGB[1], $RGB[2], $alpha);
|
||
|
}
|
||
|
|
||
|
return imagecolorallocate($this->im, $RGB[0], $RGB[1], $RGB[2]);
|
||
|
}
|
||
|
}
|