<gh_stars>1-10
# -*- 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/>.

"""Gauge chart representing values as needles on a polar scale"""

from __future__ import division

from pygal._compat import is_str
from pygal.graph.graph import Graph
from pygal.util import alter, compute_scale, cut, decorate
from pygal.view import PolarThetaLogView, PolarThetaView


class Gauge(Graph):

    """Gauge graph class"""

    needle_width = 1 / 20

    def _set_view(self):
        """Assign a view to current graph"""
        if self.logarithmic:
            view_class = PolarThetaLogView
        else:
            view_class = PolarThetaView

        self.view = view_class(
            self.width - self.margin_box.x,
            self.height - self.margin_box.y,
            self._box)

    def needle(self, serie):
        """Draw a needle for each value"""
        serie_node = self.svg.serie(serie)
        for i, theta in enumerate(serie.values):
            if theta is None:
                continue

            def point(x, y):
                return '%f %f' % self.view((x, y))

            val = self._format(serie, i)
            metadata = serie.metadata.get(i)
            gauges = decorate(
                self.svg,
                self.svg.node(serie_node['plot'], class_="dots"),
                metadata)

            tolerance = 1.15

            if theta < self._min:
                theta = self._min * tolerance

            if theta > self._max:
                theta = self._max * tolerance

            w = (self._box._tmax - self._box._tmin + self.view.aperture) / 4

            if self.logarithmic:
                w = min(w, self._min - self._min * 10 ** -10)

            alter(
                self.svg.node(
                    gauges, 'path', d='M %s L %s A %s 1 0 1 %s Z' % (
                        point(.85, theta),
                        point(self.needle_width, theta - w),
                        '%f %f' % (self.needle_width, self.needle_width),
                        point(self.needle_width, theta + w),
                    ),
                    class_='line reactive tooltip-trigger'),
                metadata)

            x, y = self.view((.75, theta))
            self._tooltip_data(
                gauges, val, x, y,
                xlabel=self._get_x_label(i))
            self._static_value(serie_node, val, x, y, metadata)

    def _y_axis(self, draw_axes=True):
        """Override y axis to plot a polar axis"""
        axis = self.svg.node(self.nodes['plot'], class_="axis x gauge")

        for i, (label, theta) in enumerate(self._y_labels):
            guides = self.svg.node(axis, class_='guides')

            self.svg.line(
                guides, [self.view((.95, theta)), self.view((1, theta))],
                close=True,
                class_='line')

            self.svg.line(
                guides, [self.view((0, theta)), self.view((.95, theta))],
                close=True,
                class_='guide line %s' % (
                    'major' if i in (0, len(self._y_labels) - 1)
                    else ''))

            x, y = self.view((.9, theta))
            self.svg.node(
                guides, 'text',
                x=x,
                y=y
            ).text = label

            self.svg.node(
                guides, 'title',
            ).text = self._y_format(theta)

    def _x_axis(self, draw_axes=True):
        """Override x axis to put a center circle in center"""
        axis = self.svg.node(self.nodes['plot'], class_="axis y gauge")
        x, y = self.view((0, 0))
        self.svg.node(axis, 'circle', cx=x, cy=y, r=4)

    def _compute(self):
        """Compute y min and max and y scale and set labels"""
        self.min_ = self._min or 0
        self.max_ = self._max or 0
        if self.max_ - self.min_ == 0:
            self.min_ -= 1
            self.max_ += 1

        self._box.set_polar_box(
            0, 1,
            self.min_,
            self.max_)

    def _compute_x_labels(self):
        pass

    def _compute_y_labels(self):
        y_pos = compute_scale(
            self.min_, self.max_, self.logarithmic,
            self.order_min, self.min_scale, self.max_scale
        )
        if self.y_labels:
            self._y_labels = []
            for i, y_label in enumerate(self.y_labels):
                if isinstance(y_label, dict):
                    pos = self._adapt(y_label.get('value'))
                    title = y_label.get('label', self._y_format(pos))
                elif is_str(y_label):
                    pos = self._adapt(y_pos[i])
                    title = y_label
                else:
                    pos = self._adapt(y_label)
                    title = self._y_format(pos)
                self._y_labels.append((title, pos))
            self.min_ = min(self.min_, min(cut(self._y_labels, 1)))
            self.max_ = max(self.max_, max(cut(self._y_labels, 1)))
            self._box.set_polar_box(
                0, 1,
                self.min_,
                self.max_)
        else:
            self._y_labels = list(zip(map(self._y_format, y_pos), y_pos))

    def _plot(self):
        """Plot all needles"""
        for serie in self.series:
            self.needle(serie)

# -*- 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/>.

"""Gauge chart representing values as needles on a polar scale"""

from __future__ import division

from pygal._compat import is_str
from pygal.graph.graph import Graph
from pygal.util import alter, compute_scale, cut, decorate
from pygal.view import PolarThetaLogView, PolarThetaView


class Gauge(Graph):

    """Gauge graph class"""

    needle_width = 1 / 20

    def _set_view(self):
        """Assign a view to current graph"""
        if self.logarithmic:
            view_class = PolarThetaLogView
        else:
            view_class = PolarThetaView

        self.view = view_class(
            self.width - self.margin_box.x,
            self.height - self.margin_box.y,
            self._box)

    def needle(self, serie):
        """Draw a needle for each value"""
        serie_node = self.svg.serie(serie)
        for i, theta in enumerate(serie.values):
            if theta is None:
                continue

            def point(x, y):
                return '%f %f' % self.view((x, y))

            val = self._format(serie, i)
            metadata = serie.metadata.get(i)
            gauges = decorate(
                self.svg,
                self.svg.node(serie_node['plot'], class_="dots"),
                metadata)

            tolerance = 1.15

            if theta < self._min:
                theta = self._min * tolerance

            if theta > self._max:
                theta = self._max * tolerance

            w = (self._box._tmax - self._box._tmin + self.view.aperture) / 4

            if self.logarithmic:
                w = min(w, self._min - self._min * 10 ** -10)

            alter(
                self.svg.node(
                    gauges, 'path', d='M %s L %s A %s 1 0 1 %s Z' % (
                        point(.85, theta),
                        point(self.needle_width, theta - w),
                        '%f %f' % (self.needle_width, self.needle_width),
                        point(self.needle_width, theta + w),
                    ),
                    class_='line reactive tooltip-trigger'),
                metadata)

            x, y = self.view((.75, theta))
            self._tooltip_data(
                gauges, val, x, y,
                xlabel=self._get_x_label(i))
            self._static_value(serie_node, val, x, y, metadata)

    def _y_axis(self, draw_axes=True):
        """Override y axis to plot a polar axis"""
        axis = self.svg.node(self.nodes['plot'], class_="axis x gauge")

        for i, (label, theta) in enumerate(self._y_labels):
            guides = self.svg.node(axis, class_='guides')

            self.svg.line(
                guides, [self.view((.95, theta)), self.view((1, theta))],
                close=True,
                class_='line')

            self.svg.line(
                guides, [self.view((0, theta)), self.view((.95, theta))],
                close=True,
                class_='guide line %s' % (
                    'major' if i in (0, len(self._y_labels) - 1)
                    else ''))

            x, y = self.view((.9, theta))
            self.svg.node(
                guides, 'text',
                x=x,
                y=y
            ).text = label

            self.svg.node(
                guides, 'title',
            ).text = self._y_format(theta)

    def _x_axis(self, draw_axes=True):
        """Override x axis to put a center circle in center"""
        axis = self.svg.node(self.nodes['plot'], class_="axis y gauge")
        x, y = self.view((0, 0))
        self.svg.node(axis, 'circle', cx=x, cy=y, r=4)

    def _compute(self):
        """Compute y min and max and y scale and set labels"""
        self.min_ = self._min or 0
        self.max_ = self._max or 0
        if self.max_ - self.min_ == 0:
            self.min_ -= 1
            self.max_ += 1

        self._box.set_polar_box(
            0, 1,
            self.min_,
            self.max_)

    def _compute_x_labels(self):
        pass

    def _compute_y_labels(self):
        y_pos = compute_scale(
            self.min_, self.max_, self.logarithmic,
            self.order_min, self.min_scale, self.max_scale
        )
        if self.y_labels:
            self._y_labels = []
            for i, y_label in enumerate(self.y_labels):
                if isinstance(y_label, dict):
                    pos = self._adapt(y_label.get('value'))
                    title = y_label.get('label', self._y_format(pos))
                elif is_str(y_label):
                    pos = self._adapt(y_pos[i])
                    title = y_label
                else:
                    pos = self._adapt(y_label)
                    title = self._y_format(pos)
                self._y_labels.append((title, pos))
            self.min_ = min(self.min_, min(cut(self._y_labels, 1)))
            self.max_ = max(self.max_, max(cut(self._y_labels, 1)))
            self._box.set_polar_box(
                0, 1,
                self.min_,
                self.max_)
        else:
            self._y_labels = list(zip(map(self._y_format, y_pos), y_pos))

    def _plot(self):
        """Plot all needles"""
        for serie in self.series:
            self.needle(serie)
# -*- 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/>. Gauge chart representing values as needles on a polar scale Gauge graph class Assign a view to current graph Draw a needle for each value Override y axis to plot a polar axis Override x axis to put a center circle in center Compute y min and max and y scale and set labels Plot all needles