diff --git a/demo/moulinrouge/tests.py b/demo/moulinrouge/tests.py index 73f9064..4d4429e 100644 --- a/demo/moulinrouge/tests.py +++ b/demo/moulinrouge/tests.py @@ -104,6 +104,8 @@ def get_test_routes(app): graph = CHARTS_BY_NAME[chart]() graph.add('1', [1, 3, 12, 3, 4, None, 9]) graph.add('2', [7, -4, 10, None, 8, 3, 1]) + graph.add('3', [7, -14, -10, None, 8, 3, 1]) + graph.add('4', [7, 4, -10, None, 8, 3, 1]) graph.x_labels = ('a', 'b', 'c', 'd', 'e', 'f', 'g') return graph.render_response() diff --git a/pygal/graph/bar.py b/pygal/graph/bar.py index de8e5bf..14165c0 100644 --- a/pygal/graph/bar.py +++ b/pygal/graph/bar.py @@ -36,17 +36,20 @@ class Bar(Graph): self._x_ranges = None super(Bar, self).__init__(*args, **kwargs) - def _bar(self, parent, x, y, index): + def _bar(self, parent, x, y, index, i, zero, shift=True): width = (self.view.x(1) - self.view.x(0)) / self._len + x, y = self.view((x, y)) series_margin = width * self._series_margin x += series_margin width -= 2 * series_margin - width /= self._order - x += index * width - serie_margin = width * self._serie_margin - x += serie_margin - width -= 2 * serie_margin - height = self.view.y(self.zero) - y + if shift: + width /= self._order + x += index * width + serie_margin = width * self._serie_margin + x += serie_margin + width -= 2 * serie_margin + height = self.view.y(zero) - y + print height r = self.rounded_bars * 1 if self.rounded_bars else 0 self.svg.transposable_node( parent, 'rect', @@ -58,8 +61,7 @@ class Bar(Graph): def bar(self, serie_node, serie, index): """Draw a bar graph for a serie""" bars = self.svg.node(serie_node['plot'], class_="bars") - view_values = map(self.view, serie.points) - for i, (x, y) in enumerate(view_values): + for i, (x, y) in enumerate(serie.points): if None in (x, y): continue metadata = serie.metadata.get(i) @@ -68,9 +70,10 @@ class Bar(Graph): self.svg, self.svg.node(bars, class_='bar'), metadata) - val = self._get_value(serie.points, i) + val = self._format(serie.values[i]) - x_center, y_center = self._bar(bar, x, y, index) + x_center, y_center = self._bar( + bar, x, y, index, i, self.zero) self._tooltip_data( bar, val, x_center, y_center, classes="centered") self._static_value(serie_node, val, x_center, y_center) diff --git a/pygal/graph/base.py b/pygal/graph/base.py index bf3b871..eeefa59 100644 --- a/pygal/graph/base.py +++ b/pygal/graph/base.py @@ -49,9 +49,9 @@ class BaseGraph(object): self.view = None if self.logarithmic and self.zero == 0: # Explicit min to avoid interpolation dependency - self.zero = min( - val for serie in self.series for val in serie.values) - + self.zero = min(filter( + lambda x: x > 0, + [val for serie in self.series for val in serie.safe_values])) if self.series and self._has_data(): self._draw() else: diff --git a/pygal/graph/stackedbar.py b/pygal/graph/stackedbar.py index 158ff84..66bf916 100644 --- a/pygal/graph/stackedbar.py +++ b/pygal/graph/stackedbar.py @@ -24,23 +24,29 @@ Stacked Bar chart from __future__ import division from pygal.graph.bar import Bar from pygal.util import compute_scale +from pygal.adapters import none_to_zero class StackedBar(Bar): """Stacked Bar graph""" + _adapters = [none_to_zero] + def _compute(self): transposed = zip(*[serie.values for serie in self.series]) positive_vals = [sum([ - val if val is not None and val > self.zero else self.zero - for val in vals]) - self.zero + val for val in vals + if val is not None and val >= self.zero]) for vals in transposed] negative_vals = [sum([ - val - self.zero - if val is not None and val < self.zero else self.zero - for val in vals]) + self.zero + val + for val in vals + if val is not None and val < self.zero]) for vals in transposed] + positive_vals = positive_vals or [self.zero] + negative_vals = negative_vals or [self.zero] + self._box.ymin, self._box.ymax = ( min(min(negative_vals), self.zero), max(max(positive_vals), self.zero)) @@ -48,6 +54,8 @@ class StackedBar(Bar): x_pos = [ x / self._len for x in range(self._len + 1) ] if self._len > 1 else [0, 1] # Center if only one value + + self._points(x_pos) y_pos = compute_scale( self._box.ymin, self._box.ymax, self.logarithmic, self.order_min ) if not self.y_labels else map(float, self.y_labels) @@ -57,12 +65,16 @@ class StackedBar(Bar): sum(x_range) / 2 for x_range in self._x_ranges]) self._y_labels = zip(map(self._format, y_pos), y_pos) - def _points(self, x_pos): - accumulation = [0] * self._len - for serie in self.series: - accumulation = map(sum, zip(accumulation, serie.values)) - serie.points = [ - (x_pos[i], v) - for i, v in enumerate(accumulation)] - if self.interpolate: - serie.interpolated = self._interpolate(accumulation, x_pos) + self.negative_cumulation = [0] * self._len + self.positive_cumulation = [0] * self._len + + def _bar(self, parent, x, val, index, i, zero): + cumulation = (self.negative_cumulation if val < self.zero else + self.positive_cumulation) + zero = cumulation[i] + cumulation[i] = zero + val + if zero == 0: + zero = self.zero + val -= self.zero + return super(StackedBar, self)._bar( + parent, x, zero + val, index, i, zero, False)