|
|
|
@ -9,73 +9,7 @@ from util import flatten, float_range
|
|
|
|
|
from svg.charts.graph import Graph |
|
|
|
|
|
|
|
|
|
class Line(Graph): |
|
|
|
|
""" === Create presentation quality SVG line graphs easily |
|
|
|
|
|
|
|
|
|
= Synopsis |
|
|
|
|
|
|
|
|
|
require 'SVG/Graph/Line' |
|
|
|
|
|
|
|
|
|
fields = %w(Jan Feb Mar); |
|
|
|
|
data_sales_02 = [12, 45, 21] |
|
|
|
|
data_sales_03 = [15, 30, 40] |
|
|
|
|
|
|
|
|
|
graph = SVG::Graph::Line.new({ |
|
|
|
|
:height => 500, |
|
|
|
|
:width => 300, |
|
|
|
|
:fields => fields, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
graph.add_data({ |
|
|
|
|
:data => data_sales_02, |
|
|
|
|
:title => 'Sales 2002', |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
graph.add_data({ |
|
|
|
|
:data => data_sales_03, |
|
|
|
|
:title => 'Sales 2003', |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
print "Content-type: image/svg+xml\r\n\r\n"; |
|
|
|
|
print graph.burn(); |
|
|
|
|
|
|
|
|
|
= Description |
|
|
|
|
|
|
|
|
|
This object aims to allow you to easily create high quality |
|
|
|
|
SVG line graphs. You can either use the default style sheet |
|
|
|
|
or supply your own. Either way there are many options which can |
|
|
|
|
be configured to give you control over how the graph is |
|
|
|
|
generated - with or without a key, data elements at each point, |
|
|
|
|
title, subtitle etc. |
|
|
|
|
|
|
|
|
|
= Examples |
|
|
|
|
|
|
|
|
|
http://www.germane-software/repositories/public/SVG/test/single.rb |
|
|
|
|
|
|
|
|
|
= Notes |
|
|
|
|
|
|
|
|
|
The default stylesheet handles upto 10 data sets, if you |
|
|
|
|
use more you must create your own stylesheet and add the |
|
|
|
|
additional settings for the extra data sets. You will know |
|
|
|
|
if you go over 10 data sets as they will have no style and |
|
|
|
|
be in black. |
|
|
|
|
|
|
|
|
|
= See also |
|
|
|
|
|
|
|
|
|
* SVG::Graph::Graph |
|
|
|
|
* SVG::Graph::BarHorizontal |
|
|
|
|
* SVG::Graph::Bar |
|
|
|
|
* SVG::Graph::Pie |
|
|
|
|
* SVG::Graph::Plot |
|
|
|
|
* SVG::Graph::TimeSeries |
|
|
|
|
|
|
|
|
|
== Author |
|
|
|
|
|
|
|
|
|
Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom> |
|
|
|
|
|
|
|
|
|
Copyright 2004 Sean E. Russell |
|
|
|
|
This software is available under the Ruby license[LICENSE.txt] |
|
|
|
|
|
|
|
|
|
""" |
|
|
|
|
"""Line Graph""" |
|
|
|
|
|
|
|
|
|
"""Show a small circle on the graph where the line goes from one point to |
|
|
|
|
the next""" |
|
|
|
@ -86,6 +20,8 @@ class Line(Graph):
|
|
|
|
|
stacked = False |
|
|
|
|
"Fill in the area under the plot" |
|
|
|
|
area_fill = False |
|
|
|
|
|
|
|
|
|
scale_divisions = None |
|
|
|
|
|
|
|
|
|
#override some defaults |
|
|
|
|
top_align = top_font = right_align = right_font = True |
|
|
|
@ -123,10 +59,10 @@ class Line(Graph):
|
|
|
|
|
|
|
|
|
|
def calculate_left_margin(self): |
|
|
|
|
super(self.__class__, self).calculate_left_margin() |
|
|
|
|
label_left = self.fields[0].length / 2 * self.font_size * 0.6 |
|
|
|
|
label_left = len(self.fields[0]) / 2 * self.font_size * 0.6 |
|
|
|
|
self.border_left = max(label_left, self.border_left) |
|
|
|
|
|
|
|
|
|
def get_y_labels(self): |
|
|
|
|
def get_y_label_values(self): |
|
|
|
|
max_value = self.max_value() |
|
|
|
|
min_value = self.min_value() |
|
|
|
|
range = max_value - min_value |
|
|
|
@ -143,6 +79,9 @@ class Line(Graph):
|
|
|
|
|
labels = tuple(float_range(min_value, max_value, scale_division)) |
|
|
|
|
return labels |
|
|
|
|
|
|
|
|
|
def get_y_labels(self): |
|
|
|
|
return map(str, self.get_y_label_values()) |
|
|
|
|
|
|
|
|
|
def calc_coords(self, field, value, width = None, height = None): |
|
|
|
|
if width is None: width = self.field_width |
|
|
|
|
if height is None: height = self.field_height |
|
|
|
@ -155,10 +94,12 @@ class Line(Graph):
|
|
|
|
|
def draw_data(self): |
|
|
|
|
min_value = self.min_value() |
|
|
|
|
field_height = self.graph_height - self.font_size*2*self.top_font |
|
|
|
|
y_label_span = max(self.get_y_labels()) - min(self.get_y_labels()) |
|
|
|
|
|
|
|
|
|
y_label_values = self.get_y_label_values() |
|
|
|
|
y_label_span = max(y_label_values) - min(y_label_values) |
|
|
|
|
field_height /= float(y_label_span) |
|
|
|
|
|
|
|
|
|
field_width = self.field_width |
|
|
|
|
field_width = self.field_width() |
|
|
|
|
#line = len(self.data) |
|
|
|
|
|
|
|
|
|
prev_sum = [0]*len(self.fields) |
|
|
|
@ -166,7 +107,7 @@ class Line(Graph):
|
|
|
|
|
|
|
|
|
|
coord_format = lambda c: '%(x)s %(y)s' % c |
|
|
|
|
|
|
|
|
|
for line_n, data in list(enumerate(self.data)).reversed(): |
|
|
|
|
for line_n, data in reversed(list(enumerate(self.data))): |
|
|
|
|
apath = '' |
|
|
|
|
|
|
|
|
|
if not self.stacked: cum_sum = [-min_value]*len(self.fields) |
|
|
|
@ -191,7 +132,7 @@ class Line(Graph):
|
|
|
|
|
origin = paths[-1] |
|
|
|
|
else: |
|
|
|
|
area_path = "V#@graph_height" |
|
|
|
|
origin = coord_format(get_coords(0,0)) |
|
|
|
|
origin = coord_format(get_coords((0,0))) |
|
|
|
|
|
|
|
|
|
d = ' '.join(( |
|
|
|
|
'M', |
|
|
|
@ -208,7 +149,7 @@ class Line(Graph):
|
|
|
|
|
|
|
|
|
|
# now draw the line itself |
|
|
|
|
etree.SubElement(self.graph, 'path', { |
|
|
|
|
'd': 'M0 '+self.graph_height+' L'+line_path, |
|
|
|
|
'd': 'M0 %s L%s' % (self.graph_height, line_path), |
|
|
|
|
'class': 'line%(line_n)s' % vars(), |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
@ -229,4 +170,4 @@ class Line(Graph):
|
|
|
|
|
value + min_value |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
prev_sum = list(cum_sum) |
|
|
|
|
prev_sum = list(cum_sum) |
|
|
|
|