Browse Source

First almost working first level only treemap

pull/130/head
Florian Mounier 11 years ago
parent
commit
b7f2ec9160
  1. 12
      demo/moulinrouge/tests.py
  2. 3
      pygal/graph/__init__.py
  3. 116
      pygal/graph/treemap.py

12
demo/moulinrouge/tests.py

@ -3,7 +3,7 @@
from pygal import (
Bar, Gauge, Pyramid, Funnel, Dot, StackedBar, StackedLine, XY,
CHARTS_BY_NAME, Config, Line, DateY, Worldmap, Histogram, Box,
FrenchMap_Departments, FrenchMap_Regions, Pie)
FrenchMap_Departments, FrenchMap_Regions, Pie, Treemap)
from pygal.style import styles, Style
from pygal.colors import rotate
from pygal.graph.frenchmap import DEPARTMENTS, REGIONS
@ -118,6 +118,14 @@ def get_test_routes(app):
bar.add('Lol', [2, None, 12])
return bar.render_response()
@app.route('/test/treemap')
def test_treemap():
treemap = Treemap()
treemap.add('A', [2])
treemap.add('B', [4])
treemap.add('C', [3])
return treemap.render_response()
@app.route('/test/gauge')
def test_gauge():
gauge = Gauge()
@ -505,4 +513,4 @@ def get_test_routes(app):
graph.legend_at_bottom = True
return graph.render_response()
return list(filter(lambda x: x.startswith('test'), locals()))
return list(sorted(filter(lambda x: x.startswith('test'), locals())))

3
pygal/graph/__init__.py

@ -41,5 +41,6 @@ CHARTS_NAMES = [
'SupranationalWorldmap',
'Histogram',
'Box',
'FrenchMap'
'FrenchMap',
'Treemap'
]

116
pygal/graph/treemap.py

@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
# This file is part of pygal
#
# A python svg graph plotting library
# Copyright © 2012-2014 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 <http://www.gnu.org/licenses/>.
"""
Treemap chart
"""
from __future__ import division
from pygal.util import decorate
from pygal.graph.graph import Graph
from pygal.adapters import positive, none_to_zero
class Treemap(Graph):
"""Treemap graph"""
_adapters = [positive, none_to_zero]
def _rect(self, serie, x, y, w, h):
rx, ry = self.view((x, y))
rw, rh = self.view((x + w, y + h))
rw -= rx
rh -= ry
serie_node = self._serie(serie._index)
rects = self.svg.node(serie_node['plot'], class_="rects")
# metadata = serie.metadata.get(i)
# value = self._format(serie.values[i])
# rect = decorate(
# self.svg,
# self.svg.node(rects, class_="rect"),
# metadata)
self.svg.node(rects, 'rect',
x=rx,
y=ry,
width=rw,
height=rh,
class_='rect reactive tooltip-trigger')
# self._tooltip_data(rect, value,
# self.view.x(acc + w / 2),
# self.view.y(.5),
# classes='centered')
# self._static_value(serie_node, value,
# self.view.x(acc + w / 2),
# self.view.y(.5))
def _binary_tree(self, series, total, x, y, w, h):
if len(series) == 1:
self._rect(series[0], x, y, w, h)
return
midpoint = total / 2
pivot_index = 1
running_sum = 0
for serie in series:
if running_sum >= midpoint:
pivot_index = serie._index
break
running_sum += sum(serie.values)
half1 = series[:pivot_index]
half2 = series[pivot_index:]
half1_sum = sum(map(sum, map(lambda x: x.values, half1)))
half2_sum = sum(map(sum, map(lambda x: x.values, half2)))
pivot_pct = half1_sum / total
if h > w:
y_pivot = pivot_pct * h
self._binary_tree(
half1, half1_sum, x, y, w, y_pivot)
self._binary_tree(
half2, half2_sum, x, y + y_pivot, w, h - y_pivot)
else:
x_pivot = pivot_pct * w
self._binary_tree(
half1, half1_sum, x, y, x_pivot, h)
self._binary_tree(
half2, half2_sum, x + x_pivot, y, w - x_pivot, h)
def _plot(self):
total = sum(map(sum, map(lambda x: x.values, self.series)))
if total == 0:
return
gw = self.width - self.margin.x
gh = self.height - self.margin.y
self.view.box.xmin = self.view.box.ymin = x = y = 0
self.view.box.xmax = w = (total * gw / gh) ** .5
self.view.box.ymax = h = total / w
self.view.box.fix()
for index, serie in enumerate(self.series):
serie._index = index
self._binary_tree(self.series, total, x, y, w, h)
Loading…
Cancel
Save