Chris@0
|
1 require 'rexml/document'
|
Chris@0
|
2 require 'SVG/Graph/Graph'
|
Chris@0
|
3 require 'SVG/Graph/BarBase'
|
Chris@0
|
4
|
Chris@0
|
5 module SVG
|
Chris@0
|
6 module Graph
|
Chris@0
|
7 # === Create presentation quality SVG bar graphs easily
|
Chris@0
|
8 #
|
Chris@0
|
9 # = Synopsis
|
Chris@0
|
10 #
|
Chris@0
|
11 # require 'SVG/Graph/Bar'
|
Chris@0
|
12 #
|
Chris@0
|
13 # fields = %w(Jan Feb Mar);
|
Chris@0
|
14 # data_sales_02 = [12, 45, 21]
|
Chris@0
|
15 #
|
Chris@0
|
16 # graph = SVG::Graph::Bar.new(
|
Chris@0
|
17 # :height => 500,
|
Chris@0
|
18 # :width => 300,
|
Chris@0
|
19 # :fields => fields
|
Chris@0
|
20 # )
|
Chris@0
|
21 #
|
Chris@0
|
22 # graph.add_data(
|
Chris@0
|
23 # :data => data_sales_02,
|
Chris@0
|
24 # :title => 'Sales 2002'
|
Chris@0
|
25 # )
|
Chris@0
|
26 #
|
Chris@0
|
27 # print "Content-type: image/svg+xml\r\n\r\n"
|
Chris@0
|
28 # print graph.burn
|
Chris@0
|
29 #
|
Chris@0
|
30 # = Description
|
Chris@0
|
31 #
|
Chris@0
|
32 # This object aims to allow you to easily create high quality
|
Chris@0
|
33 # SVG[http://www.w3c.org/tr/svg bar graphs. You can either use the default
|
Chris@0
|
34 # style sheet or supply your own. Either way there are many options which
|
Chris@0
|
35 # can be configured to give you control over how the graph is generated -
|
Chris@0
|
36 # with or without a key, data elements at each point, title, subtitle etc.
|
Chris@0
|
37 #
|
Chris@0
|
38 # = Notes
|
Chris@0
|
39 #
|
Chris@0
|
40 # The default stylesheet handles upto 12 data sets, if you
|
Chris@0
|
41 # use more you must create your own stylesheet and add the
|
Chris@0
|
42 # additional settings for the extra data sets. You will know
|
Chris@0
|
43 # if you go over 12 data sets as they will have no style and
|
Chris@0
|
44 # be in black.
|
Chris@0
|
45 #
|
Chris@0
|
46 # = Examples
|
Chris@0
|
47 #
|
Chris@0
|
48 # * http://germane-software.com/repositories/public/SVG/test/test.rb
|
Chris@0
|
49 #
|
Chris@0
|
50 # = See also
|
Chris@0
|
51 #
|
Chris@0
|
52 # * SVG::Graph::Graph
|
Chris@0
|
53 # * SVG::Graph::BarHorizontal
|
Chris@0
|
54 # * SVG::Graph::Line
|
Chris@0
|
55 # * SVG::Graph::Pie
|
Chris@0
|
56 # * SVG::Graph::Plot
|
Chris@0
|
57 # * SVG::Graph::TimeSeries
|
Chris@0
|
58 class Bar < BarBase
|
Chris@0
|
59 include REXML
|
Chris@0
|
60
|
Chris@0
|
61 # See Graph::initialize and BarBase::set_defaults
|
Chris@0
|
62 def set_defaults
|
Chris@0
|
63 super
|
Chris@0
|
64 self.top_align = self.top_font = 1
|
Chris@0
|
65 end
|
Chris@0
|
66
|
Chris@0
|
67 protected
|
Chris@0
|
68
|
Chris@0
|
69 def get_x_labels
|
Chris@0
|
70 @config[:fields]
|
Chris@0
|
71 end
|
Chris@0
|
72
|
Chris@0
|
73 def get_y_labels
|
Chris@0
|
74 maxvalue = max_value
|
Chris@0
|
75 minvalue = min_value
|
Chris@0
|
76 range = maxvalue - minvalue
|
Chris@0
|
77
|
Chris@0
|
78 top_pad = range == 0 ? 10 : range / 20.0
|
Chris@0
|
79 scale_range = (maxvalue + top_pad) - minvalue
|
Chris@0
|
80
|
Chris@0
|
81 scale_division = scale_divisions || (scale_range / 10.0)
|
Chris@0
|
82
|
Chris@0
|
83 if scale_integers
|
Chris@0
|
84 scale_division = scale_division < 1 ? 1 : scale_division.round
|
Chris@0
|
85 end
|
Chris@0
|
86
|
Chris@0
|
87 rv = []
|
Chris@0
|
88 maxvalue = maxvalue%scale_division == 0 ?
|
Chris@0
|
89 maxvalue : maxvalue + scale_division
|
Chris@0
|
90 minvalue.step( maxvalue, scale_division ) {|v| rv << v}
|
Chris@0
|
91 return rv
|
Chris@0
|
92 end
|
Chris@0
|
93
|
Chris@0
|
94 def x_label_offset( width )
|
Chris@0
|
95 width / 2.0
|
Chris@0
|
96 end
|
Chris@0
|
97
|
Chris@0
|
98 def draw_data
|
Chris@0
|
99 minvalue = min_value
|
Chris@0
|
100 fieldwidth = field_width
|
Chris@0
|
101
|
Chris@0
|
102 unit_size = (@graph_height.to_f - font_size*2*top_font) /
|
Chris@0
|
103 (get_y_labels.max - get_y_labels.min)
|
Chris@0
|
104 bargap = bar_gap ? (fieldwidth < 10 ? fieldwidth / 2 : 10) : 0
|
Chris@0
|
105
|
Chris@0
|
106 bar_width = fieldwidth - bargap
|
Chris@0
|
107 bar_width /= @data.length if stack == :side
|
Chris@0
|
108 x_mod = (@graph_width-bargap)/2 - (stack==:side ? bar_width/2 : 0)
|
Chris@0
|
109
|
Chris@0
|
110 bottom = @graph_height
|
Chris@0
|
111
|
Chris@0
|
112 field_count = 0
|
Chris@0
|
113 @config[:fields].each_index { |i|
|
Chris@0
|
114 dataset_count = 0
|
Chris@0
|
115 for dataset in @data
|
Chris@0
|
116
|
Chris@0
|
117 # cases (assume 0 = +ve):
|
Chris@0
|
118 # value min length
|
Chris@0
|
119 # +ve +ve value - min
|
Chris@0
|
120 # +ve -ve value - 0
|
Chris@0
|
121 # -ve -ve value.abs - 0
|
Chris@0
|
122
|
Chris@0
|
123 value = dataset[:data][i]
|
Chris@0
|
124
|
Chris@0
|
125 left = (fieldwidth * field_count)
|
Chris@0
|
126
|
Chris@0
|
127 length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
|
Chris@0
|
128 # top is 0 if value is negative
|
Chris@0
|
129 top = bottom - (((value < 0 ? 0 : value) - minvalue) * unit_size)
|
Chris@0
|
130 left += bar_width * dataset_count if stack == :side
|
Chris@0
|
131
|
Chris@0
|
132 @graph.add_element( "rect", {
|
Chris@0
|
133 "x" => left.to_s,
|
Chris@0
|
134 "y" => top.to_s,
|
Chris@0
|
135 "width" => bar_width.to_s,
|
Chris@0
|
136 "height" => length.to_s,
|
Chris@0
|
137 "class" => "fill#{dataset_count+1}"
|
Chris@0
|
138 })
|
Chris@0
|
139
|
Chris@0
|
140 make_datapoint_text(left + bar_width/2.0, top - 6, value.to_s)
|
Chris@0
|
141 dataset_count += 1
|
Chris@0
|
142 end
|
Chris@0
|
143 field_count += 1
|
Chris@0
|
144 }
|
Chris@0
|
145 end
|
Chris@0
|
146 end
|
Chris@0
|
147 end
|
Chris@0
|
148 end
|