diff --git a/demo/moulinrouge/__init__.py b/demo/moulinrouge/__init__.py
index b61787e..fff61cb 100644
--- a/demo/moulinrouge/__init__.py
+++ b/demo/moulinrouge/__init__.py
@@ -83,7 +83,7 @@ def create_app():
random_value((-max, min)[random.randrange(0, 2)], max))
for i in range(data)]
else:
- values = [random_value((-max, min)[random.randrange(0, 2)],
+ values = [random_value((-max, min)[random.randrange(1, 2)],
max) for i in range(data)]
g.add(random_label(), values)
return g.render_response()
@@ -94,8 +94,10 @@ def create_app():
svgs = [url_for('all_svg', type=type, style=style, fill=fill)
for style in styles
for fill in (False, True)
- for type in ('Bar', 'Line', 'XY', 'Pie', 'StackedBar',
- 'HorizontalBar', 'HorizontalStackedBar', 'Radar')]
+ for type in ('Bar', 'Line', 'XY', 'StackedBar',
+ 'StackedLine', 'HorizontalBar',
+ 'HorizontalStackedBar',
+ 'Pie', 'Radar')]
return render_template('svgs.jinja2',
svgs=svgs,
width=width,
diff --git a/demo/simple_test.py b/demo/simple_test.py
index be2d2da..f514eb4 100755
--- a/demo/simple_test.py
+++ b/demo/simple_test.py
@@ -19,7 +19,7 @@
# along with pygal. If not, see .
from pygal import (
Line, Bar, XY, Pie, Radar, StackedBar, Config,
- HorizontalBar, HorizontalStackedBar)
+ StackedLine, HorizontalBar, HorizontalStackedBar)
from pygal.style import NeonStyle
from math import cos, sin
@@ -72,16 +72,25 @@ hstackedbar.add('--->', rng3)
with open('out-horizontalstackedbar.svg', 'w') as f:
f.write(hstackedbar.render())
-line = Line(Config(y_scale=.0005))
+line = Line(Config(y_scale=.0005, fill=True, style=NeonStyle))
rng = range(-30, 31, 5)
-line.add('test1', [1000 + cos(x / 10.) for x in rng])
-line.add('test2', [1000 + sin(x / 10.) for x in rng])
-line.add('test3', [1000 + cos(x / 10.) - sin(x / 10.) for x in rng])
+line.add('test1', [cos(x / 10.) for x in rng])
+line.add('test2', [sin(x / 10.) for x in rng])
+line.add('test3', [cos(x / 10.) - sin(x / 10.) for x in rng])
line.x_labels = map(str, rng)
line.title = "Line test"
with open('out-line.svg', 'w') as f:
f.write(line.render())
+stackedline = StackedLine(Config(y_scale=.0005, fill=True, style=NeonStyle))
+stackedline.add('test1', [1, 3, 2, 18, 2, 13, 8])
+stackedline.add('test2', [4, 1, 0, 1, 3, 12, 3])
+stackedline.add('test3', [9, 3, 2, 10, 8, 2, 3])
+stackedline.x_labels = map(str, ['a', 'b', 'c', 'd', 'e', 'f', 'g'])
+stackedline.title = "Stackedline test"
+with open('out-stackedline.svg', 'w') as f:
+ f.write(stackedline.render())
+
xy = XY(Config(x_scale=1))
xy.add('test1', [(1981, 1), (2004, 2), (2003, 10), (2012, 8), (1999, -4)])
xy.add('test2', [(1988, -1), (1986, 12), (2007, 7), (2010, 4), (1999, 2)])
diff --git a/pygal/__init__.py b/pygal/__init__.py
index 1bb8cba..3b389b9 100644
--- a/pygal/__init__.py
+++ b/pygal/__init__.py
@@ -24,6 +24,7 @@ from pygal.graph.horizontal import HorizontalBar
from pygal.graph.stackedbar import StackedBar
from pygal.graph.horizontal import HorizontalStackedBar
from pygal.graph.line import Line
+from pygal.graph.stackedline import StackedLine
from pygal.graph.xy import XY
from pygal.graph.pie import Pie
from pygal.graph.radar import Radar
diff --git a/pygal/graph/base.py b/pygal/graph/base.py
index aed77ab..162279e 100644
--- a/pygal/graph/base.py
+++ b/pygal/graph/base.py
@@ -88,6 +88,10 @@ class BaseGraph(object):
def _values(self):
return [val for serie in self.series for val in serie.values]
+ @cached_property
+ def _len(self):
+ return len(self.series[0].values)
+
def _draw(self):
self._compute()
self._compute_margin()
diff --git a/pygal/graph/line.py b/pygal/graph/line.py
index 77ed8b8..f71f16d 100644
--- a/pygal/graph/line.py
+++ b/pygal/graph/line.py
@@ -38,6 +38,11 @@ class Line(Graph):
self.svg.node(dot, 'circle', cx=x, cy=y, r=2.5)
self.svg.node(dot, 'text', x=x, y=y
).text = self._get_value(values, i)
+ if self.fill:
+ zero = self.view.y(min(max(0, self._box.ymin), self._box.ymax))
+ view_values = ([(view_values[0][0], zero)] +
+ view_values +
+ [(view_values[-1][0], zero)])
self.svg.line(
serie_node, view_values, class_='line', close=self._line_close)
@@ -49,9 +54,8 @@ class Line(Graph):
self._box.ymin = min(self._values)
self._box.ymax = max(self._values)
- x_step = len(self.series[0].values)
- self._x_pos = [x / float(x_step - 1) for x in range(x_step)
- ] if x_step != 1 else [.5] # Center if only one value
+ self._x_pos = [x / float(self._len - 1) for x in range(self._len)
+ ] if self._len != 1 else [.5] # Center if only one value
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)
diff --git a/pygal/graph/radar.py b/pygal/graph/radar.py
index e04ddfa..ab44155 100644
--- a/pygal/graph/radar.py
+++ b/pygal/graph/radar.py
@@ -19,7 +19,7 @@
from pygal.graph.line import Line
from pygal.view import PolarView
from pygal.util import deg
-from math import cos, sin, pi
+from math import cos, pi
class Radar(Line):
diff --git a/pygal/graph/stackedline.py b/pygal/graph/stackedline.py
new file mode 100644
index 0000000..2d4a620
--- /dev/null
+++ b/pygal/graph/stackedline.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# This file is part of pygal
+#
+# A python svg graph plotting library
+# Copyright © 2012 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 .
+from pygal.graph.line import Line
+
+
+class StackedLine(Line):
+ """Stacked Line graph"""
+
+ @property
+ def _values(self):
+ sums = map(sum, zip(*[serie.values for serie in self.series]))
+ return sums + super(StackedLine, self)._values
+
+ def _plot(self):
+ accumulation = map(sum, zip(*[serie.values for serie in self.series]))
+ for serie in self.series:
+ self.line(
+ self._serie(serie.index), [
+ (self._x_pos[i], v)
+ for i, v in enumerate(accumulation)])
+ accumulation = map(sum, zip(accumulation,
+ [-v for v in serie.values]))