mirror of https://github.com/Kozea/pygal.git
Florian Mounier
13 years ago
16 changed files with 358 additions and 282 deletions
@ -0,0 +1,128 @@
|
||||
from pygal.graph.base import BaseGraph |
||||
from pygal.view import View |
||||
|
||||
|
||||
class Graph(BaseGraph): |
||||
"""Graph super class containing generic common functions""" |
||||
|
||||
def _decorate(self): |
||||
self._set_view() |
||||
self._make_graph() |
||||
self._x_axis() |
||||
self._y_axis() |
||||
self._legend() |
||||
self._title() |
||||
|
||||
def _set_view(self): |
||||
self.view = View( |
||||
self.width - self.margin.x, |
||||
self.height - self.margin.y, |
||||
self._box) |
||||
|
||||
def _make_graph(self): |
||||
self.graph_node = self.svg.node( |
||||
class_='graph %s' % self.__class__.__name__) |
||||
self.svg.node(self.graph_node, 'rect', |
||||
class_='background', |
||||
x=0, y=0, |
||||
width=self.width, |
||||
height=self.height) |
||||
self.plot = self.svg.node( |
||||
self.graph_node, class_="plot", |
||||
transform="translate(%d, %d)" % ( |
||||
self.margin.left, self.margin.top)) |
||||
self.svg.node(self.plot, 'rect', |
||||
class_='background', |
||||
x=0, y=0, |
||||
width=self.view.width, |
||||
height=self.view.height) |
||||
|
||||
def _x_axis(self): |
||||
if not self._x_labels: |
||||
return |
||||
|
||||
axis = self.svg.node(self.plot, class_="axis x") |
||||
|
||||
if 0 not in [label[1] for label in self._x_labels]: |
||||
self.svg.node(axis, 'path', |
||||
d='M%f %f v%f' % (0, 0, self.view.height), |
||||
class_='line') |
||||
for label, position in self._x_labels: |
||||
guides = self.svg.node(axis, class_='guides') |
||||
x = self.view.x(position) |
||||
y = self.view.height + 5 |
||||
self.svg.node(guides, 'path', |
||||
d='M%f %f v%f' % (x, 0, self.view.height), |
||||
class_='%sline' % ( |
||||
'guide ' if position != 0 else '')) |
||||
text = self.svg.node(guides, 'text', |
||||
x=x, |
||||
y=y + .5 * self.label_font_size + 5 |
||||
) |
||||
text.text = label |
||||
if self.x_label_rotation: |
||||
text.attrib['transform'] = "rotate(%d %f %f)" % ( |
||||
self.x_label_rotation, x, y) |
||||
|
||||
def _y_axis(self): |
||||
if not self._y_labels: |
||||
return |
||||
|
||||
axis = self.svg.node(self.plot, class_="axis y") |
||||
|
||||
if 0 not in [label[1] for label in self._y_labels]: |
||||
self.svg.node(axis, 'path', |
||||
d='M%f %f h%f' % (0, self.view.height, self.view.width), |
||||
class_='line') |
||||
for label, position in self._y_labels: |
||||
guides = self.svg.node(axis, class_='guides') |
||||
x = -5 |
||||
y = self.view.y(position) |
||||
self.svg.node(guides, 'path', |
||||
d='M%f %f h%f' % (0, y, self.view.width), |
||||
class_='%sline' % ( |
||||
'guide ' if position != 0 else '')) |
||||
text = self.svg.node(guides, 'text', |
||||
x=x, |
||||
y=y + .35 * self.label_font_size |
||||
) |
||||
text.text = label |
||||
if self.y_label_rotation: |
||||
text.attrib['transform'] = "rotate(%d %f %f)" % ( |
||||
self.y_label_rotation, x, y) |
||||
|
||||
def _legend(self): |
||||
if not self.show_legend: |
||||
return |
||||
legends = self.svg.node( |
||||
self.graph_node, class_='legends', |
||||
transform='translate(%d, %d)' % ( |
||||
self.margin.left + self.view.width + 10, |
||||
self.margin.top + 10)) |
||||
for i, title in enumerate(self._legends): |
||||
legend = self.svg.node(legends, class_='legend') |
||||
self.svg.node(legend, 'rect', |
||||
x=0, |
||||
y=1.5 * i * self.legend_box_size, |
||||
width=self.legend_box_size, |
||||
height=self.legend_box_size, |
||||
class_="color-%d" % i, |
||||
).text = title |
||||
# Serious magical numbers here |
||||
self.svg.node(legend, 'text', |
||||
x=self.legend_box_size + 5, |
||||
y=1.5 * i * self.legend_box_size |
||||
+ .5 * self.legend_box_size |
||||
+ .3 * self.legend_font_size |
||||
).text = title |
||||
|
||||
def _title(self): |
||||
if self.title: |
||||
self.svg.node(self.graph_node, 'text', class_='title', |
||||
x=self.margin.left + self.view.width / 2, |
||||
y=self.title_font_size + 10 |
||||
).text = self.title |
||||
|
||||
def _serie(self, serie): |
||||
return self.svg.node( |
||||
self.plot, class_='series serie-%d color-%d' % (serie, serie)) |
@ -1,27 +1,81 @@
|
||||
from pygal.graph.base import BaseGraph |
||||
from math import pi |
||||
from pygal.graph.line import Line |
||||
from pygal.view import PolarView |
||||
from pygal.util import deg |
||||
from math import cos, sin, pi |
||||
|
||||
|
||||
class Radar(BaseGraph): |
||||
class Radar(Line): |
||||
"""Kiviat graph""" |
||||
|
||||
def _set_view(self): |
||||
self.view = PolarView( |
||||
self.width - self.margin.x, |
||||
self.height - self.margin.y, |
||||
self._box) |
||||
|
||||
def _x_axis(self): |
||||
if not self._x_labels: |
||||
return |
||||
|
||||
axis = self.svg.node(self.plot, class_="axis x web") |
||||
format = lambda x: '%f %f' % x |
||||
center = self.view((0, 0)) |
||||
r = self._rmax |
||||
for label, theta in self._x_labels: |
||||
guides = self.svg.node(axis, class_='guides') |
||||
end = self.view((r, theta)) |
||||
self.svg.node(guides, 'path', |
||||
d='M%s L%s' % (format(center), format(end)), |
||||
class_='line') |
||||
r_txt = (1 - self._box.__class__._margin) * self._box.ymax |
||||
pos_text = self.view((r_txt, theta)) |
||||
text = self.svg.node(guides, 'text', |
||||
x=pos_text[0], |
||||
y=pos_text[1] |
||||
) |
||||
text.text = label |
||||
angle = - theta + pi / 2. |
||||
if cos(angle) < 0: |
||||
angle -= pi |
||||
text.attrib['transform'] = 'rotate(%f %s)' % ( |
||||
deg(angle), format(pos_text)) |
||||
|
||||
def _y_axis(self): |
||||
if not self._y_labels: |
||||
return |
||||
|
||||
axis = self.svg.node(self.plot, class_="axis y web") |
||||
|
||||
for label, r in reversed(self._y_labels): |
||||
guides = self.svg.node(axis, class_='guides') |
||||
self.svg.line( |
||||
guides, [self.view((r, theta)) for theta in self._x_pos], |
||||
close=True, |
||||
class_='guide line') |
||||
x, y = self.view((r, self._x_pos[0])) |
||||
self.svg.node(guides, 'text', |
||||
x=x - 5, |
||||
y=y |
||||
).text = label |
||||
|
||||
def _compute(self): |
||||
vals = [val for serie in self.series for val in serie.values] |
||||
self._box.ymax = 2 * max(vals) |
||||
self._box.ymin = - self._box.ymax |
||||
self._box.xmin = self._box.ymin |
||||
self._box.xmax = self._box.ymax |
||||
self._box._margin *= 2 |
||||
self._box.xmin = self._box.ymin = 0 |
||||
self._box.xmax = self._box.ymax = self._rmax = max(vals) |
||||
|
||||
delta = 2 * pi / float(len(self.x_labels)) |
||||
x_step = len(self.series[0].values) |
||||
delta = 2 * pi / float(len(self.x_labels)) |
||||
self._x_pos = [.5 * pi - i * delta for i in range(x_step)] |
||||
self._y_pos = self._pos(self._box.ymin, self._box.ymax, self.y_scale |
||||
) if not self.y_labels else map(int, self.y_labels) |
||||
self._x_labels = self.x_labels and zip(self.x_labels, self._x_pos) |
||||
self._y_labels = zip(map(str, self._y_pos), self._y_pos) |
||||
self._box.xmin = self._box.ymin = - self._box.ymax |
||||
|
||||
def _plot(self): |
||||
for serie in self.series: |
||||
serie_node = self.svg.serie(serie.index) |
||||
# self.svg.web(serie_node, serie, |
||||
# [val / float(self._rmax) for val in serie.values]) |
||||
serie_node = self._serie(serie.index) |
||||
self.line(serie_node, [ |
||||
(v, self._x_pos[i]) |
||||
for i, v in enumerate(serie.values)]) |
||||
|
Loading…
Reference in new issue