svg-graph 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ $:.unshift("./lib")
6
+ require 'svggraph'
7
+ Hoe.spec 'svg-graph' do |p|
8
+ p.version=SVG::Graph::VERSION
9
+ p.developer('Sean Russell', 'ser_AT_germane-software.com')
10
+ p.developer('Claudio Bustos', 'clbustos_AT_gmail.com')
11
+ p.rubyforge_name = 'ruby-statsample' # if different than 'svg_graph'
12
+ p.remote_rdoc_dir = 'svg-graph'
13
+ end
14
+
15
+ # vim: syntax=ruby
@@ -0,0 +1,148 @@
1
+ require 'rexml/document'
2
+ require 'SVG/Graph/Graph'
3
+ require 'SVG/Graph/BarBase'
4
+
5
+ module SVG
6
+ module Graph
7
+ # === Create presentation quality SVG bar graphs easily
8
+ #
9
+ # = Synopsis
10
+ #
11
+ # require 'SVG/Graph/Bar'
12
+ #
13
+ # fields = %w(Jan Feb Mar);
14
+ # data_sales_02 = [12, 45, 21]
15
+ #
16
+ # graph = SVG::Graph::Bar.new(
17
+ # :height => 500,
18
+ # :width => 300,
19
+ # :fields => fields
20
+ # )
21
+ #
22
+ # graph.add_data(
23
+ # :data => data_sales_02,
24
+ # :title => 'Sales 2002'
25
+ # )
26
+ #
27
+ # print "Content-type: image/svg+xml\r\n\r\n"
28
+ # print graph.burn
29
+ #
30
+ # = Description
31
+ #
32
+ # This object aims to allow you to easily create high quality
33
+ # SVG[http://www.w3c.org/tr/svg bar graphs. You can either use the default
34
+ # style sheet or supply your own. Either way there are many options which
35
+ # can be configured to give you control over how the graph is generated -
36
+ # with or without a key, data elements at each point, title, subtitle etc.
37
+ #
38
+ # = Notes
39
+ #
40
+ # The default stylesheet handles upto 12 data sets, if you
41
+ # use more you must create your own stylesheet and add the
42
+ # additional settings for the extra data sets. You will know
43
+ # if you go over 12 data sets as they will have no style and
44
+ # be in black.
45
+ #
46
+ # = Examples
47
+ #
48
+ # * http://germane-software.com/repositories/public/SVG/test/test.rb
49
+ #
50
+ # = See also
51
+ #
52
+ # * SVG::Graph::Graph
53
+ # * SVG::Graph::BarHorizontal
54
+ # * SVG::Graph::Line
55
+ # * SVG::Graph::Pie
56
+ # * SVG::Graph::Plot
57
+ # * SVG::Graph::TimeSeries
58
+ class Bar < BarBase
59
+ include REXML
60
+
61
+ # See Graph::initialize and BarBase::set_defaults
62
+ def set_defaults
63
+ super
64
+ self.top_align = self.top_font = 1
65
+ end
66
+
67
+ protected
68
+
69
+ def get_x_labels
70
+ @config[:fields]
71
+ end
72
+
73
+ def get_y_labels
74
+ maxvalue = max_value
75
+ minvalue = min_value
76
+ range = maxvalue - minvalue
77
+
78
+ top_pad = range == 0 ? 10 : range / 20.0
79
+ scale_range = (maxvalue + top_pad) - minvalue
80
+
81
+ scale_division = scale_divisions || (scale_range / 10.0)
82
+
83
+ if scale_integers
84
+ scale_division = scale_division < 1 ? 1 : scale_division.round
85
+ end
86
+
87
+ rv = []
88
+ maxvalue = maxvalue%scale_division == 0 ?
89
+ maxvalue : maxvalue + scale_division
90
+ minvalue.step( maxvalue, scale_division ) {|v| rv << v}
91
+ return rv
92
+ end
93
+
94
+ def x_label_offset( width )
95
+ width / 2.0
96
+ end
97
+
98
+ def draw_data
99
+ minvalue = min_value
100
+ fieldwidth = field_width
101
+
102
+ unit_size = (@graph_height.to_f - font_size*2*top_font) /
103
+ (get_y_labels.max - get_y_labels.min)
104
+ bargap = bar_gap ? (fieldwidth < 10 ? fieldwidth / 2 : 10) : 0
105
+
106
+ bar_width = fieldwidth - bargap
107
+ bar_width /= @data.length if stack == :side
108
+ x_mod = (@graph_width-bargap)/2 - (stack==:side ? bar_width/2 : 0)
109
+
110
+ bottom = @graph_height
111
+
112
+ field_count = 0
113
+ @config[:fields].each_index { |i|
114
+ dataset_count = 0
115
+ for dataset in @data
116
+
117
+ # cases (assume 0 = +ve):
118
+ # value min length
119
+ # +ve +ve value - min
120
+ # +ve -ve value - 0
121
+ # -ve -ve value.abs - 0
122
+
123
+ value = dataset[:data][i]
124
+
125
+ left = (fieldwidth * field_count)
126
+
127
+ length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
128
+ # top is 0 if value is negative
129
+ top = bottom - (((value < 0 ? 0 : value) - minvalue) * unit_size)
130
+ left += bar_width * dataset_count if stack == :side
131
+
132
+ @graph.add_element( "rect", {
133
+ "x" => left.to_s,
134
+ "y" => top.to_s,
135
+ "width" => bar_width.to_s,
136
+ "height" => length.to_s,
137
+ "class" => "fill#{dataset_count+1}"
138
+ })
139
+
140
+ make_datapoint_text(left + bar_width/2.0, top - 6, value.to_s)
141
+ dataset_count += 1
142
+ end
143
+ field_count += 1
144
+ }
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,139 @@
1
+ require 'rexml/document'
2
+ require 'SVG/Graph/Graph'
3
+
4
+ module SVG
5
+ module Graph
6
+ # = Synopsis
7
+ #
8
+ # A superclass for bar-style graphs. Do not attempt to instantiate
9
+ # directly; use one of the subclasses instead.
10
+ #
11
+ # = Author
12
+ #
13
+ # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
14
+ #
15
+ # Copyright 2004 Sean E. Russell
16
+ # This software is available under the Ruby license[LICENSE.txt]
17
+ #
18
+ class BarBase < SVG::Graph::Graph
19
+ # Ensures that :fields are provided in the configuration.
20
+ def initialize config
21
+ raise "fields was not supplied or is empty" unless config[:fields] &&
22
+ config[:fields].kind_of?(Array) &&
23
+ config[:fields].length > 0
24
+ super
25
+ end
26
+
27
+ # In addition to the defaults set in Graph::initialize, sets
28
+ # [bar_gap] true
29
+ # [stack] :overlap
30
+ def set_defaults
31
+ init_with( :bar_gap => true, :stack => :overlap )
32
+ end
33
+
34
+ # Whether to have a gap between the bars or not, default
35
+ # is true, set to false if you don't want gaps.
36
+ attr_accessor :bar_gap
37
+ # How to stack data sets. :overlap overlaps bars with
38
+ # transparent colors, :top stacks bars on top of one another,
39
+ # :side stacks the bars side-by-side. Defaults to :overlap.
40
+ attr_accessor :stack
41
+
42
+
43
+ protected
44
+
45
+ def max_value
46
+ @data.collect{|x| x[:data].max}.max
47
+ end
48
+
49
+ def min_value
50
+ min = 0
51
+ if min_scale_value.nil?
52
+ min = @data.collect{|x| x[:data].min}.min
53
+ min = min > 0 ? 0 : min
54
+ else
55
+ min = min_scale_value
56
+ end
57
+ return min
58
+ end
59
+
60
+ def get_css
61
+ return <<EOL
62
+ /* default fill styles for multiple datasets (probably only use a single dataset on this graph though) */
63
+ .key1,.fill1{
64
+ fill: #ff0000;
65
+ fill-opacity: 0.5;
66
+ stroke: none;
67
+ stroke-width: 0.5px;
68
+ }
69
+ .key2,.fill2{
70
+ fill: #0000ff;
71
+ fill-opacity: 0.5;
72
+ stroke: none;
73
+ stroke-width: 1px;
74
+ }
75
+ .key3,.fill3{
76
+ fill: #00ff00;
77
+ fill-opacity: 0.5;
78
+ stroke: none;
79
+ stroke-width: 1px;
80
+ }
81
+ .key4,.fill4{
82
+ fill: #ffcc00;
83
+ fill-opacity: 0.5;
84
+ stroke: none;
85
+ stroke-width: 1px;
86
+ }
87
+ .key5,.fill5{
88
+ fill: #00ccff;
89
+ fill-opacity: 0.5;
90
+ stroke: none;
91
+ stroke-width: 1px;
92
+ }
93
+ .key6,.fill6{
94
+ fill: #ff00ff;
95
+ fill-opacity: 0.5;
96
+ stroke: none;
97
+ stroke-width: 1px;
98
+ }
99
+ .key7,.fill7{
100
+ fill: #00ffff;
101
+ fill-opacity: 0.5;
102
+ stroke: none;
103
+ stroke-width: 1px;
104
+ }
105
+ .key8,.fill8{
106
+ fill: #ffff00;
107
+ fill-opacity: 0.5;
108
+ stroke: none;
109
+ stroke-width: 1px;
110
+ }
111
+ .key9,.fill9{
112
+ fill: #cc6666;
113
+ fill-opacity: 0.5;
114
+ stroke: none;
115
+ stroke-width: 1px;
116
+ }
117
+ .key10,.fill10{
118
+ fill: #663399;
119
+ fill-opacity: 0.5;
120
+ stroke: none;
121
+ stroke-width: 1px;
122
+ }
123
+ .key11,.fill11{
124
+ fill: #339900;
125
+ fill-opacity: 0.5;
126
+ stroke: none;
127
+ stroke-width: 1px;
128
+ }
129
+ .key12,.fill12{
130
+ fill: #9966FF;
131
+ fill-opacity: 0.5;
132
+ stroke: none;
133
+ stroke-width: 1px;
134
+ }
135
+ EOL
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,149 @@
1
+ require 'rexml/document'
2
+ require 'SVG/Graph/BarBase'
3
+
4
+ module SVG
5
+ module Graph
6
+ # === Create presentation quality SVG horitonzal bar graphs easily
7
+ #
8
+ # = Synopsis
9
+ #
10
+ # require 'SVG/Graph/BarHorizontal'
11
+ #
12
+ # fields = %w(Jan Feb Mar)
13
+ # data_sales_02 = [12, 45, 21]
14
+ #
15
+ # graph = SVG::Graph::BarHorizontal.new({
16
+ # :height => 500,
17
+ # :width => 300,
18
+ # :fields => fields,
19
+ # })
20
+ #
21
+ # graph.add_data({
22
+ # :data => data_sales_02,
23
+ # :title => 'Sales 2002',
24
+ # })
25
+ #
26
+ # print "Content-type: image/svg+xml\r\n\r\n"
27
+ # print graph.burn
28
+ #
29
+ # = Description
30
+ #
31
+ # This object aims to allow you to easily create high quality
32
+ # SVG horitonzal bar graphs. You can either use the default style sheet
33
+ # or supply your own. Either way there are many options which can
34
+ # be configured to give you control over how the graph is
35
+ # generated - with or without a key, data elements at each point,
36
+ # title, subtitle etc.
37
+ #
38
+ # = Examples
39
+ #
40
+ # * http://germane-software.com/repositories/public/SVG/test/test.rb
41
+ #
42
+ # = See also
43
+ #
44
+ # * SVG::Graph::Graph
45
+ # * SVG::Graph::Bar
46
+ # * SVG::Graph::Line
47
+ # * SVG::Graph::Pie
48
+ # * SVG::Graph::Plot
49
+ # * SVG::Graph::TimeSeries
50
+ #
51
+ # == Author
52
+ #
53
+ # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
54
+ #
55
+ # Copyright 2004 Sean E. Russell
56
+ # This software is available under the Ruby license[LICENSE.txt]
57
+ #
58
+ class BarHorizontal < BarBase
59
+ # In addition to the defaults set in BarBase::set_defaults, sets
60
+ # [rotate_y_labels] true
61
+ # [show_x_guidelines] true
62
+ # [show_y_guidelines] false
63
+ def set_defaults
64
+ super
65
+ init_with(
66
+ :rotate_y_labels => true,
67
+ :show_x_guidelines => true,
68
+ :show_y_guidelines => false
69
+ )
70
+ self.right_align = self.right_font = 1
71
+ end
72
+
73
+ protected
74
+
75
+ def get_x_labels
76
+ maxvalue = max_value
77
+ minvalue = min_value
78
+ range = maxvalue - minvalue
79
+ top_pad = range == 0 ? 10 : range / 20.0
80
+ scale_range = (maxvalue + top_pad) - minvalue
81
+
82
+ scale_division = scale_divisions || (scale_range / 10.0)
83
+
84
+ if scale_integers
85
+ scale_division = scale_division < 1 ? 1 : scale_division.round
86
+ end
87
+
88
+ rv = []
89
+ maxvalue = maxvalue%scale_division == 0 ?
90
+ maxvalue : maxvalue + scale_division
91
+ minvalue.step( maxvalue, scale_division ) {|v| rv << v}
92
+ return rv
93
+ end
94
+
95
+ def get_y_labels
96
+ @config[:fields]
97
+ end
98
+
99
+ def y_label_offset( height )
100
+ height / -2.0
101
+ end
102
+
103
+ def draw_data
104
+ minvalue = min_value
105
+ fieldheight = field_height
106
+
107
+ unit_size = (@graph_width.to_f - font_size*2*right_font ) /
108
+ (get_x_labels.max - get_x_labels.min )
109
+ bargap = bar_gap ? (fieldheight < 10 ? fieldheight / 2 : 10) : 0
110
+
111
+ bar_height = fieldheight - bargap
112
+ bar_height /= @data.length if stack == :side
113
+ y_mod = (bar_height / 2) + (font_size / 2)
114
+
115
+ field_count = 1
116
+ @config[:fields].each_index { |i|
117
+ dataset_count = 0
118
+ for dataset in @data
119
+ value = dataset[:data][i]
120
+
121
+ top = @graph_height - (fieldheight * field_count)
122
+ top += (bar_height * dataset_count) if stack == :side
123
+ # cases (assume 0 = +ve):
124
+ # value min length left
125
+ # +ve +ve value.abs - min minvalue.abs
126
+ # +ve -ve value.abs - 0 minvalue.abs
127
+ # -ve -ve value.abs - 0 minvalue.abs + value
128
+ length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
129
+ left = (minvalue.abs + (value < 0 ? value : 0)) * unit_size
130
+
131
+ @graph.add_element( "rect", {
132
+ "x" => left.to_s,
133
+ "y" => top.to_s,
134
+ "width" => length.to_s,
135
+ "height" => bar_height.to_s,
136
+ "class" => "fill#{dataset_count+1}"
137
+ })
138
+
139
+ make_datapoint_text(
140
+ left+length+5, top+y_mod, value, "text-anchor: start; "
141
+ )
142
+ dataset_count += 1
143
+ end
144
+ field_count += 1
145
+ }
146
+ end
147
+ end
148
+ end
149
+ end