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