Browse Source

Cleanup a bit and prevent crash with log

pull/130/head
Florian Mounier 11 years ago
parent
commit
51c7263e4f
  1. 7
      pygal/graph/base.py
  2. 2
      pygal/graph/dot.py
  3. 6
      pygal/graph/graph.py
  4. 13
      pygal/graph/stackedbar.py
  5. 51
      pygal/graph/verticalpyramid.py
  6. 3
      pygal/test/test_config.py
  7. 23
      pygal/view.py

7
pygal/graph/base.py

@ -55,9 +55,8 @@ class BaseGraph(object):
self.view = None self.view = None
if self.logarithmic and self.zero == 0: if self.logarithmic and self.zero == 0:
# Explicit min to avoid interpolation dependency # Explicit min to avoid interpolation dependency
from pygal.graph.xy import XY if self._dual:
if isinstance(self, XY): get = lambda x: x[1] or 1
get = lambda x: x[1]
else: else:
get = lambda x: x get = lambda x: x
@ -66,7 +65,7 @@ class BaseGraph(object):
[get(val) [get(val)
for serie in self.series for val in serie.safe_values])) for serie in self.series for val in serie.safe_values]))
self.zero = min(positive_values) if positive_values else 0 self.zero = min(positive_values or 1,) or 1
self._draw() self._draw()
self.svg.pre_render() self.svg.pre_render()

2
pygal/graph/dot.py

@ -76,6 +76,6 @@ class Dot(Graph):
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) - self.view.y(1)) / (2 * (self._max or 1) * 1.05) (self.view.y(0) or 0) - self.view.y(1)) / (2 * (self._max or 1) * 1.05)
for index, serie in enumerate(self.series): for index, serie in enumerate(self.series):
self.dot(self._serie(index), serie, r_max) self.dot(self._serie(index), serie, r_max)

6
pygal/graph/graph.py

@ -128,8 +128,8 @@ class Graph(BaseGraph):
if self.x_label_rotation or len(self._x_labels) <= 1: if self.x_label_rotation or len(self._x_labels) <= 1:
truncation = 25 truncation = 25
else: else:
first_label_position = self.view.x(self._x_labels[0][1]) first_label_position = self.view.x(self._x_labels[0][1]) or 0
last_label_position = self.view.x(self._x_labels[-1][1]) last_label_position = self.view.x(self._x_labels[-1][1]) or 0
available_space = ( available_space = (
last_label_position - first_label_position) / ( last_label_position - first_label_position) / (
len(self._x_labels) - 1) len(self._x_labels) - 1)
@ -169,7 +169,7 @@ class Graph(BaseGraph):
last_guide = (self._y_2nd_labels and label == lastlabel) last_guide = (self._y_2nd_labels and label == lastlabel)
self.svg.node( self.svg.node(
guides, 'path', guides, 'path',
d='M%f %f v%f' % (x, 0, self.view.height), d='M%f %f v%f' % (x or 0, 0, self.view.height),
class_='%s%sline' % ( class_='%s%sline' % (
'major ' if major else '', 'major ' if major else '',
'guide ' if position != 0 and not last_guide else '')) 'guide ' if position != 0 and not last_guide else ''))

13
pygal/graph/stackedbar.py

@ -82,13 +82,13 @@ class StackedBar(Bar):
positive_vals, negative_vals = self._get_separated_values(True) positive_vals, negative_vals = self._get_separated_values(True)
self.secondary_negative_cumulation = [0] * self._len self.secondary_negative_cumulation = [0] * self._len
self.secondary_positive_cumulation = [0] * self._len self.secondary_positive_cumulation = [0] * self._len
self._pre_compute_secondary(positive_vals, negative_vals)
# In case of pyramids def _pre_compute_secondary(self, positive_vals, negative_vals):
sum_ = lambda x: sum(x) if isinstance(x, tuple) else x self._secondary_min = (negative_vals and min(
self._secondary_min = (negative_vals and min( min(negative_vals), self.zero)) or self.zero
sum_(min(negative_vals)), self.zero)) or self.zero self._secondary_max = (positive_vals and max(
self._secondary_max = (positive_vals and max( max(positive_vals), self.zero)) or self.zero
sum_(max(positive_vals)), self.zero)) or self.zero
def _bar(self, parent, x, y, index, i, zero, def _bar(self, parent, x, y, index, i, zero,
shift=False, secondary=False, rounded=False): shift=False, secondary=False, rounded=False):
@ -109,6 +109,7 @@ class StackedBar(Bar):
width = (self.view.x(1) - self.view.x(0)) / self._len width = (self.view.x(1) - self.view.x(0)) / self._len
x, y = self.view((x, y)) x, y = self.view((x, y))
y = y or 0
series_margin = width * self._series_margin series_margin = width * self._series_margin
x += series_margin x += series_margin
width -= 2 * series_margin width -= 2 * series_margin

51
pygal/graph/verticalpyramid.py

@ -37,49 +37,24 @@ class VerticalPyramid(StackedBar):
def _get_separated_values(self, secondary=False): def _get_separated_values(self, secondary=False):
series = self.secondary_series if secondary else self.series series = self.secondary_series if secondary else self.series
positive_vals = zip(*[serie.safe_values positive_vals = map(sum, zip(
for index, serie in enumerate(series) *[serie.safe_values
if index % 2]) for index, serie in enumerate(series)
negative_vals = zip(*[serie.safe_values if index % 2]))
for index, serie in enumerate(series) negative_vals = map(sum, zip(
if not index % 2]) *[serie.safe_values
for index, serie in enumerate(series)
if not index % 2]))
return list(positive_vals), list(negative_vals) return list(positive_vals), list(negative_vals)
def _compute_box(self, positive_vals, negative_vals): def _compute_box(self, positive_vals, negative_vals):
positive_sum = list(map(sum, positive_vals)) or [self.zero] self._box.ymax = max(max(positive_vals or [self.zero]),
negative_sum = list(map(sum, negative_vals)) or [self.zero] max(negative_vals or [self.zero]))
self._box.ymax = max(max(positive_sum), max(negative_sum))
self._box.ymin = - self._box.ymax self._box.ymin = - self._box.ymax
def _compute_secondary(self): def _pre_compute_secondary(self, positive_vals, negative_vals):
# Need refactoring self._secondary_max = max(max(positive_vals), max(negative_vals))
if self.secondary_series: self._secondary_min = - self._secondary_max
y_pos = list(zip(*self._y_labels))[1]
positive_vals, negative_vals = self._get_separated_values(True)
positive_sum = map(sum, positive_vals) or [self.zero]
negative_sum = map(sum, negative_vals) or [self.zero]
ymax = max(max(positive_sum), max(negative_sum))
ymin = -ymax
min_0_ratio = (self.zero - self._box.ymin) / self._box.height
max_0_ratio = (self._box.ymax - self.zero) / self._box.height
new_ymax = (self.zero - ymin) * (1 / min_0_ratio - 1)
new_ymin = -(ymax - self.zero) * (1 / max_0_ratio - 1)
if ymax > self._box.ymax:
ymin = new_ymin
else:
ymax = new_ymax
left_range = abs(self._box.ymax - self._box.ymin)
right_range = abs(ymax - ymin)
self._scale = left_range / right_range
self._scale_diff = self._box.ymin
self._scale_min_2nd = ymin
self._y_2nd_labels = [
(self._format(self._box.xmin + y * right_range / left_range),
y)
for y in y_pos]
def _bar(self, parent, x, y, index, i, zero, def _bar(self, parent, x, y, index, i, zero,
shift=True, secondary=False, rounded=False): shift=True, secondary=False, rounded=False):

3
pygal/test/test_config.py

@ -23,9 +23,8 @@ from pygal import (
FrenchMap_Regions, FrenchMap_Departments) FrenchMap_Regions, FrenchMap_Departments)
from pygal._compat import u from pygal._compat import u
from pygal.test.utils import texts from pygal.test.utils import texts
from pygal.test import pytest_generate_tests, make_data from pygal.test import pytest_generate_tests
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from uuid import uuid4
def test_config_behaviours(): def test_config_behaviours():

23
pygal/view.py

@ -288,7 +288,6 @@ class PolarThetaLogView(View):
class LogView(View): class LogView(View):
"""Logarithmic projection """ """Logarithmic projection """
# Do not want to call the parent here # Do not want to call the parent here
# pylint: disable-msg=W0231
def __init__(self, width, height, box): def __init__(self, width, height, box):
self.width = width self.width = width
self.height = height self.height = height
@ -297,11 +296,10 @@ class LogView(View):
self.log10_ymin = log10(self.box.ymin) if self.box.ymin > 0 else 0 self.log10_ymin = log10(self.box.ymin) if self.box.ymin > 0 else 0
self.box.fix(False) self.box.fix(False)
# pylint: enable-msg=W0231
def y(self, y): def y(self, y):
"""Project y""" """Project y"""
if y is None or y <= 0 or self.log10_ymax - self.log10_ymin == 0: if y is None or y <= 0 or self.log10_ymax - self.log10_ymin == 0:
return None return 0
return (self.height - self.height * return (self.height - self.height *
(log10(y) - self.log10_ymin) (log10(y) - self.log10_ymin)
/ (self.log10_ymax - self.log10_ymin)) / (self.log10_ymax - self.log10_ymin))
@ -310,16 +308,14 @@ class LogView(View):
class XLogView(View): class XLogView(View):
"""Logarithmic projection """ """Logarithmic projection """
# Do not want to call the parent here # Do not want to call the parent here
# pylint: disable-msg=W0231
def __init__(self, width, height, box): def __init__(self, width, height, box):
self.width = width self.width = width
self.height = height self.height = height
self.box = box self.box = box
self.log10_xmax = log10(self.box.xmax) self.log10_xmax = log10(self.box.xmax) if self.box.xmax > 0 else 0
self.log10_xmin = log10(self.box.xmin) self.log10_xmin = log10(self.box.xmin) if self.box.xmin > 0 else 0
self.box.fix(False) self.box.fix(False)
# pylint: enable-msg=W0231
def x(self, x): def x(self, x):
"""Project x""" """Project x"""
if x is None or x <= 0 or self.log10_xmax - self.log10_xmin == 0: if x is None or x <= 0 or self.log10_xmax - self.log10_xmin == 0:
@ -334,24 +330,23 @@ class XYLogView(XLogView, LogView):
self.width = width self.width = width
self.height = height self.height = height
self.box = box self.box = box
self.log10_ymax = log10(self.box.ymax) self.log10_ymax = log10(self.box.ymax) if self.box.ymax > 0 else 0
self.log10_ymin = log10(self.box.ymin) self.log10_ymin = log10(self.box.ymin) if self.box.ymin > 0 else 0
self.log10_xmax = log10(self.box.xmax) self.log10_xmax = log10(self.box.xmax) if self.box.xmax > 0 else 0
self.log10_xmin = log10(self.box.xmin) self.log10_xmin = log10(self.box.xmin) if self.box.xmin > 0 else 0
self.box.fix(False) self.box.fix(False)
class HorizontalLogView(XLogView): class HorizontalLogView(XLogView):
"""Logarithmic projection """ """Logarithmic projection """
# Do not want to call the parent here # Do not want to call the parent here
# pylint: disable-msg=W0231
def __init__(self, width, height, box): def __init__(self, width, height, box):
self._force_vertical = None self._force_vertical = None
self.width = width self.width = width
self.height = height self.height = height
self.box = box self.box = box
self.log10_xmax = log10(self.box.ymax) self.log10_xmax = log10(self.box.ymax) if self.box.ymax > 0 else 0
self.log10_xmin = log10(self.box.ymin) self.log10_xmin = log10(self.box.ymin) if self.box.ymin > 0 else 0
self.box.fix(False) self.box.fix(False)
self.box.swap() self.box.swap()

Loading…
Cancel
Save