|
|
@ -22,32 +22,47 @@ Dot chart |
|
|
|
""" |
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
from __future__ import division |
|
|
|
from __future__ import division |
|
|
|
from pygal.util import decorate, cut, safe_enumerate |
|
|
|
from pygal.util import decorate, cut, safe_enumerate, cached_property |
|
|
|
from pygal.adapters import positive |
|
|
|
|
|
|
|
from pygal.graph.graph import Graph |
|
|
|
from pygal.graph.graph import Graph |
|
|
|
|
|
|
|
from pygal.view import View, ReverseView |
|
|
|
|
|
|
|
from math import log10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Dot(Graph): |
|
|
|
class Dot(Graph): |
|
|
|
"""Dot graph""" |
|
|
|
"""Dot graph""" |
|
|
|
|
|
|
|
|
|
|
|
_adapters = [positive] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def dot(self, serie, r_max): |
|
|
|
def dot(self, serie, r_max): |
|
|
|
"""Draw a dot line""" |
|
|
|
"""Draw a dot line""" |
|
|
|
serie_node = self.svg.serie(serie) |
|
|
|
serie_node = self.svg.serie(serie) |
|
|
|
view_values = list(map(self.view, serie.points)) |
|
|
|
view_values = list(map(self.view, serie.points)) |
|
|
|
for i, value in safe_enumerate(serie.values): |
|
|
|
for i, value in safe_enumerate(serie.values): |
|
|
|
x, y = view_values[i] |
|
|
|
x, y = view_values[i] |
|
|
|
size = r_max * value |
|
|
|
|
|
|
|
value = self._format(value) |
|
|
|
if self.logarithmic: |
|
|
|
|
|
|
|
log10min = log10(self._min) - 1 |
|
|
|
|
|
|
|
log10max = log10(self._max or 1) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if value != 0: |
|
|
|
|
|
|
|
size = r_max * ( |
|
|
|
|
|
|
|
(log10(abs(value)) - log10min) / |
|
|
|
|
|
|
|
(log10max - log10min) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
size = 0 |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
size = r_max * (abs(value) / (self._max or 1)) |
|
|
|
|
|
|
|
|
|
|
|
metadata = serie.metadata.get(i) |
|
|
|
metadata = serie.metadata.get(i) |
|
|
|
dots = decorate( |
|
|
|
dots = decorate( |
|
|
|
self.svg, |
|
|
|
self.svg, |
|
|
|
self.svg.node(serie_node['plot'], class_="dots"), |
|
|
|
self.svg.node(serie_node['plot'], class_="dots"), |
|
|
|
metadata) |
|
|
|
metadata) |
|
|
|
self.svg.node(dots, 'circle', cx=x, cy=y, r=size, |
|
|
|
self.svg.node(dots, 'circle', |
|
|
|
class_='dot reactive tooltip-trigger') |
|
|
|
cx=x, cy=y, r=size, |
|
|
|
|
|
|
|
class_='dot reactive tooltip-trigger' + ( |
|
|
|
|
|
|
|
' negative' if value < 0 else '')) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
value = self._format(value) |
|
|
|
self._tooltip_data(dots, value, x, y, classes='centered') |
|
|
|
self._tooltip_data(dots, value, x, y, classes='centered') |
|
|
|
self._static_value(serie_node, value, x, y) |
|
|
|
self._static_value(serie_node, value, x, y) |
|
|
|
|
|
|
|
|
|
|
@ -69,10 +84,30 @@ class Dot(Graph): |
|
|
|
self._y_labels = list(zip( |
|
|
|
self._y_labels = list(zip( |
|
|
|
self.y_labels or cut(self.series, 'title'), y_pos)) |
|
|
|
self.y_labels or cut(self.series, 'title'), y_pos)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _set_view(self): |
|
|
|
|
|
|
|
"""Assign a view to current graph""" |
|
|
|
|
|
|
|
view_class = ReverseView if self.inverse_y_axis else View |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.view = view_class( |
|
|
|
|
|
|
|
self.width - self.margin_box.x, |
|
|
|
|
|
|
|
self.height - self.margin_box.y, |
|
|
|
|
|
|
|
self._box) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@cached_property |
|
|
|
|
|
|
|
def _values(self): |
|
|
|
|
|
|
|
"""Getter for series values (flattened)""" |
|
|
|
|
|
|
|
return [abs(val) for val in super()._values if val != 0] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@cached_property |
|
|
|
|
|
|
|
def _max(self): |
|
|
|
|
|
|
|
"""Getter for the maximum series value""" |
|
|
|
|
|
|
|
return (self.range[1] if (self.range and self.range[1] is not None) |
|
|
|
|
|
|
|
else (max(map(abs, self._values)) if self._values else None)) |
|
|
|
|
|
|
|
|
|
|
|
def _plot(self): |
|
|
|
def _plot(self): |
|
|
|
r_max = min( |
|
|
|
r_max = min( |
|
|
|
self.view.x(1) - self.view.x(0), |
|
|
|
self.view.x(1) - self.view.x(0), |
|
|
|
(self.view.y(0) or 0) - self.view.y(1)) / ( |
|
|
|
(self.view.y(0) or 0) - self.view.y(1)) / ( |
|
|
|
2 * (self._max or 1) * 1.05) |
|
|
|
2 * 1.05) |
|
|
|
for serie in self.series: |
|
|
|
for serie in self.series: |
|
|
|
self.dot(serie, r_max) |
|
|
|
self.dot(serie, r_max) |
|
|
|