def bar(self, serie, rescale=False): """Draw a bar graph for a serie""" serie_node = self.svg.serie(serie) bars = self.svg.node(serie_node['plot'], class_="histbars") points = serie.points for i, (y, x0, x1) in enumerate(points): if None in (x0, x1, y) or (self.logarithmic and y <= 0): continue metadata = serie.metadata.get(i) bar = decorate(self.svg, self.svg.node(bars, class_='histbar'), metadata) val = self._format(serie, i) bounds = self._bar(serie, bar, x0, x1, y, i, self.zero, secondary=rescale) self._tooltip_and_print_values(serie_node, serie, bar, i, val, metadata, *bounds)
def funnel(self, serie): """Draw a funnel slice""" serie_node = self.svg.serie(serie) fmt = lambda x: '%f %f' % x for i, poly in enumerate(serie.points): metadata = serie.metadata.get(i) val = self._format(serie, i) funnels = decorate( self.svg, self.svg.node(serie_node['plot'], class_="funnels"), metadata) alter( self.svg.node(funnels, 'polygon', points=' '.join(map(fmt, map(self.view, poly))), class_='funnel reactive tooltip-trigger'), metadata) # Poly center from label x, y = self.view((self._center(self._x_pos[serie.index]), sum([point[1] for point in poly]) / len(poly))) self._tooltip_data(funnels, val, x, y, 'centered', self._get_x_label(serie.index)) self._static_value(serie_node, val, x, y, metadata)
def bar(self, serie, rescale=False): """Draw a bar graph for a serie""" serie_node = self.svg.serie(serie) bars = self.svg.node(serie_node['plot'], class_="bars") if rescale and self.secondary_series: points = self._rescale(serie.points) else: points = serie.points for i, (x, y) in enumerate(points): if None in (x, y) or (self.logarithmic and y <= 0): continue metadata = serie.metadata.get(i) val = self._format(serie, i) bar = decorate(self.svg, self.svg.node(bars, class_='bar'), metadata) x_, y_, width, height = self._bar(serie, bar, x, y, i, self.zero, secondary=rescale) self._confidence_interval(serie_node['overlay'], x_ + width / 2, y_, serie.values[i], metadata) self._tooltip_and_print_values(serie_node, serie, bar, i, val, metadata, x_, y_, width, height)
def line(self, serie_node, serie): """Draw the line serie""" view_values = map(self.view, serie.points) if self.show_dots: for i, (x, y) in enumerate(view_values): if None in (x, y): continue metadata = serie.metadata.get(i) classes = [] if x > self.view.width / 2: classes.append("left") if y > self.view.height / 2: classes.append("top") classes = " ".join(classes) dots = decorate(self.svg, self.svg.node(serie_node["overlay"], class_="dots"), metadata) val = self._get_value(serie.points, i) self.svg.node(dots, "circle", cx=x, cy=y, r=2.5, class_="dot reactive tooltip-trigger") self._tooltip_data(dots, val, x, y) self._static_value(serie_node, val, x + self.value_font_size, y + self.value_font_size) if self.stroke: if self.interpolate: view_values = map(self.view, serie.interpolated) if self.fill: view_values = self._fill(view_values) self.svg.line( serie_node["plot"], view_values, close=self._self_close, class_="line reactive" + (" nofill" if not self.fill else ""), )
def bar(self, serie_node, serie, index, rescale=False): """Draw a bar graph for a serie""" bars = self.svg.node(serie_node['plot'], class_="bars") if rescale and self.secondary_series: points = [ (x, self._scale_diff + (y - self._scale_min_2nd) * self._scale) for x, y in serie.points ] else: points = serie.points for i, (x, y) in enumerate(points): if None in (x, y) or (self.logarithmic and y <= 0): continue metadata = serie.metadata.get(i) bar = decorate(self.svg, self.svg.node(bars, class_='bar'), metadata) val = self._format(serie.values[i]) x_center, y_center = self._bar(bar, x, y, index, i, self.zero, secondary=rescale) self._tooltip_data(bar, val, x_center, y_center, classes="centered") self._static_value(serie_node, val, x_center, y_center)
def bar(self, serie, rescale=False): """Draw a bar graph for a serie""" serie_node = self.svg.serie(serie) bars = self.svg.node(serie_node['plot'], class_="bars") if rescale and self.secondary_series: points = self._rescale(serie.points) else: points = serie.points for i, (x, y) in enumerate(points): if None in (x, y) or (self.logarithmic and y <= 0): continue metadata = serie.metadata.get(i) bar = decorate(self.svg, self.svg.node(bars, class_='bar'), metadata) val = self._format(serie.values[i]) x_center, y_center = self._bar(serie, bar, x, y, i, self.zero, secondary=rescale) self._tooltip_data(bar, val, x_center, y_center, "centered", self._get_x_label(i)) self._static_value(serie_node, val, x_center, y_center, metadata)
def _rect(self, serie, serie_node, rects, val, x, y, w, h, i): rx, ry = self.view((x, y)) rw, rh = self.view((x + w, y + h)) rw -= rx rh -= ry metadata = serie.metadata.get(i) value = self._format(val) rect = decorate( self.svg, self.svg.node(rects, class_="rect"), metadata) self.svg.node(rect, 'rect', x=rx, y=ry, width=rw, height=rh, class_='rect reactive tooltip-trigger') self._tooltip_data(rect, value, rx + rw / 2, ry + rh / 2, classes='centered') self._static_value(serie_node, value, rx + rw / 2, ry + rh / 2)
def funnel(self, serie): """Draw a funnel slice""" serie_node = self.svg.serie(serie) fmt = lambda x: '%f %f' % x for i, poly in enumerate(serie.points): metadata = serie.metadata.get(i) val = self._format(serie, i) funnels = decorate( self.svg, self.svg.node(serie_node['plot'], class_="funnels"), metadata) alter(self.svg.node( funnels, 'polygon', points=' '.join(map(fmt, map(self.view, poly))), class_='funnel reactive tooltip-trigger'), metadata) # Poly center from label x, y = self.view(( self._center(self._x_pos[serie.index]), sum([point[1] for point in poly]) / len(poly))) self._tooltip_data( funnels, val, x, y, 'centered', self._get_x_label(serie.index)) self._static_value(serie_node, val, x, y, metadata)
def _rect(self, serie, serie_node, rects, val, x, y, w, h, i): rx, ry = self.view((x, y)) rw, rh = self.view((x + w, y + h)) rw -= rx rh -= ry metadata = serie.metadata.get(i) value = self._format(val) rect = decorate(self.svg, self.svg.node(rects, class_="rect"), metadata) alter( self.svg.node(rect, 'rect', x=rx, y=ry, width=rw, height=rh, class_='rect reactive tooltip-trigger'), metadata) self._tooltip_data(rect, value, rx + rw / 2, ry + rh / 2, 'centered', self._get_x_label(i)) self._static_value(serie_node, value, rx + rw / 2, ry + rh / 2, metadata)
def needle(self, serie_node, serie,): thickness = .05 for i, value in enumerate(serie.values): if not value: continue theta = self.arc_pos(value) fmt = lambda x: '%f %f' % x value = self._format(serie.values[i]) metadata = serie.metadata.get(i) gauges = decorate( self.svg, self.svg.node(serie_node['plot'], class_="dots"), metadata) self.svg.node( gauges, 'polygon', points=' '.join([ fmt(self.view((0, 0))), fmt(self.view((.75, theta + thickness))), fmt(self.view((.8, theta))), fmt(self.view((.75, theta - thickness)))]), class_='line reactive tooltip-trigger') x, y = self.view((.75, theta)) self._tooltip_data(gauges, value, x, y) self._static_value(serie_node, value, x, y)
def dot(self, serie, r_max): """Draw a dot line""" serie_node = self.svg.serie(serie) view_values = list(map(self.view, serie.points)) for i, value in safe_enumerate(serie.values): x, y = view_values[i] if self.logarithmic: log10min = log10(self._min) - 1 log10max = log10(self._max or 1) if value != 0: size = r_max * ( (log10(abs(value)) - log10min) / (log10max - log10min) ) else: size = 0 else: size = r_max * (abs(value) / (self._max or 1)) metadata = serie.metadata.get(i) dots = decorate( self.svg, self.svg.node(serie_node['plot'], class_="dots"), metadata) alter(self.svg.node( dots, 'circle', cx=x, cy=y, r=size, class_='dot reactive tooltip-trigger' + ( ' negative' if value < 0 else '')), metadata) value = self._format(value) self._tooltip_data(dots, value, x, y, classes='centered') self._static_value(serie_node, value, x, y)
def bar(self, serie_node, serie, index, rescale=False): """Draw a bar graph for a serie""" bars = self.svg.node(serie_node["plot"], class_="bars") if rescale and self.secondary_series: points = [ (x, self._scale_diff + (y - self._scale_min_2nd) * self._scale) for x, y in serie.points if y is not None ] else: points = serie.points for i, (x, y) in enumerate(points): if None in (x, y) or (self.logarithmic and y <= 0): continue metadata = serie.metadata.get(i) bar = decorate(self.svg, self.svg.node(bars, class_="bar"), metadata) val = self._format(serie.values[i]) x_center, y_center = self._bar( bar, x, y, index, i, self.zero, secondary=rescale, rounded=serie.rounded_bars ) self._tooltip_data(bar, val, x_center, y_center, classes="centered") self._static_value(serie_node, val, x_center, y_center)
def bar(self, serie, rescale=False): """Draw a bar graph for a serie""" serie_node = self.svg.serie(serie) bars = self.svg.node(serie_node['plot'], class_="bars") if rescale and self.secondary_series: points = self._rescale(serie.points) else: points = serie.points for i, (x, y) in enumerate(points): if None in (x, y) or (self.logarithmic and y <= 0): continue metadata = serie.metadata.get(i) val = self._format(serie.values[i]) bar = decorate( self.svg, self.svg.node(bars, class_='bar'), metadata) bounds = self._bar( serie, bar, x, y, i, self.zero, secondary=rescale) self._tooltip_and_print_values( serie_node, serie, bar, i, val, metadata, *bounds)
def bar(self, serie, rescale=False): """Draw a bar graph for a serie""" serie_node = self.svg.serie(serie) bars = self.svg.node(serie_node['plot'], class_="bars") if rescale and self.secondary_series: points = self._rescale(serie.points) else: points = serie.points total = sum(list(filter(None, serie.values))) for i, (x, y) in enumerate(points): if None in (x, y) or (self.logarithmic and y <= 0): continue metadata = serie.metadata.get(i) val = self._format(serie, i) bar = decorate( self.svg, self.svg.node(bars, class_='bar'), metadata) x_, y_, width, height = self._bar( serie, bar, x, y, i, self.zero, secondary=rescale) self._confidence_interval( serie_node['overlay'], x_ + width / 2, y_, serie.values[i], metadata) self._tooltip_and_print_values( serie_node, serie, bar, i, val, metadata, x_, y_, width, height, total)
def gaugify(self, serie, squares, sq_dimensions, current_square): serie_node = self.svg.serie(serie) if self.half_pie: start_angle = 3 * pi / 2 center = ((current_square[1] * sq_dimensions[0]) - (sq_dimensions[0] / 2.), (current_square[0] * sq_dimensions[1]) - (sq_dimensions[1] / 4)) end_angle = pi / 2 else: start_angle = 0 center = ((current_square[1] * sq_dimensions[0]) - (sq_dimensions[0] / 2.), (current_square[0] * sq_dimensions[1]) - (sq_dimensions[1] / 2.)) end_angle = 2 * pi max_value = serie.metadata.get(0, {}).get('max_value', 100) radius = min([sq_dimensions[0] / 2, sq_dimensions[1] / 2]) * .9 small_radius = radius * serie.inner_radius self.svg.gauge_background(serie_node, start_angle, center, radius, small_radius, end_angle, self.half_pie, self._serie_format(serie, max_value)) sum_ = 0 for i, value in enumerate(serie.values): if value is None: continue ratio = min(value, max_value) / max_value if self.half_pie: angle = 2 * pi * ratio / 2 else: angle = 2 * pi * ratio val = self._format(serie, i) metadata = serie.metadata.get(i) gauge_ = decorate( self.svg, self.svg.node(serie_node['plot'], class_="gauge"), metadata) alter( self.svg.solid_gauge(serie_node, gauge_, radius, small_radius, angle, start_angle, center, val, i, metadata, self.half_pie, end_angle, self._serie_format(serie, max_value)), metadata) start_angle += angle sum_ += value x, y = center self.svg.node(serie_node['text_overlay'], 'text', class_='value gauge-sum', x=x, y=y + self.style.value_font_size / 3, attrib={ 'text-anchor': 'middle' }).text = self._serie_format(serie, sum_)
def needle( self, serie_node, serie, ): thickness = .05 for i, value in enumerate(serie.values): if not value: continue theta = self.arc_pos(value) fmt = lambda x: '%f %f' % x value = self._format(serie.values[i]) metadata = serie.metadata.get(i) gauges = decorate(self.svg, self.svg.node(serie_node['plot'], class_="dots"), metadata) self.svg.node(gauges, 'polygon', points=' '.join([ fmt(self.view((0, 0))), fmt(self.view((.75, theta + thickness))), fmt(self.view((.8, theta))), fmt(self.view((.75, theta - thickness))) ]), class_='line reactive tooltip-trigger') x, y = self.view((.75, theta)) self._tooltip_data(gauges, value, x, y) self._static_value(serie_node, value, x, y)
def bar(self, serie, rescale=False): """Draw a bar graph for a serie""" serie_node = self.svg.serie(serie) bars = self.svg.node(serie_node['plot'], class_="bars") if rescale and self.secondary_series: points = self._rescale(serie.points) else: points = serie.points for i, (x, y) in enumerate(points): if None in (x, y) or (self.logarithmic and y <= 0): continue metadata = serie.metadata.get(i) bar = decorate( self.svg, self.svg.node(bars, class_='bar'), metadata) val = self._format(serie.values[i]) x_center, y_center = self._bar( serie, bar, x, y, self.zero, secondary=rescale) self._tooltip_data( bar, val, x_center, y_center, classes="centered") self._static_value(serie_node, val, x_center, y_center)
def needle(self, serie): serie_node = self.svg.serie(serie) for i, theta in enumerate(serie.values): if theta is None: continue fmt = lambda x: '%f %f' % x value = self._format(serie.values[i]) metadata = serie.metadata.get(i) gauges = decorate( self.svg, self.svg.node(serie_node['plot'], class_="dots"), metadata) alter(self.svg.node( gauges, 'polygon', points=' '.join([ fmt(self.view((0, 0))), fmt(self.view((.75, theta))), fmt(self.view((.8, theta))), fmt(self.view((.75, theta)))]), class_='line reactive tooltip-trigger'), metadata) x, y = self.view((.75, theta)) self._tooltip_data(gauges, value, x, y) self._static_value(serie_node, value, x, y)
def needle(self, serie): serie_node = self.svg.serie(serie) for i, theta in enumerate(serie.values): if theta is None: continue fmt = lambda x: '%f %f' % x value = self._format(serie.values[i]) metadata = serie.metadata.get(i) gauges = decorate(self.svg, self.svg.node(serie_node['plot'], class_="dots"), metadata) self.svg.node(gauges, 'polygon', points=' '.join([ fmt(self.view((0, 0))), fmt(self.view((.75, theta))), fmt(self.view((.8, theta))), fmt(self.view((.75, theta))) ]), class_='line reactive tooltip-trigger') x, y = self.view((.75, theta)) self._tooltip_data(gauges, value, x, y) self._static_value(serie_node, value, x, y)
def bar(self, serie_node, serie, index, rescale=False): """Draw a bar graph for a serie""" bars = self.svg.node(serie_node['plot'], class_="histbars") points = serie.points for i, (y, x0, x1) in enumerate(points): if None in (x0, x1, y) or (self.logarithmic and y <= 0): continue metadata = serie.metadata.get(i) bar = decorate(self.svg, self.svg.node(bars, class_='histbar'), metadata) val = self._format(serie.values[i][0]) x_center, y_center = self._bar(bar, x0, x1, y, index, i, self.zero, secondary=rescale, rounded=serie.rounded_bars) self._tooltip_data(bar, val, x_center, y_center, classes="centered") self._static_value(serie_node, val, x_center, y_center)
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)
def gaugify(self, serie, squares, sq_dimensions, current_square): serie_node = self.svg.serie(serie) if self.half_pie: start_angle = 3*pi/2 center = ( (current_square[1]*sq_dimensions[0]) - (sq_dimensions[0] / 2.), (current_square[0]*sq_dimensions[1]) - (sq_dimensions[1] / 4)) end_angle = pi / 2 else: start_angle = 0 center = ( (current_square[1]*sq_dimensions[0]) - (sq_dimensions[0] / 2.), (current_square[0]*sq_dimensions[1]) - (sq_dimensions[1] / 2.)) end_angle = 2 * pi max_value = serie.metadata.get(0, {}).get('max_value', 100) radius = min([sq_dimensions[0]/2, sq_dimensions[1]/2]) * .9 small_radius = radius * serie.inner_radius self.svg.gauge_background( serie_node, start_angle, center, radius, small_radius, end_angle, self.half_pie, self._serie_format(serie, max_value)) sum_ = 0 for i, value in enumerate(serie.values): if value is None: continue ratio = min(value, max_value) / max_value if self.half_pie: angle = 2 * pi * ratio / 2 else: angle = 2 * pi * ratio val = self._format(serie, i) metadata = serie.metadata.get(i) gauge_ = decorate( self.svg, self.svg.node(serie_node['plot'], class_="gauge"), metadata) alter( self.svg.solid_gauge( serie_node, gauge_, radius, small_radius, angle, start_angle, center, val, i, metadata, self.half_pie, end_angle, self._serie_format(serie, max_value)), metadata) start_angle += angle sum_ += value x, y = center self.svg.node( serie_node['text_overlay'], 'text', class_='value gauge-sum', x=x, y=y + self.style.value_font_size / 3, attrib={'text-anchor': 'middle'} ).text = self._serie_format(serie, sum_)
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)
def line(self, serie, rescale=False): """Draw the line serie""" serie_node = self.svg.serie(serie) if rescale and self.secondary_series: points = [ (x, self._scale_diff + (y - self._scale_min_2nd) * self._scale) for x, y in serie.points if y is not None] else: points = serie.points view_values = list(map(self.view, points)) if serie.show_dots: for i, (x, y) in enumerate(view_values): if None in (x, y): continue if (serie.show_only_major_dots and self.x_labels and i < len(self.x_labels) and self.x_labels[i] not in self._x_major_labels): continue metadata = serie.metadata.get(i) classes = [] if x > self.view.width / 2: classes.append('left') if y > self.view.height / 2: classes.append('top') classes = ' '.join(classes) dots = decorate( self.svg, self.svg.node(serie_node['overlay'], class_="dots"), metadata) val = self._get_value(serie.points, i) self.svg.node(dots, 'circle', cx=x, cy=y, r=serie.dots_size, class_='dot reactive tooltip-trigger') self._tooltip_data( dots, val, x, y) self._static_value( serie_node, val, x + self.value_font_size, y + self.value_font_size) line_view_values = [[]] for view_value in view_values: if None in view_value: line_view_values.append([]) else: line_view_values[-1].append(view_value) if serie.stroke: if self.interpolate: view_values = list(map(self.view, serie.interpolated)) if serie.fill: view_values = self._fill(view_values) for row in line_view_values: if not row: continue self.svg.line( serie_node['plot'], row, close=self._self_close, class_='line reactive' + ( ' nofill' if not serie.fill else ''))
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)
def line(self, serie, rescale=False): """Draw the line serie""" serie_node = self.svg.serie(serie) if rescale and self.secondary_series: points = self._rescale(serie.points) else: points = serie.points view_values = list(map(self.view, points)) if serie.show_dots: for i, (x, y) in enumerate(view_values): if None in (x, y): continue if ( serie.show_only_major_dots and self.x_labels and i < len(self.x_labels) and self.x_labels[i] not in self._x_labels_major ): continue metadata = serie.metadata.get(i) classes = [] if x > self.view.width / 2: classes.append("left") if y > self.view.height / 2: classes.append("top") classes = " ".join(classes) self._confidence_interval(serie_node["overlay"], x, y, serie.values[i], metadata) dots = decorate(self.svg, self.svg.node(serie_node["overlay"], class_="dots"), metadata) val = self._get_value(serie.points, i) alter( self.svg.transposable_node( dots, "circle", cx=x, cy=y, r=serie.dots_size, class_="dot reactive tooltip-trigger" ), metadata, ) self._tooltip_data(dots, val, x, y, xlabel=self._get_x_label(i)) self._static_value( serie_node, val, x + self.style.value_font_size, y + self.style.value_font_size, metadata ) if serie.stroke: if self.interpolate: points = serie.interpolated if rescale and self.secondary_series: points = self._rescale(points) view_values = list(map(self.view, points)) if serie.fill: view_values = self._fill(view_values) self.svg.line( serie_node["plot"], view_values, close=self._self_close, class_="line reactive" + (" nofill" if not serie.fill else ""), )
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)
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)
def line(self, serie, rescale=False): """Draw the line serie""" serie_node = self.svg.serie(serie) if rescale and self.secondary_series: points = self._rescale(serie.points) else: points = serie.points view_values = list(map(self.view, points)) if serie.show_dots: for i, (x, y) in enumerate(view_values): if None in (x, y): continue if (serie.show_only_major_dots and self.x_labels and i < len(self.x_labels) and self.x_labels[i] not in self._x_labels_major): continue metadata = serie.metadata.get(i) classes = [] if x > self.view.width / 2: classes.append('left') if y > self.view.height / 2: classes.append('top') classes = ' '.join(classes) self._confidence_interval( serie_node['overlay'], x, y, serie.values[i], metadata) dots = decorate( self.svg, self.svg.node(serie_node['overlay'], class_="dots"), metadata) val = self._get_value(serie.points, i) alter(self.svg.transposable_node( dots, 'circle', cx=x, cy=y, r=serie.dots_size, class_='dot reactive tooltip-trigger'), metadata) self._tooltip_data( dots, val, x, y, xlabel=self._get_x_label(i)) self._static_value( serie_node, val, x + self.style.value_font_size, y + self.style.value_font_size, metadata) if serie.stroke: if self.interpolate: points = serie.interpolated if rescale and self.secondary_series: points = self._rescale(points) view_values = list(map(self.view, points)) if serie.fill: view_values = self._fill(view_values) self.svg.line( serie_node['plot'], view_values, close=self._self_close, class_='line reactive' + (' nofill' if not serie.fill else ''))
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): if self.clockwise: transform = compose(self.clockwiser, self.view) else: transform = self.view return '%f %f' % transform((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) sweep_flag = '0' if self.clockwise else '1' alter( self.svg.node( gauges, 'path', d='M %s L %s A %s 1 0 %s %s Z' % ( point(.85, theta), point(self.needle_width, theta - w), '%f %f' % (self.needle_width, self.needle_width), sweep_flag, point(self.needle_width, theta + w), ), class_='line reactive tooltip-trigger'), metadata) x, y = self.view((.75, theta)) if self.clockwise: x, y = self.clockwiser((x, y)) self._tooltip_data( gauges, val, x, y, xlabel=self._get_x_label(i)) self._static_value(serie_node, val, x, y, metadata)
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)
def slice(self, serie, start_angle, total): """Make a serie slice""" #serie_angle = totallist[2] #total=totallist[0] #start_angle=totallist[1] print(serie, total, start_angle, serie_angle) serie_node = self.svg.serie(serie) dual = self._len > 1 and not self._order == 1 slices = self.svg.node(serie_node['plot'], class_="slices") original_start_angle = start_angle if self.half_pie: center = ((self.width - self.margin_box.x) / 2., (self.height - self.margin_box.y) / 1.25) else: center = ((self.width - self.margin_box.x) / 2., (self.height - self.margin_box.y) / 2.) radius = min(center) for i, val in enumerate(serie.values): perc = val / total if self.half_pie: angle = 2 * pi * perc / 2 else: angle = 2 * pi * perc serie_angle += angle val = self._format(serie, i) metadata = serie.metadata.get(i) slice_ = decorate(self.svg, self.svg.node(slices, class_="slice"), metadata) if dual: small_radius = radius * .9 big_radius = radius else: big_radius = radius * .9 small_radius = radius * serie.inner_radius alter( self.svg.slice(serie_node, slice_, big_radius, small_radius, angle, start_angle, center, val, i, metadata), metadata) start_angle += angle if dual: val = self._serie_format(serie, sum(serie.values)) self.svg.slice(serie_node, self.svg.node(slices, class_="big_slice"), radius * .9, 0, serie_angle, original_start_angle, center, val, i, metadata) return serie_angle
def dot(self, serie_node, serie, r_max): """Draw a dot line""" view_values = list(map(self.view, serie.points)) for i, value in safe_enumerate(serie.values): x, y = view_values[i] size = r_max * value value = self._format(value) metadata = serie.metadata.get(i) dots = decorate(self.svg, self.svg.node(serie_node["plot"], class_="dots"), metadata) self.svg.node(dots, "circle", cx=x, cy=y, r=size, class_="dot reactive tooltip-trigger") self._tooltip_data(dots, value, x, y) self._static_value(serie_node, value, x, y)
def slice(self, serie, start_angle, total): """Make a serie slice""" serie_node = self.svg.serie(serie) dual = self._len > 1 and not self._order == 1 slices = self.svg.node(serie_node['plot'], class_="slices") serie_angle = 0 original_start_angle = start_angle if self.half_pie: center = ((self.width - self.margin_box.x) / 2., (self.height - self.margin_box.y) / 1.25) else: center = ((self.width - self.margin_box.x) / 2., (self.height - self.margin_box.y) / 2.) radius = min(center) for i, val in enumerate(serie.values): perc = val / total if self.half_pie: angle = 2 * pi * perc / 2 else: angle = 2 * pi * perc serie_angle += angle val = self._format(serie, i) metadata = serie.metadata.get(i) slice_ = decorate( self.svg, self.svg.node(slices, class_="slice"), metadata ) if dual: small_radius = radius * .9 big_radius = radius else: big_radius = radius * .9 small_radius = radius * serie.inner_radius alter( self.svg.slice( serie_node, slice_, big_radius, small_radius, angle, start_angle, center, val, i, metadata ), metadata ) start_angle += angle if dual: val = self._serie_format(serie, sum(serie.values)) self.svg.slice( serie_node, self.svg.node(slices, class_="big_slice"), radius * .9, 0, serie_angle, original_start_angle, center, val, i, metadata ) return serie_angle
def line(self, serie, rescale=False): """Draw the line serie""" serie_node = self.svg.serie(serie) if rescale and self.secondary_series: points = self._rescale(serie.points) else: points = serie.points view_values = list(map(self.view, points)) if serie.show_dots: for i, (x, y) in enumerate(view_values): if None in (x, y): continue if (serie.show_only_major_dots and self.x_labels and i < len(self.x_labels) and self.x_labels[i] not in self._x_major_labels): continue metadata = serie.metadata.get(i) classes = [] if x > self.view.width / 2: classes.append('left') if y > self.view.height / 2: classes.append('top') classes = ' '.join(classes) dots = decorate( self.svg, self.svg.node(serie_node['overlay'], class_="dots"), metadata) val = self._get_value(serie.points, i) alter(self.svg.transposable_node( dots, 'circle', cx=x, cy=y, r=serie.dots_size, class_='dot reactive tooltip-trigger'), metadata) self._tooltip_data( dots, val, x, y, xlabel=self._get_x_label(i)) self._static_value( serie_node, val, x + self.style.value_font_size, y + self.style.value_font_size) if serie.stroke: if self.interpolate: points = serie.interpolated if rescale and self.secondary_series: points = self._rescale(points) view_values = list(map(self.view, points)) if serie.fill: view_values = self._fill(view_values) self.svg.line( serie_node['plot'], view_values, close=self._self_close, class_='line reactive' + (' nofill' if not serie.fill else ''))
def line(self, serie_node, serie, rescale=False): """Draw the line serie""" if rescale and self.secondary_series: points = [ (x, self._scale_diff + (y - self._scale_min_2nd) * self._scale) for x, y in serie.points if y is not None ] else: points = serie.points view_values = list(map(self.view, points)) if serie.show_dots: for i, (x, y) in enumerate(view_values): if None in (x, y): continue if (serie.show_only_major_dots and self.x_labels and i < len(self.x_labels) and self.x_labels[i] not in self._x_major_labels): continue metadata = serie.metadata.get(i) classes = [] if x > self.view.width / 2: classes.append('left') if y > self.view.height / 2: classes.append('top') classes = ' '.join(classes) dots = decorate( self.svg, self.svg.node(serie_node['overlay'], class_="dots"), metadata) val = self._get_value(serie.points, i) self.svg.node(dots, 'circle', cx=x, cy=y, r=serie.dots_size, class_='dot reactive tooltip-trigger') self._tooltip_data(dots, val, x, y) self._static_value(serie_node, val, x + self.value_font_size, y + self.value_font_size) if serie.stroke: if self.interpolate: view_values = list(map(self.view, serie.interpolated)) if serie.fill: view_values = self._fill(view_values) self.svg.line(serie_node['plot'], view_values, close=self._self_close, class_='line reactive' + (' nofill' if not serie.fill else ''))
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)
def slice(self, serie, start_angle, total): """Make a serie slice""" serie_node = self.svg.serie(serie) dual = self._len > 1 and not self._order == 1 slices = self.svg.node(serie_node['plot'], class_="slices") serie_angle = 0 total_perc = 0 original_start_angle = start_angle if self.half_pie: center = ((self.width - self.margin.x) / 2., (self.height - self.margin.y) / 1.25) else: center = ((self.width - self.margin.x) / 2., (self.height - self.margin.y) / 2.) radius = min(center) for i, val in enumerate(serie.values): perc = val / total if self.half_pie: angle = 2 * pi * perc / 2 else: angle = 2 * pi * perc serie_angle += angle val = '{0:.2%}'.format(perc) metadata = serie.metadata.get(i) slice_ = decorate( self.svg, self.svg.node(slices, class_="slice"), metadata) if dual: small_radius = radius * .9 big_radius = radius else: big_radius = radius * .9 small_radius = radius * serie.inner_radius self.svg.slice( serie_node, slice_, big_radius, small_radius, angle, start_angle, center, val) start_angle += angle total_perc += perc if dual: val = '{0:.2%}'.format(total_perc) self.svg.slice(serie_node, self.svg.node(slices, class_="big_slice"), radius * .9, 0, serie_angle, original_start_angle, center, val) return serie_angle
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)) value = self._format(serie.values[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, value, x, y, xlabel=self._get_x_label(i)) self._static_value(serie_node, value, x, y, metadata)
def bar(self, serie_node, serie, index, rescale=False): """Draw a bar graph for a serie""" bars = self.svg.node(serie_node["plot"], class_="histbars") points = serie.points for i, (y, x0, x1) in enumerate(points): if None in (x0, x1, y) or (self.logarithmic and y <= 0): continue metadata = serie.metadata.get(i) bar = decorate(self.svg, self.svg.node(bars, class_="histbar"), metadata) val = self._format(serie.values[i][0]) x_center, y_center = self._bar(bar, x0, x1, y, index, i, self.zero, secondary=rescale) self._tooltip_data(bar, val, x_center, y_center, classes="centered") self._static_value(serie_node, val, x_center, y_center)
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)
def line(self, serie_node, serie, rescale=False): """Draw the line serie""" if rescale and self.secondary_series: points = [ (x, self._scale_diff + (y - self._scale_min_2nd) * self._scale) for x, y in serie.points if y is not None ] else: points = serie.points view_values = list(map(self.view, points)) if serie.show_dots: for i, (x, y) in enumerate(view_values): if None in (x, y): continue if ( serie.show_only_major_dots and self.x_labels and i < len(self.x_labels) and self.x_labels[i] not in self._x_major_labels ): continue metadata = serie.metadata.get(i) classes = [] if x > self.view.width / 2: classes.append("left") if y > self.view.height / 2: classes.append("top") classes = " ".join(classes) dots = decorate(self.svg, self.svg.node(serie_node["overlay"], class_="dots"), metadata) val = self._get_value(serie.points, i) self.svg.node(dots, "circle", cx=x, cy=y, r=serie.dots_size, class_="dot reactive tooltip-trigger") self._tooltip_data(dots, val, x, y) self._static_value(serie_node, val, x + self.value_font_size, y + self.value_font_size) if serie.stroke: if self.interpolate: view_values = list(map(self.view, serie.interpolated)) if serie.fill: view_values = self._fill(view_values) self.svg.line( serie_node["plot"], view_values, close=self._self_close, class_="line reactive" + (" nofill" if not serie.fill else ""), )
def dot(self, serie_node, serie, r_max): """Draw a dot line""" view_values = map(self.view, serie.points) for i, value in safe_enumerate(serie.values): x, y = view_values[i] size = r_max * value value = self._format(value) metadata = serie.metadata.get(i) dots = decorate( self.svg, self.svg.node(serie_node['plot'], class_="dots"), metadata) self.svg.node(dots, 'circle', cx=x, cy=y, r=size, class_='dot reactive tooltip-trigger') self._tooltip_data(dots, value, x, y) self._static_value(serie_node, value, x, y)
def _boxf(self, serie_node, serie, index): """ For a specific series, draw the box plot. """ # Note: q0 and q4 do not literally mean the zero-th quartile # and the fourth quartile, but rather the distance from 1.5 times # the inter-quartile range to Q1 and Q3, respectively. boxes = self.svg.node(serie_node['plot'], class_="boxes") metadata = serie.metadata.get(0) box = decorate(self.svg, self.svg.node(boxes, class_='box'), metadata) val = self._format(serie.values) x_center, y_center = self._draw_box(box, serie.values, index) self._tooltip_data(box, val, x_center, y_center, classes="centered") self._static_value(serie_node, val, x_center, y_center)
def dot(self, serie_node, serie, r_max): """Draw a dot line""" view_values = map(self.view, serie.points) for i, (x, y) in enumerate(view_values): value = serie.values[i] size = r_max * value value = self._format(value) metadata = serie.metadata[i] dots = decorate( self.svg, self.svg.node(serie_node['plot'], class_="dots"), metadata) self.svg.node(dots, 'circle', cx=x, cy=y, r=size, class_='dot reactive tooltip-trigger') self._tooltip_data(dots, value, x, y) self._static_value(serie_node, value, x, y)
def _boxf(self, serie): """ For a specific series, draw the box plot. """ serie_node = self.svg.serie(serie) # Note: q0 and q4 do not literally mean the zero-th quartile # and the fourth quartile, but rather the distance from 1.5 times # the inter-quartile range to Q1 and Q3, respectively. boxes = self.svg.node(serie_node["plot"], class_="boxes") metadata = serie.metadata.get(0) box = decorate(self.svg, self.svg.node(boxes, class_="box"), metadata) val = self._format(serie.values) x_center, y_center = self._draw_box(box, serie.values, serie.index) self._tooltip_data(box, val, x_center, y_center, classes="centered") self._static_value(serie_node, val, x_center, y_center)
def line(self, serie_node, serie, rescale=False): """Draw the line serie""" if rescale and self.secondary_series: points = [ (x, self._scale_diff + (y - self._scale_min_2nd) * self._scale) for x, y in serie.points] else: points = serie.points view_values = map(self.view, points) if self.show_dots: for i, (x, y) in enumerate(view_values): if None in (x, y): continue metadata = serie.metadata.get(i) classes = [] if x > self.view.width / 2: classes.append('left') if y > self.view.height / 2: classes.append('top') classes = ' '.join(classes) dots = decorate( self.svg, self.svg.node(serie_node['overlay'], class_="dots"), metadata) val = self._get_value(serie.points, i) self.svg.node(dots, 'circle', cx=x, cy=y, r=self.dots_size, class_='dot reactive tooltip-trigger') self._tooltip_data( dots, val, x, y) self._static_value( serie_node, val, x + self.value_font_size, y + self.value_font_size) if self.stroke: if self.interpolate: view_values = map(self.view, serie.interpolated) if self.fill: view_values = self._fill(view_values) self.svg.line( serie_node['plot'], view_values, close=self._self_close, class_='line reactive' + (' nofill' if not self.fill else ''))
def needle(self, serie): 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)) value = self._format(serie.values[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, value, x, y) self._static_value(serie_node, value, x, y)
def bar(self, serie_node, serie, index): """Draw a bar graph for a serie""" bars = self.svg.node(serie_node['plot'], class_="bars") for i, (x, y) in enumerate(serie.points): if None in (x, y) or (self.logarithmic and y <= 0): continue metadata = serie.metadata.get(i) bar = decorate( self.svg, self.svg.node(bars, class_='bar'), metadata) val = self._format(serie.values[i]) x_center, y_center = self._bar( bar, x, y, index, i, self.zero) self._tooltip_data( bar, val, x_center, y_center, classes="centered") self._static_value(serie_node, val, x_center, y_center)
def _boxf(self, serie_node, serie, index): """ For a specific series, draw the box plot. """ # Note: q0 and q4 do not literally mean the zero-th quartile # and the fourth quartile, but rather the distance from 1.5 times # the inter-quartile range to Q1 and Q3, respectively. q0, q1, q2, q3, q4 = serie.values boxes = self.svg.node(serie_node['plot'], class_="boxes") metadata = serie.metadata.get(0) box = decorate( self.svg, self.svg.node(boxes, class_='box'), metadata) val = self._format(q2) x_center, y_center = self._draw_box(box, (q0, q1, q2, q3, q4), index) self._tooltip_data(box, val, x_center, y_center, classes="centered")
def line(self, serie_node, serie): """Draw the line serie""" view_values = map(self.view, serie.points) if self.show_dots: for i, (x, y) in enumerate(view_values): if None in (x, y): continue metadata = serie.metadata.get(i) classes = [] if x > self.view.width / 2: classes.append('left') if y > self.view.height / 2: classes.append('top') classes = ' '.join(classes) dots = decorate( self.svg, self.svg.node(serie_node['overlay'], class_="dots"), metadata) val = self._get_value(serie.points, i) self.svg.node(dots, 'circle', cx=x, cy=y, r=2.5, class_='dot reactive tooltip-trigger') self._tooltip_data(dots, val, x, y) self._static_value(serie_node, val, x + self.value_font_size, y + self.value_font_size) if self.stroke: if self.interpolate: view_values = map(self.view, serie.interpolated) if self.fill: view_values = self._fill(view_values) self.svg.line(serie_node['plot'], view_values, close=self._self_close, class_='line reactive' + (' nofill' if not self.fill else ''))
def dot(self, serie, r_max): """Draw a dot line""" serie_node = self.svg.serie(serie) view_values = list(map(self.view, serie.points)) for i, value in safe_enumerate(serie.values): x, y = view_values[i] if self.logarithmic: log10min = log10(self._min) - 1 log10max = log10(self._max or 1) if value != 0: size = r_max * ((log10(abs(value)) - log10min) / (log10max - log10min)) else: size = 0 else: size = r_max * (abs(value) / (self._max or 1)) metadata = serie.metadata.get(i) dots = decorate( self.svg, self.svg.node(serie_node['plot'], class_="dots"), metadata ) alter( self.svg.node( dots, 'circle', cx=x, cy=y, r=size, class_='dot reactive tooltip-trigger' + (' negative' if value < 0 else '') ), metadata ) val = self._format(serie, i) self._tooltip_data( dots, val, x, y, 'centered', self._get_x_label(i) ) self._static_value(serie_node, val, x, y, metadata)
def funnel(self, serie_node, serie, index): """Draw a dot line""" fmt = lambda x: '%f %f' % x for i, poly in enumerate(serie.points): metadata = serie.metadata.get(i) value = self._format(serie.values[i]) funnels = decorate( self.svg, self.svg.node(serie_node['plot'], class_="funnels"), metadata) self.svg.node(funnels, 'polygon', points=' '.join(map(fmt, map(self.view, poly))), class_='funnel reactive tooltip-trigger') x, y = self.view(( self._x_labels[index][1], # Poly center from label sum([point[1] for point in poly]) / len(poly))) self._tooltip_data(funnels, value, x, y, classes='centered') self._static_value(serie_node, value, x, y)
def dot(self, serie, r_max): """Draw a box line""" width = (self.view.x(1) - self.view.x(0)) serie_node = self.svg.serie(serie) view_values = list(map(self.view, serie.points)) for i, value in safe_enumerate(serie.values): x, y = view_values[i] if self.logarithmic: log10min = log10(self._min) - 1 log10max = log10(self._max or 1) if value != 0: size = r_max * ((log10(abs(value)) - log10min) / (log10max - log10min)) else: size = 0 else: size = r_max * (abs(value) / (self._max or 1)) metadata = serie.metadata.get(i) dots = decorate(self.svg, self.svg.node(serie_node['plot'], class_="dots"), metadata) alter( self.svg.node(dots, 'rect', x=x - 0.5 * width, y=(y - size) if value > 0 else y, width=width, height=size, class_='dot reactive tooltip-trigger' + (' negative' if value < 0 else '')), metadata) val = self._format(serie, i) self._tooltip_data(dots, val, x, y, '', self._get_x_label(i)) self._static_value(serie_node, val, x, y, metadata)
def line(self, serie, rescale=False): """Draw the line serie""" serie_node = self.svg.serie(serie) if rescale and self.secondary_series: points = self._rescale(serie.points) else: points = serie.points view_values = list(map(self.view, points)) if serie.show_dots: for i, (x, y) in enumerate(view_values): if None in (x, y): continue if (serie.show_only_major_dots and self.x_labels and i < len(self.x_labels) and self.x_labels[i] not in self._x_labels_major): continue metadata = serie.metadata.get(i) classes = [] if x > self.view.width / 2: classes.append('left') if y > self.view.height / 2: classes.append('top') classes = ' '.join(classes) self._confidence_interval( serie_node['overlay'], x, y, serie.values[i], metadata) dots = decorate( self.svg, self.svg.node(serie_node['overlay'], class_="dots"), metadata) val = self._format(serie, i) alter(self.svg.transposable_node( dots, 'circle', cx=x, cy=y, r=serie.dots_size, class_='dot reactive tooltip-trigger'), metadata) self._tooltip_data( dots, val, x, y, xlabel=self._get_x_label(i)) self._static_value( serie_node, val, x + self.style.value_font_size, y + self.style.value_font_size, metadata) if serie.stroke: if self.interpolate: points = serie.interpolated if rescale and self.secondary_series: points = self._rescale(points) view_values = list(map(self.view, points)) if serie.fill: view_values = self._fill(view_values) if serie.allow_interruptions: # view_values are in form [(x1, y1), (x2, y2)]. We # need to split that into multiple sequences if a # None is present here sequences = [] cur_sequence = [] for x, y in view_values: if y is None and len(cur_sequence) > 0: # emit current subsequence sequences.append(cur_sequence) cur_sequence = [] elif y is None: # just discard continue else: cur_sequence.append((x, y)) # append the element if len(cur_sequence) > 0: # emit last possible sequence sequences.append(cur_sequence) else: # plain vanilla rendering sequences = [view_values] for seq in sequences: self.svg.line( serie_node['plot'], seq, close=self._self_close, class_='line reactive' + (' nofill' if not serie.fill else ''))
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)
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
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
def bar(self, serie_node, serie, values, index, stack_vals=None): """Draw a bar graph for a serie""" # value here is a list of tuple range of tuple coord def view(rng): """Project range""" t, T = rng fun = swap if self.horizontal else ident return self.view(fun(t)), self.view(fun(T)) bars = self.svg.node(serie_node['plot'], class_="bars") view_values = map(view, values) for i, ((x, y), (X, Y)) in enumerate(view_values): if None in (x, y): continue # +-------+ # | | # | | # | +-------+ # | | | # | | | # | | | # +-------+-------+ # (x,y) (X,Y) # # x and y are left range coords and X, Y right ones metadata = serie.metadata.get(i) val = self._format(values[i][1][1]) if self.horizontal: x, y, X, Y = Y, X, y, x width = X - x padding = .1 * width inner_width = width - 2 * padding if self.horizontal: height = self.view.x(self.zero) - y else: height = self.view.y(self.zero) - y if stack_vals is None: bar_width = inner_width / self._order bar_padding = .1 * bar_width bar_inner_width = bar_width - 2 * bar_padding offset = index * bar_width + bar_padding shift = 0 else: offset = 0 bar_inner_width = inner_width shift = stack_vals[i][int(height < 0)] stack_vals[i][int(height < 0)] += height x = x + padding + offset if height < 0: y = y + height height = -height y -= shift bar = decorate(self.svg, self.svg.node(bars, class_='bar'), metadata) self.svg.transposable_node( bar, 'rect', x=x, y=y, rx=self.rounded_bars * 1 if self.rounded_bars else 0, ry=self.rounded_bars * 1 if self.rounded_bars else 0, width=bar_inner_width, height=height, class_='rect reactive tooltip-trigger') x += bar_inner_width / 2 y += height / 2 if self.horizontal: x, y = y, x self._tooltip_data(bar, val, x, y, classes="centered") self._static_value(serie_node, val, x, y) return stack_vals