Esempio n. 1
0
 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))
Esempio n. 2
0
 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))
Esempio n. 3
0
    def _compute_margin(self):
        """Compute graph margins from set texts"""
        if self.show_legend and self.series:
            h, w = get_texts_box(
                map(lambda x: truncate(x, self.truncate_legend or 15),
                    cut(self.series, 'title')), self.legend_font_size)
            if self.legend_at_bottom:
                h_max = max(h, self.legend_box_size)
                self.margin.bottom += 10 + h_max * round(
                    sqrt(self._order) - 1) * 1.5 + h_max
            else:
                self.margin.right += 10 + w + self.legend_box_size

        if self.title:
            h, _ = get_text_box(self.title[0], self.title_font_size)
            self.margin.top += len(self.title) * (10 + h)

        if self._x_labels:
            h, w = get_texts_box(cut(self._x_labels), self.label_font_size)
            self._x_labels_height = 10 + max(
                w * sin(rad(self.x_label_rotation)), h)
            self.margin.bottom += self._x_labels_height
            if self.x_label_rotation:
                self.margin.right = max(w * cos(rad(self.x_label_rotation)),
                                        self.margin.right)
        else:
            self._x_labels_height = 0
        if self._y_labels:
            h, w = get_texts_box(cut(self._y_labels), self.label_font_size)
            self.margin.left += 10 + max(w * cos(rad(self.y_label_rotation)),
                                         h)
Esempio n. 4
0
    def _compute(self):
        x_pos = [(x + 1) / self._order for x in range(self._order)
                 ] if self._order != 1 else [.5]  # Center if only one value

        previous = [[self.zero, self.zero] for i in range(self._len)]
        for i, serie in enumerate(self.series):
            y_height = -sum(serie.safe_values) / 2
            all_x_pos = [0] + x_pos
            serie.points = []
            for j, value in enumerate(serie.values):
                poly = []
                poly.append((all_x_pos[i], previous[j][0]))
                poly.append((all_x_pos[i], previous[j][1]))
                previous[j][0] = y_height
                y_height = previous[j][1] = y_height + value
                poly.append((all_x_pos[i + 1], previous[j][1]))
                poly.append((all_x_pos[i + 1], previous[j][0]))
                serie.points.append(poly)

        val_max = max(list(map(sum, cut(self.series, 'values'))) + [self.zero])
        self._box.ymin = -val_max
        self._box.ymax = val_max

        y_pos = compute_scale(self._box.ymin, self._box.ymax, self.logarithmic,
                              self.order_min) if not self.y_labels else map(
                                  float, self.y_labels)

        self._x_labels = list(
            zip(cut(self.series, 'title'),
                map(lambda x: x - 1 / (2 * self._order), x_pos)))
        self._y_labels = list(zip(map(self._format, y_pos), y_pos))
Esempio n. 5
0
    def _compute_margin(self):
        """Compute graph margins from set texts"""
        if self.show_legend and self.series:
            h, w = get_texts_box(
                map(lambda x: truncate(x, self.truncate_legend or 15),
                    cut(self.series, 'title')),
                self.legend_font_size)
            if self.legend_at_bottom:
                h_max = max(h, self.legend_box_size)
                self.margin.bottom += 10 + h_max * round(
                    sqrt(self._order) - 1) * 1.5 + h_max
            else:
                self.margin.right += 10 + w + self.legend_box_size

        if self.title:
            h, _ = get_text_box(self.title[0], self.title_font_size)
            self.margin.top += len(self.title) * (10 + h)

        if self._x_labels:
            h, w = get_texts_box(
                cut(self._x_labels), self.label_font_size)
            self._x_labels_height = 10 + max(
                w * sin(rad(self.x_label_rotation)), h)
            self.margin.bottom += self._x_labels_height
            if self.x_label_rotation:
                self.margin.right = max(
                    w * cos(rad(self.x_label_rotation)),
                    self.margin.right)
        else:
            self._x_labels_height = 0
        if self._y_labels:
            h, w = get_texts_box(
                cut(self._y_labels), self.label_font_size)
            self.margin.left += 10 + max(
                w * cos(rad(self.y_label_rotation)), h)
Esempio n. 6
0
    def _compute(self):
        xlen = len(self.series)
        x_pos = [(x + 1) / xlen for x in range(xlen)
        ] if xlen != 1 else [.5]  # Center if only one value

        previous = [[0, 0] for i in range(self._len)]
        for i, serie in enumerate(self.series):
            y_height = - sum(serie.values) / 2
            all_x_pos = [0] + x_pos
            serie.points = []
            for j, value in enumerate(serie.values):
                poly = []
                poly.append((all_x_pos[i], previous[j][0]))
                poly.append((all_x_pos[i], previous[j][1]))
                previous[j][0] = y_height
                y_height = previous[j][1] = y_height + value
                poly.append((all_x_pos[i + 1], previous[j][1]))
                poly.append((all_x_pos[i + 1], previous[j][0]))
                serie.points.append(poly)

        val_max = max(map(sum, cut(self.series, 'values')))
        self._box.ymin = -val_max
        self._box.ymax = val_max

        y_pos = compute_scale(
            self._box.ymin, self._box.ymax, self.logarithmic, self.order_min
        ) if not self.y_labels else map(float, self.y_labels)

        self._x_labels = zip(cut(self.series, 'title'),
                             map(lambda x: x - 1 / (2 * xlen), x_pos))
        self._y_labels = zip(map(self._format, y_pos), y_pos)
Esempio n. 7
0
    def _compute(self):
        x_pos = [
            (x + 1) / self._order for x in range(self._order)
        ] if self._order != 1 else [.5]  # Center if only one value

        previous = [[self.zero, self.zero] for i in range(self._len)]
        for i, serie in enumerate(self.series):
            y_height = - sum(serie.safe_values) / 2
            all_x_pos = [0] + x_pos
            serie.points = []
            for j, value in enumerate(serie.values):
                poly = []
                poly.append((all_x_pos[i], previous[j][0]))
                poly.append((all_x_pos[i], previous[j][1]))
                previous[j][0] = y_height
                y_height = previous[j][1] = y_height + value
                poly.append((all_x_pos[i + 1], previous[j][1]))
                poly.append((all_x_pos[i + 1], previous[j][0]))
                serie.points.append(poly)

        val_max = max(list(map(sum, cut(self.series, 'values'))) + [self.zero])
        self._box.ymin = -val_max
        self._box.ymax = val_max

        y_pos = compute_scale(
            self._box.ymin, self._box.ymax, self.logarithmic, self.order_min,
            self.min_scale, self.max_scale
        ) if not self.y_labels else list(map(float, self.y_labels))

        self._x_labels = list(
            zip(cut(self.series, 'title'),
                map(lambda x: x - 1 / (2 * self._order), x_pos)))
        self._y_labels = list(zip(map(self._format, y_pos), y_pos))
Esempio n. 8
0
 def populate(self):
     all = self.get_query().order_by(self.criteria).all()
     if self.criteria_name == 'spent_time':
         self.chart.x_labels = [
             "<1s", "1s", "2s", "5s", "10s", "20s",
             "30s", "1min", "2min", "5min",  ">10min"]
     else:
         self.chart.x_labels = list(map(str, map(int, cut(all, 0))))
     self.chart.add(labelize(self.criteria_name, self.lang),
                    list(map(float, cut(all, 1))))
Esempio n. 9
0
    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
            )
Esempio n. 10
0
    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)
Esempio n. 11
0
def adapt(chart, data):
    if isinstance(chart, pygal.DateY):
        # Convert to a credible datetime
        return list(map(
            lambda t:
            (datetime.fromtimestamp(1360000000 + t[0] * 987654)
             if t[0] is not None else None, t[1]), data))

    if isinstance(chart, pygal.XY):
        return data

    data = cut(data)
    if isinstance(chart, pygal.Worldmap):
        return list(
            map(lambda x: list(
                COUNTRIES.keys())[
                    int(x) % len(COUNTRIES)]
                if x is not None else None, data))
    elif isinstance(chart, pygal.FrenchMap_Regions):
        return list(
            map(lambda x: list(
                REGIONS.keys())[
                    int(x) % len(REGIONS)]
                if x is not None else None, data))
    elif isinstance(chart, pygal.FrenchMap_Departments):
        return list(
            map(lambda x: list(
                DEPARTMENTS.keys())[
                    int(x) % len(DEPARTMENTS)]
                if x is not None else None, data))
    return data
Esempio n. 12
0
def adapt(chart, data):
    if isinstance(chart, pygal.DateY):
        # Convert to a credible datetime
        return list(
            map(
                lambda t: (datetime.fromtimestamp(1360000000 + t[0] * 987654)
                           if t[0] is not None else None, t[1]), data))

    if isinstance(chart, pygal.XY):
        return data

    data = cut(data)
    if isinstance(chart, pygal.Worldmap):
        return list(
            map(
                lambda x: list(COUNTRIES.keys())[x % len(COUNTRIES)]
                if x is not None else None, data))
    elif isinstance(chart, pygal.FrenchMap_Regions):
        return list(
            map(
                lambda x: list(REGIONS.keys())[x % len(REGIONS)]
                if x is not None else None, data))
    elif isinstance(chart, pygal.FrenchMap_Departments):
        return list(
            map(
                lambda x: list(DEPARTMENTS.keys())[x % len(DEPARTMENTS)]
                if x is not None else None, data))
    return data
Esempio n. 13
0
    def _plot(self):
        map = etree.fromstring(MAP)
        map.set('width', str(self.view.width))
        map.set('height', str(self.view.height))

        for i, serie in enumerate(self.series):
            safe_vals = list(filter(
                lambda x: x is not None, cut(serie.values, 1)))
            if not safe_vals:
                continue
            min_ = min(safe_vals)
            max_ = max(safe_vals)
            for j, (country_code, value) in enumerate(serie.values):
                if value is None:
                    continue
                if max_ == min_:
                    ratio = 1
                else:
                    ratio = .3 + .7 * (value - min_) / (max_ - min_)

                try:
                    country = map.find('.//*[@id="%s"]' % country_code)
                except SyntaxError:
                    # Python 2.6 (you'd better install lxml)
                    country = None
                    for e in map:
                        if e.attrib.get('id', '') == country_code:
                            country = e

                if country is None:
                    continue
                cls = country.get('class', '').split(' ')
                cls.append('color-%d' % i)
                country.set('class', ' '.join(cls))
                country.set(
                    'style', 'fill-opacity: %f' % (
                        ratio))

                metadata = serie.metadata.get(j)
                if metadata:
                    node = decorate(self.svg, country, metadata)
                    if node != country:
                        country.remove(node)
                        index = list(map).index(country)
                        map.remove(country)
                        node.append(country)
                        map.insert(index, node)

                last_node = len(country) > 0 and country[-1]
                if last_node is not None and last_node.tag == 'title':
                    title_node = last_node
                    text = title_node.text + '\n'
                else:
                    title_node = self.svg.node(country, 'title')
                    text = ''
                title_node.text = text + '[%s] %s: %s' % (
                    serie.title,
                    self.country_names[country_code], self._format(value))

        self.nodes['plot'].append(map)
Esempio n. 14
0
def test_metadata(Chart):
    chart = Chart()
    v = range(7)
    if Chart == pygal.XY:
        v = map(lambda x: (x, x + 1), v)

    chart.add('Serie with metadata', [
        v[0],
        {'value': v[1]},
        {'value': v[2], 'label': 'Three'},
        {'value': v[3], 'xlink': 'http://4.example.com/'},
        {'value': v[4], 'xlink': 'http://5.example.com/', 'label': 'Five'},
        {'value': v[5], 'xlink': {
            'href': 'http://6.example.com/'}, 'label': 'Six'},
        {'value': v[6], 'xlink': {
            'href': 'http://7.example.com/',
            'target': '_blank'}, 'label': 'Seven'}
    ])
    q = chart.render_pyquery()
    for md in (
            'Three', 'http://4.example.com/',
            'Five', 'http://7.example.com/', 'Seven'):
        assert md in cut(q('desc'), 'text')

    assert len(v) == len(q('.tooltip-trigger').siblings('.value'))
Esempio n. 15
0
    def _compute(self):
        """Compute y min and max and y scale and set labels"""
        self._x_pos = [
            (x + 1) / self._order for x in range(self._order)
        ] if self._order != 1 else [.5]  # Center if only one value

        previous = [[self.zero, self.zero] for i in range(self._len)]
        for i, serie in enumerate(self.series):
            y_height = -sum(serie.safe_values) / 2
            all_x_pos = [0] + self._x_pos
            serie.points = []
            for j, value in enumerate(serie.values):
                poly = []
                poly.append((all_x_pos[i], previous[j][0]))
                poly.append((all_x_pos[i], previous[j][1]))
                previous[j][0] = y_height
                y_height = previous[j][1] = y_height + value
                poly.append((all_x_pos[i + 1], previous[j][1]))
                poly.append((all_x_pos[i + 1], previous[j][0]))
                serie.points.append(poly)

        val_max = max(list(map(sum, cut(self.series, 'values'))) + [self.zero])
        self._box.ymin = -val_max
        self._box.ymax = val_max

        if self.range and self.range[0] is not None:
            self._box.ymin = self.range[0]

        if self.range and self.range[1] is not None:
            self._box.ymax = self.range[1]
Esempio n. 16
0
def test_metadata(Chart):
    chart = Chart()
    v = range(7)
    if Chart in (pygal.Box,):
        return  # summary charts cannot display per-value metadata
    elif Chart == pygal.XY:
        v = list(map(lambda x: (x, x + 1), v))
    elif issubclass(Chart, BaseMap):
        v = [(i, k) for k, i in enumerate(Chart.x_labels)]

    chart.add('Serie with metadata', [
        v[0],
        {'value': v[1]},
        {'value': v[2], 'label': 'Three'},
        {'value': v[3], 'xlink': 'http://4.example.com/'},
        {'value': v[4], 'xlink': 'http://5.example.com/', 'label': 'Five'},
        {'value': v[5], 'xlink': {
            'href': 'http://6.example.com/'}, 'label': 'Six'},
        {'value': v[6], 'xlink': {
            'href': 'http://7.example.com/',
            'target': '_blank'}, 'label': 'Seven'}
    ])
    q = chart.render_pyquery()
    for md in (
            'Three', 'http://4.example.com/',
            'Five', 'http://7.example.com/', 'Seven'):
        assert md in cut(q('desc'), 'text')

    if Chart in (pygal.Pie, pygal.Treemap):
        # Slices with value 0 are not rendered
        assert len(v) - 1 == len(q('.tooltip-trigger').siblings('.value'))
    elif not issubclass(Chart, BaseMap):

        # Tooltip are not working on maps
        assert len(v) == len(q('.tooltip-trigger').siblings('.value'))
Esempio n. 17
0
    def all(style='default', interpolate=None):
        width, height = 600, 400
        data = random.randrange(1, 10)
        order = random.randrange(1, 10)
        xy_series = _random(data, order)
        other_series = []
        for title, values in xy_series:
            other_series.append((title, cut(values, 1)))
        xy_series = b64encode(pickle.dumps(xy_series))
        other_series = b64encode(pickle.dumps(other_series))
        config = Config()
        config.width = width
        config.height = height
        config.fill = bool(random.randrange(0, 2))
        config.human_readable = True
        config.interpolate = interpolate
        config.style = styles[style]
        config.x_labels = [random_label() for i in range(data)]
        svgs = []
        for chart in pygal.CHARTS:
            type = chart.__name__
            svgs.append({
                'type': type,
                'series': xy_series if type == 'XY' else other_series,
                'config': b64encode(pickle.dumps(config))
            })

        return render_template('svgs.jinja2',
                               svgs=svgs,
                               width=width,
                               height=height)
Esempio n. 18
0
 def get(data):
     if isinstance(chart, pygal.XY):
         if isinstance(chart, pygal.DateY):
             # Convert to a credible datetime
             return datetime.fromtimestamp(1360000000 + data * 987654)
         return data
     return cut(data)
Esempio n. 19
0
File: graph.py Progetto: Kozea/pygal
    def _compute_y_labels_major(self):
        if self.y_labels_major_every:
            self._y_labels_major = [
                self._y_labels[i][1] for i in
                range(0, len(self._y_labels), self.y_labels_major_every)
            ]

        elif self.y_labels_major_count:
            label_count = len(self._y_labels)
            major_count = self.y_labels_major_count
            if (major_count >= label_count):
                self._y_labels_major = [label[1] for label in self._y_labels]
            else:
                self._y_labels_major = [
                    self._y_labels[int(
                        i * (label_count - 1) / (major_count - 1)
                    )][1] for i in range(major_count)
                ]

        elif self.y_labels_major:
            self._y_labels_major = list(map(self._adapt, self.y_labels_major))
        elif self._y_labels:
            self._y_labels_major = majorize(cut(self._y_labels, 1))
        else:
            self._y_labels_major = []
Esempio n. 20
0
    def all(style='default', color=None, interpolate=None, base_style=None):
        width, height = 600, 400
        data = random.randrange(1, 10)
        order = random.randrange(1, 10)
        if color is None:
            style = styles[style]
        else:
            style = parametric_styles[style](
                color, base_style=styles[base_style or 'default'])
        xy_series = _random(data, order)
        other_series = []
        for title, values in xy_series:
            other_series.append(
                (title, cut(values, 1)))
        xy_series = b64encode(pickle.dumps(xy_series))
        other_series = b64encode(pickle.dumps(other_series))
        config = Config()
        config.width = width
        config.height = height
        config.fill = bool(random.randrange(0, 2))
        config.human_readable = True
        config.interpolate = interpolate
        config.style = style
        config.x_labels = [random_label() for i in range(data)]
        svgs = []
        for chart in pygal.CHARTS:
            type = chart.__name__
            svgs.append({'type': type,
                         'series': xy_series if type == 'XY' else other_series,
                         'config': b64encode(pickle.dumps(config))})

        return render_template('svgs.jinja2',
                               svgs=svgs,
                               width=width,
                               height=height)
Esempio n. 21
0
    def _compute(self):
        """Compute y min and max and y scale and set labels"""
        self._x_pos = [
            (x + 1) / self._order for x in range(self._order)
        ] if self._order != 1 else [.5]  # Center if only one value

        previous = [[self.zero, self.zero] for i in range(self._len)]
        for i, serie in enumerate(self.series):
            y_height = - sum(serie.safe_values) / 2
            all_x_pos = [0] + self._x_pos
            serie.points = []
            for j, value in enumerate(serie.values):
                poly = []
                poly.append((all_x_pos[i], previous[j][0]))
                poly.append((all_x_pos[i], previous[j][1]))
                previous[j][0] = y_height
                y_height = previous[j][1] = y_height + value
                poly.append((all_x_pos[i + 1], previous[j][1]))
                poly.append((all_x_pos[i + 1], previous[j][0]))
                serie.points.append(poly)

        val_max = max(list(map(sum, cut(self.series, 'values'))) + [self.zero])
        self._box.ymin = -val_max
        self._box.ymax = val_max

        if self.range and self.range[0] is not None:
            self._box.ymin = self.range[0]

        if self.range and self.range[1] is not None:
            self._box.ymax = self.range[1]
Esempio n. 22
0
    def _compute_y_labels_major(self):
        if self.y_labels_major_every:
            self._y_labels_major = [
                self._y_labels[i][1] for i in
                range(0, len(self._y_labels), self.y_labels_major_every)
            ]

        elif self.y_labels_major_count:
            label_count = len(self._y_labels)
            major_count = self.y_labels_major_count
            if (major_count >= label_count):
                self._y_labels_major = [label[1] for label in self._y_labels]
            else:
                self._y_labels_major = [
                    self._y_labels[int(
                        i * (label_count - 1) / (major_count - 1)
                    )][1] for i in range(major_count)
                ]

        elif self.y_labels_major:
            self._y_labels_major = list(map(self._adapt, self.y_labels_major))
        elif self._y_labels:
            self._y_labels_major = majorize(cut(self._y_labels, 1))
        else:
            self._y_labels_major = []
Esempio n. 23
0
def test_metadata(Chart):
    chart = Chart()
    v = range(7)
    if Chart == pygal.XY:
        v = list(map(lambda x: (x, x + 1), v))
    elif Chart == pygal.Worldmap or Chart == pygal.SupranationalWorldmap:
        v = list(map(lambda x: x, i18n.COUNTRIES))

    chart.add('Serie with metadata', [
        v[0],
        {'value': v[1]},
        {'value': v[2], 'label': 'Three'},
        {'value': v[3], 'xlink': 'http://4.example.com/'},
        {'value': v[4], 'xlink': 'http://5.example.com/', 'label': 'Five'},
        {'value': v[5], 'xlink': {
            'href': 'http://6.example.com/'}, 'label': 'Six'},
        {'value': v[6], 'xlink': {
            'href': 'http://7.example.com/',
            'target': '_blank'}, 'label': 'Seven'}
    ])
    q = chart.render_pyquery()
    for md in (
            'Three', 'http://4.example.com/',
            'Five', 'http://7.example.com/', 'Seven'):
        assert md in cut(q('desc'), 'text')

    if Chart == pygal.Pie:
        # Slices with value 0 are not rendered
        assert len(v) - 1 == len(q('.tooltip-trigger').siblings('.value'))
    elif Chart != pygal.Worldmap and Chart != pygal.SupranationalWorldmap:
        # Tooltip are not working on worldmap
        assert len(v) == len(q('.tooltip-trigger').siblings('.value'))
Esempio n. 24
0
 def get(data):
     if isinstance(chart, pygal.XY):
         if isinstance(chart, pygal.DateY):
             # Convert to a credible datetime
             return datetime.fromtimestamp(1360000000 + data * 987654)
         return data
     return cut(data)
Esempio n. 25
0
def test_metadata(Chart):
    chart = Chart()
    v = range(7)
    if Chart in (pygal.Box,):
        return  # summary charts cannot display per-value metadata
    elif Chart == pygal.XY:
        v = list(map(lambda x: (x, x + 1), v))
    elif Chart == pygal.Worldmap or Chart == pygal.SupranationalWorldmap:
        v = list(map(lambda x: x, i18n.COUNTRIES))

    chart.add(
        "Serie with metadata",
        [
            v[0],
            {"value": v[1]},
            {"value": v[2], "label": "Three"},
            {"value": v[3], "xlink": "http://4.example.com/"},
            {"value": v[4], "xlink": "http://5.example.com/", "label": "Five"},
            {"value": v[5], "xlink": {"href": "http://6.example.com/"}, "label": "Six"},
            {"value": v[6], "xlink": {"href": "http://7.example.com/", "target": "_blank"}, "label": "Seven"},
        ],
    )
    q = chart.render_pyquery()
    for md in ("Three", "http://4.example.com/", "Five", "http://7.example.com/", "Seven"):
        assert md in cut(q("desc"), "text")

    if Chart == pygal.Pie:
        # Slices with value 0 are not rendered
        assert len(v) - 1 == len(q(".tooltip-trigger").siblings(".value"))
    elif Chart != pygal.Worldmap and Chart != pygal.SupranationalWorldmap:
        # Tooltip are not working on worldmap
        assert len(v) == len(q(".tooltip-trigger").siblings(".value"))
Esempio n. 26
0
    def _plot(self):
        map = etree.fromstring(self.svg_map)
        map.set('width', str(self.view.width))
        map.set('height', str(self.view.height))

        for i, serie in enumerate(self.series):
            safe_vals = list(filter(
                lambda x: x is not None, cut(serie.values, 1)))
            if not safe_vals:
                continue
            min_ = min(safe_vals)
            max_ = max(safe_vals)
            for j, (area_code, value) in enumerate(serie.values):
                if isinstance(area_code, Number):
                    area_code = '%2d' % area_code
                if value is None:
                    continue
                if max_ == min_:
                    ratio = 1
                else:
                    ratio = .3 + .7 * (value - min_) / (max_ - min_)
                areae = map.findall(
                    ".//*[@class='%s%s %s map-element']" % (
                        self.area_prefix, area_code,
                        self.kind))

                if not areae:
                    continue
                for area in areae:
                    cls = area.get('class', '').split(' ')
                    cls.append('color-%d' % i)
                    area.set('class', ' '.join(cls))
                    area.set('style', 'fill-opacity: %f' % (ratio))

                    metadata = serie.metadata.get(j)
                    if metadata:
                        node = decorate(self.svg, area, metadata)
                        if node != area:
                            area.remove(node)
                            for g in map:
                                if area not in g:
                                    continue
                                index = list(g).index(area)
                                g.remove(area)
                                node.append(area)
                                g.insert(index, node)

                    last_node = len(area) > 0 and area[-1]
                    if last_node is not None and last_node.tag == 'title':
                        title_node = last_node
                        text = title_node.text + '\n'
                    else:
                        title_node = self.svg.node(area, 'title')
                        text = ''
                    title_node.text = text + '[%s] %s: %s' % (
                        serie.title,
                        self.area_names[area_code], self._format(value))

        self.nodes['plot'].append(map)
Esempio n. 27
0
    def _plot(self):
        map = etree.fromstring(self.svg_map)
        map.set("width", str(self.view.width))
        map.set("height", str(self.view.height))

        for i, serie in enumerate(self.series):
            safe_vals = list(filter(lambda x: x is not None, cut(serie.values, 1)))
            if not safe_vals:
                continue
            min_ = min(safe_vals)
            max_ = max(safe_vals)
            for j, (area_code, value) in enumerate(serie.values):
                if isinstance(area_code, Number):
                    area_code = "%2d" % area_code
                if value is None:
                    continue
                if max_ == min_:
                    ratio = 1
                else:
                    ratio = 0.3 + 0.7 * (value - min_) / (max_ - min_)
                areae = map.xpath(
                    "//*[contains(concat(' ', normalize-space(@class), ' '),"
                    " ' %s%s ')]" % (self.area_prefix, area_code)
                )

                if not areae:
                    continue
                for area in areae:
                    cls = area.get("class", "").split(" ")
                    cls.append("color-%d" % i)
                    area.set("class", " ".join(cls))
                    area.set("style", "fill-opacity: %f" % (ratio))

                    metadata = serie.metadata.get(j)
                    if metadata:
                        parent = area.getparent()
                        node = decorate(self.svg, area, metadata)
                        if node != area:
                            area.remove(node)
                            index = parent.index(area)
                            parent.remove(area)
                            node.append(area)
                            parent.insert(index, node)

                    last_node = len(area) > 0 and area[-1]
                    if last_node is not None and last_node.tag == "title":
                        title_node = last_node
                        text = title_node.text + "\n"
                    else:
                        title_node = self.svg.node(area, "title")
                        text = ""
                    title_node.text = text + "[%s] %s: %s" % (
                        serie.title,
                        self.area_names[area_code],
                        self._format(value),
                    )

        self.nodes["plot"].append(map)
Esempio n. 28
0
    def _plot(self):
        map = etree.fromstring(MAP)
        map.set('width', str(self.view.width))
        map.set('height', str(self.view.height))

        for i, serie in enumerate(self.series):
            safe_vals = list(
                filter(lambda x: x is not None, cut(serie.values, 1)))
            if not safe_vals:
                continue
            min_ = min(safe_vals)
            max_ = max(safe_vals)
            for j, (country_code, value) in enumerate(serie.values):
                if value is None:
                    continue
                if max_ == min_:
                    ratio = 1
                else:
                    ratio = .3 + .7 * (value - min_) / (max_ - min_)

                try:
                    country = map.find('.//*[@id="%s"]' % country_code)
                except SyntaxError:
                    # Python 2.6 (you'd better install lxml)
                    country = None
                    for e in map:
                        if e.attrib.get('id', '') == country_code:
                            country = e

                if country is None:
                    continue
                cls = country.get('class', '').split(' ')
                cls.append('color-%d' % i)
                country.set('class', ' '.join(cls))
                country.set('style', 'fill-opacity: %f' % (ratio))

                metadata = serie.metadata.get(j)
                if metadata:
                    node = decorate(self.svg, country, metadata)
                    if node != country:
                        country.remove(node)
                        index = list(map).index(country)
                        map.remove(country)
                        node.append(country)
                        map.insert(index, node)

                last_node = len(country) > 0 and country[-1]
                if last_node is not None and last_node.tag == 'title':
                    title_node = last_node
                    text = title_node.text + '\n'
                else:
                    title_node = self.svg.node(country, 'title')
                    text = ''
                title_node.text = text + '[%s] %s: %s' % (
                    serie.title, self.country_names[country_code],
                    self._format(value))

        self.nodes['plot'].append(map)
Esempio n. 29
0
    def _plot(self):
        map = etree.fromstring(self.svg_map)
        map.set('width', str(self.view.width))
        map.set('height', str(self.view.height))

        for i, serie in enumerate(self.series):
            safe_vals = list(
                filter(lambda x: x is not None, cut(serie.values, 1)))
            if not safe_vals:
                continue
            min_ = min(safe_vals)
            max_ = max(safe_vals)
            for j, (area_code, value) in enumerate(serie.values):
                if isinstance(area_code, Number):
                    area_code = '%2d' % area_code
                if value is None:
                    continue
                if max_ == min_:
                    ratio = 1
                else:
                    ratio = .3 + .7 * (value - min_) / (max_ - min_)
                areae = map.findall(".//*[@class='%s%s %s map-element']" %
                                    (self.area_prefix, area_code, self.kind))

                if not areae:
                    continue
                for area in areae:
                    cls = area.get('class', '').split(' ')
                    cls.append('color-%d' % i)
                    area.set('class', ' '.join(cls))
                    area.set('style', 'fill-opacity: %f' % (ratio))

                    metadata = serie.metadata.get(j)
                    if metadata:
                        node = decorate(self.svg, area, metadata)
                        if node != area:
                            area.remove(node)
                            for g in map:
                                if area not in g:
                                    continue
                                index = list(g).index(area)
                                g.remove(area)
                                node.append(area)
                                g.insert(index, node)

                    last_node = len(area) > 0 and area[-1]
                    if last_node is not None and last_node.tag == 'title':
                        title_node = last_node
                        text = title_node.text + '\n'
                    else:
                        title_node = self.svg.node(area, 'title')
                        text = ''
                    title_node.text = text + '[%s] %s: %s' % (
                        serie.title, self.area_names[area_code],
                        self._format(value))

        self.nodes['plot'].append(map)
Esempio n. 30
0
def test_metadata(Chart):
    """Test metadata values"""
    chart = Chart()
    v = range(7)
    if Chart in (pygal.Box, ):
        return  # summary charts cannot display per-value metadata
    elif Chart == pygal.XY:
        v = list(map(lambda x: (x, x + 1), v))
    elif issubclass(Chart, BaseMap):
        v = [(k, i) for i, k in enumerate(Chart.x_labels)
             if k not in ['oecd', 'nafta', 'eur']]

    chart.add(
        'Serie with metadata', [
            v[0], {
                'value': v[1]
            }, {
                'value': v[2],
                'label': 'Three'
            }, {
                'value': v[3],
                'xlink': 'http://4.example.com/'
            }, {
                'value': v[4],
                'xlink': 'http://5.example.com/',
                'label': 'Five'
            }, {
                'value': v[5],
                'xlink': {
                    'href': 'http://6.example.com/'
                },
                'label': 'Six'
            }, {
                'value': v[6],
                'xlink': {
                    'href': 'http://7.example.com/',
                    'target': '_blank'
                },
                'label': 'Seven'
            }
        ]
    )
    q = chart.render_pyquery()
    for md in ('Three', 'Five', 'Seven'):
        assert md in cut(q('desc'), 'text')

    for md in ('http://7.example.com/', 'http://4.example.com/'):
        assert md in [e.attrib.get('xlink:href') for e in q('a')]

    if Chart in (pygal.Pie, pygal.Treemap, pygal.SolidGauge):
        # Slices with value 0 are not rendered
        assert len(v) - 1 == len(q('.tooltip-trigger').siblings('.value'))
    elif not issubclass(Chart, BaseMap):

        # Tooltip are not working on maps
        assert len(v) == len(q('.tooltip-trigger').siblings('.value'))
Esempio n. 31
0
def test_metadata(Chart):
    chart = Chart()
    v = range(7)
    if Chart in (pygal.Box, ):
        return  # summary charts cannot display per-value metadata
    elif Chart == pygal.XY:
        v = list(map(lambda x: (x, x + 1), v))
    elif Chart == pygal.Worldmap or Chart == pygal.SupranationalWorldmap:
        v = [(i, k) for k, i in enumerate(i18n.COUNTRIES.keys())]
    elif Chart == pygal.FrenchMap_Regions:
        v = [(i, k) for k, i in enumerate(REGIONS.keys())]
    elif Chart == pygal.FrenchMap_Departments:
        v = [(i, k) for k, i in enumerate(DEPARTMENTS.keys())]

    chart.add('Serie with metadata', [
        v[0], {
            'value': v[1]
        }, {
            'value': v[2],
            'label': 'Three'
        }, {
            'value': v[3],
            'xlink': 'http://4.example.com/'
        }, {
            'value': v[4],
            'xlink': 'http://5.example.com/',
            'label': 'Five'
        }, {
            'value': v[5],
            'xlink': {
                'href': 'http://6.example.com/'
            },
            'label': 'Six'
        }, {
            'value': v[6],
            'xlink': {
                'href': 'http://7.example.com/',
                'target': '_blank'
            },
            'label': 'Seven'
        }
    ])
    q = chart.render_pyquery()
    for md in ('Three', 'http://4.example.com/', 'Five',
               'http://7.example.com/', 'Seven'):
        assert md in cut(q('desc'), 'text')

    if Chart == pygal.Pie:
        # Slices with value 0 are not rendered
        assert len(v) - 1 == len(q('.tooltip-trigger').siblings('.value'))
    elif Chart not in (pygal.Worldmap, pygal.SupranationalWorldmap,
                       pygal.FrenchMap_Regions, pygal.FrenchMap_Departments):
        # Tooltip are not working on maps
        assert len(v) == len(q('.tooltip-trigger').siblings('.value'))
Esempio n. 32
0
    def populate(self):
        all = (self.filter(self.db
               .query(Visit.day, count(1), count(distinct(Visit.uuid))))
               .group_by(Visit.day)
               .order_by(Visit.day)
               .all())

        self.chart.x_labels = list(map(
            lambda x: x.strftime('%Y-%m-%d'), cut(all, 0)))
        self.chart.add(labelize('all', self.lang), cut(all, 1))
        self.chart.add(labelize('unique', self.lang), cut(all, 2))

        new = (self.filter(
            self.db
            .query(count(distinct(Visit.uuid))))
            .filter(Visit.last_visit == None)
            .group_by(Visit.day)
            .order_by(Visit.day)
            .all())
        self.chart.add(labelize('new', self.lang), cut(new, 0))
        self.chart.x_label_rotation = 45
Esempio n. 33
0
    def _plot(self):
        map = etree.fromstring(MAP)
        map.set('width', str(self.view.width))
        map.set('height', str(self.view.height))

        for i, serie in enumerate(self.series):
            safe_vals = list(filter(
                lambda x: x is not None, cut(serie.values, 1)))
            if not safe_vals:
                continue
            min_ = min(safe_vals)
            max_ = max(safe_vals)
            serie.values = self.replace_supranationals(serie.values)
            for j, (country_code, value) in enumerate(serie.values):
                if value is None:
                    continue
                if max_ == min_:
                    ratio = 1
                else:
                    ratio = .3 + .7 * (value - min_) / (max_ - min_)
                country = map.find('.//*[@id="%s"]' % country_code)
                if country is None:
                    continue
                cls = country.get('class', '').split(' ')
                cls.append('color-%d' % i)
                country.set('class', ' '.join(cls))
                country.set(
                    'style', 'fill-opacity: %f' % (
                        ratio))

                metadata = serie.metadata.get(j)
                if metadata:
                    parent = country.getparent()
                    node = decorate(self.svg, country, metadata)
                    if node != country:
                        country.remove(node)
                        index = parent.index(country)
                        parent.remove(country)
                        node.append(country)
                        parent.insert(index, node)

                last_node = len(country) > 0 and country[-1]
                if last_node is not None and last_node.tag == 'title':
                    title_node = last_node
                    text = title_node.text + '\n'
                else:
                    title_node = self.svg.node(country, 'title')
                    text = ''
                title_node.text = text + '[%s] %s: %d' % (
                    serie.title,
                    self.country_names[country_code], value)

        self.nodes['plot'].append(map)
Esempio n. 34
0
def adapt(chart, data):
    """Adapt data to chart type"""
    if isinstance(chart, pygal.XY):
        return data

    data = cut(data)
    if isinstance(chart, BaseMap):
        return list(
            map(lambda x: chart.__class__.x_labels[
                int(x) % len(chart.__class__.x_labels)]
                if x is not None else None, data))
    return data
Esempio n. 35
0
    def _compute_x_labels(self):
        x_pos = compute_scale(self._box.xmin, self._box.xmax, self.logarithmic,
                              self.order_min, self.min_scale, self.max_scale)
        if self.x_labels:
            self._x_labels = []
            for i, x_label in enumerate(self.x_labels):
                if isinstance(x_label, dict):
                    pos = self._x_adapt(x_label.get('value'))
                    title = x_label.get('label', self._x_format(pos))
                elif is_str(x_label):
                    pos = self._x_adapt(x_pos[i % len(x_pos)])
                    title = x_label
                else:
                    pos = self._x_adapt(x_label)
                    title = self._x_format(pos)

                self._x_labels.append((title, pos))
            self._box.xmin = min(self._box.xmin, min(cut(self._x_labels, 1)))
            self._box.xmax = max(self._box.xmax, max(cut(self._x_labels, 1)))

        else:
            self._x_labels = list(zip(map(self._x_format, x_pos), x_pos))
Esempio n. 36
0
File: dot.py Progetto: psibi/pygal
    def _compute(self):
        x_len = self._len
        y_len = self._order
        self._box.xmax = x_len
        self._box.ymax = y_len

        x_pos = [n / 2 for n in range(1, 2 * x_len, 2)]
        y_pos = [n / 2 for n in reversed(range(1, 2 * y_len, 2))]

        for j, serie in enumerate(self.series):
            serie.points = [(x_pos[i], y_pos[j]) for i in range(x_len)]

        self._x_labels = self.x_labels and list(zip(self.x_labels, x_pos))
        self._y_labels = list(zip(self.y_labels or cut(self.series, "title"), y_pos))
Esempio n. 37
0
    def _plot(self):
        map = etree.fromstring(MAP)
        map.set('width', str(self.view.width))
        map.set('height', str(self.view.height))

        for i, serie in enumerate(self.series):
            safe_vals = list(
                filter(lambda x: x is not None, cut(serie.values, 1)))
            if not safe_vals:
                continue
            min_ = min(safe_vals)
            max_ = max(safe_vals)
            serie.values = self.replace_supranationals(serie.values)
            for j, (country_code, value) in enumerate(serie.values):
                if value is None:
                    continue
                if max_ == min_:
                    ratio = 1
                else:
                    ratio = .3 + .7 * (value - min_) / (max_ - min_)
                country = map.find('.//*[@id="%s"]' % country_code)
                if country is None:
                    continue
                cls = country.get('class', '').split(' ')
                cls.append('color-%d' % i)
                country.set('class', ' '.join(cls))
                country.set('style', 'fill-opacity: %f' % (ratio))

                metadata = serie.metadata.get(j)
                if metadata:
                    parent = country.getparent()
                    node = decorate(self.svg, country, metadata)
                    if node != country:
                        country.remove(node)
                        index = parent.index(country)
                        parent.remove(country)
                        node.append(country)
                        parent.insert(index, node)

                last_node = len(country) > 0 and country[-1]
                if last_node is not None and last_node.tag == 'title':
                    title_node = last_node
                    text = title_node.text + '\n'
                else:
                    title_node = self.svg.node(country, 'title')
                    text = ''
                title_node.text = text + '[%s] %s: %d' % (
                    serie.title, self.country_names[country_code], value)

        self.nodes['plot'].append(map)
Esempio n. 38
0
    def _compute(self):
        x_len = self._len
        y_len = self._order
        self._box.xmax = x_len
        self._box.ymax = y_len

        x_pos = [n / 2 for n in range(1, 2 * x_len, 2)]
        y_pos = [n / 2 for n in reversed(range(1, 2 * y_len, 2))]

        for j, serie in enumerate(self.series):
            serie.points = [(x_pos[i], y_pos[j]) for i in range(x_len)]

        self._x_labels = self.x_labels and list(zip(self.x_labels, x_pos))
        self._y_labels = list(
            zip(self.y_labels or cut(self.series, 'title'), y_pos))
Esempio n. 39
0
    def _compute_x_labels(self):
        x_pos = compute_scale(
            self._box.xmin, self._box.xmax, self.logarithmic,
            self.order_min, self.min_scale, self.max_scale
        )
        if self.x_labels:
            self._x_labels = []
            for i, x_label in enumerate(self.x_labels):
                if isinstance(x_label, dict):
                    pos = self._x_adapt(x_label.get('value'))
                    title = x_label.get('label', self._x_format(pos))
                elif is_str(x_label):
                    pos = self._x_adapt(x_pos[i % len(x_pos)])
                    title = x_label
                else:
                    pos = self._x_adapt(x_label)
                    title = self._x_format(pos)

                self._x_labels.append((title, pos))
            self._box.xmin = min(self._box.xmin, min(cut(self._x_labels, 1)))
            self._box.xmax = max(self._box.xmax, max(cut(self._x_labels, 1)))

        else:
            self._x_labels = list(zip(map(self._x_format, x_pos), x_pos))
Esempio n. 40
0
def adapt(chart, data):
    if isinstance(chart, pygal.DateY):
        # Convert to a credible datetime
        return list(map(
            lambda t:
            (datetime.fromtimestamp(1360000000 + t[0] * 987654)
             if t[0] is not None else None, t[1]), data))

    if isinstance(chart, pygal.XY):
        return data

    data = cut(data)
    if isinstance(chart, pygal.Worldmap):
        return list(map(lambda x: COUNTRY_KEYS[x % len(COUNTRIES)]
                        if x is not None else None, data))
    return data
Esempio n. 41
0
    def _plot(self):
        map = etree.fromstring(MAP)
        map.set("width", str(self.view.width))
        map.set("height", str(self.view.height))

        for i, serie in enumerate(self.series):
            safe_vals = list(filter(lambda x: x is not None, cut(serie.values, 1)))
            if not safe_vals:
                continue
            min_ = min(safe_vals)
            max_ = max(safe_vals)
            for j, (country_code, value) in enumerate(serie.values):
                if value is None:
                    continue
                if max_ == min_:
                    ratio = 1
                else:
                    ratio = 0.3 + 0.7 * (value - min_) / (max_ - min_)
                country = map.find('.//*[@id="%s"]' % country_code)
                if country is None:
                    continue
                cls = country.get("class", "").split(" ")
                cls.append("color-%d" % i)
                country.set("class", " ".join(cls))
                country.set("style", "fill-opacity: %f" % (ratio))

                metadata = serie.metadata.get(j)
                if metadata:
                    parent = country.getparent()
                    node = decorate(self.svg, country, metadata)
                    if node != country:
                        country.remove(node)
                        index = parent.index(country)
                        parent.remove(country)
                        node.append(country)
                        parent.insert(index, node)

                last_node = len(country) > 0 and country[-1]
                if last_node is not None and last_node.tag == "title":
                    title_node = last_node
                    text = title_node.text + "\n"
                else:
                    title_node = self.svg.node(country, "title")
                    text = ""
                title_node.text = text + "[%s] %s: %d" % (serie.title, self.country_names[country_code], value)

        self.nodes["plot"].append(map)
Esempio n. 42
0
def test_metadata(Chart):
    chart = Chart()
    v = range(7)
    if Chart == pygal.XY:
        v = list(map(lambda x: (x, x + 1), v))

    chart.add('Serie with metadata', [
        v[0], {
            'value': v[1]
        }, {
            'value': v[2],
            'label': 'Three'
        }, {
            'value': v[3],
            'xlink': 'http://4.example.com/'
        }, {
            'value': v[4],
            'xlink': 'http://5.example.com/',
            'label': 'Five'
        }, {
            'value': v[5],
            'xlink': {
                'href': 'http://6.example.com/'
            },
            'label': 'Six'
        }, {
            'value': v[6],
            'xlink': {
                'href': 'http://7.example.com/',
                'target': '_blank'
            },
            'label': 'Seven'
        }
    ])
    q = chart.render_pyquery()
    for md in ('Three', 'http://4.example.com/', 'Five',
               'http://7.example.com/', 'Seven'):
        assert md in cut(q('desc'), 'text')

    if Chart == pygal.Pie:
        # Slices with value 0 are not rendered
        assert len(v) - 1 == len(q('.tooltip-trigger').siblings('.value'))
    elif Chart != pygal.Worldmap:
        # Tooltip are not working on worldmap
        assert len(v) == len(q('.tooltip-trigger').siblings('.value'))
Esempio n. 43
0
def adapt(chart, data):
    # if isinstance(chart, pygal.DateY):
    #     # Convert to a credible datetime
    #     return list(map(
    #         lambda t:
    #         (datetime.fromtimestamp(1360000000 + t[0] * 987654)
    #          if t[0] is not None else None, t[1]), data))

    if isinstance(chart, pygal.XY):
        return data

    data = cut(data)
    if isinstance(chart, BaseMap):
        return list(
            map(lambda x: chart.__class__.x_labels[
                int(x) % len(chart.__class__.x_labels)]
                if x is not None else None, data))
    return data
Esempio n. 44
0
    def _y_axis(self, draw_axes=True):
        """Override y axis to make it polar"""
        if not self._y_labels:
            return

        axis = self.svg.node(self.nodes['plot'], class_="axis y web")

        if self.y_labels_major:
            y_labels_major = self.y_labels_major
        elif self.y_labels_major_every:
            y_labels_major = [self._y_labels[i][1] for i in range(
                0, len(self._y_labels), self.y_labels_major_every)]
        elif self.y_labels_major_count:
            label_count = len(self._y_labels)
            major_count = self.y_labels_major_count
            if (major_count >= label_count):
                y_labels_major = [label[1] for label in self._y_labels]
            else:
                y_labels_major = [self._y_labels[
                    int(i * (label_count - 1) / (major_count - 1))][1]
                    for i in range(major_count)]
        else:
            y_labels_major = majorize(
                cut(self._y_labels, 1)
            )
        for label, r in reversed(self._y_labels):
            major = r in y_labels_major
            if not (self.show_minor_y_labels or major):
                continue
            guides = self.svg.node(axis, class_='guides')
            self.svg.line(
                guides, [self.view((r, theta)) for theta in self._x_pos],
                close=True,
                class_='%sguide line' % (
                    'major ' if major else ''))
            x, y = self.view((r, self._x_pos[0]))
            self.svg.node(
                guides, 'text',
                x=x - 5,
                y=y,
                class_='major' if major else ''
            ).text = label
Esempio n. 45
0
    def _y_major_labels(self):
        """Getter for the y major label"""
        if self.y_labels_major:
            return self.y_labels_major
        if self.y_labels_major_every:
            return [self._y_labels[i][1] for i in range(
                0, len(self._y_labels), self.y_labels_major_every)]
        if self.y_labels_major_count:
            label_count = len(self._y_labels)
            major_count = self.y_labels_major_count
            if (major_count >= label_count):
                return [label[1] for label in self._y_labels]

            return [self._y_labels[
                int(i * (label_count - 1) / (major_count - 1))][1]
                for i in range(major_count)]

        return majorize(
            cut(self._y_labels, 1)
        )
Esempio n. 46
0
    def _y_axis(self, draw_axes=True):
        if not self._y_labels:
            return

        axis = self.svg.node(self.nodes['plot'], class_="axis y web")

        if self.y_labels_major:
            y_labels_major = self.y_labels_major
        elif self.y_labels_major_every:
            y_labels_major = [
                self._y_labels[i][1] for i in range(0, len(self._y_labels),
                                                    self.y_labels_major_every)
            ]
        elif self.y_labels_major_count:
            label_count = len(self._y_labels)
            major_count = self.y_labels_major_count
            if (major_count >= label_count):
                y_labels_major = [label[1] for label in self._y_labels]
            else:
                y_labels_major = [
                    self._y_labels[int(i * (label_count - 1) /
                                       (major_count - 1))][1]
                    for i in range(major_count)
                ]
        else:
            y_labels_major = majorize(cut(self._y_labels, 1))
        for label, r in reversed(self._y_labels):
            major = r in y_labels_major
            if not (self.show_minor_y_labels or major):
                continue
            guides = self.svg.node(axis, class_='guides')
            self.svg.line(guides,
                          [self.view((r, theta)) for theta in self.x_pos],
                          close=True,
                          class_='%sguide line' % ('major ' if major else ''))
            x, y = self.view((r, self.x_pos[0]))
            self.svg.node(guides,
                          'text',
                          x=x - 5,
                          y=y,
                          class_='major' if major else '').text = label
Esempio n. 47
0
def test_metadata(Chart):
    chart = Chart()
    v = range(1, 8)
    if Chart == pygal.XY:
        v = map(lambda x: (x, x + 1), v)

    chart.add('Serie with metadata', [
        v[0], {
            'value': v[1]
        }, {
            'value': v[2],
            'label': 'Three'
        }, {
            'value': v[3],
            'xlink': 'http://4.example.com/'
        }, {
            'value': v[4],
            'xlink': 'http://5.example.com/',
            'label': 'Five'
        }, {
            'value': v[5],
            'xlink': {
                'href': 'http://6.example.com/'
            },
            'label': 'Six'
        }, {
            'value': v[6],
            'xlink': {
                'href': 'http://7.example.com/',
                'target': '_blank'
            },
            'label': 'Seven'
        }
    ])
    q = chart.render_pyquery()
    for md in ('Three', 'http://4.example.com/', 'Five',
               'http://7.example.com/', 'Seven'):
        assert md in cut(q('desc'), 'text')

    assert len(v) == len(q('.tooltip-trigger').siblings('.value'))
Esempio n. 48
0
def test_metadata(Chart):
    chart = Chart()
    v = range(7)
    if Chart in (pygal.Box,):
        return  # summary charts cannot display per-value metadata
    elif Chart == pygal.XY:
        v = list(map(lambda x: (x, x + 1), v))
    elif Chart == pygal.Worldmap or Chart == pygal.SupranationalWorldmap:
        v = [(i, k) for k, i in enumerate(i18n.COUNTRIES.keys())]
    elif Chart == pygal.FrenchMap_Regions:
        v = [(i, k) for k, i in enumerate(REGIONS.keys())]
    elif Chart == pygal.FrenchMap_Departments:
        v = [(i, k) for k, i in enumerate(DEPARTMENTS.keys())]

    chart.add('Serie with metadata', [
        v[0],
        {'value': v[1]},
        {'value': v[2], 'label': 'Three'},
        {'value': v[3], 'xlink': 'http://4.example.com/'},
        {'value': v[4], 'xlink': 'http://5.example.com/', 'label': 'Five'},
        {'value': v[5], 'xlink': {
            'href': 'http://6.example.com/'}, 'label': 'Six'},
        {'value': v[6], 'xlink': {
            'href': 'http://7.example.com/',
            'target': '_blank'}, 'label': 'Seven'}
    ])
    q = chart.render_pyquery()
    for md in (
            'Three', 'http://4.example.com/',
            'Five', 'http://7.example.com/', 'Seven'):
        assert md in cut(q('desc'), 'text')

    if Chart == pygal.Pie:
        # Slices with value 0 are not rendered
        assert len(v) - 1 == len(q('.tooltip-trigger').siblings('.value'))
    elif Chart not in (
            pygal.Worldmap, pygal.SupranationalWorldmap,
            pygal.FrenchMap_Regions, pygal.FrenchMap_Departments):
        # Tooltip are not working on maps
        assert len(v) == len(q('.tooltip-trigger').siblings('.value'))
Esempio n. 49
0
    def all(style='default', color=None, interpolate=None, base_style=None):
        width, height = 600, 400
        data = random.randrange(1, 10)
        order = random.randrange(1, 10)
        if color is None:
            style = styles[style]
        else:
            style = parametric_styles[style](color,
                                             base_style=styles[base_style
                                                               or 'default'])

        xy_series = _random(data, order)
        other_series = []
        for title, values, config in xy_series:
            other_series.append((title, cut(values, 1), config))
        xy_series = b64encode(pickle.dumps(xy_series))
        other_series = b64encode(pickle.dumps(other_series))
        config = Config()
        config.width = width
        config.height = height
        config.fill = bool(random.randrange(0, 2))
        config.interpolate = interpolate
        config.style = style
        svgs = []
        for chart in pygal.CHARTS:
            type = '.'.join((chart.__module__, chart.__name__))
            if chart._dual:
                config.x_labels = None
            else:
                config.x_labels = [random_label() for i in range(data)]
            svgs.append({
                'type': type,
                'series': xy_series if chart._dual else other_series,
                'config': b64encode(pickle.dumps(config))
            })

        return render_template('svgs.jinja2',
                               svgs=svgs,
                               width=width,
                               height=height)
Esempio n. 50
0
    def all(style="default", color=None, interpolate=None, base_style=None):
        width, height = 600, 400
        data = random.randrange(1, 10)
        order = random.randrange(1, 10)
        if color is None:
            style = styles[style]
        else:
            style = parametric_styles[style](color, base_style=styles[base_style or "default"])

        xy_series = _random(data, order)
        other_series = []
        for title, values, config in xy_series:
            other_series.append((title, cut(values, 1), config))
        xy_series = b64encode(pickle.dumps(xy_series))
        other_series = b64encode(pickle.dumps(other_series))
        config = Config()
        config.width = width
        config.height = height
        config.fill = bool(random.randrange(0, 2))
        config.interpolate = interpolate
        config.style = style
        svgs = []
        for chart in pygal.CHARTS:
            type = ".".join((chart.__module__, chart.__name__))
            if chart._dual:
                config.x_labels = None
            else:
                config.x_labels = [random_label() for i in range(data)]
            svgs.append(
                {
                    "type": type,
                    "series": xy_series if chart._dual else other_series,
                    "config": b64encode(pickle.dumps(config)),
                }
            )

        return render_template("svgs.jinja2", svgs=svgs, width=width, height=height)
Esempio n. 51
0
    def _y_axis(self):
        """Make the y axis: labels and guides"""
        if not self._y_labels or not self.show_y_labels:
            return

        axis = self.svg.node(self.nodes['plot'], class_="axis y")

        if (0 not in [label[1] for label in self._y_labels]
                and self.show_y_guides):
            self.svg.node(axis,
                          'path',
                          d='M%f %f h%f' %
                          (0, self.view.height, self.view.width),
                          class_='line')

        if self.y_labels_major:
            y_labels_major = self.y_labels_major
        elif self.y_labels_major_every:
            y_labels_major = [
                self._y_labels[i][1] for i in range(0, len(self._y_labels),
                                                    self.y_labels_major_every)
            ]
        elif self.y_labels_major_count:
            label_count = len(self._y_labels)
            major_count = self.y_labels_major_count
            if (major_count >= label_count):
                y_labels_major = [label[1] for label in self._y_labels]
            else:
                y_labels_major = [
                    self._y_labels[int(i * (label_count - 1) /
                                       (major_count - 1))][1]
                    for i in range(major_count)
                ]
        else:
            y_labels_major = majorize(cut(self._y_labels, 1))
        for label, position in self._y_labels:
            major = position in y_labels_major
            if not (self.show_minor_y_labels or major):
                continue
            guides = self.svg.node(
                axis,
                class_='%sguides' %
                ('logarithmic ' if self.logarithmic else ''))
            x = -5
            y = self.view.y(position)
            if not y:
                continue
            if self.show_y_guides:
                self.svg.node(
                    guides,
                    'path',
                    d='M%f %f h%f' % (0, y, self.view.width),
                    class_='%s%sline' %
                    ('major ' if major else '', 'guide '
                     if position != 0 or not self.show_y_guides else ''))
            text = self.svg.node(guides,
                                 'text',
                                 x=x,
                                 y=y + .35 * self.label_font_size,
                                 class_='major' if major else '')

            if isinstance(label, dict):
                label = label['title']
            text.text = label

            if self.y_label_rotation:
                text.attrib['transform'] = "rotate(%d %f %f)" % (
                    self.y_label_rotation, x, y)

        if self._y_2nd_labels:
            secondary_ax = self.svg.node(self.nodes['plot'], class_="axis y2")
            for label, position in self._y_2nd_labels:
                major = position in y_labels_major
                if not (self.show_minor_x_labels or major):
                    continue
                # it is needed, to have the same structure as primary axis
                guides = self.svg.node(secondary_ax, class_='guides')
                x = self.view.width + 5
                y = self.view.y(position)
                text = self.svg.node(guides,
                                     'text',
                                     x=x,
                                     y=y + .35 * self.label_font_size,
                                     class_='major' if major else '')
                text.text = label
                if self.y_label_rotation:
                    text.attrib['transform'] = "rotate(%d %f %f)" % (
                        self.y_label_rotation, x, y)
Esempio n. 52
0
    def _legend(self):
        """Make the legend box"""
        if not self.show_legend:
            return
        truncation = self.truncate_legend
        if self.legend_at_bottom:
            x = self.margin_box.left + self.spacing
            y = (
                self.margin_box.top + self.view.height + self._x_title_height +
                self._x_labels_height + self.spacing
            )
            cols = self.legend_at_bottom_columns or ceil(sqrt(self._order)
                                                         ) or 1

            if not truncation:
                available_space = self.view.width / cols - (
                    self.legend_box_size + 5
                )
                truncation = reverse_text_len(
                    available_space, self.style.legend_font_size
                )
        else:
            x = self.spacing
            y = self.margin_box.top + self.spacing
            cols = 1
            if not truncation:
                truncation = 15

        legends = self.svg.node(
            self.nodes['graph'],
            class_='legends',
            transform='translate(%d, %d)' % (x, y)
        )

        h = max(self.legend_box_size, self.style.legend_font_size)
        x_step = self.view.width / cols
        if self.legend_at_bottom:
            secondary_legends = legends  # svg node is the same
        else:

            # draw secondary axis on right
            x = self.margin_box.left + self.view.width + self.spacing
            if self._y_2nd_labels:
                h, w = get_texts_box(
                    cut(self._y_2nd_labels), self.style.label_font_size
                )
                x += self.spacing + max(
                    w * abs(cos(rad(self.y_label_rotation))), h
                )

            y = self.margin_box.top + self.spacing

            secondary_legends = self.svg.node(
                self.nodes['graph'],
                class_='legends',
                transform='translate(%d, %d)' % (x, y)
            )

        serie_number = -1
        i = 0

        for titles, is_secondary in ((self._legends, False),
                                     (self._secondary_legends, True)):
            if not self.legend_at_bottom and is_secondary:
                i = 0

            for title in titles:
                serie_number += 1
                if title is None:
                    continue
                col = i % cols
                row = i // cols

                legend = self.svg.node(
                    secondary_legends if is_secondary else legends,
                    class_='legend reactive activate-serie',
                    id="activate-serie-%d" % serie_number
                )
                self.svg.node(
                    legend,
                    'rect',
                    x=col * x_step,
                    y=1.5 * row * h + (
                        self.style.legend_font_size - self.legend_box_size
                        if self.style.legend_font_size > self.legend_box_size
                        else 0
                    ) / 2,
                    width=self.legend_box_size,
                    height=self.legend_box_size,
                    class_="color-%d reactive" % serie_number
                )

                if isinstance(title, dict):
                    node = decorate(self.svg, legend, title)
                    title = title['title']
                else:
                    node = legend

                truncated = truncate(title, truncation)
                self.svg.node(
                    node,
                    'text',
                    x=col * x_step + self.legend_box_size + 5,
                    y=1.5 * row * h + .5 * h + .3 * self.style.legend_font_size
                ).text = truncated

                if truncated != title:
                    self.svg.node(legend, 'title').text = title

                i += 1
Esempio n. 53
0
    def _compute_margin(self):
        """Compute graph margins from set texts"""
        self._legend_at_left_width = 0
        for series_group in (self.series, self.secondary_series):
            if self.show_legend and series_group:
                h, w = get_texts_box(
                    map(lambda x: truncate(x, self.truncate_legend or 15),
                        cut(series_group, 'title')),
                    self.legend_font_size)
                if self.legend_at_bottom:
                    h_max = max(h, self.legend_box_size)
                    cols = (self._order // self.legend_at_bottom_columns
                            if self.legend_at_bottom_columns
                            else ceil(sqrt(self._order)) or 1)
                    self.margin.bottom += self.spacing + h_max * round(
                        cols - 1) * 1.5 + h_max
                else:
                    if series_group is self.series:
                        legend_width = self.spacing + w + self.legend_box_size
                        self.margin.left += legend_width
                        self._legend_at_left_width += legend_width
                    else:
                        self.margin.right += (
                            self.spacing + w + self.legend_box_size)

        self._x_labels_height = 0
        if (self._x_labels or self._x_2nd_labels) and self.show_x_labels:
            for xlabels in (self._x_labels, self._x_2nd_labels):
                if xlabels:
                    h, w = get_texts_box(
                        map(lambda x: truncate(x, self.truncate_label or 25),
                            cut(xlabels)),
                        self.label_font_size)
                    self._x_labels_height = self.spacing + max(
                        w * sin(rad(self.x_label_rotation)), h)
                    if xlabels is self._x_labels:
                        self.margin.bottom += self._x_labels_height
                    else:
                        self.margin.top += self._x_labels_height
                    if self.x_label_rotation:
                        self.margin.right = max(
                            w * cos(rad(self.x_label_rotation)),
                            self.margin.right)

        if self.show_y_labels:
            for ylabels in (self._y_labels, self._y_2nd_labels):
                if ylabels:
                    h, w = get_texts_box(
                        cut(ylabels), self.label_font_size)
                    if ylabels is self._y_labels:
                        self.margin.left += self.spacing + max(
                            w * cos(rad(self.y_label_rotation)), h)
                    else:
                        self.margin.right += self.spacing + max(
                            w * cos(rad(self.y_label_rotation)), h)

        self.title = split_title(
            self.title, self.width, self.title_font_size)

        if self.title:
            h, _ = get_text_box(self.title[0], self.title_font_size)
            self.margin.top += len(self.title) * (self.spacing + h)

        self.x_title = split_title(
            self.x_title, self.width - self.margin.x, self.title_font_size)

        self._x_title_height = 0
        if self.x_title:
            h, _ = get_text_box(self.x_title[0], self.title_font_size)
            height = len(self.x_title) * (self.spacing + h)
            self.margin.bottom += height
            self._x_title_height = height + self.spacing

        self.y_title = split_title(
            self.y_title, self.height - self.margin.y, self.title_font_size)

        self._y_title_height = 0
        if self.y_title:
            h, _ = get_text_box(self.y_title[0], self.title_font_size)
            height = len(self.y_title) * (self.spacing + h)
            self.margin.left += height
            self._y_title_height = height + self.spacing
Esempio n. 54
0
def make_data(chart, datas, secondary=False):
    for data in datas:
        chart.add(data[0],
                  data[1] if chart.__class__ == pygal.XY else cut(data[1]),
                  secondary=secondary)
    return chart
Esempio n. 55
0
    def _compute_margin(self):
        """Compute graph margins from set texts"""
        self._legend_at_left_width = 0
        for series_group in (self.series, self.secondary_series):
            if self.show_legend and series_group:
                h, w = get_texts_box(
                    map(
                        lambda x: truncate(x, self.truncate_legend or 15), [
                            serie.title['title']
                            if isinstance(serie.title, dict) else serie.title
                            or '' for serie in series_group
                        ]
                    ), self.style.legend_font_size
                )
                if self.legend_at_bottom:
                    h_max = max(h, self.legend_box_size)
                    cols = (
                        self._order // self.legend_at_bottom_columns
                        if self.legend_at_bottom_columns else
                        ceil(sqrt(self._order)) or 1
                    )
                    self.margin_box.bottom += self.spacing + h_max * round(
                        cols - 1
                    ) * 1.5 + h_max
                else:
                    if series_group is self.series:
                        legend_width = self.spacing + w + self.legend_box_size
                        self.margin_box.left += legend_width
                        self._legend_at_left_width += legend_width
                    else:
                        self.margin_box.right += (
                            self.spacing + w + self.legend_box_size
                        )

        self._x_labels_height = 0
        if (self._x_labels or self._x_2nd_labels) and self.show_x_labels:
            for xlabels in (self._x_labels, self._x_2nd_labels):
                if xlabels:
                    h, w = get_texts_box(
                        map(
                            lambda x: truncate(x, self.truncate_label or 25),
                            cut(xlabels)
                        ), self.style.label_font_size
                    )
                    self._x_labels_height = self.spacing + max(
                        w * abs(sin(rad(self.x_label_rotation))), h
                    )
                    if xlabels is self._x_labels:
                        self.margin_box.bottom += self._x_labels_height
                    else:
                        self.margin_box.top += self._x_labels_height
                    if self.x_label_rotation:
                        if self.x_label_rotation % 180 < 90:
                            self.margin_box.right = max(
                                w * abs(cos(rad(self.x_label_rotation))),
                                self.margin_box.right
                            )
                        else:
                            self.margin_box.left = max(
                                w * abs(cos(rad(self.x_label_rotation))),
                                self.margin_box.left
                            )

        if self.show_y_labels:
            for ylabels in (self._y_labels, self._y_2nd_labels):
                if ylabels:
                    h, w = get_texts_box(
                        cut(ylabels), self.style.label_font_size
                    )
                    if ylabels is self._y_labels:
                        self.margin_box.left += self.spacing + max(
                            w * abs(cos(rad(self.y_label_rotation))), h
                        )
                    else:
                        self.margin_box.right += self.spacing + max(
                            w * abs(cos(rad(self.y_label_rotation))), h
                        )

        self._title = split_title(
            self.title, self.width, self.style.title_font_size
        )

        if self.title:
            h, _ = get_text_box(self._title[0], self.style.title_font_size)
            self.margin_box.top += len(self._title) * (self.spacing + h)

        self._x_title = split_title(
            self.x_title, self.width - self.margin_box.x,
            self.style.title_font_size
        )

        self._x_title_height = 0
        if self._x_title:
            h, _ = get_text_box(self._x_title[0], self.style.title_font_size)
            height = len(self._x_title) * (self.spacing + h)
            self.margin_box.bottom += height
            self._x_title_height = height + self.spacing

        self._y_title = split_title(
            self.y_title, self.height - self.margin_box.y,
            self.style.title_font_size
        )

        self._y_title_height = 0
        if self._y_title:
            h, _ = get_text_box(self._y_title[0], self.style.title_font_size)
            height = len(self._y_title) * (self.spacing + h)
            self.margin_box.left += height
            self._y_title_height = height + self.spacing

        # Inner margin
        if self.print_values_position == 'top':
            gh = self.height - self.margin_box.y
            alpha = 1.1 * (self.style.value_font_size / gh) * self._box.height
            if self._max and self._max > 0:
                self._box.ymax += alpha
            if self._min and self._min < 0:
                self._box.ymin -= alpha
Esempio n. 56
0
File: map.py Progetto: sattel/pygal
    def _plot(self):
        """Insert a map in the chart and apply data on it"""
        map = etree.fromstring(self.svg_map)
        map.set('width', str(self.view.width))
        map.set('height', str(self.view.height))

        for i, serie in enumerate(self.series):
            safe_vals = list(
                filter(lambda x: x is not None, cut(serie.values, 1)))
            if not safe_vals:
                continue
            min_ = min(safe_vals)
            max_ = max(safe_vals)
            for j, (area_code, value) in self.enumerate_values(serie):
                area_code = self.adapt_code(area_code)
                if value is None:
                    continue
                if max_ == min_:
                    ratio = 1
                else:
                    ratio = .3 + .7 * (value - min_) / (max_ - min_)

                try:
                    areae = map.findall(
                        ".//*[@class='%s%s %s map-element']" %
                        (self.area_prefix, area_code, self.kind))
                except SyntaxError:
                    # Python 2.6 (you'd better install lxml)
                    raise ImportError('lxml is required under python 2.6')

                if not areae:
                    continue

                for area in areae:
                    cls = area.get('class', '').split(' ')
                    cls.append('color-%d' % i)
                    cls.append('serie-%d' % i)
                    cls.append('series')
                    area.set('class', ' '.join(cls))
                    area.set('style', 'fill-opacity: %f' % ratio)

                    metadata = serie.metadata.get(j)

                    if metadata:
                        node = decorate(self.svg, area, metadata)
                        if node != area:
                            area.remove(node)
                            for g in map:
                                if area not in g:
                                    continue
                                index = list(g).index(area)
                                g.remove(area)
                                node.append(area)
                                g.insert(index, node)

                    for node in area:
                        cls = node.get('class', '').split(' ')
                        cls.append('reactive')
                        cls.append('tooltip-trigger')
                        cls.append('map-area')
                        node.set('class', ' '.join(cls))
                        alter(node, metadata)

                    val = self._format(serie, j)
                    self._tooltip_data(area, val, 0, 0, 'auto')

        self.nodes['plot'].append(map)
Esempio n. 57
0
    def _legend(self):
        """Make the legend box"""
        if not self.show_legend:
            return
        truncation = self.truncate_legend
        if self.legend_at_bottom:
            x = self.margin.left + self.spacing
            y = (self.margin.top + self.view.height + self._x_title_height +
                 self._x_labels_height + self.spacing)
            cols = self.legend_at_bottom_columns or ceil(sqrt(
                self._order)) or 1

            if not truncation:
                available_space = self.view.width / cols - (
                    self.legend_box_size + 5)
                truncation = reverse_text_len(available_space,
                                              self.legend_font_size)
        else:
            x = self.spacing
            y = self.margin.top + self.spacing
            cols = 1
            if not truncation:
                truncation = 15

        legends = self.svg.node(self.nodes['graph'],
                                class_='legends',
                                transform='translate(%d, %d)' % (x, y))

        h = max(self.legend_box_size, self.legend_font_size)
        x_step = self.view.width / cols
        if self.legend_at_bottom:
            # if legends at the bottom, we dont split the windows
            # gen structure - (i, (j, (l, tf)))
            # i - global serie number - used for coloring and identification
            # j - position within current legend box
            # l - label
            # tf - whether it is secondary label
            gen = enumerate(
                enumerate(
                    chain(zip(self._legends, repeat(False)),
                          zip(self._secondary_legends, repeat(True)))))
            secondary_legends = legends  # svg node is the same
        else:
            gen = enumerate(
                chain(enumerate(zip(self._legends, repeat(False))),
                      enumerate(zip(self._secondary_legends, repeat(True)))))

            # draw secondary axis on right
            x = self.margin.left + self.view.width + self.spacing
            if self._y_2nd_labels:
                h, w = get_texts_box(cut(self._y_2nd_labels),
                                     self.label_font_size)
                x += self.spacing + max(w * cos(rad(self.y_label_rotation)), h)

            y = self.margin.top + self.spacing

            secondary_legends = self.svg.node(self.nodes['graph'],
                                              class_='legends',
                                              transform='translate(%d, %d)' %
                                              (x, y))

        for (global_serie_number, (i, (title, is_secondary))) in gen:

            col = i % cols
            row = i // cols

            legend = self.svg.node(
                secondary_legends if is_secondary else legends,
                class_='legend reactive activate-serie',
                id="activate-serie-%d" % global_serie_number)
            self.svg.node(
                legend,
                'rect',
                x=col * x_step,
                y=1.5 * row * h +
                (self.legend_font_size - self.legend_box_size
                 if self.legend_font_size > self.legend_box_size else 0) / 2,
                width=self.legend_box_size,
                height=self.legend_box_size,
                class_="color-%d reactive" %
                (global_serie_number % len(self.style['colors'])))

            if isinstance(title, dict):
                node = decorate(self.svg, legend, title)
                title = title['title']
            else:
                node = legend

            truncated = truncate(title, truncation)
            self.svg.node(node,
                          'text',
                          x=col * x_step + self.legend_box_size + 5,
                          y=1.5 * row * h + .5 * h +
                          .3 * self.legend_font_size).text = truncated

            if truncated != title:
                self.svg.node(legend, 'title').text = title