Merge branch 'feature/svg-rendering-4614'

fixes #4614
This commit is contained in:
Marius Hein 2013-09-25 16:40:55 +02:00
commit 0d6dc0cc7b
42 changed files with 5249 additions and 0 deletions

321
doc/graphs.md Normal file
View File

@ -0,0 +1,321 @@
# Drawing Graphs
## Feature Set
Icinga Web comes with an SVG based graphing library that supports the basic graph types required for displaying monitoring
data. These include:
* **Pie Charts**, which display a set of data in a typical pie diagram.
* **Stacked Pie Charts**, which render one or multiple pies nested in another pie chart
* **Line Charts**, which display a set of datapoints as a line graph
* **Stacked Line Charts**, which display multiple line charts on top of each other, providing a cumulative view over
a set of datapoints
* **Bar Charts**, which display a set of datapoints as bars
* **Stacked Bar Charts**, which, like the Stacked Line Chart, combines several charts and displays them on top of each other
## Creating Grid Charts (Line and Bar Charts)
### Base Api Synopsis
The `Icinga/Chart/GridChart` class provides the calls required for setting up Grid Charts. A GridChart draws three
separate parts: Axis, Legend and the Gridarea.
To create a new Grid, simply create a `GridChart` object (the constructor takes no parameters):
**Example #1: Create a grid chart**
$this->chart = new GridChart();
Now you can go on and customize the chart to fit your needs (this will be explained in depth in the next sections).
**Example #2: Customize the grid chart**
$this->chart
->setAxisMin(null, 0) // Set the Y-axis to always start at 0
->setAxisMax(null, 100) // Set the Y-Axis to end at 100
->setAxisLabel("X axis label", "Y axis label"); // Set labels for X-axis and Y-axis
And finally you can draw data:
**Example #3: Drawing graphs**
$this->chart->drawLines(
array(
'label' => 'A Graph Line',
'color' => 'red',
'width' => '5',
'data' => array(array(0, 10), array(2, 40), array(3, 55), array(7, 92))
)
);
This example would produce a graph like this if rendered:
![Simple Line Graph][graph1]
### Graph Setup Methods
When creating the above graph without any setup options (like `setAxisMin`), it would use default values when being rendered.
This means:
* No label for X-Axis and Y-Axis
* The X/Y axis minimal value is the lowest X/Y value from the dataset
* The X/Y axis maximum value is the highest X/Y value from the dataset
Let's create a minimal example for this:
**Example #4: The most simple line graph**
$this->chart = new GridChart();
$this->chart->drawLines(
array(
'data' => array(array(0, 10), array(2, 40), array(3, 55), array(7, 92))
)
);
![The Most Simple Line Graph][graph2]
#### Adding Axis Labels
A graph without axis labels is rather useless. With the `GridChart::setAxisLabel($xAxisLabel, $yAxisLabel)` method you
can define the axis labels for both the X and Y axis:
**Example #5: Adding axis labels**
$this->chart = new GridChart();
$this->chart->setAxisLabel("X axis label", "Y axis label");
$this->chart->drawLines(
array(
'data' => array(array(0, 10), array(2, 40), array(3, 55), array(7, 92))
)
);
![Line Graph With Label][graph3]
#### Defining Axis Types
Normally, axis display their values as numeric, linear types. You can overwrite the axis for the X or Y direction with
one that suits your needs more specifically. Supported axis are:
* Linear Axis: This is the default axis that displays numeric values with an equal distance between each tick
**Example #6: Defining A Linear Axis With A Custom Number Of Ticks**
$this->chart = new GridChart();
$this->chart->setAxisLabel("X axis label", "Y axis label");
$this->chart->setXAxis(Axis::linearUnit(40));
$this->chart->setYAxis(Axis::linearUnit(10));
$this->chart->drawLines(
array(
'data' => array(array(0, 10), array(2, 40), array(3, 55), array(7, 92))
)
);
![Line Graph With Custom Tick Count][graph4]
* Calendar Axis: The calendar axis is a special axis for using timestamps in the axis. It will display the ticks as
sensible time values
**Example #7: Defining A Calendar Axis**
$this->chart = new GridChart();
$this->chart->setAxisLabel("X axis label", "Y axis label");
$this->chart->setXAxis(Axis::calendarUnit());
$this->chart->drawLines(
array(
'data' => array(
array(time()-7200, 10),array(time()-3620, 30), array(time()-1800, 15), array(time(), 92))
)
);
![Line Graph With Custom Tick Count][graph5]
## Line Charts
We've already seen an example of line charts in the last section, but this was rather minimal. The call for creating
Line Charts in the Chart Api is `GridChart::drawLines(array $lineDefinition1, array $lineDefinition2, ...)`, while '...'
means 'as many definitions as you want'.
$lineDefinition is an configuration array that describes how your data will be displayed. Possible configuration options
are:
* **label** The text that will be displayed in the legend of the graph for this line. If none is given simply
'Dataset %nr%' will be displayed, with %nr% meaning a number starting at 1 and incrementing for every
line without a label
* **stack** If provided, this graph will be shown on top of each other graph in the same stack and causes all
graphs in the same stack to be rendered cumulative
* **discrete** Set to display the line in a discrete manner, i.e. using hard steps between values instead of drawing
a interpolated line between points
* **color** The color to use for the line or fill, either in Hex form or as a string supported in the SVG style tag
* **palette** (Ignored if 'color' is set) The color palette to use for determining the line or fill color
* **fill** True to fill the graph instead of drawing a line. Take care of the graph ordering when using this
option, as previously drawn graphs will be hidden if they overlap this graph.
* **showPoints** Set true to emphasize datapoints with additional dots
* **width** Set the thickness of the line stroke in px (default: 5)
* **data** The dataset as an two dimensional array in the form `array(array($x1, $y2), array($x2, $y2), ...)
**Example #8: Various Line Graph Options**
$this->chart->drawLines(
array(
'label' => 'Hosts critical',
'palette' => Palette::PROBLEM,
'stack' => 'stack1',
'fill' => true,
'data' => $data2
),
array(
'label' => 'Hosts warning',
'stack' => 'stack1',
'palette' => Palette::WARNING,
'fill' => true,
'showPoints' => true,
'data' => $data
),
array(
'label' => 'Hosts ok',
'discrete' => true,
'color' => '#00ff00',
'fill' => false,
'showPoints' => true,
'width' => '10',
'data' => $data3
)
);
You can see the effects here, notice how the first two lines are stacked:
![Various Line Graph Options][graph6]
## Bar Charts
Bar Charts almost offer the same functionality as Line Charts, but some configuration options from Line Charts don't make sense
and are therefore omitted.
The call for creating Line Charts in the Chart Api is `GridChart::drawBars(array $lineDefinition1, array $lineDefinition2, ...)`,
while '...' means 'as many definitions as you want'. Possible configuration options are:
* **label** The text that will be displayed in the legend of the graph for this line. If none is given simply
'Dataset %nr%' will be displayed, with %nr% meaning a number starting at 1 and incrementing for every
line without a label
* **stack** If provided, this graph will be shown on top of each other graph in the same stack and causes all
graphs in the same stack to be rendered cumulative
* **color** The color to use for filling the bar, either in Hex form or as a string supported in the SVG style tag
* **palette** (Ignored if 'color' is set) The color palette to use for determining the fill color
* **width** Set the thickness of the line stroke in px (default: 1)
* **data** The dataset as an two dimensional array in the form `array(array($x1, $y2), array($x2, $y2), ...)
The same graph as rendered above would look as follows when using `drawBars` instead of `drawLines`. If you don't want
the labels to show you can use the 'disableLegend()' call on the GridChart object.
**Example #9: Various Bar Chart Options**
$this->chart->drawBars(
array(
'label' => 'Hosts critical',
'palette' => Palette::PROBLEM,
'stack' => 'stack1',
'data' => $data2
),
array(
'label' => 'Hosts warning',
'stack' => 'stack1',
'palette' => Palette::WARNING,
'data' => $data
),
array(
'label' => 'Hosts ok',
'color' => '#00ff00',
'width' => '10',
'data' => $data3
)
);
![Various Line Graph Options][graph7]
## Pie Charts
### The PieChart Object
Additionally to Line and Bar Charts, the Graphing Api also supports drawing Pie charts. In order to work with Pie charts
you have to create an `Icinga\Chart\PieChart` object first:
**Example #10: Creating a PieChart Object**
$pie = new PieChart();
### Drawing Pies
Pies are now drawn using the `PieChart::drawPies(array $pieDefinition1, array $pieDefinition2, ...)` method:
**Example #11: Example PieChart Definition**
$pie->drawPie(array(
'data' => array(5,80,1),
'palette' => array(Palette::PROBLEM, Palette::OK, Palette::WARNING),
'labels' => array(
'Hosts down', 'Hosts up', 'Hosts unknown'
)
));
This would produce a Pie Chart similar to this:
![Example Pie Chart][graph8]
Notice how every datapoint has it's own label and palette definition. Possible attributes for $pieDefinition are:
* **labels**: An array containing a label for every definition in the 'data' array
* **colors**: An array of colors to use for every definition in the 'data' array
* **palette**: (ignored when using 'colors') An array containing the palette to user for every definition in the 'data'
array
* **data** An array containing of numeric values that define the relative sizes of the pie slices to the whole pie
If you don't want the labels to show you can use the 'disableLegend()' call on the PieChart object.
### Stacked Pies
When adding multiple pies, they will be per default shown as a stacked pie:
**Example #12: Stacked Pie Charts**
$pie = new PieChart();
$pie->drawPie(array(
'data' => array(5,80,1),
'palette' => array(Palette::PROBLEM, Palette::OK, Palette::WARNING),
'labels' => array(
'Hosts down', 'Hosts up', 'Hosts unknown'
)
), array(
'data' => array(40,60,90,2),
'palette' => array(Palette::PROBLEM, Palette::WARNING, Palette::OK, Palette::NEUTRAL),
'labels' => array('Services down', 'Services Warning', 'Services OK', 'Services pending')
));
![Example Pie Chart][graph9]
## Rendering in templates:
Rendering is straightforward, assuming $svg is the PieChart/GridChart object, you just call render() to create an SVG:
myTemplate.phtml
<div style="border:1px dashed black;width:800px;height:400px">
<?=
$svg->render();
?>
</div>
[graph1]: res/GraphExample#1.png
[graph2]: res/GraphExample#2.png
[graph3]: res/GraphExample#3.png
[graph4]: res/GraphExample#4.png
[graph5]: res/GraphExample#5.png
[graph6]: res/GraphExample#6.png
[graph7]: res/GraphExample#7.png
[graph8]: res/GraphExample#8.png
[graph9]: res/GraphExample#9.png

BIN
doc/res/GraphExample#1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
doc/res/GraphExample#2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
doc/res/GraphExample#3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
doc/res/GraphExample#4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
doc/res/GraphExample#5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
doc/res/GraphExample#6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
doc/res/GraphExample#7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
doc/res/GraphExample#8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
doc/res/GraphExample#9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -0,0 +1,399 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart;
use \DOMElement;
use \Icinga\Chart\Primitive\Drawable;
use \Icinga\Chart\Primitive\Line;
use \Icinga\Chart\Primitive\Text;
use \Icinga\Chart\Render\RenderContext;
use \Icinga\Chart\Unit\AxisUnit;
use \Icinga\Chart\Unit\CalendarUnit;
use \Icinga\Chart\Unit\LinearUnit;
/**
* Axis class for the GridChart class.
*
* Implements drawing functions for the axis and it's labels but delegates tick and label calculations
* to the AxisUnit implementations
*
* @see GridChart
* @see AxisUnit
*/
class Axis implements Drawable
{
/**
* Whether to draw the horizontal lines for the background grid
*
* @var bool
*/
private $drawXGrid = true;
/**
* Whether to draw the vertical lines for the background grid
*
* @var bool
*/
private $drawYGrid = true;
/**
* The label for the x axis
*
* @var string
*/
private $xLabel = "";
/**
* The label for the y axis
*
* @var string
*/
private $yLabel = "";
/**
* The AxisUnit implementation to use for calculating the ticks for the x axis
*
* @var AxisUnit
*/
private $xUnit = null;
/**
* The AxisUnit implementation to use for calculating the ticks for the y axis
*
* @var AxisUnit
*/
private $yUnit = null;
/**
* Inform the axis about an added dataset
*
* This is especially needed when one or more AxisUnit implementations dynamically define
* their min or max values, as this is the point where they detect the min and max value
* from the datasets
*
* @param array $dataset An dataset to respect on axis generation
*/
public function addDataset(array $dataset)
{
$this->xUnit->addValues($dataset, 0);
$this->yUnit->addValues($dataset, 1);
}
/**
* Set the AxisUnit implementation to use for generating the x axis
*
* @param AxisUnit $unit The AxisUnit implementation to use for the x axis
*
* @return self This Axis Object
* @see Axis::CalendarUnit
* @see Axis::LinearUnit
*/
public function setUnitForXAxis(AxisUnit $unit)
{
$this->xUnit = $unit;
return $this;
}
/**
* Set the AxisUnit implementation to use for generating the y axis
*
* @param AxisUnit $unit The AxisUnit implementation to use for the y axis
*
* @return self This Axis Object
* @see Axis::CalendarUnit
* @see Axis::LinearUnit
*/
public function setUnitForYAxis(AxisUnit $unit)
{
$this->yUnit = $unit;
return $this;
}
/**
* Return the padding this axis requires
*
* @return array An array containing the padding for all sides
*/
public function getRequiredPadding()
{
return array(10, 5, 15, 10);
}
/**
* Render the horizontal axis
*
* @param RenderContext $ctx The context to use for rendering
* @param DOMElement $group The DOMElement this axis will be added to
*/
private function renderHorizontalAxis(RenderContext $ctx, DOMElement $group)
{
$line = new Line(0, 100, 100, 100);
$line->setStrokeWidth(2);
$group->appendChild($line->toSvg($ctx));
// contains the approximate end position of the last label
$lastLabelEnd = -1;
$shift = 0;
foreach ($this->xUnit as $label => $pos) {
// If the last label would overlap this label we shift the y axis a bit
if ($lastLabelEnd > $pos) {
$shift = ($shift + 5) % 10;
} else {
$shift = 0;
}
$tick = new Line($pos, 100, $pos, 102);
$group->appendChild($tick->toSvg($ctx));
$labelField = new Text($pos + 0.5, 105 + $shift, $label);
$labelField->setAlignment(Text::ALIGN_MIDDLE)
->setFontSize('1.8em');
$group->appendChild($labelField->toSvg($ctx));
if ($this->drawYGrid) {
$bgLine = new Line($pos, 0, $pos, 100);
$bgLine->setStrokeWidth(0.5)
->setStrokeColor('#232');
$group->appendChild($bgLine->toSvg($ctx));
}
$lastLabelEnd = $pos + strlen($label);
}
// render the label for this axis
if ($this->xLabel) {
$axisLabel = new Text(50, 115, $this->xLabel);
$axisLabel->setFontSize('2em')
->setAlignment(Text::ALIGN_MIDDLE);
$group->appendChild($axisLabel->toSvg($ctx));
}
}
/**
* Render the vertical axis
*
* @param RenderContext $ctx The context to use for rendering
* @param DOMElement $group The DOMElement this axis will be added to
*/
private function renderVerticalAxis(RenderContext $ctx, DOMElement $group)
{
$line = new Line(0, 0, 0, 100);
$line->setStrokeWidth(2);
$group->appendChild($line->toSvg($ctx));
foreach ($this->yUnit as $label => $pos) {
$pos = 100 - $pos;
$tick = new Line(0, $pos, -1, $pos);
$group->appendChild($tick->toSvg($ctx));
$labelField = new Text(-0.5, $pos+0.5, $label);
$labelField->setFontSize('1.8em')
->setAlignment(Text::ALIGN_END);
$group->appendChild($labelField->toSvg($ctx));
if ($this->drawXGrid) {
$bgLine = new Line(0, $pos, 100, $pos);
$bgLine->setStrokeWidth(0.5)
->setStrokeColor('#343');
$group->appendChild($bgLine->toSvg($ctx));
}
}
if ($this->yLabel) {
$axisLabel = new Text(-10, 50, $this->yLabel);
$axisLabel->setFontSize('2em')
->setAdditionalStyle(Text::ORIENTATION_VERTICAL)
->setAlignment(Text::ALIGN_MIDDLE);
$group->appendChild($axisLabel->toSvg($ctx));
}
}
/**
* Factory method, create an Axis instance using Linear ticks as the unit
*
* @return Axis The axis that has been created
* @see LinearUnit
*/
public static function createLinearAxis()
{
$axis = new Axis();
$axis->setUnitForXAxis(self::linearUnit());
$axis->setUnitForYAxis(self::linearUnit());
return $axis;
}
/**
* Set the label for the x axis
*
* An empty string means 'no label'.
*
* @param string $label The label to use for the x axis
*
* @return $this Fluid interface
*/
public function setXLabel($label)
{
$this->xLabel = $label;
return $this;
}
/**
* Set the label for the y axis
*
* An empty string means 'no label'.
*
* @param string $label The label to use for the y axis
*
* @return self Fluid interface
*/
public function setYLabel($label)
{
$this->yLabel = $label;
return $this;
}
/**
* Set the labels minimum value for the x axis
*
* Setting the value to null let's the axis unit decide which value to use for the minimum
*
* @param int $xMin The minimum value to use for the x axis
*
* @return self Fluid interface
*/
public function setXMin($xMin)
{
$this->xUnit->setMin($xMin);
return $this;
}
/**
* Set the labels minimum value for the y axis
*
* Setting the value to null let's the axis unit decide which value to use for the minimum
*
* @param int $yMin The minimum value to use for the x axis
*
* @return self Fluid interface
*/
public function setYMin($yMin)
{
$this->yUnit->setMin($yMin);
return $this;
}
/**
* Set the labels maximum value for the x axis
*
* Setting the value to null let's the axis unit decide which value to use for the maximum
*
* @param int $xMax The minimum value to use for the x axis
*
* @return self Fluid interface
*/
public function setXMax($xMax)
{
$this->xUnit->setMax($xMax);
return $this;
}
/**
* Set the labels maximum value for the y axis
*
* Setting the value to null let's the axis unit decide which value to use for the maximum
*
* @param int $yMax The minimum value to use for the y axis
*
* @return self Fluid interface
*/
public function setYMax($yMax)
{
$this->yUnit->setMax($yMax);
return $this;
}
/**
* Transform all coordinates of the given dataset to coordinates that fit the graph's coordinate system
*
* @param array $dataSet The absolute coordinates as provided in the draw call
*
* @return array A graph relative representation of the given coordinates
*/
public function transform(array &$dataSet)
{
$result = array();
foreach ($dataSet as &$points) {
$result[] = array(
$this->xUnit->transform($points[0]),
100 - $this->yUnit->transform($points[1])
);
}
return $result;
}
/**
* Create an AxisUnit that can be used in the axis to represent timestamps
*
* @return CalendarUnit
*/
public static function calendarUnit()
{
return new CalendarUnit();
}
/**
* Create an AxisUnit that can be used in the axis to represent a dataset as equally distributed
* ticks
*
* @param int $ticks
* @return LinearUnit
*/
public static function linearUnit($ticks = 10)
{
return new LinearUnit($ticks);
}
/**
* Return the SVG representation of this object
*
* @param RenderContext $ctx The context to use for calculations
*
* @return DOMElement
* @see Drawable::toSvg
*/
public function toSvg(RenderContext $ctx)
{
$group = $ctx->getDocument()->createElement('g');
$this->renderHorizontalAxis($ctx, $group);
$this->renderVerticalAxis($ctx, $group);
return $group;
}
}

View File

@ -0,0 +1,130 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart;
use \Exception;
use \Icinga\Chart\Legend;
use \Icinga\Chart\Palette;
use \Icinga\Chart\Primitive\Drawable;
use \Icinga\Chart\SVGRenderer;
/**
* Base class for charts, extended by all other Chart classes.
*/
abstract class Chart implements Drawable
{
/**
* SVG renderer that handles
*
* @var SVGRenderer
*/
protected $renderer;
/**
* Legend to use for this chart
*
* @var Legend
*/
protected $legend;
/**
* The style-palette for this chart
*
* @var Palette
*/
protected $palette;
/**
* Create a new chart object and create internal objects
*
* If you want to extend this class use the init() method as an extension point,
* as this will be called at the end o fthe construct call
*/
public function __construct()
{
$this->legend = new Legend();
$this->palette = new Palette();
$this->init();
}
/**
* Extension point for subclasses, called on __construct
*/
protected function init()
{
}
/**
* Extension point for implementing rendering logic
*
* This method is called after data validation, but before toSvg is called
*/
protected function build()
{
}
/**
* Check if the current dataset has the proper structure for this chart.
*
* Needs to be overwritten by extending classes. The default implementation returns false.
*
* @return bool True when the dataset is valid, otherwise false
*/
abstract public function isValidDataFormat();
/**
* Disable the legend for this chart
*/
public function disableLegend()
{
$this->legend = null;
}
/**
*
* Render this graph and return the created SVG
*
* @return string The SVG created by the SvgRenderer
*
* @throws Exception Thrown wen the dataset is not valid for this graph
* @see SVGRenderer::render
*/
public function render()
{
if (!$this->isValidDataFormat()) {
throw new Exception('Dataset for graph doesn\'t have the proper structure');
}
$this->build();
$this->renderer->getCanvas()->addElement($this);
return $this->renderer->render();
}
}

View File

@ -0,0 +1,108 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Graph;
use \DOMElement;
use \Icinga\Chart\Primitive\Animation;
use \Icinga\Chart\Primitive\Drawable;
use \Icinga\Chart\Primitive\Rect;
use \Icinga\Chart\Primitive\Styleable;
use \Icinga\Chart\Render\RenderContext;
/**
* Bar graph implementation
*/
class BarGraph extends Styleable implements Drawable
{
/**
* The dataset to use for this bar graph
*
* @var array
*/
private $dataSet;
/**
* Create a new BarGraph with the given dataset
*
* @param array $dataSet An array of datapoints
*/
public function __construct(array $dataSet)
{
$this->dataSet = $dataSet;
}
/**
* Apply configuration styles from the $cfg
*
* @param array $cfg The configuration as given in the drawBars call
*/
public function setStyleFromConfig(array $cfg)
{
foreach ($cfg as $elem => $value) {
if ($elem === 'color') {
$this->setFill($value);
} elseif ($elem === 'width') {
$this->setStrokeWidth($value);
}
}
}
/**
* Render this BarChart
*
* @param RenderContext $ctx The rendering context to use for drawing
*
* @return DOMElement $dom Element
*/
public function toSvg(RenderContext $ctx)
{
$doc = $ctx->getDocument();
$group = $doc->createElement('g');
$idx = 0;
foreach ($this->dataSet as $point) {
$rect = new Rect($point[0]-1, $point[1], 2, 100- $point[1]);
$rect->setFill($this->fill);
$rect->setStrokeWidth($this->strokeWidth);
$rect->setStrokeColor('black');
$rect->setAttribute('data-icinga-graph-index', $idx++);
$rect->setAttribute('data-icinga-graph-type', 'bar');
$rect->setAdditionalStyle('clip-path: url(#clip);');
$rect->setAnimation(
new Animation(
'y',
$ctx->yToAbsolute(100),
$ctx->yToAbsolute($point[1]),
rand(1, 1.5)/2
)
);
$group->appendChild($rect->toSvg($ctx));
}
return $group;
}
}

View File

@ -0,0 +1,173 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Graph;
use \DOMElement;
use \Icinga\Chart\Primitive\Drawable;
use \Icinga\Chart\Primitive\Path;
use \Icinga\Chart\Primitive\Circle;
use \Icinga\Chart\Primitive\Styleable;
use \Icinga\Chart\Render\RenderContext;
/**
* LineGraph implementation for drawing a set of datapoints as
* a connected path
*/
class LineGraph extends Styleable implements Drawable
{
/**
* The dataset to use
*
* @var array
*/
private $dataset;
/**
* True to show dots for each datapoint
*
* @var bool
*/
private $showDataPoints = false;
/**
* When true, the path will be discrete, i.e. showing hard steps instead of a direct line
*
* @var bool
*/
private $isDiscrete = false;
/**
* The default stroke width
* @var int
*/
public $strokeWidth = 5;
/**
* Create a new LineGraph displaying the given dataset
*
* @param array $dataset An array of [x, y] arrays to display
*/
public function __construct(array $dataset)
{
usort($dataset, array($this, 'sortByX'));
$this->dataset = $dataset;
}
/**
* Set datapoints to be emphased via dots
*
* @param bool $bool True to enable datapoints, otherwise false
*/
public function setShowDataPoints($bool)
{
$this->showDataPoints = $bool;
}
/**
* Sort the daset by the xaxis
*
* @param array $v1
* @param array $v2
* @return int
*/
private function sortByX(array $v1, array $v2)
{
if ($v1[0] === $v2[0]) {
return 0;
}
return ($v1[0] < $v2[0]) ? -1 : 1;
}
/**
* Configure this style
*
* @param array $cfg The configuration as given in the drawLine call
*/
public function setStyleFromConfig(array $cfg)
{
$fill = false;
foreach ($cfg as $elem => $value) {
if ($elem === 'color') {
$this->setStrokeColor($value);
} elseif ($elem === 'width') {
$this->setStrokeWidth($value);
} elseif ($elem === 'showPoints') {
$this->setShowDataPoints($value);
} elseif ($elem === 'fill') {
$fill = $value;
} elseif ($elem === 'discrete') {
$this->isDiscrete = true;
}
}
if ($fill) {
$this->setFill($this->strokeColor);
$this->setStrokeColor('black');
}
}
/**
* Render this BarChart
*
* @param RenderContext $ctx The rendering context to use for drawing
*
* @return DOMElement $dom Element
*/
public function toSvg(RenderContext $ctx)
{
$path = new Path($this->dataset);
if ($this->isDiscrete) {
$path->setDiscrete(true);
}
$path->setStrokeColor($this->strokeColor);
$path->setStrokeWidth($this->strokeWidth);
$path->setAttribute('data-icinga-graph-type', 'line');
if ($this->fill !== 'none') {
$firstX = $this->dataset[0][0];
$lastX = $this->dataset[count($this->dataset)-1][0];
$path->prepend(array($firstX, 100))
->append(array($lastX, 100));
$path->setFill($this->fill);
}
$path->setAdditionalStyle('clip-path: url(#clip);');
$path->setId($this->id);
$group = $path->toSvg($ctx);
if ($this->showDataPoints === true) {
foreach ($this->dataset as $point) {
$dot = new Circle($point[0], $point[1], $this->strokeWidth*5);
$dot->setFill('black');
$group->appendChild($dot->toSvg($ctx));
}
}
return $group;
}
}

View File

@ -0,0 +1,109 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Graph;
use \DOMElement;
use \Icinga\Chart\Primitive\Drawable;
use \Icinga\Chart\Render\RenderContext;
/**
* Graph implementation that stacks several graphs and displays them in a cumulative way
*/
class StackedGraph implements Drawable
{
/**
* All graphs displayed in this stackedgraph
*
* @var array
*/
private $stack = array();
/**
* An associative array containing x points as the key and an array of y values as the value
*
* @var array
*/
private $points = array();
/**
* Add a graph to this stack and aggregate the values on the fly
*
* This modifies the dataset as a side effect
*
* @param array $subGraph
*/
public function addGraph(array &$subGraph)
{
foreach ($subGraph['data'] as &$point) {
$x = $point[0];
if (!isset($this->points[$x])) {
$this->points[$x] = 0;
}
$this->points[$x] += $point[1];
$point[1] = $this->points[$x];
}
}
/**
* Add a graph to the stack
*
* @param $graph
*/
public function addToStack($graph)
{
$this->stack[] = $graph;
}
/**
* Empty the stack
*
* @return bool
*/
public function stackEmpty()
{
return empty($this->stack);
}
/**
* Render this stack in the correct order
*
* @param RenderContext $ctx The context to use for rendering
*
* @return DOMElement The SVG representation of this graph
*/
public function toSvg(RenderContext $ctx)
{
$group = $ctx->getDocument()->createElement('g');
$renderOrder = array_reverse($this->stack);
foreach ($renderOrder as $stackElem) {
$group->appendChild($stackElem->toSvg($ctx));
}
return $group;
}
}

View File

@ -0,0 +1,424 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart;
use \DOMElement;
use \Icinga\Chart\Chart;
use \Icinga\Chart\Axis;
use \Icinga\Chart\Graph\BarGraph;
use \Icinga\Chart\Graph\LineGraph;
use \Icinga\Chart\Graph\StackedGraph;
use \Icinga\Chart\Primitive\Canvas;
use \Icinga\Chart\Primitive\Rect;
use \Icinga\Chart\Primitive\Path;
use \Icinga\Chart\Render\LayoutBox;
use \Icinga\Chart\Render\RenderContext;
use \Icinga\Chart\Unit\AxisUnit;
/**
* Base class for grid based charts.
*
* Allows drawing of Line and Barcharts. See the graphing documentation for further details.
*
* Example:
* <pre>
* <code>
* $this->chart = new GridChart();
* $this->chart->setAxisLabel("X axis label", "Y axis label");
* $this->chart->setXAxis(Axis::CalendarUnit());
* $this->chart->drawLines(
* array(
* 'data' => array(
* array(time()-7200, 10),array(time()-3620, 30), array(time()-1800, 15), array(time(), 92))
* )
* );
* </code>
* </pre>
*/
class GridChart extends Chart
{
/**
* Internal identifier for Line Chart elements
*/
const TYPE_LINE = "LINE";
/**
* Internal identifier fo Bar Chart elements
*/
const TYPE_BAR = "BAR";
/**
* Internal array containing all elements to be drawn in the order they are drawn
*
* @var array
*/
private $graphs = array();
/**
* An associative array containing all axis of this Chart in the "name" => Axis() form.
*
* Currently only the 'default' axis is really supported
*
* @var array
*/
private $axis = array();
/**
* An associative array containing all StackedGraph objects used for cumulative graphs
*
* The array key is the 'stack' value given in the graph definitions
*
* @var array
*/
private $stacks = array();
/**
* Check if the current dataset has the proper structure for this chart.
*
* Needs to be overwritten by extending classes. The default implementation returns false.
*
* @return bool True when the dataset is valid, otherwise false
*/
public function isValidDataFormat()
{
foreach ($this->graphs as $values) {
foreach ($values as $value) {
if (!isset($value['data']) || !is_array($value['data'])) {
return false;
}
}
}
return true;
}
/**
* Calls Axis::addDataset for every graph added to this GridChart
*
* @see Axis::addDataset
*/
private function configureAxisFromDatasets()
{
foreach ($this->graphs as $axis => &$graphs) {
$axisObj = $this->axis[$axis];
foreach ($graphs as &$graph) {
$axisObj->addDataset($graph);
}
}
}
/**
* Add an arbitrary number of lines to be drawn
*
* Refer to the graphs.md for a detailed list of allowed attributes
*
* @param array $axis,... The line definitions to draw
*
* @return self Fluid interface
*/
public function drawLines(array $axis)
{
$this->draw(self::TYPE_LINE, func_get_args());
return $this;
}
/**
* Add arbitrary number of bars to be drawn
*
* Refer to the graphs.md for a detailed list of allowed attributes
*
* @param array $axis
* @return self
*/
public function drawBars(array $axis)
{
$this->draw(self::TYPE_BAR, func_get_args());
return $this;
}
/**
* Generic method for adding elements to the drawing stack
*
* @param string $type The type of the element to draw (see TYPE_ constants in this class)
* @param array $data The data given to the draw call
*/
private function draw($type, $data)
{
$axisName = 'default';
if (is_string($data[0])) {
$axisName = $data[0];
array_shift($data);
}
foreach ($data as &$graph) {
$graph['graphType'] = $type;
if (isset($graph['stack'])) {
if (!isset($this->stacks[$graph['stack']])) {
$this->stacks[$graph['stack']] = new StackedGraph();
}
$this->stacks[$graph['stack']]->addGraph($graph);
$graph['stack'] = $this->stacks[$graph['stack']];
}
if (!isset($graph['color'])) {
$colorType = isset($graph['palette']) ? $graph['palette'] : Palette::NEUTRAL;
$graph['color'] = $this->palette->getNext($colorType);
}
$this->graphs[$axisName][] = $graph;
if ($this->legend) {
$this->legend->addDataset($graph);
}
}
}
/**
* Set the label for the x and y axis
*
* @param string $xAxisLabel The label to use for the x axis
* @param string $yAxisLabel The label to use for the y axis
* @param string $axisName The name of the axis, for now 'default'
*
* @return self Fluid interface
*/
public function setAxisLabel($xAxisLabel, $yAxisLabel, $axisName = 'default')
{
$this->axis[$axisName]->setXLabel($xAxisLabel)->setYLabel($yAxisLabel);
return $this;
}
/**
* Set the AxisUnit to use for calculating the values of the x axis
*
* @param AxisUnit $unit The unit for the x axis
* @param string $axisName The name of the axis to set the label for, currently only 'default'
*
* @return self Fluid interface
*/
public function setXAxis(AxisUnit $unit, $axisName = 'default')
{
$this->axis[$axisName]->setUnitForXAxis($unit);
return $this;
}
/**
* Set the AxisUnit to use for calculating the values of the y axis
*
* @param AxisUnit $unit The unit for the y axis
* @param string $axisName The name of the axis to set the label for, currently only 'default'
*
* @return self Fluid interface
*/
public function setYAxis(AxisUnit $unit, $axisName = 'default')
{
$this->axis[$axisName]->setUnitForYAxis($unit);
return $this;
}
/**
* Pre-render setup of the axis
*
* @see Chart::build
*/
protected function build()
{
$this->configureAxisFromDatasets();
}
/**
* Initialize the renderer and overwrite it with an 2:1 ration renderer
*/
protected function init()
{
$this->renderer = new SVGRenderer(2, 1);
$this->setAxis(Axis::createLinearAxis());
}
/**
* Overwrite the axis to use
*
* @param Axis $axis The new axis to use
* @param string $name The name of the axis, currently only 'default'
*
* @return self Fluid interface
*/
public function setAxis(Axis $axis, $name = 'default')
{
$this->axis = array($name => $axis);
return $this;
}
/**
* Add an axis to this graph (not really supported right now)
*
* @param Axis $axis The axis object to add
* @param string $name The name of the axis
*
* @return self Fluid interface
*/
public function addAxis(Axis $axis, $name)
{
$this->axis[$name] = $axis;
return $this;
}
/**
* Set minimum values for the x and y axis.
*
* Setting null to an axis means this will use a value determined by the dataset
*
* @param int $xMin The minimum value for the x axis or null to use a dynamic value
* @param int $yMin The minimum value for the y axis or null to use a dynamic value
* @param string $axisName The name of the axis to set the minimum, currently only 'default'
*
* @return self Fluid interface
*/
public function setAxisMin($xMin = null, $yMin = null, $axisName = 'default')
{
$this->axis[$axisName]->setXMin($xMin)->setYMin($yMin);
return $this;
}
/**
* Set maximum values for the x and y axis.
*
* Setting null to an axis means this will use a value determined by the dataset
*
* @param int $xMax The maximum value for the x axis or null to use a dynamic value
* @param int $yMax The maximum value for the y axis or null to use a dynamic value
* @param string $axisName The name of the axis to set the maximum, currently only 'default'
*
* @return self Fluid interface
*/
public function setAxisMax($xMax = null, $yMax = null, $axisName = 'default')
{
$this->axis[$axisName]->setXMax($xMax)->setYMax($yMax);
return $this;
}
/**
* Render this GridChart to SVG
*
* @param RenderContext $ctx The context to use for rendering
*
* @return DOMElement
*/
public function toSvg(RenderContext $ctx)
{
$outerBox = new Canvas('outerGraph', new LayoutBox(0, 0, 100, 100));
$innerBox = new Canvas('graph', new LayoutBox(0, 0, 95, 90));
$maxPadding = array(0,0,0,0);
foreach ($this->axis as $axis) {
$padding = $axis->getRequiredPadding();
for ($i=0; $i < count($padding); $i++) {
$maxPadding[$i] = max($maxPadding[$i], $padding[$i]);
}
$innerBox->addElement($axis);
}
$this->renderGraphContent($innerBox);
$innerBox->getLayout()->setPadding($maxPadding[0], $maxPadding[1], $maxPadding[2], $maxPadding[3]);
$this->createContentClipBox($innerBox);
$outerBox->addElement($innerBox);
if ($this->legend) {
$outerBox->addElement($this->legend);
}
return $outerBox->toSvg($ctx);
}
/**
* Create a clip box that defines which area of the graph is drawable and adds it to the graph.
*
* The clipbox has the id '#clip' and can be used in the clip-mask element
*
* @param Canvas $innerBox The inner canvas of the graph to add the clip box to
*/
private function createContentClipBox(Canvas $innerBox)
{
$clipBox = new Canvas('clip', new LayoutBox(0, 0, 100, 100));
$clipBox->toClipPath();
$innerBox->addElement($clipBox);
$rect = new Rect(0.1, 0, 100, 99.9);
$clipBox->addElement($rect);
}
/**
* Render the content of the graph, i.e. the draw stack
*
* @param Canvas $innerBox The inner canvas of the graph to add the content to
*/
private function renderGraphContent(Canvas $innerBox)
{
foreach ($this->graphs as $axisName => $graphs) {
$axis = $this->axis[$axisName];
$graphObj = null;
foreach ($graphs as $graph) {
// determine the type and create a graph object for it
switch ($graph['graphType']) {
case self::TYPE_BAR:
$graphObj = new BarGraph($axis->transform($graph['data']));
break;
case self::TYPE_LINE:
$graphObj = new LineGraph($axis->transform($graph['data']));
break;
default:
continue;
}
$el = $this->setupGraph($graphObj, $graph);
if ($el) {
$innerBox->addElement($el);
}
}
}
}
/**
* Setup the provided Graph type
*
* @param mixed $graphObject The graph class, needs the setStyleFromConfig method
* @param array $graphConfig The configration array of the graph
*
* @return mixed Either the graph to be added or null if the graph is not directly added
* to the document (e.g. stacked graphs are added by
* the StackedGraph Composite object)
*/
private function setupGraph($graphObject, array $graphConfig)
{
$graphObject->setStyleFromConfig($graphConfig);
// When in a stack return the StackedGraph object instead of the graphObject
if (isset($graphConfig['stack'])) {
$graphConfig['stack']->addToStack($graphObject);
if (!$graphConfig['stack']->stackEmpty()) {
return $graphConfig['stack'];
}
// return no object when the graph should not be rendered
return null;
}
return $graphObject;
}
}

View File

@ -0,0 +1,126 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart;
use \DOMElement;
use \Icinga\Chart\Palette;
use \Icinga\Chart\Primitive\Canvas;
use \Icinga\Chart\Primitive\Drawable;
use \Icinga\Chart\Primitive\Rect;
use \Icinga\Chart\Primitive\Text;
use \Icinga\Chart\Render\LayoutBox;
use \Icinga\Chart\Render\RenderContext;
/**
* Drawable for creating a Graph Legend on the bottom of a graph.
*
* Usually used by the GridChart class internally.
*/
class Legend implements Drawable
{
/**
* Internal counter for unnamed label identifiers
*
* @var int
*/
private $internalCtr = 0;
/**
*
* Content of this legend
*
* @var array
*/
private $dataset = array();
/**
* Set the content to be displayed by this legend
*
* @param array $dataset An array of datasets in the form they are provided to the graphing implementation
*/
public function addDataset(array $dataset)
{
if (!isset($dataset['label'])) {
$dataset['label'] = 'Dataset ' . (++$this->internalCtr);
}
if (!isset($dataset['color'])) {
return;
}
$this->dataset[$dataset['color']] = $dataset['label'];
}
/**
* Render the legend to an SVG object
*
* @param RenderContext $ctx The context to use for rendering this legend
*
* @return DOMElement The SVG representation of this legend
*/
public function toSvg(RenderContext $ctx)
{
$outer = new Canvas('legend', new LayoutBox(0, 90, 100, 100));
$outer->getLayout()->setPadding(2, 2, 2, 2);
$nrOfColumns = 4;
$leftstep = 100 / $nrOfColumns;
$topstep = 10 / $nrOfColumns + 2;
$top = 0;
$left = 0;
$lastLabelEndPos = -1;
foreach ($this->dataset as $color => $text) {
// Make sure labels don't overlap each other
while ($lastLabelEndPos >= $left) {
$left += $leftstep;
}
// When a label is longer than the available space, use the next line
if ($left + strlen($text) > 100) {
$top += $topstep;
$left = 0;
}
$colorBox = new Rect($left, $top, 2, 2);
$colorBox->setFill($color)->setStrokeWidth(2);
$colorBox->keepRatio();
$outer->addElement($colorBox);
$textBox = new Text($left+5, $top+2, $text);
$textBox->setFontSize('2em');
$outer->addElement($textBox);
// readjust layout
$lastLabelEndPos = $left + strlen($text);
$left += $leftstep;
}
$svg = $outer->toSvg($ctx);
return $svg;
}
}

View File

@ -0,0 +1,89 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart;
/**
* Provide a set of colors that will be used by the chart as default values
*/
class Palette
{
/**
* Neutral colors without special meaning
*/
const NEUTRAL = 'neutral';
/**
* A set of problem (i.e. red) colors
*/
const PROBLEM = 'problem';
/**
* A set of ok (i.e. green) colors
*/
const OK = 'ok';
/**
* A set of warning (i.e. yellow) colors
*/
const WARNING = 'warning';
/**
* The colorsets for specific categories
*
* @var array
*/
public $colorSets = array(
self::OK => array('#00FF00','#00C90D', '#008209', '#238C47', '#00BB3F', '#37DD6F'),
self::PROBLEM => array('#FF0000','#FF1300', '#FF4E40', '#A60C00', '#FF4500', '#A62D00'),
self::WARNING => array('#FFFF00', 'B4B400' , '#A6A600', '#F5FF73', '#FFB300', '#BFA730'),
self::NEUTRAL => array('#232323', '#009999', '#1D7373', '#ACACFF', '#8F9ABF', '#356AA6')
);
/**
* Return the next available color as an hex string for the given type
*
* @param string $type The type to receive a color from
*
* @return string The color in hex format
*/
public function getNext($type = self::NEUTRAL)
{
if (!isset($this->colorSets[$type])) {
$type = self::NEUTRAL;
}
$color = current($this->colorSets[$type]);
if ($color === false) {
reset($this->colorSets[$type]);
$color = current($this->colorSets[$type]);
}
next($this->colorSets[$type]);
return $color;
}
}

View File

@ -0,0 +1,306 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart;
use \DOMElement;
use \Icinga\Chart\Chart;
use \Icinga\Chart\Primitive\Canvas;
use \Icinga\Chart\Primitive\PieSlice;
use \Icinga\Chart\Primitive\RawElement;
use \Icinga\Chart\Primitive\Rect;
use \Icinga\Chart\Render\RenderContext;
use \Icinga\Chart\Render\LayoutBox;
/**
* Graphing component for rendering Pie Charts.
*
* See the graphs.md documentation for futher information about how to use this component
*/
class PieChart extends Chart
{
/**
* Stack multiple pies
*/
const STACKED = "stacked";
/**
* Draw multiple pies beneath each other
*/
const ROW = "row";
/**
* The drawing stack containing all pie definitions in the order they will be drawn
*
* @var array
*/
private $pies = array();
/**
* The composition type currently used
*
* @var string
*/
private $type = PieChart::STACKED;
/**
* Disable drawing of captions when set true
*
* @var bool
*/
private $noCaption = false;
/**
* Test if the given pies have the correct format
*
* @return bool True when the given pies are correct, otherwise false
*/
public function isValidDataFormat()
{
foreach ($this->pies as $pie) {
if (!isset($pie['data']) || !is_array($pie['data'])) {
return false;
}
}
return true;
}
/**
* Create renderer and normalize the dataset to represent percentage information
*/
protected function build()
{
$this->renderer = new SVGRenderer(($this->type === self::STACKED) ? 1 : count($this->pies), 1);
foreach ($this->pies as &$pie) {
$this->normalizeDataSet($pie);
}
}
/**
* Normalize the given dataset to represent percentage information instead of absolute valuess
*
* @param array $pie The pie definition given in the drawPie call
*/
private function normalizeDataSet(&$pie)
{
$total = array_sum($pie['data']);
if ($total === 100) {
return;
}
foreach ($pie['data'] as &$slice) {
$slice = $slice/$total * 100;
}
}
/**
* Draw an arbitrary number of pies in this chart
*
* @param array $dataSet,... The pie definition, see graphs.md for further details concerning the format
*
* @return self Fluent interface
*/
public function drawPie(array $dataSet)
{
$dataSets = func_get_args();
$this->pies += $dataSets;
foreach ($dataSets as $dataSet) {
$this->legend->addDataset($dataSet);
}
return $this;
}
/**
* Return the SVG representation of this graph
*
* @param RenderContext $ctx The context to use for drawings
*
* @return DOMElement The SVG representation of this graph
*/
public function toSvg(RenderContext $ctx)
{
$outerBox = new Canvas('outerGraph', new LayoutBox(0, 0, 100, 100));
$innerBox = new Canvas('graph', new LayoutBox(0, 0, 100, 100));
$labelBox = $ctx->getDocument()->createElement('g');
$innerBox->getLayout()->setPadding(10, 10, 10, 10);
$this->createContentClipBox($innerBox);
$this->renderPies($innerBox, $labelBox);
$innerBox->addElement(new RawElement($labelBox));
$outerBox->addElement($innerBox);
return $outerBox->toSvg($ctx);
}
/**
* Render the pies in the draw stack using the selected algorithm for composition
*
* @param Canvas $innerBox The canvas to use for inserting the pies
* @param DOMElement $labelBox The DOM element to add the labels to (so they can't be overlapped by pie elements)
*/
private function renderPies(Canvas $innerBox, DOMElement $labelBox)
{
if ($this->type === self::STACKED) {
$this->renderStackedPie($innerBox, $labelBox);
} else {
$this->renderPieRow($innerBox, $labelBox);
}
}
/**
* Return the color to be used for the given pie slice
*
* @param array $pie The pie configuration as provided in the drawPie call
* @param int $dataIdx The index of the pie slice in the pie configuration
*
* @return string The hex color string to use for the pie slice
*/
private function getColorForPieSlice(array $pie, $dataIdx)
{
if (isset($pie['colors']) && is_array($pie['colors']) && isset($pie['colors'][$dataIdx])) {
return $pie['colors']['dataIdx'];
}
$type = Palette::NEUTRAL;
if (isset($pie['palette']) && is_array($pie['palette']) && isset($pie['palette'][$dataIdx])) {
$type = $pie['palette'][$dataIdx];
}
return $this->palette->getNext($type);
}
/**
* Render a row of pies
*
* @param Canvas $innerBox The canvas to insert the pies to
* @param DOMElement $labelBox The DOMElement to use for adding label elements
*/
private function renderPieRow(Canvas $innerBox, DOMElement $labelBox)
{
$radius = 50 / count($this->pies);
$x = $radius;
foreach ($this->pies as $pie) {
$labelPos = 0;
$lastRadius = 0;
foreach ($pie['data'] as $idx => $dataset) {
$slice = new PieSlice($radius, $dataset, $lastRadius);
$slice->setX($x)
->setStrokeColor('#000')
->setStrokeWidth(1)
->setY(50)
->setFill($this->getColorForPieSlice($pie, $idx));
$innerBox->addElement($slice);
// add caption if not disabled
if (!$this->noCaption && isset($pie['labels'])) {
$slice->setCaption($pie['labels'][$labelPos++])
->setLabelGroup($labelBox);
}
$lastRadius += $dataset;
}
// shift right for next pie
$x += $radius*2;
}
}
/**
* Render pies in a stacked way so one pie is nested in the previous pie
*
* @param Canvas $innerBox The canvas to insert the pie to
* @param DOMElement $labelBox The DOMElement to use for adding label elements
*/
private function renderStackedPie(Canvas $innerBox, DOMElement $labelBox)
{
$radius = 50;
$minRadius = 20;
$shrinkStep = ($radius - $minRadius) / count($this->pies);
$x = $radius;
for ($i = 0; $i < count($this->pies); $i++) {
$pie = $this->pies[$i];
// the offset for the caption path, outer caption indicator shouldn't point
// to the middle of the slice as there will be another pie
$offset = isset($this->pies[$i+1]) ? $radius - $shrinkStep : 0;
$labelPos = 0;
$lastRadius = 0;
foreach ($pie['data'] as $idx => $dataset) {
$slice = new PieSlice($radius, $dataset, $lastRadius);
$slice->setY(50)
->setX($x)
->setStrokeColor('#000')
->setStrokeWidth(1)
->setFill($this->getColorForPieSlice($pie, $idx))
->setLabelGroup($labelBox);
if (!$this->noCaption && isset($pie['labels'])) {
$slice->setCaption($pie['labels'][$labelPos++])
->setCaptionOffset($offset)
->setOuterCaptionBound(50);
}
$innerBox->addElement($slice);
$lastRadius += $dataset;
}
// shrinken the next pie
$radius -= $shrinkStep;
}
}
/**
* Set the composition type of this PieChart
*
* @param string $type Either self::STACKED or self::ROW
*
* @return self Fluent interface
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* Hide the caption from this PieChart
*
* @return self Fluent interface
*/
public function disableLegend()
{
$this->noCaption = true;
return $this;
}
/**
* Create the content for this PieChart
*
* @param Canvas $innerBox The innerbox to add the clip mask to
*/
private function createContentClipBox(Canvas $innerBox)
{
$clipBox = new Canvas('clip', new LayoutBox(0, 0, 100, 100));
$clipBox->toClipPath();
$innerBox->addElement($clipBox);
$rect = new Rect(0.1, 0, 100, 99.9);
$clipBox->addElement($rect);
}
}

View File

@ -0,0 +1,68 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DOMElement;
use \Icinga\Chart\Render\RenderContext;
/**
* Base interface for animatable objects
*/
abstract class Animatable extends Styleable
{
/**
* The animation object set
*
* @var Animation
*/
public $animation = null;
/**
* Set the animation for this object
*
* @param Animation $anim The animation to use
*/
public function setAnimation(Animation $anim)
{
$this->animation = $anim;
}
/**
* Append the animation to the given element
*
* @param DOMElement $dom The element to append the animation to
* @param RenderContext $ctx The context to use for rendering the animation object
*/
protected function appendAnimation(DOMElement $dom, RenderContext $ctx)
{
if ($this->animation) {
$dom->appendChild($this->animation->toSvg($ctx));
}
}
}

View File

@ -0,0 +1,112 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DOMElement;
use \Icinga\Chart\Render\RenderContext;
/**
* Drawable for the SVG animate tag
*/
class Animation implements Drawable
{
/**
* The attribute to animate
*
* @var string
*/
private $attribute;
/**
* The 'from' value
*
* @var mixed
*/
private $from;
/**
* The to value
*
* @var mixed
*/
private $to;
/**
* The begin value (in seconds)
*
* @var float
*/
private $begin = 0;
/**
* The duration value (in seconds)
*
* @var float
*/
private $duration = 0.5;
/**
* Create an animation object
*
* @param string $attribute The attribute to animate
* @param string $from The from value for the animation
* @param string $to The to value for the animation
* @param float $duration The duration of the duration
* @param float $begin The begin of the duration
*/
public function __construct($attribute, $from, $to, $duration = 0.5, $begin = 0.0)
{
$this->attribute = $attribute;
$this->from = $from;
$this->to = $to;
$this->duration = $duration;
$this->begin = $begin;
}
/**
* Create the SVG representation from this Drawable
*
* @param RenderContext $ctx The context to use for rendering
* @return DOMElement The SVG Element
*/
public function toSvg(RenderContext $ctx)
{
$animate = $ctx->getDocument()->createElement('animate');
$animate->setAttribute('attributeName', $this->attribute);
$animate->setAttribute('attributeType', 'XML');
$animate->setAttribute('from', $this->from);
$animate->setAttribute('to', $this->to);
$animate->setAttribute('begin', $this->begin . 's');
$animate->setAttribute('dur', $this->duration . 's');
$animate->setAttributE('fill', "freeze");
return $animate;
}
}

View File

@ -0,0 +1,141 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DOMElement;
use \Icinga\Chart\Render\LayoutBox;
use \Icinga\Chart\Render\RenderContext;
/**
* Canvas SVG component that encapsulates grouping and padding and allows rendering
* multiple elements in a group
*
*/
class Canvas implements Drawable
{
/**
* The name of the canvas, will be used as the id
*
* @var string
*/
private $name;
/**
* An array of child elements of this Canvas
*
* @var array
*/
private $children = array();
/**
* When true, this canvas is encapsulated in a clipPath tag and not drawn
*
* @var bool
*/
private $isClipPath = false;
/**
* The LayoutBox of this Canvas
*
* @var LayoutBox
*/
private $rect;
/**
* Create this canvas
*
* @param String $name The name of this canvas
* @param LayoutBox $rect The layout and size of this canvas
*/
public function __construct($name, LayoutBox $rect)
{
$this->rect = $rect;
$this->name = $name;
}
/**
* Convert this canvas to a clipPath element
*/
public function toClipPath()
{
$this->isClipPath = true;
}
/**
* Return the layout of this canvas
*
* @return LayoutBox
*/
public function getLayout()
{
return $this->rect;
}
/**
* Add an element to this canvas
*
* @param Drawable $child
*/
public function addElement(Drawable $child)
{
$this->children[] = $child;
}
/**
* Create the SVG representation from this Drawable
*
* @param RenderContext $ctx The context to use for rendering
* @return DOMElement The SVG Element
*/
public function toSvg(RenderContext $ctx)
{
$doc = $ctx->getDocument();
if ($this->isClipPath) {
$outer = $doc->createElement('defs');
$innerContainer = $element = $doc->createElement('clipPath');
$outer->appendChild($element);
} else {
$outer = $element = $doc->createElement('g');
$innerContainer = $doc->createElement('g');
$innerContainer->setAttribute('x', 0);
$innerContainer->setAttribute('y', 0);
$innerContainer->setAttribute('id', $this->name . '_inner');
$innerContainer->setAttribute('transform', $this->rect->getInnerTransform($ctx));
$element->appendChild($innerContainer);
}
$element->setAttribute('id', $this->name);
foreach ($this->children as $child) {
$innerContainer->appendChild($child->toSvg($ctx));
}
return $outer;
}
}

View File

@ -0,0 +1,92 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DOMElement;
use \Icinga\Chart\Render\RenderContext;
/**
* Drawable for svg circles
*/
class Circle extends Styleable implements Drawable
{
/**
* The circles x position
*
* @var int
*/
private $x;
/**
* The circles y position
*
* @var int
*/
private $y;
/**
* The circles radius
*
* @var int
*/
private $radius;
/**
* Construct the circle
*
* @param int $x The x position of the circle
* @param int $y The y position of the circle
* @param int $radius The radius of the circle
*/
public function __construct($x, $y, $radius)
{
$this->x = $x;
$this->y = $y;
$this->radius = $radius;
}
/**
* Create the SVG representation from this Drawable
*
* @param RenderContext $ctx The context to use for rendering
* @return DOMElement The SVG Element
*/
public function toSvg(RenderContext $ctx)
{
$coords = $ctx->toAbsolute($this->x, $this->y);
$circle = $ctx->getDocument()->createElement('circle');
$circle->setAttribute('cx', $coords[0]);
$circle->setAttribute('cy', $coords[1]);
$circle->setAttribute('r', 5);
$circle->setAttribute('style', $this->getStyle());
$this->applyAttributes($circle);
return $circle;
}
}

View File

@ -0,0 +1,47 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DOMElement;
use \Icinga\Chart\Render\RenderContext;
/**
* Drawable element for creating svg out of components
*/
interface Drawable
{
/**
* Create the SVG representation from this Drawable
*
* @param RenderContext $ctx The context to use for rendering
*
* @return DOMElement The SVG Element
*/
public function toSvg(RenderContext $ctx);
}

View File

@ -0,0 +1,111 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DOMElement;
use \Icinga\Chart\Render\RenderContext;
/**
* Drawable for the svg line element
*/
class Line extends Styleable implements Drawable
{
/**
* The default stroke width
*
* @var int
*/
public $strokeWidth = 1;
/**
* The line's start x coordinate
*
* @var int
*/
private $xStart = 0;
/**
* The line's end x coordinate
*
* @var int
*/
private $xEnd = 0;
/**
* The line's start y coordinate
*
* @var int
*/
private $yStart = 0;
/**
* The line's end y coordinate
*
* @var int
*/
private $yEnd = 0;
/**
* Create a line object starting at the first coordinate and ending at the second one
*
* @param int $x1 The line's start x coordinate
* @param int $y1 The line's start y coordinate
* @param int $x2 The line's end x coordinate
* @param int $y2 The line's end y coordinate
*/
public function __construct($x1, $y1, $x2, $y2)
{
$this->xStart = $x1;
$this->xEnd = $x2;
$this->yStart = $y1;
$this->yEnd = $y2;
}
/**
* Create the SVG representation from this Drawable
*
* @param RenderContext $ctx The context to use for rendering
* @return DOMElement The SVG Element
*/
public function toSvg(RenderContext $ctx)
{
$doc = $ctx->getDocument();
list($x1, $y1) = $ctx->toAbsolute($this->xStart, $this->yStart);
list($x2, $y2) = $ctx->toAbsolute($this->xEnd, $this->yEnd);
$line = $doc->createElement('line');
$line->setAttribute('x1', $x1);
$line->setAttribute('x2', $x2);
$line->setAttribute('y1', $y1);
$line->setAttribute('y2', $y2);
$line->setAttribute('style', $this->getStyle());
$this->applyAttributes($line);
return $line;
}
}

View File

@ -0,0 +1,197 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DOMElement;
use \Icinga\Chart\Render\RenderContext;
/**
* Drawable for creating a svg path element
*/
class Path extends Styleable implements Drawable
{
/**
* Syntax template for moving
*
* @see http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
*/
const TPL_MOVE = 'M %s %s ';
/**
* Syntax template for bezier curve
*
* @see http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands
*/
const TPL_BEZIER = 'S %s %s ';
/**
* Syntax template for straight lines
*
* @see http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
*/
const TPL_STRAIGHT = 'L %s %s ';
/**
* The default stroke width
*
* @var int
*/
public $strokeWidth = 1;
/**
* True to treat coordinates as absolute values
*
* @var bool
*/
protected $isAbsolute = false;
/**
* The points to draw, in the order they are drawn
*
* @var array
*/
protected $points = array();
/**
* True to draw the path discrete, i.e. make hard steps between points
*
* @var bool
*/
protected $discrete = false;
/**
* Create the path using the given points
*
* @param array $points Either a single [x, y] point or an array of x, y points
*/
public function __construct(array $points)
{
$this->append($points);
}
/**
* Append a single point or an array of points to this path
*
* @param array $points Either a single [x, y] point or an array of x, y points
*
* @return self Fluid interface
*/
public function append(array $points)
{
if (count($points) === 0) {
return $this;
}
if (!is_array($points[0])) {
$points = array($points);
}
$this->points = array_merge($this->points, $points);
return $this;
}
/**
* Prepend a single point or an array of points to this path
*
* @param array $points Either a single [x, y] point or an array of x, y points
*
* @return self Fluid interface
*/
public function prepend(array $points)
{
if (count($points) === 0) {
return $this;
}
if (!is_array($points[0])) {
$points = array($points);
}
$this->points = array_merge($points, $this->points);
return $this;
}
/**
* Set this path to be discrete
*
* @param boolean $bool True to draw discrete or false to draw straight lines between points
*
* @return self Fluid interface
*/
public function setDiscrete($bool)
{
$this->discrete = $bool;
return $this;
}
/**
* Mark this path as containing absolute coordinates
*
* @return self Fluid interface
*/
public function toAbsolute()
{
$this->isAbsolute = true;
return $this;
}
/**
* Create the SVG representation from this Drawable
*
* @param RenderContext $ctx The context to use for rendering
* @return DOMElement The SVG Element
*/
public function toSvg(RenderContext $ctx)
{
$doc = $ctx->getDocument();
$group = $doc->createElement('g');
$pathDescription = '';
$tpl = self::TPL_MOVE;
$lastPoint = null;
foreach ($this->points as $point) {
if (!$this->isAbsolute) {
$point = $ctx->toAbsolute($point[0], $point[1]);
}
if ($lastPoint && $this->discrete) {
$pathDescription .= sprintf($tpl, $point[0], $lastPoint[1]);
}
$pathDescription .= vsprintf($tpl, $point);
$lastPoint = $point;
$tpl = self::TPL_STRAIGHT;
}
$path = $doc->createElement('path');
if ($this->id) {
$path->setAttribute('id', $this->id);
}
$path->setAttribute('d', $pathDescription);
$path->setAttribute('style', $this->getStyle());
$this->applyAttributes($path);
$group->appendChild($path);
return $group;
}
}

View File

@ -0,0 +1,310 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DOMElement;
use \Icinga\Chart\Render\RenderContext;
/**
* Component for drawing a pie slice
*/
class PieSlice extends Animatable implements Drawable
{
/**
* The radius of this pieslice relative to the canvas
*
* @var int
*/
private $radius = 50;
/**
* The start radian of the pie slice
*
* @var float
*/
private $startRadian = 0;
/**
* The end radian of the pie slice
*
* @var float
*/
private $endRadian= 0;
/**
* The x position of the pie slice's center
*
* @var int
*/
private $x;
/**
* The y position of the pie slice's center
*
* @var int
*/
private $y;
/**
* The caption of the pie slice, empty string means no caption
*
* @var string
*/
private $caption = "";
/**
* The offset of the caption, shifting the indicator from the center of the pie slice
*
* This is required for nested pie slices.
*
* @var int
*/
private $captionOffset = 0;
/**
* The minimum radius the label must respect
*
* @var int
*/
private $outerCaptionBound = 0;
/**
* An optional group element to add labels to when rendering
*
* @var DOMElement
*/
private $labelGroup;
/**
* Create a pie slice
*
* @param int $radius The radius of the slice
* @param int $percent The percentage the slice represents
* @param int $percentStart The percentage where this slice starts
*/
public function __construct($radius, $percent, $percentStart = 0)
{
$this->x = $this->y = $this->radius = $radius;
$this->startRadian = M_PI * $percentStart/50;
$this->endRadian = M_PI * ($percent + $percentStart)/50;
}
/**
* Create the path for the pie slice
*
* @param int $x The x position of the pie slice
* @param int $y The y position of the pie slice
* @param int $r The absolute radius of the pie slice
*
* @return string A SVG path string
*/
private function getPieSlicePath($x, $y, $r)
{
// start at the center of the pieslice
$pathString = 'M ' . $x . ' ' . $y . ' ';
// The coordinate system is mirrored on the Y axis, so we have to flip cos and sin
$xStart = $x + intval($r * sin($this->startRadian));
$yStart = $y - intval($r * cos($this->startRadian));
$xEnd = $x + intval($r * sin($this->endRadian));
$yEnd = $y - intval($r * cos($this->endRadian));
// Draw a straight line to the upper part of the arc
$pathString .= 'L ' . $xStart . ' ' . $yStart;
// Instead of directly connecting the upper part of the arc (leaving a triangle), draw a bow with the radius
$pathString .= ' A ' . $r . ' ' . $r ;
// These are the flags for the bow, see the SVG path documentation for details
// http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
$pathString .= ' 0 ' . (($this->endRadian - $this->startRadian > M_PI) ? '1' : '0 ') . ' 1';
// xEnd and yEnd are the lower point of the arc
$pathString .= ' '.$xEnd . ' ' . $yEnd;
return $pathString;
}
/**
* Draw the label handler and the text for this pie slice
*
* @param RenderContext $ctx The rendering context to use for coordinate translation
* @param int $r The radius of the pie in absolute coordinates
*
* @return DOMElement The group DOMElement containing the handle and label
*/
private function drawDescriptionLabel(RenderContext $ctx, $r)
{
$group = $ctx->getDocument()->createElement('g');
$rOuter = ($ctx->xToAbsolute($this->outerCaptionBound) + $ctx->yToAbsolute($this->outerCaptionBound)) / 2;
$addOffset = $rOuter - $r ;
if ($addOffset < 0) {
$addOffset = 0;
}
list($x, $y) = $ctx->toAbsolute($this->x, $this->y);
$midRadius = $this->startRadian + ($this->endRadian - $this->startRadian) / 2;
list($offsetX, $offsetY) = $ctx->toAbsolute($this->captionOffset, $this->captionOffset);
$midX = $x + intval(($offsetX + $r)/2 * sin($midRadius));
$midY = $y - intval(($offsetY + $r)/2 * cos($midRadius));
// Draw the handle
$path = new Path(array($midX, $midY));
$midX += ($addOffset + $r/1.8) * ($midRadius > M_PI ? -1 : 1);
$path->append(array($midX, $midY))->toAbsolute();
$midX += intval($r/2 * sin(M_PI/7)) * ($midRadius > M_PI ? -1 : 1);
$midY -= intval($r/2 * cos(M_PI/7)) * ($midRadius < M_PI*1.5 && $midRadius > M_PI/2 ? -1 : 1);
if ($ctx->ytoRelative($midY) > 100) {
$midY = $ctx->yToAbsolute(100);
} elseif ($ctx->ytoRelative($midY) < 0) {
$midY = $ctx->yToAbsolute(0);
}
$path->append(array($midX , $midY));
$rel = $ctx->toRelative($midX, $midY);
// Draw the text box
$text = new Text($rel[0]+1.5, $rel[1], $this->caption);
$text->setFontSize('2.5em');
$text->setAlignment(($midRadius > M_PI ? Text::ALIGN_END : Text::ALIGN_START));
$group->appendChild($path->toSvg($ctx));
$group->appendChild($text->toSvg($ctx));
return $group;
}
/**
* Set the x position of the pie slice
*
* @param int $x The new x position
*
* @return self Fluid interface
*/
public function setX($x)
{
$this->x = $x;
return $this;
}
/**
* Set the y position of the pie slice
*
* @param int $y The new y position
*
* @return self Fluid interface
*/
public function setY($y)
{
$this->y = $y;
return $this;
}
/**
* Set a root element to be used for drawing labels
*
* @param DOMElement $group The label group
*
* @return self Fluid interface
*/
public function setLabelGroup(DOMElement $group)
{
$this->labelGroup = $group;
return $this;
}
/**
* Set the caption for this label
*
* @param string $caption The caption for this element
*
* @return self Fluid interface
*/
public function setCaption($caption)
{
$this->caption = $caption;
return $this;
}
/**
* Set the internal offset of the caption handle
*
* @param int $offset The offset for the caption handle
*
* @return self Fluid interface
*/
public function setCaptionOffset($offset)
{
$this->captionOffset = $offset;
return $this;
}
/**
* Set the minimum radius to be used for drawing labels
*
* @param int $bound The offset for the caption text
*
* @return self Fluid interface
*/
public function setOuterCaptionBound($bound)
{
$this->outerCaptionBound = $bound;
return $this;
}
/**
* Create the SVG representation from this Drawable
*
* @param RenderContext $ctx The context to use for rendering
*
* @return DOMElement The SVG Element
*/
public function toSvg(RenderContext $ctx)
{
$doc = $ctx->getDocument();
$group = $doc->createElement('g');
$r = ($ctx->xToAbsolute($this->radius) + $ctx->yToAbsolute($this->radius)) / 2;
list($x, $y) = $ctx->toAbsolute($this->x, $this->y);
$slicePath = $doc->createElement('path');
$slicePath->setAttribute('d', $this->getPieSlicePath($x, $y, $r));
$slicePath->setAttribute('style', $this->getStyle());
$slicePath->setAttribute('data-icinga-graph-type', 'pieslice');
$this->applyAttributes($slicePath);
$group->appendChild($slicePath);
if ($this->caption != "") {
$lblGroup = ($this->labelGroup ? $this->labelGroup : $group);
$lblGroup->appendChild($this->drawDescriptionLabel($ctx, $r));
}
return $group;
}
}

View File

@ -0,0 +1,68 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DOMElement;
use \Icinga\Chart\Render\RenderContext;
/**
* Wrapper for raw elements to be added as Drawable's
*/
class RawElement implements Drawable
{
/**
* The DOMElement wrapped by this Drawable
*
* @var DOMElement
*/
private $domEl;
/**
* Create this RawElement
*
* @param DOMElement $el The element to wrap here
*/
public function __construct(DOMElement $el)
{
$this->domEl = $el;
}
/**
* Create the SVG representation from this Drawable
*
* @param RenderContext $ctx The context to use for rendering
*
* @return DOMElement The SVG Element
*/
public function toSvg(RenderContext $ctx)
{
return $this->domEl;
}
}

View File

@ -0,0 +1,129 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DomElement;
use \Icinga\Chart\Render\RenderContext;
/**
* Drawable representing the SVG rect element
*/
class Rect extends Animatable implements Drawable
{
/**
* The x position
*
* @var int
*/
private $x;
/**
* The y position
*
* @var int
*/
private $y;
/**
* The width of this rect
*
* @var int
*/
private $width;
/**
* The height of this rect
*
* @var int
*/
private $height;
/**
* Whether to keep the ratio
*
* @var bool
*/
private $keepRatio = false;
/**
* Create this rect
*
* @param int $x The x position of the rect
* @param int $y The y position of the rectangle
* @param int $width The width of the rectangle
* @param int $height The height of the rectangle
*/
public function __construct($x, $y, $width, $height)
{
$this->x = $x;
$this->y = $y;
$this->width = $width;
$this->height = $height;
}
/**
* Call to let the rectangle keep the ratio
*/
public function keepRatio()
{
$this->keepRatio = true;
}
/**
* Create the SVG representation from this Drawable
*
* @param RenderContext $ctx The context to use for rendering
*
* @return DOMElement The SVG Element
*/
public function toSvg(RenderContext $ctx)
{
$doc = $ctx->getDocument();
$rect = $doc->createElement('rect');
list($x, $y) = $ctx->toAbsolute($this->x, $this->y);
if ($this->keepRatio) {
$ctx->keepRatio();
}
list($width, $height) = $ctx->toAbsolute($this->width, $this->height);
if ($this->keepRatio) {
$ctx->ignoreRatio();
}
$rect->setAttribute('x', $x);
$rect->setAttribute('y', $y);
$rect->setAttribute('width', $width);
$rect->setAttribute('height', $height);
$rect->setAttribute('style', $this->getStyle());
$this->applyAttributes($rect);
$this->appendAnimation($rect, $ctx);
return $rect;
}
}

View File

@ -0,0 +1,179 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DOMElement;
/**
* Base class for stylable drawables
*/
class Styleable
{
/**
* The stroke width to use
*
* @var int
*/
public $strokeWidth = 0;
/**
* The stroke color to use
*
* @var string
*/
public $strokeColor = '#000';
/**
* The fill color to use
*
* @var string
*/
public $fill = 'none';
/**
* Additional styles to be appended to the style attribute
*
* @var string
*/
public $additionalStyle = '';
/**
* The id of this element
*
* @var string
*/
public $id = null;
/**
* Additional attributes to be set
*
* @var array
*/
public $attributes = array();
/**
* Set the stroke width for this drawable
*
* @param string $width The stroke with with unit
*
* @return self Fluid interface
*/
public function setStrokeWidth($width)
{
$this->strokeWidth = $width;
return $this;
}
/**
* Set the color for the stroke or none for no stroke
*
* @param string $color The color to set for the stroke
*
* @return self Fluid interface
*/
public function setStrokeColor($color)
{
$this->strokeColor = $color ? $color : 'none';
return $this;
}
/**
* Set additional styles for this drawable
*
* @param string $styles The styles to set additionally
*
* @return self Fluid interface
*/
public function setAdditionalStyle($styles)
{
$this->additionalStyle = $styles;
return $this;
}
/**
* Set the fill for this styleable
*
* @param string $color The color to use for filling or null to use no fill
*
* @return self Fluid interface
*/
public function setFill($color = null)
{
$this->fill = $color ? $color : 'none';
return $this;
}
/**
* Set the id for this element
*
* @param string $id The id to set for this element
*
* @return self Fluid interface
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Return the content of the style attribute as a string
*
* @return string A string containing styles
*/
public function getStyle()
{
$base = sprintf("fill: %s; stroke: %s;stroke-width: %s;", $this->fill, $this->strokeColor, $this->strokeWidth);
$base .= ';' . $this->additionalStyle . ';';
return $base;
}
/**
* Add an additional attribute to this element
*/
public function setAttribute($key, $value)
{
$this->attributes[$key] = $value;
}
/**
* Apply attribute to a DOMElement
*
* @param DOMElement $el Element to apply attributes
*/
protected function applyAttributes(DOMElement $el)
{
foreach ($this->attributes as $name => $value) {
$el->setAttribute($name, $value);
}
}
}

View File

@ -0,0 +1,175 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DOMElement;
use \DOMText;
use \Icinga\Chart\Render\RenderContext;
/**
* Wrapper for the SVG text element
*/
class Text extends Styleable implements Drawable
{
/**
* Align the text to end at the x and y position
*/
const ALIGN_END = 'end';
/**
* Align the text to start at the x and y position
*/
const ALIGN_START = 'start';
/**
* Align the text to be centered at the x and y position
*/
const ALIGN_MIDDLE = 'middle';
/**
* Normal left to right orientation
*/
const ORIENTATION_HORIZONTAL = "";
/**
* Top down orientation (rotated by 90°)
*/
const ORIENTATION_VERTICAL = "writing-mode: tb;";
/**
* The x position of the Text
*
* @var int
*/
private $x;
/**
* The y position of the Text
*
* @var int
*/
private $y;
/**
* The text content
*
* @var string
*/
private $text;
/**
* The size of the font
*
* @var string
*/
private $fontSize = '1.5em';
/**
* The default fill color
*
* @var string
*/
public $fill = '#000';
/**
* The alignment of the text
*
* @var string
*/
private $alignment = self::ALIGN_START;
/**
* Construct a new text drawable
*
* @param int $x The x position of the text
* @param int $y The y position of the text
* @param string $text The text this component should contain
* @param string $fontSize The font size of the text
*/
public function __construct($x, $y, $text, $fontSize = '1.5em')
{
$this->x = $x;
$this->y = $y;
$this->text = $text;
$this->fontSize = $fontSize;
}
/**
* Set the font size of the svg text element
*
* @param string $size The font size including a unit
*
* @return self Fluid interface
*/
public function setFontSize($size)
{
$this->fontSize = $size;
return $this;
}
/**
* Set the the text alignment with one of the ALIGN_* constants
*
* @param String $align Value how to align
*
* @return self Fluid interface
*/
public function setAlignment($align)
{
$this->alignment = $align;
return $this;
}
/**
* Create the SVG representation from this Drawable
*
* @param RenderContext $ctx The context to use for rendering
*
* @return DOMElement The SVG Element
*/
public function toSvg(RenderContext $ctx)
{
list($x, $y) = $ctx->toAbsolute($this->x, $this->y);
$text = $ctx->getDocument()->createElement('text');
$text->setAttribute('x', $x - 15);
$text->setAttribute(
'style',
$this->getStyle()
. ';font-size:' . $this->fontSize
. '; font-family: Verdana, serif;'
. 'font-weight: normal; font-style: normal;'
. 'text-anchor: ' . $this->alignment
);
$text->setAttribute('y', $y);
$text->appendChild(new DOMText($this->text));
return $text;
}
}

View File

@ -0,0 +1,217 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Render;
/**
* Layout class encapsulating size, padding and margin information
*/
class LayoutBox
{
/**
* Padding index for top padding
*/
const PADDING_TOP = 0;
/**
* Padding index for right padding
*/
const PADDING_RIGHT = 1;
/**
* Padding index for bottom padding
*/
const PADDING_BOTTOM = 2;
/**
* Padding index for left padding
*/
const PADDING_LEFT = 3;
/**
* The height of this layout element
*
* @var int
*/
private $height;
/**
* The width of this layout element
*
* @var int
*/
private $width;
/**
* The x position of this layout
*
* @var int
*/
private $x;
/**
* The y position of this layout
*
* @var int
*/
private $y;
/**
* The padding of this layout
*
* @var array
*/
private $padding = array(0, 0, 0, 0);
/**
* Create this layout box
*
* Note that x, y, width and height are relative: x with 0 means leftmost, x with 100 means rightmost
*
* @param int $x The relative x coordinate
* @param int $y The relative y coordinate
* @param int $width The optional, relative width
* @param int $height The optional, relative height
*/
public function __construct($x, $y, $width = null, $height = null)
{
$this->height = $height ? $height : 100;
$this->width = $width ? $width : 100;
$this->x = $x;
$this->y = $y;
}
/**
* Set a padding to all four sides uniformly
*
* @param int $padding The padding to set for all four sides
*/
public function setUniformPadding($padding)
{
$this->padding = array($padding, $padding, $padding, $padding);
}
/**
* Set the padding for this LayoutBox
*
* @param int $top The top side padding
* @param int $right The right side padding
* @param int $bottom The bottom side padding
* @param int $left The left side padding
*/
public function setPadding($top, $right, $bottom, $left)
{
$this->padding = array($top, $right, $bottom, $left);
}
/**
* Return a string containing the SVG transform attribute values for the padding
*
* @param RenderContext $ctx The context to determine the translation coordinates
*
* @return string The transformation string
*/
public function getInnerTransform(RenderContext $ctx)
{
list($translateX, $translateY) = $ctx->toAbsolute(
$this->padding[self::PADDING_LEFT] + $this->getX(),
$this->padding[self::PADDING_TOP] + $this->getY()
);
list($scaleX, $scaleY) = $ctx->paddingToScaleFactor($this->padding);
$scaleX *= $this->getWidth()/100;
$scaleY *= $this->getHeight()/100;
return sprintf('translate(%s, %s) scale(%s, %s)', $translateX, $translateY, $scaleX, $scaleY);
}
/**
* String representation for this Layout, for debug purposes
*
* @return string A string containing the bounds of this LayoutBox
*/
public function __toString()
{
return sprintf(
'Rectangle: x: %s y: %s, height: %s, width: %s',
$this->x,
$this->y,
$this->height,
$this->width
);
}
/**
* Return a four element array with the padding
*
* @return array The padding of this LayoutBox
*/
public function getPadding()
{
return $this->padding;
}
/**
* Return the height of this LayoutBox
*
* @return int The height of this box
*/
public function getHeight()
{
return $this->height;
}
/**
* Return the width of this LayoutBox
*
* @return int The width of this box
*/
public function getWidth()
{
return $this->width;
}
/**
* Return the x position of this LayoutBox
*
* @return int The x position of this box
*/
public function getX()
{
return $this->x;
}
/**
* Return the y position of this LayoutBox
*
* @return int The y position of this box
*/
public function getY()
{
return $this->y;
}
}

View File

@ -0,0 +1,251 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Render;
use \DOMDocument;
use \Icinga\Util\Dimension;
/**
* Context for rendering, handles ratio based coordinate calculations.
*
* The most important functions when rendering are the toAbsolute and roRelative
* values, taking world coordinates and translating them into local coordinates.
*/
class RenderContext
{
/**
* The base size of the viewport, i.e. how many units are available on a 1:1 ratio
*
* @var array
*/
private $viewBoxSize = array(1000, 1000);
/**
* The DOMDocument for modifying the elements
*
* @var DOMDocument
*/
private $document;
/**
* If true no ratio correction will be made
*
* @var bool
*/
private $respectRatio = false;
/**
* The ratio on the x side. A x ration of 2 means that the width of the SVG is divided in 2000
* units (see $viewBox)
*
* @var int
*/
private $xratio = 1;
/**
* The ratio on the y side. A y ration of 2 means that the height of the SVG is divided in 2000
* units (see $viewBox)
*
* @var int
*/
private $yratio = 1;
/**
* Creates a new context for the given DOM Document
*
* @param DOMDocument $document The DOM document represented by this context
* @param int $width The width (may be approximate) of the document
* (only required for ratio calculation)
* @param int $height The height (may be approximate) of the document
* (only required for ratio calculation)
*/
public function __construct(DOMDocument $document, $width, $height)
{
$this->document = $document;
if ($width > $height) {
$this->xratio = $width/$height;
} elseif ($height < $width) {
$this->yratio = $width/$height;
}
}
/**
* Return the document represented by this Rendering context
*
* @return DOMDocument The DOMDocument for creating files
*/
public function getDocument()
{
return $this->document;
}
/**
* Let successive toAbsolute operations ignore ratio correction
*
* This can be called to avoid distortion on certain elements like rectangles.
*/
public function keepRatio()
{
$this->respectRatio = true;
}
/**
* Let successive toAbsolute operations perform ratio correction
*
* This will cause distortion on certain elements like rectangles.
*/
public function ignoreRatio()
{
$this->respectRatio = false;
}
/**
* Return how many unit s are available in the Y axis
*
* @return int The number of units available on the y axis
*/
public function getNrOfUnitsY()
{
return intval($this->viewBoxSize[1] * $this->yratio);
}
/**
* Return how many unit s are available in the X axis
*
* @return int The number of units available on the x axis
*/
public function getNrOfUnitsX()
{
return intval($this->viewBoxSize[0] * $this->xratio);
}
/**
* Transforms the x,y coordinate from relative coordinates to absolute world coordinates
*
* (50, 50) would be a point in the middle of the document and map to 500, 1000 on a
* 1000 x 1000 viewbox with a 1:2 ratio.
*
* @param int $x The relative x coordinate
* @param int $y The relative y coordinate
*
* @return array An x,y tupel containing absolute coordinates
* @see RenderContext::toRelative
*/
public function toAbsolute($x, $y)
{
return array($this->xToAbsolute($x), $this->yToAbsolute($y));
}
/**
* Transforms the x,y coordinate from absolute coordinates to relative world coordinates
*
* This is the inverse function of toAbsolute
*
* @param int $x The absolute x coordinate
* @param int $y The absolute y coordinate
*
* @return array An x,y tupel containing absolute coordinates
* @see RenderContext::toAbsolute
*/
public function toRelative($x, $y)
{
return array($this->xToRelative($x), $this->yToRelative($y));
}
/**
* Calculates the scale transformation required to apply the padding on an Canvas
*
* @param array $padding A 4 element array containing top, right, bottom and left padding
*
* @return array An array containing the x and y scale
*/
public function paddingToScaleFactor(array $padding)
{
list($horizontalPadding, $verticalPadding) = $this->toAbsolute(
$padding[LayoutBox::PADDING_RIGHT] + $padding[LayoutBox::PADDING_LEFT],
$padding[LayoutBox::PADDING_TOP] + $padding[LayoutBox::PADDING_BOTTOM]
);
return array(
($this->getNrOfUnitsX() - $horizontalPadding) / $this->getNrOfUnitsX(),
($this->getNrOfUnitsY() - $verticalPadding) / $this->getNrOfUnitsY()
);
}
/**
* Transform a relative x coordinate to an absolute one
*
* @param int $x A relative x coordinate
*
* @return int An absolute x coordinate
**/
public function xToAbsolute($x)
{
return $this->getNrOfUnitsX() / 100 * $x / ($this->respectRatio ? $this->xratio : 1);
}
/**
* Transform a relative y coordinate to an absolute one
*
* @param int $y A relative y coordinate
*
* @return int An absolute y coordinate
*/
public function yToAbsolute($y)
{
return $this->getNrOfUnitsY() / 100 * $y / ($this->respectRatio ? $this->yratio : 1);
}
/**
* Transform a absolute x coordinate to an relative one
*
* @param int $x An absolute x coordinate
*
* @return int A relative x coordinate
*/
public function xToRelative($x)
{
return $x / $this->getNrOfUnitsX() * 100 * ($this->respectRatio ? $this->xratio : 1);
}
/**
* Transform a absolute y coordinate to an relative one
*
* @param int $y An absolute x coordinate
*
* @return int A relative x coordinate
*/
public function yToRelative($y)
{
return $y / $this->getNrOfUnitsY() * 100 * ($this->respectRatio ? $this->yratio : 1);
}
}

View File

@ -0,0 +1,172 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart;
use \DOMNode;
use \DOMElement;
use \DOMDocument;
use \DOMImplementation;
use \Icinga\Util\Dimension;
use \Icinga\Chart\Render\LayoutBox;
use \Icinga\Chart\Render\RenderContext;
use \Icinga\Chart\Primitive\Canvas;
/**
* SVG Renderer component.
*
* Creates the basic DOM tree of the SVG to use
*/
class SVGRenderer
{
/**
* The XML-document
*
* @var DOMDocument
*/
private $document;
/**
* The SVG-element
*
* @var DOMNode
*/
private $svg;
/**
* The root layer for all elements
*
* @var Canvas
*/
private $rootCanvas;
/**
* The width of this renderer
*
* @var int
*/
private $width = 100;
/**
* The height of this renderer
*
* @var int
*/
private $height = 100;
/**
* Create the root document and the SVG root node
*/
private function createRootDocument()
{
$implementation = new DOMImplementation();
$docType = $implementation->createDocumentType(
'svg',
'-//W3C//DTD SVG 1.1//EN',
'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'
);
$this->document = $implementation->createDocument(null, null, $docType);
$this->svg = $this->createOuterBox();
$this->document->appendChild($this->svg);
}
/**
* Create the outer SVG box containing the root svg element and namespace and return it
*
* @return DOMElement The SVG root node
*/
private function createOuterBox()
{
$ctx = $this->createRenderContext();
$svg = $this->document->createElement('svg');
$svg->setAttribute('xmlns', 'http://www.w3.org/2000/svg');
$svg->setATtribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
$svg->setAttribute("width", "100%");
$svg->setAttribute("height", "100%");
$svg->setAttribute(
'viewBox',
sprintf(
'0 0 %s %s',
$ctx->getNrOfUnitsX(),
$ctx->getNrOfUnitsY()
)
);
return $svg;
}
/**
* Initialises the XML-document, SVG-element and this figure's root canvas
*
* @param int $width The width ratio
* @param int $height The height ratio
*/
public function __construct($width, $height)
{
$this->width = $width;
$this->height = $height;
$this->rootCanvas = new Canvas('root', new LayoutBox(0, 0));
}
/**
* Render the SVG-document
*
* @return string The resulting XML structure
*/
public function render()
{
$this->createRootDocument();
$ctx = $this->createRenderContext();
$this->svg->appendChild($this->rootCanvas->toSvg($ctx));
$this->document->formatOutput = true;
return $this->document->saveXML();
}
/**
* Create a render context that will be used for rendering elements
*
* @return RenderContext The created RenderContext instance
*/
public function createRenderContext()
{
return new RenderContext($this->document, $this->width, $this->height);
}
/**
* Return the root canvas of this rendered
*
* @return Canvas The canvas that will be the uppermost element in this figure
*/
public function getCanvas()
{
return $this->rootCanvas;
}
}

View File

@ -0,0 +1,73 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Unit;
use \Iterator;
/**
* Base class for Axis Units
*
* Concrete subclasses must implement the iterator interface, with
* getCurrent returning the axis relative position and getValue the label
* that will be displayed
*/
interface AxisUnit extends Iterator
{
/**
* Add a dataset to this AxisUnit, required for dynamic min and max vlaues
*
* @param array $dataset The dataset that will be shown in the Axis
* @param int $id The idx in the dataset (0 for x, 1 for y)
*/
public function addValues(array $dataset, $id = 0);
/**
* Transform the given absolute value in an axis relative value
*
* @param int $value The absolute, dataset dependent value
*
* @return int An axis relative value
*/
public function transform($value);
/**
* Set the axis minimum value to a fixed value
*
* @param int $min The new minimum value
*/
public function setMin($min);
/**
* Set the axis maximum value to a fixed value
*
* @param int $max The new maximum value
*/
public function setMax($max);
}

View File

@ -0,0 +1,195 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Unit;
use \Zend_Config;
use \Icinga\Application\Icinga;
use \Icinga\Application\Config as IcingaConfig;
use \Icinga\Util\DateTimeFactory;
/**
* Calendar Axis Unit that transforms timestamps into user-readable values
*
*/
class CalendarUnit extends LinearUnit
{
/**
* Constant for a minute
*/
const MINUTE = 60;
/**
* Constant for an hour
*/
const HOUR = 3600;
/**
* Constant for a day
*/
const DAY = 864000;
/**
* Constant for ~a month
* 30 Days, this is sufficient for our needs
*/
const MONTH = 2592000; // x
/**
* An array containing all labels that will be displayed
*
* @var array
*/
private $labels = array();
/**
* The date format to use
*
* @var string
*/
private $dateFormat = 'd-m';
/**
* The time format to use
*
* @var string
*/
private $timeFormat = 'g:i:s';
/**
* Create the labels for the given dataset
*/
private function createLabels()
{
$this->labels = array();
$duration = $this->getMax() - $this->getMin();
if ($duration <= self::HOUR) {
$unit = self::MINUTE;
} elseif ($duration <= self::DAY) {
$unit = self::HOUR;
} elseif ($duration <= self::MONTH) {
$unit = self::DAY;
} else {
$unit = self::MONTH;
}
$this->calculateLabels($unit);
}
/**
* Calculate the labels for this dataset
*
* @param integer $unit The unit to use as the basis for calculation
*/
private function calculateLabels($unit)
{
$fac = DateTimeFactory::create();
$duration = $this->getMax() - $this->getMin();
// Calculate number of ticks, but not more than 30
$tickCount = ($duration/$unit * 10);
if ($tickCount > 30) {
$tickCount = 30;
}
$step = $duration / $tickCount;
$format = $this->timeFormat;
if ($unit === self::DAY) {
$format = $this->dateFormat;
} elseif ($unit === self::MONTH) {
$format = $this->dateFormat;
}
for ($i = 0; $i <= $duration; $i += $step) {
$this->labels[] = $fac->setTimestamp($this->getMin() + $i)->format($format);
}
}
/**
* Add a dataset to this CalendarUnit and update labels
*
* @param array $dataset The dataset to update
* @param int $idx The index to use for determining the data
*
* @return self Fluid interface
*/
public function addValues(array $dataset, $idx = 0)
{
parent::addValues($dataset, $idx);
$this->createLabels();
return $this;
}
/**
* Return the current axis relative position
*
* @return int The position of the next tick (between 0 and 100)
*/
public function current()
{
return 100 * (key($this->labels) / count($this->labels));
}
/**
* Move to next tick
*/
public function next()
{
next($this->labels);
}
/**
* Return the current tick caption
*
* @return string
*/
public function key()
{
return current($this->labels);
}
/**
* Return true when the iterator is in a valid range
*
* @return bool
*/
public function valid()
{
return current($this->labels) !== false;
}
/**
* Rewind the internal array
*/
public function rewind()
{
reset($this->labels);
}
}

View File

@ -0,0 +1,238 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Unit;
/**
* Linear tick distribution over the axis
*/
class LinearUnit implements AxisUnit
{
/**
* The minimum value to display
*
* @var int
*/
protected $min;
/**
* The maximum value to display
*
* @var int
*/
protected $max;
/**
* True when the minimum value is static and isn't affected by the dataset
*
* @var bool
*/
protected $staticMin = false;
/**
* True when the maximum value is static and isn't affected by the dataset
*
* @var bool
*/
protected $staticMax = false;
/**
* The number of ticks to use
*
* @var int
*/
private $nrOfTicks = 10;
/**
* The currently displayed tick
*
* @var int
*/
private $currentTick = 0;
/**
* The currently displayed value
* @var int
*/
private $currentValue = 0;
/**
* Create and initialize this AxisUnit
*
* @param int $nrOfTicks The number of ticks to use
*/
public function __construct($nrOfTicks = 10)
{
$this->min = PHP_INT_MAX;
$this->max = ~PHP_INT_MAX;
$this->nrOfTicks = $nrOfTicks;
}
/**
* Add a dataset and calculate the minimum and maximum value for this AxisUnit
*
* @param array $dataset The dataset to add
* @param int $idx The idx (0 for x, 1 for y)
*
* @return self Fluent interface
*/
public function addValues(array $dataset, $idx = 0)
{
$datapoints = array();
foreach ($dataset['data'] as $points) {
$datapoints[] = $points[$idx];
}
if (empty($datapoints)) {
return $this;
}
sort($datapoints);
if (!$this->staticMax) {
$this->max = max($this->max, $datapoints[count($datapoints)-1]);
}
if (!$this->staticMin) {
$this->min = min($this->min, $datapoints[0]);
}
$this->currentTick = 0;
$this->currentValue = $this->min;
return $this;
}
/**
* Transform the absolute value to an axis relative value
*
* @param int $value The absolute coordinate from the dataset
* @return float|int The axis relative coordinate (between 0 and 100)
*/
public function transform($value)
{
if ($value < $this->min) {
return 0;
} elseif ($value > $this->max) {
return 100;
} else {
return 100 * ($value - $this->min) / ($this->max - $this->min);
}
}
/**
* Return the position of the current tick
*
* @return int
*/
public function current()
{
return $this->currentTick;
}
/**
* Calculate the next tick and tick value
*/
public function next()
{
$this->currentTick += (100 / $this->nrOfTicks);
$this->currentValue += (($this->max - $this->min) / $this->nrOfTicks);
}
/**
* Return the label for the current tick
*
* @return string The label for the current tick
*/
public function key()
{
return (string) $this->currentValue;
}
/**
* True when we're at a valid tick (iterator interface)
*
* @return bool
*/
public function valid()
{
return $this->currentTick >= 0 && $this->currentTick <= 100;
}
/**
* Reset the current tick and label value
*/
public function rewind()
{
$this->currentTick = 0;
$this->currentValue = $this->min;
}
/**
* Set the axis maximum value to a fixed value
*
* @param int $max The new maximum value
*/
public function setMax($max)
{
if ($max !== null) {
$this->max = $max;
$this->staticMax = true;
}
}
/**
* Set the axis minimum value to a fixed value
*
* @param int $min The new minimum value
*/
public function setMin($min)
{
if ($min !== null) {
$this->min = $min;
$this->staticMin = true;
}
}
/**
* Return the current minimum value of the axis
*
* @return int The minimum set for this axis
*/
public function getMin()
{
return $this->min;
}
/**
* Return the current maximum value of the axis
*
* @return int The maximum set for this axis
*/
public function getMax()
{
return $this->max;
}
}

View File

@ -0,0 +1,121 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
use Icinga\Application\Icinga;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use Icinga\Web\Form;
use Icinga\Web\Controller\ActionController;
use Icinga\Chart\SVGRenderer;
use Icinga\Chart\GridChart;
use Icinga\Module\Monitoring\Backend;
use Icinga\Chart\Axis;
/**
* Class Monitoring_CommandController
*
* Interface to send commands and display forms
*/
class Monitoring_ChartController extends ActionController
{
/**
* The backend used for this controller
*
* @var Backend
*/
protected $backend;
/**
* Set to a string containing the compact layout name to use when
* 'compact' is set as the layout parameter, otherwise null
*
* @var string
*/
private $compactView;
/**
* Retrieve backend and hooks for this controller
*
* @see ActionController::init
*/
public function init()
{
$this->backend = Backend::getInstance($this->_getParam('backend'));
}
public function testAction() {
$this->chart = new GridChart();
$this->chart->setAxisLabel("X axis label", "Y axis label")
->setAxisMin(null, 0);
$data1 = array();
$data2 = array();
$data3 = array();
for ($i=0; $i<25; $i++) {
$data[] = array(1379344218+$i*10, rand(0,12));
$data2[] = array(1379344218+$i*10, rand(4,30));
$data3[] = array(1379344218+$i*10, rand(0,30));
}
$this->chart->drawLines(
array(
'label' => 'Nr of outtakes',
'color' => 'red',
'width' => '5',
'data' => $data
), array(
'label' => 'Some line',
'color' => 'blue',
'width' => '4',
'data' => $data3,
'showPoints' => true
)
);
$this->chart->drawBars(
array(
'label' => 'Some other line',
'color' => 'black',
'data' => $data3,
'showPoints' => true
)
);
$this->chart->drawLines(
array(
'label' => 'Nr of outtakes',
'color' => 'yellow',
'width' => '5',
'data' => $data2
)
);
$this->view->svg = $this->chart;
}
}

View File

@ -0,0 +1,6 @@
mah
<div style="border:1px dashed black;width:900px;height:520px">
<?=
$svg->render();
?>
</div>

View File

@ -0,0 +1,88 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Tests\Icinga\Chart;
use DOMXPath;
use DOMDocument;
use Icinga\Chart\GridChart;
use Icinga\Test\BaseTestCase;
use Test\Icinga\LibraryLoader;
// TODO: Use autoloader #4673
require_once realpath(__DIR__ . '/../../../../../library/Icinga/Test/BaseTestCase.php');
require_once realpath(BaseTestCase::$testDir . '/library/Icinga/LibraryLoader.php');
require_once realpath(BaseTestCase::$libDir . '/Chart/Primitive/Drawable.php');
require_once realpath(BaseTestCase::$libDir . '/Chart/Primitive/Styleable.php');
require_once realpath(BaseTestCase::$libDir . '/Chart/Primitive/Animatable.php');
require_once realpath(BaseTestCase::$libDir . '/Chart/Unit/AxisUnit.php');
require_once realpath(BaseTestCase::$libDir . '/Chart/Unit/LinearUnit.php');
LibraryLoader::loadFolder(realpath(BaseTestCase::$libDir . '/Chart'));
class GraphChartTest extends BaseTestCase
{
public function testBarChartCreation()
{
$chart = new GridChart();
$chart->drawBars(
array(
'label' => 'My bar',
'color' => 'black',
'data' => array(array(0, 0), array(1,1), array(1,2))
)
);
$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
$doc->loadXML($chart->render());
$xpath = new DOMXPath($doc);
$xpath->registerNamespace('x', 'http://www.w3.org/2000/svg');
$path = $xpath->query('//x:rect[@data-icinga-graph-type="bar"]');
$this->assertEquals(3, $path->length, 'Assert the correct number of datapoints being drawn as SVG bars');
}
public function testLineChartCreation()
{
$chart = new GridChart();
$chart->drawLines(
array(
'label' => 'My bar',
'color' => 'black',
'data' => array(array(0, 0), array(1,1), array(1,2))
)
);
$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
$doc->loadXML($chart->render());
$xpath = new DOMXPath($doc);
$xpath->registerNamespace('x', 'http://www.w3.org/2000/svg');
$path = $xpath->query('//x:path[@data-icinga-graph-type="line"]');
$this->assertEquals(1, $path->length, 'Assert the correct number of datapoints being drawn as SVG lines');
}
}

View File

@ -0,0 +1,70 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* 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.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Tests\Icinga\Chart;
use DOMXPath;
use DOMDocument;
use Icinga\Chart\GridChart;
use Icinga\Chart\PieChart;
use Icinga\Test\BaseTestCase;
use Test\Icinga\LibraryLoader;
// TODO: Use autoloader #4673
require_once realpath(__DIR__ . '/../../../../../library/Icinga/Test/BaseTestCase.php');
require_once realpath(BaseTestCase::$testDir . '/library/Icinga/LibraryLoader.php');
require_once realpath(BaseTestCase::$libDir . '/Chart/Primitive/Drawable.php');
require_once realpath(BaseTestCase::$libDir . '/Chart/Primitive/Styleable.php');
require_once realpath(BaseTestCase::$libDir . '/Chart/Primitive/Animatable.php');
require_once realpath(BaseTestCase::$libDir . '/Chart/Unit/AxisUnit.php');
require_once realpath(BaseTestCase::$libDir . '/Chart/Unit/LinearUnit.php');
LibraryLoader::loadFolder(realpath(BaseTestCase::$libDir . '/Chart'));
class PieChartTest extends BaseTestCase
{
public function testPieChartCreation()
{
$chart = new PieChart();
$chart->drawPie(
array(
'label' => 'My bar',
'color' => 'black', 'green', 'red',
'data' => array(50,50,50)
)
);
$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
$doc->loadXML($chart->render());
$xpath = new DOMXPath($doc);
$xpath->registerNamespace('x', 'http://www.w3.org/2000/svg');
$path = $xpath->query('//x:path[@data-icinga-graph-type="pieslice"]');
$this->assertEquals(3, $path->length, 'Assert the correct number of datapoints being drawn as SVG bars');
}
}

View File

@ -31,7 +31,11 @@ abstract class LibraryLoader {
{
$files = scandir($folder);
foreach ($files as $file) {
if ($file[0] == '.') {
continue;
}
if ($recursive && is_dir(realpath($folder."/".$file))) {
self::loadFolder(realpath($folder."/".$file));
}
if (!preg_match('/php$/', $file)) {