Source code for pygal.graph.treemap

# -*- coding: utf-8 -*-
# This file is part of pygal
#
# A python svg graph plotting library
# Copyright © 2012-2016 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: Visualize data using nested recangles"""

from __future__ import division

from pygal.adapters import none_to_zero, positive
from pygal.graph.graph import Graph
from pygal.util import alter, cut, decorate


[docs]class Treemap(Graph): """Treemap graph class""" _adapters = [positive, none_to_zero] def _rect(self, serie, serie_node, rects, val, x, y, w, h, i): rx, ry = self.view((x, y)) rw, rh = self.view((x + w, y + h)) rw -= rx rh -= ry metadata = serie.metadata.get(i) val = self._format(serie, i) rect = decorate( self.svg, self.svg.node(rects, class_="rect"), metadata) alter( self.svg.node( rect, 'rect', x=rx, y=ry, width=rw, height=rh, class_='rect reactive tooltip-trigger'), metadata) self._tooltip_data( rect, val, rx + rw / 2, ry + rh / 2, 'centered', self._get_x_label(i)) self._static_value( serie_node, val, rx + rw / 2, ry + rh / 2, metadata) def _binary_tree(self, data, total, x, y, w, h, parent=None): if total == 0: return if len(data) == 1: if parent: i, datum = data[0] serie, serie_node, rects = parent self._rect(serie, serie_node, rects, datum, x, y, w, h, i) else: datum = data[0] serie_node = self.svg.serie(datum) self._binary_tree( list(enumerate(datum.values)), total, x, y, w, h, (datum, serie_node, self.svg.node(serie_node['plot'], class_="rects"))) return midpoint = total / 2 pivot_index = 1 running_sum = 0 for i, elt in enumerate(data): if running_sum >= midpoint: pivot_index = i break running_sum += elt[1] if parent else sum(elt.values) half1 = data[:pivot_index] half2 = data[pivot_index:] if parent: half1_sum = sum(cut(half1, 1)) half2_sum = sum(cut(half2, 1)) else: 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, parent) self._binary_tree( half2, half2_sum, x, y + y_pivot, w, h - y_pivot, parent) else: x_pivot = pivot_pct * w self._binary_tree( half1, half1_sum, x, y, x_pivot, h, parent) self._binary_tree( half2, half2_sum, x + x_pivot, y, w - x_pivot, h, parent) def _compute_x_labels(self): pass def _compute_y_labels(self): pass def _plot(self): total = sum(map(sum, map(lambda x: x.values, self.series))) if total == 0: return gw = self.width - self.margin_box.x gh = self.height - self.margin_box.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() self._binary_tree(self.series, total, x, y, w, h)