debian-python-pygal/pygal/graph/solidgauge.py

154 lines
5.0 KiB
Python

# -*- coding: utf-8 -*-
# This file is part of pygal
#
# A python svg graph plotting library
# Copyright © 2012-2016 Kozea
#
# This library is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Solid Guage
For each series a solid guage is shown on the plot area.
"""
from __future__ import division
from math import pi, sqrt
from pygal.graph.graph import Graph
from pygal.util import alter, decorate
class SolidGauge(Graph):
def gaugify(self, serie, squares, sq_dimensions, current_square):
serie_node = self.svg.serie(serie)
if self.half_pie:
start_angle = 3 * pi / 2
center = (
(current_square[1] * sq_dimensions[0]) - (
sq_dimensions[0] / 2.),
(current_square[0] * sq_dimensions[1]) - (
sq_dimensions[1] / 4))
end_angle = pi / 2
else:
start_angle = 0
center = (
(current_square[1] * sq_dimensions[0]) - (
sq_dimensions[0] / 2.),
(current_square[0] * sq_dimensions[1]) - (
sq_dimensions[1] / 2.))
end_angle = 2 * pi
max_value = serie.metadata.get(0, {}).get('max_value', 100)
radius = min([sq_dimensions[0] / 2, sq_dimensions[1] / 2]) * .9
small_radius = radius * serie.inner_radius
self.svg.gauge_background(
serie_node, start_angle, center, radius, small_radius, end_angle,
self.half_pie, self._serie_format(serie, max_value))
sum_ = 0
for i, value in enumerate(serie.values):
if value is None:
continue
ratio = min(value, max_value) / max_value
if self.half_pie:
angle = 2 * pi * ratio / 2
else:
angle = 2 * pi * ratio
val = self._format(serie, i)
metadata = serie.metadata.get(i)
gauge_ = decorate(
self.svg,
self.svg.node(serie_node['plot'], class_="gauge"),
metadata)
alter(
self.svg.solid_gauge(
serie_node, gauge_, radius, small_radius,
angle, start_angle, center, val, i, metadata,
self.half_pie, end_angle,
self._serie_format(serie, max_value)),
metadata)
start_angle += angle
sum_ += value
x, y = center
self.svg.node(
serie_node['text_overlay'], 'text',
class_='value gauge-sum',
x=x,
y=y + self.style.value_font_size / 3,
attrib={'text-anchor': 'middle'}
).text = self._serie_format(serie, sum_)
def _compute_x_labels(self):
pass
def _compute_y_labels(self):
pass
def _plot(self):
"""Draw all the serie slices"""
squares = self._squares()
sq_dimensions = self.add_squares(squares)
for index, serie in enumerate(self.series):
current_square = self._current_square(squares, index)
self.gaugify(
serie, squares, sq_dimensions, current_square)
def _squares(self):
n_series_ = len(self.series)
i = 2
if sqrt(n_series_).is_integer():
_x = int(sqrt(n_series_))
_y = int(sqrt(n_series_))
else:
while i * i < n_series_:
while n_series_ % i == 0:
n_series_ = n_series_ / i
i = i + 1
_y = int(n_series_)
_x = int(len(self.series) / _y)
if len(self.series) == 5:
_x, _y = 2, 3
if abs(_x - _y) > 2:
_sq = 3
while (_x * _y) - 1 < len(self.series):
_x, _y = _sq, _sq
_sq += 1
return (_x, _y)
def _current_square(self, squares, index):
current_square = [1, 1]
steps = index + 1
steps_taken = 0
for i in range(squares[0] * squares[1]):
steps_taken += 1
if steps_taken != steps and steps_taken % squares[0] != 0:
current_square[1] += 1
elif steps_taken != steps and steps_taken % squares[0] == 0:
current_square[1] = 1
current_square[0] += 1
else:
return tuple(current_square)
raise Exception(
'Something went wrong with the current square assignment.')