From 6d9bcd5ef74437c1a2ef07625944a99858ce20a2 Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Fri, 23 Mar 2012 17:52:03 +0100 Subject: [PATCH] Work on log scale + minor fixes --- demo/simple_test.py | 25 ++++++++++-------- pygal/css/graph.css | 2 +- pygal/graph/base.py | 20 ++++++++++++--- pygal/graph/line.py | 4 +-- pygal/test/test_config.py | 53 +++++++++++++++++++++++++++++++++++++++ pygal/view.py | 2 +- 6 files changed, 87 insertions(+), 19 deletions(-) create mode 100644 pygal/test/test_config.py diff --git a/demo/simple_test.py b/demo/simple_test.py index f652e3d..6a9fa3f 100755 --- a/demo/simple_test.py +++ b/demo/simple_test.py @@ -32,7 +32,7 @@ bar.add('inc', [None, 1, None, 2]) bar.x_labels = map(str, rng) bar.fill = True -bar.render_to_file('out-bar.svg') +# bar.render_to_file('out-bar.svg') hbar = HorizontalBar() rng = [18, 9, 7, 3, 1, None, -5] @@ -44,7 +44,7 @@ hbar.add('test3', rng3) hbar.x_labels = map( lambda x: '%s / %s' % x, zip(map(str, rng), map(str, rng2))) hbar.title = "Horizontal Bar test" -hbar.render_to_file('out-horizontalbar.svg') +# hbar.render_to_file('out-horizontalbar.svg') rng = [3, -32, 39, 12] rng2 = [24, -8, 18, 12] @@ -63,18 +63,18 @@ stackedbar.add('@@@@@@@', rng) stackedbar.add('++++++', rng2) stackedbar.add('--->', rng3) stackedbar.add('None', [None, 42, 42]) -stackedbar.render_to_file('out-stackedbar.svg') +# stackedbar.render_to_file('out-stackedbar.svg') config.title = "Horizontal Stacked Bar test" hstackedbar = HorizontalStackedBar(config) hstackedbar.add('@@@@@@@', rng) hstackedbar.add('++++++', rng2) hstackedbar.add('--->', rng3) -hstackedbar.render_to_file('out-horizontalstackedbar.svg') +# hstackedbar.render_to_file('out-horizontalstackedbar.svg') line = Line(Config(style=NeonStyle, - zero=1, fill=True, - human_readable=True, logarithmic=True)) + zero=.0001, fill=True, + human_readable=not True, logarithmic=True)) rng = range(-30, 31, 1) # line.add('test1', [1000 ** cos(x / 10.) for x in rng]) @@ -88,7 +88,10 @@ rng = range(-30, 31, 1) # line.add('2', [None, None, 2, 4, 8, None, 14, 10, None]) # line.add('1', [1, 5, 3, 4, 6, 12, 13, 7, 2]) # line.add('1', [.000000091, .000000094, .000092]) -line.add('_', [12, 200001, 12]) +# line.add('_', [2 ** -3, 2.9 ** -8, 2]) +# line.add('_', [.001, .0001, .00001]) +# line.add('_', [1 + 10 ** 10, 3 + 10 ** 10, 2 + 10 ** 10]) +line.add('_', [1, -4, 2, 8, -2]) line.x_labels = map(str, rng) line.title = "Line test" # line.interpolate = "cubic" @@ -102,7 +105,7 @@ stackedline.add('test3', [9, 3, 2, 10, 8, 2]) stackedline.x_labels = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] stackedline.title = "Stackedline test" stackedline.interpolate = "cubic" -stackedline.render_to_file('out-stackedline.svg') +# stackedline.render_to_file('out-stackedline.svg') xy = XY(Config(fill=True, style=NeonStyle, interpolate='cubic')) xy.add('test1', [(1981, 1), (1999, -4), (2001, 2), (2003, 10), (2012, 8)]) @@ -111,7 +114,7 @@ xy.add('test2', [(None, None), (None, 12), (2007, None), (2002.3, 12)]) # xy.add('test2', [(1980, 0), (1985, 2), (1995, -2), (2005, 4), (2020, -4)]) # (2005, 6), (2010, -6), (2015, 3), (2020, -3), (2025, 0)]) xy.title = "XY test" -xy.render_to_file('out-xy.svg') +# xy.render_to_file('out-xy.svg') pie = Pie(Config(style=NeonStyle)) pie.add('test', [11, 8, 21]) @@ -121,7 +124,7 @@ pie.add('test4', [20, 18, 9]) pie.add('test5', [17, 5, 10]) pie.add('test6', [None, None, 10]) pie.title = "Pie test" -pie.render_to_file('out-pie.svg') +# pie.render_to_file('out-pie.svg') config = Config() config.fill = True @@ -135,4 +138,4 @@ radar.add('test2', [10, 2, 0, 5, 1, 9, 4]) radar.title = "Radar test" -radar.render_to_file('out-radar.svg') +# radar.render_to_file('out-radar.svg') diff --git a/pygal/css/graph.css b/pygal/css/graph.css index 30c32c7..366c46d 100644 --- a/pygal/css/graph.css +++ b/pygal/css/graph.css @@ -90,7 +90,7 @@ text.no_data { } .axis.y .logarithmic text:not(.major) { - opacity: 0; + font-size: 50%; } .axis .line { diff --git a/pygal/graph/base.py b/pygal/graph/base.py index 797f3bc..d342b1c 100644 --- a/pygal/graph/base.py +++ b/pygal/graph/base.py @@ -29,10 +29,19 @@ class BaseGraph(object): min_order = int(floor(log10(min_))) max_order = int(ceil(log10(max_))) positions = [] + amplitude = max_order - min_order + if amplitude <= 1: + return [] + detail = 10. + while amplitude * detail < 20: + detail *= 2 + while amplitude * detail > 50: + detail /= 2 for order in range(min_order, max_order + 1): - for i in range(10): - tick = i * 10 ** order - if min_ <= tick <= max_: + for i in range(int(detail)): + tick = (10 * i / detail or 1.) * 10 ** order + tick = round_to_scale(tick, tick) + if min_ <= tick <= max_ and tick not in positions: positions.append(tick) return positions @@ -42,7 +51,10 @@ class BaseGraph(object): if max_ - min_ == 0: return [min_] if self.logarithmic: - return self._compute_logarithmic_scale(min_, max_) + log_scale = self._compute_logarithmic_scale(min_, max_) + if log_scale: + return log_scale + # else we fallback to normal scalling order = round(log10(max(abs(min_), abs(max_)))) - 1 while (max_ - min_) / float(10 ** order) < min_scale: order -= 1 diff --git a/pygal/graph/line.py b/pygal/graph/line.py index e279e84..5604cd8 100644 --- a/pygal/graph/line.py +++ b/pygal/graph/line.py @@ -34,12 +34,12 @@ class Line(Graph): return [val[1] for serie in self.series for val in serie.interpolated - if val[1] != None] + if val[1] != None and (not self.logarithmic or val[1] > 0)] else: return [val[1] for serie in self.series for val in serie.points - if val[1] != None] + if val[1] != None and (not self.logarithmic or val[1] > 0)] def _fill(self, values): zero = self.view.y(min(max(self.zero, self._box.ymin), self._box.ymax)) diff --git a/pygal/test/test_config.py b/pygal/test/test_config.py new file mode 100644 index 0000000..fce2a22 --- /dev/null +++ b/pygal/test/test_config.py @@ -0,0 +1,53 @@ +# -*- 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 import Line + + +def test_logarithmic(): + line = Line(logarithmic=True) + line.add('_', [1, 10 ** 10, 1]) + q = line.render_pyquery() + assert len(q(".axis.x")) == 0 + assert len(q(".axis.y")) == 1 + assert len(q(".plot .series path")) == 1 + assert len(q(".legend")) == 1 + assert len(q(".x.axis .guides")) == 0 + assert len(q(".y.axis .guides")) == 51 + assert len(q(".dots")) == 3 + + +def test_logarithmic_bad_interpolation(): + line = Line(logarithmic=True, interpolate='cubic') + line.add('_', [.001, .00000001, 1]) + q = line.render_pyquery() + assert len(q(".y.axis .guides")) == 40 + + +def test_logarithmic_big_scale(): + line = Line(logarithmic=True) + line.add('_', [10 ** -10, 10 ** 10, 1]) + q = line.render_pyquery() + assert len(q(".y.axis .guides")) == 41 + + +def test_logarithmic_small_scale(): + line = Line(logarithmic=True) + line.add('_', [1 + 10 ** 10, 3 + 10 ** 10, 2 + 10 ** 10]) + q = line.render_pyquery() + assert len(q(".y.axis .guides")) == 21 diff --git a/pygal/view.py b/pygal/view.py index d189ced..25238fd 100644 --- a/pygal/view.py +++ b/pygal/view.py @@ -114,7 +114,7 @@ class LogView(View): self.box.fix(False) def y(self, y): - if y == None: + if y == None or y <= 0: return None return (self.height - self.height * (log10(y) - self.log10_ymin)