svg-graph19 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -1,4 +1,14 @@
1
1
  SVG::Graph19
2
2
  ============
3
3
 
4
- This is a minor revision of the SVG::Graph library by Sean Russell with few minor touch-upd to make it run on Ruby 1.9.x and to have it gem-installable.
4
+ Description
5
+ -----------
6
+ This is a minor revision of the [SVG::Graph library](http://www.germane-software.com/software/SVG/SVG::Graph/) by Sean Russell with few minor touch-ups to make it run on Ruby 1.9.x and to have it [gem-installable](http://gemcutter.org/gems/svg-graph19).
7
+
8
+ Warning
9
+ -------
10
+ I'm not sure that all the parts of the original SVG library work as expected under 1.9.x too. Please notify me (via github messages or on the Issues section) if you find any bug.
11
+
12
+ Usage
13
+ -----
14
+ Yet to be written. Look at the original [SVG::Graph web page](http://www.germane-software.com/software/SVG/SVG::Graph/) for the moment (I'm not introducing new methods nor changing APIs, for the moment).
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: svg-graph19
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Russell. Paolo Bosetti moved into gem and made 1.9-compatible
@@ -9,11 +9,15 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-01 00:00:00 +01:00
12
+ date: 2009-12-02 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
16
- description: THIS VERSION IS RUBY 1.9.x COMPATIBLE! Gem version of SVG:::Graph. SVG:::Graph is a pure Ruby library for generating charts, which are a type of graph where the values of one axis are not scalar. SVG::Graph has a verry similar API to the Perl library SVG::TT::Graph, and the resulting charts also look the same. This isn't surprising, because SVG::Graph started as a loose port of SVG::TT::Graph, although the internal code no longer resembles the Perl original at all.
16
+ description: |
17
+ THIS VERSION IS RUBY 1.9.x COMPATIBLE!
18
+ Gem version of SVG:::Graph. SVG:::Graph is a pure Ruby library for generating charts, which are a type of graph where the values of one axis are not scalar.
19
+ SVG::Graph has a very similar API to the Perl library SVG::TT::Graph, and the resulting charts also look the same. This isn't surprising, because SVG::Graph started as a loose port of SVG::TT::Graph, although the internal code no longer resembles the Perl original at all.
20
+
17
21
  email: paolo.bosetti@me.com
18
22
  executables: []
19
23
 
@@ -23,15 +27,6 @@ extra_rdoc_files: []
23
27
 
24
28
  files:
25
29
  - README.markdown
26
- - lib/SVG/Graph/bar.rb
27
- - lib/SVG/Graph/BarBase.rb
28
- - lib/SVG/Graph/BarHorizontal.rb
29
- - lib/SVG/Graph/Graph.rb
30
- - lib/SVG/Graph/Line.rb
31
- - lib/SVG/Graph/Pie.rb
32
- - lib/SVG/Graph/Plot.rb
33
- - lib/SVG/Graph/Schedule.rb
34
- - lib/SVG/Graph/TimeSeries.rb
35
30
  has_rdoc: true
36
31
  homepage: http://github.com/pbosetti/svg-graph19
37
32
  licenses: []
@@ -1,139 +0,0 @@
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
@@ -1,149 +0,0 @@
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
@@ -1,978 +0,0 @@
1
- begin
2
- require 'zlib'
3
- @@__have_zlib = true
4
- rescue
5
- @@__have_zlib = false
6
- end
7
-
8
- require 'rexml/document'
9
-
10
- module SVG
11
- module Graph
12
- VERSION = '@ANT_VERSION@'
13
-
14
- # === Base object for generating SVG Graphs
15
- #
16
- # == Synopsis
17
- #
18
- # This class is only used as a superclass of specialized charts. Do not
19
- # attempt to use this class directly, unless creating a new chart type.
20
- #
21
- # For examples of how to subclass this class, see the existing specific
22
- # subclasses, such as SVG::Graph::Pie.
23
- #
24
- # == Examples
25
- #
26
- # For examples of how to use this package, see either the test files, or
27
- # the documentation for the specific class you want to use.
28
- #
29
- # * file:test/plot.rb
30
- # * file:test/single.rb
31
- # * file:test/test.rb
32
- # * file:test/timeseries.rb
33
- #
34
- # == Description
35
- #
36
- # This package should be used as a base for creating SVG graphs.
37
- #
38
- # == Acknowledgements
39
- #
40
- # Leo Lapworth for creating the SVG::TT::Graph package which this Ruby
41
- # port is based on.
42
- #
43
- # Stephen Morgan for creating the TT template and SVG.
44
- #
45
- # == See
46
- #
47
- # * SVG::Graph::BarHorizontal
48
- # * SVG::Graph::Bar
49
- # * SVG::Graph::Line
50
- # * SVG::Graph::Pie
51
- # * SVG::Graph::Plot
52
- # * SVG::Graph::TimeSeries
53
- #
54
- # == Author
55
- #
56
- # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
57
- #
58
- # Copyright 2004 Sean E. Russell
59
- # This software is available under the Ruby license[LICENSE.txt]
60
- #
61
- class Graph
62
- include REXML
63
-
64
- # Initialize the graph object with the graph settings. You won't
65
- # instantiate this class directly; see the subclass for options.
66
- # [width] 500
67
- # [height] 300
68
- # [show_x_guidelines] false
69
- # [show_y_guidelines] true
70
- # [show_data_values] true
71
- # [min_scale_value] 0
72
- # [show_x_labels] true
73
- # [stagger_x_labels] false
74
- # [rotate_x_labels] false
75
- # [step_x_labels] 1
76
- # [step_include_first_x_label] true
77
- # [show_y_labels] true
78
- # [rotate_y_labels] false
79
- # [scale_integers] false
80
- # [show_x_title] false
81
- # [x_title] 'X Field names'
82
- # [show_y_title] false
83
- # [y_title_text_direction] :bt
84
- # [y_title] 'Y Scale'
85
- # [show_graph_title] false
86
- # [graph_title] 'Graph Title'
87
- # [show_graph_subtitle] false
88
- # [graph_subtitle] 'Graph Sub Title'
89
- # [key] true,
90
- # [key_position] :right, # bottom or righ
91
- # [font_size] 12
92
- # [title_font_size] 16
93
- # [subtitle_font_size] 14
94
- # [x_label_font_size] 12
95
- # [x_title_font_size] 14
96
- # [y_label_font_size] 12
97
- # [y_title_font_size] 14
98
- # [key_font_size] 10
99
- # [no_css] false
100
- # [add_popups] false
101
- def initialize( config )
102
- @config = config
103
-
104
- self.top_align = self.top_font = self.right_align = self.right_font = 0
105
-
106
- init_with({
107
- :width => 500,
108
- :height => 300,
109
- :show_x_guidelines => false,
110
- :show_y_guidelines => true,
111
- :show_data_values => true,
112
-
113
- # :min_scale_value => 0,
114
-
115
- :show_x_labels => true,
116
- :stagger_x_labels => false,
117
- :rotate_x_labels => false,
118
- :step_x_labels => 1,
119
- :step_include_first_x_label => true,
120
-
121
- :show_y_labels => true,
122
- :rotate_y_labels => false,
123
- :stagger_y_labels => false,
124
- :scale_integers => false,
125
-
126
- :show_x_title => false,
127
- :x_title => 'X Field names',
128
-
129
- :show_y_title => false,
130
- :y_title_text_direction => :bt,
131
- :y_title => 'Y Scale',
132
-
133
- :show_graph_title => false,
134
- :graph_title => 'Graph Title',
135
- :show_graph_subtitle => false,
136
- :graph_subtitle => 'Graph Sub Title',
137
- :key => true,
138
- :key_position => :right, # bottom or right
139
-
140
- :font_size =>12,
141
- :title_font_size =>16,
142
- :subtitle_font_size =>14,
143
- :x_label_font_size =>12,
144
- :x_title_font_size =>14,
145
- :y_label_font_size =>12,
146
- :y_title_font_size =>14,
147
- :key_font_size =>10,
148
-
149
- :no_css =>false,
150
- :add_popups =>false,
151
- })
152
-
153
- set_defaults if methods.include? "set_defaults"
154
-
155
- init_with config
156
- end
157
-
158
-
159
- # This method allows you do add data to the graph object.
160
- # It can be called several times to add more data sets in.
161
- #
162
- # data_sales_02 = [12, 45, 21];
163
- #
164
- # graph.add_data({
165
- # :data => data_sales_02,
166
- # :title => 'Sales 2002'
167
- # })
168
- def add_data conf
169
- @data = [] unless defined? @data
170
-
171
- if conf[:data] and conf[:data].kind_of? Array
172
- @data << conf
173
- else
174
- raise "No data provided by #{conf.inspect}"
175
- end
176
- end
177
-
178
-
179
- # This method removes all data from the object so that you can
180
- # reuse it to create a new graph but with the same config options.
181
- #
182
- # graph.clear_data
183
- def clear_data
184
- @data = []
185
- end
186
-
187
-
188
- # This method processes the template with the data and
189
- # config which has been set and returns the resulting SVG.
190
- #
191
- # This method will croak unless at least one data set has
192
- # been added to the graph object.
193
- #
194
- # print graph.burn
195
- def burn
196
- raise "No data available" unless @data.size > 0
197
-
198
- calculations if methods.include? 'calculations'
199
-
200
- start_svg
201
- calculate_graph_dimensions
202
- @foreground = Element.new( "g" )
203
- draw_graph
204
- draw_titles
205
- draw_legend
206
- draw_data
207
- @graph.add_element( @foreground )
208
- style
209
-
210
- data = ""
211
- @doc.write( data, 0 )
212
-
213
- if @config[:compress]
214
- if @@__have_zlib
215
- inp, out = IO.pipe
216
- gz = Zlib::GzipWriter.new( out )
217
- gz.write data
218
- gz.close
219
- data = inp.read
220
- else
221
- data << "<!-- Ruby Zlib not available for SVGZ -->";
222
- end
223
- end
224
-
225
- return data
226
- end
227
-
228
-
229
- # Set the height of the graph box, this is the total height
230
- # of the SVG box created - not the graph it self which auto
231
- # scales to fix the space.
232
- attr_accessor :height
233
- # Set the width of the graph box, this is the total width
234
- # of the SVG box created - not the graph it self which auto
235
- # scales to fix the space.
236
- attr_accessor :width
237
- # Set the path to an external stylesheet, set to '' if
238
- # you want to revert back to using the defaut internal version.
239
- #
240
- # To create an external stylesheet create a graph using the
241
- # default internal version and copy the stylesheet section to
242
- # an external file and edit from there.
243
- attr_accessor :style_sheet
244
- # (Bool) Show the value of each element of data on the graph
245
- attr_accessor :show_data_values
246
- # The point at which the Y axis starts, defaults to '0',
247
- # if set to nil it will default to the minimum data value.
248
- attr_accessor :min_scale_value
249
- # Whether to show labels on the X axis or not, defaults
250
- # to true, set to false if you want to turn them off.
251
- attr_accessor :show_x_labels
252
- # This puts the X labels at alternative levels so if they
253
- # are long field names they will not overlap so easily.
254
- # Default it false, to turn on set to true.
255
- attr_accessor :stagger_x_labels
256
- # This puts the Y labels at alternative levels so if they
257
- # are long field names they will not overlap so easily.
258
- # Default it false, to turn on set to true.
259
- attr_accessor :stagger_y_labels
260
- # This turns the X axis labels by 90 degrees.
261
- # Default it false, to turn on set to true.
262
- attr_accessor :rotate_x_labels
263
- # This turns the Y axis labels by 90 degrees.
264
- # Default it false, to turn on set to true.
265
- attr_accessor :rotate_y_labels
266
- # How many "steps" to use between displayed X axis labels,
267
- # a step of one means display every label, a step of two results
268
- # in every other label being displayed (label <gap> label <gap> label),
269
- # a step of three results in every third label being displayed
270
- # (label <gap> <gap> label <gap> <gap> label) and so on.
271
- attr_accessor :step_x_labels
272
- # Whether to (when taking "steps" between X axis labels) step from
273
- # the first label (i.e. always include the first label) or step from
274
- # the X axis origin (i.e. start with a gap if step_x_labels is greater
275
- # than one).
276
- attr_accessor :step_include_first_x_label
277
- # Whether to show labels on the Y axis or not, defaults
278
- # to true, set to false if you want to turn them off.
279
- attr_accessor :show_y_labels
280
- # Ensures only whole numbers are used as the scale divisions.
281
- # Default it false, to turn on set to true. This has no effect if
282
- # scale divisions are less than 1.
283
- attr_accessor :scale_integers
284
- # This defines the gap between markers on the Y axis,
285
- # default is a 10th of the max_value, e.g. you will have
286
- # 10 markers on the Y axis. NOTE: do not set this too
287
- # low - you are limited to 999 markers, after that the
288
- # graph won't generate.
289
- attr_accessor :scale_divisions
290
- # Whether to show the title under the X axis labels,
291
- # default is false, set to true to show.
292
- attr_accessor :show_x_title
293
- # What the title under X axis should be, e.g. 'Months'.
294
- attr_accessor :x_title
295
- # Whether to show the title under the Y axis labels,
296
- # default is false, set to true to show.
297
- attr_accessor :show_y_title
298
- # Aligns writing mode for Y axis label.
299
- # Defaults to :bt (Bottom to Top).
300
- # Change to :tb (Top to Bottom) to reverse.
301
- attr_accessor :y_title_text_direction
302
- # What the title under Y axis should be, e.g. 'Sales in thousands'.
303
- attr_accessor :y_title
304
- # Whether to show a title on the graph, defaults
305
- # to false, set to true to show.
306
- attr_accessor :show_graph_title
307
- # What the title on the graph should be.
308
- attr_accessor :graph_title
309
- # Whether to show a subtitle on the graph, defaults
310
- # to false, set to true to show.
311
- attr_accessor :show_graph_subtitle
312
- # What the subtitle on the graph should be.
313
- attr_accessor :graph_subtitle
314
- # Whether to show a key, defaults to false, set to
315
- # true if you want to show it.
316
- attr_accessor :key
317
- # Where the key should be positioned, defaults to
318
- # :right, set to :bottom if you want to move it.
319
- attr_accessor :key_position
320
- # Set the font size (in points) of the data point labels
321
- attr_accessor :font_size
322
- # Set the font size of the X axis labels
323
- attr_accessor :x_label_font_size
324
- # Set the font size of the X axis title
325
- attr_accessor :x_title_font_size
326
- # Set the font size of the Y axis labels
327
- attr_accessor :y_label_font_size
328
- # Set the font size of the Y axis title
329
- attr_accessor :y_title_font_size
330
- # Set the title font size
331
- attr_accessor :title_font_size
332
- # Set the subtitle font size
333
- attr_accessor :subtitle_font_size
334
- # Set the key font size
335
- attr_accessor :key_font_size
336
- # Show guidelines for the X axis
337
- attr_accessor :show_x_guidelines
338
- # Show guidelines for the Y axis
339
- attr_accessor :show_y_guidelines
340
- # Do not use CSS if set to true. Many SVG viewers do not support CSS, but
341
- # not using CSS can result in larger SVGs as well as making it impossible to
342
- # change colors after the chart is generated. Defaults to false.
343
- attr_accessor :no_css
344
- # Add popups for the data points on some graphs
345
- attr_accessor :add_popups
346
-
347
-
348
- protected
349
-
350
- def sort( *arrys )
351
- sort_multiple( arrys )
352
- end
353
-
354
- # Overwrite configuration options with supplied options. Used
355
- # by subclasses.
356
- def init_with config
357
- config.each { |key, value|
358
- self.send( key.to_s+"=", value ) if methods.include? key.to_s
359
- }
360
- end
361
-
362
- attr_accessor :top_align, :top_font, :right_align, :right_font
363
-
364
- KEY_BOX_SIZE = 12
365
-
366
- # Override this (and call super) to change the margin to the left
367
- # of the plot area. Results in @border_left being set.
368
- def calculate_left_margin
369
- @border_left = 7
370
- # Check for Y labels
371
- max_y_label_height_px = rotate_y_labels ?
372
- y_label_font_size :
373
- get_y_labels.max{|a,b|
374
- a.to_s.length<=>b.to_s.length
375
- }.to_s.length * y_label_font_size * 0.6
376
- @border_left += max_y_label_height_px if show_y_labels
377
- @border_left += max_y_label_height_px + 10 if stagger_y_labels
378
- @border_left += y_title_font_size + 5 if show_y_title
379
- end
380
-
381
-
382
- # Calculates the width of the widest Y label. This will be the
383
- # character height if the Y labels are rotated
384
- def max_y_label_width_px
385
- return font_size if rotate_y_labels
386
- end
387
-
388
-
389
- # Override this (and call super) to change the margin to the right
390
- # of the plot area. Results in @border_right being set.
391
- def calculate_right_margin
392
- @border_right = 7
393
- if key and key_position == :right
394
- val = keys.max { |a,b| a.length <=> b.length }
395
- @border_right += val.length * key_font_size * 0.6
396
- @border_right += KEY_BOX_SIZE
397
- @border_right += 10 # Some padding around the box
398
- end
399
- end
400
-
401
-
402
- # Override this (and call super) to change the margin to the top
403
- # of the plot area. Results in @border_top being set.
404
- def calculate_top_margin
405
- @border_top = 5
406
- @border_top += title_font_size if show_graph_title
407
- @border_top += 5
408
- @border_top += subtitle_font_size if show_graph_subtitle
409
- end
410
-
411
-
412
- # Adds pop-up point information to a graph.
413
- def add_popup( x, y, label )
414
- txt_width = label.length * font_size * 0.6 + 10
415
- tx = (x+txt_width > width ? x-5 : x+5)
416
- t = @foreground.add_element( "text", {
417
- "x" => tx.to_s,
418
- "y" => (y - font_size).to_s,
419
- "visibility" => "hidden",
420
- })
421
- t.attributes["style"] = "fill: #000; "+
422
- (x+txt_width > width ? "text-anchor: end;" : "text-anchor: start;")
423
- t.text = label.to_s
424
- t.attributes["id"] = t.object_id.to_s
425
-
426
- @foreground.add_element( "circle", {
427
- "cx" => x.to_s,
428
- "cy" => y.to_s,
429
- "r" => "10",
430
- "style" => "opacity: 0",
431
- "onmouseover" =>
432
- "document.getElementById(#{t.object_id}).setAttribute('visibility', 'visible' )",
433
- "onmouseout" =>
434
- "document.getElementById(#{t.object_id}).setAttribute('visibility', 'hidden' )",
435
- })
436
-
437
- end
438
-
439
-
440
- # Override this (and call super) to change the margin to the bottom
441
- # of the plot area. Results in @border_bottom being set.
442
- def calculate_bottom_margin
443
- @border_bottom = 7
444
- if key and key_position == :bottom
445
- @border_bottom += @data.size * (font_size + 5)
446
- @border_bottom += 10
447
- end
448
- if show_x_labels
449
- max_x_label_height_px = (not rotate_x_labels) ?
450
- x_label_font_size :
451
- get_x_labels.max{|a,b|
452
- a.to_s.length<=>b.to_s.length
453
- }.to_s.length * x_label_font_size * 0.6
454
- @border_bottom += max_x_label_height_px
455
- @border_bottom += max_x_label_height_px + 10 if stagger_x_labels
456
- end
457
- @border_bottom += x_title_font_size + 5 if show_x_title
458
- end
459
-
460
-
461
- # Draws the background, axis, and labels.
462
- def draw_graph
463
- @graph = @root.add_element( "g", {
464
- "transform" => "translate( #@border_left #@border_top )"
465
- })
466
-
467
- # Background
468
- @graph.add_element( "rect", {
469
- "x" => "0",
470
- "y" => "0",
471
- "width" => @graph_width.to_s,
472
- "height" => @graph_height.to_s,
473
- "class" => "graphBackground"
474
- })
475
-
476
- # Axis
477
- @graph.add_element( "path", {
478
- "d" => "M 0 0 v#@graph_height",
479
- "class" => "axis",
480
- "id" => "xAxis"
481
- })
482
- @graph.add_element( "path", {
483
- "d" => "M 0 #@graph_height h#@graph_width",
484
- "class" => "axis",
485
- "id" => "yAxis"
486
- })
487
-
488
- draw_x_labels
489
- draw_y_labels
490
- end
491
-
492
-
493
- # Where in the X area the label is drawn
494
- # Centered in the field, should be width/2. Start, 0.
495
- def x_label_offset( width )
496
- 0
497
- end
498
-
499
- def make_datapoint_text( x, y, value, style="" )
500
- if show_data_values
501
- @foreground.add_element( "text", {
502
- "x" => x.to_s,
503
- "y" => y.to_s,
504
- "class" => "dataPointLabel",
505
- "style" => "#{style} stroke: #fff; stroke-width: 2;"
506
- }).text = value.to_s
507
- text = @foreground.add_element( "text", {
508
- "x" => x.to_s,
509
- "y" => y.to_s,
510
- "class" => "dataPointLabel"
511
- })
512
- text.text = value.to_s
513
- text.attributes["style"] = style if style.length > 0
514
- end
515
- end
516
-
517
-
518
- # Draws the X axis labels
519
- def draw_x_labels
520
- stagger = x_label_font_size + 5
521
- if show_x_labels
522
- label_width = field_width
523
-
524
- count = 0
525
- for label in get_x_labels
526
- if step_include_first_x_label == true then
527
- step = count % step_x_labels
528
- else
529
- step = (count + 1) % step_x_labels
530
- end
531
-
532
- if step == 0 then
533
- text = @graph.add_element( "text" )
534
- text.attributes["class"] = "xAxisLabels"
535
- text.text = label.to_s
536
-
537
- x = count * label_width + x_label_offset( label_width )
538
- y = @graph_height + x_label_font_size + 3
539
- t = 0 - (font_size / 2)
540
-
541
- if stagger_x_labels and count % 2 == 1
542
- y += stagger
543
- @graph.add_element( "path", {
544
- "d" => "M#{x} #@graph_height v#{stagger}",
545
- "class" => "staggerGuideLine"
546
- })
547
- end
548
-
549
- text.attributes["x"] = x.to_s
550
- text.attributes["y"] = y.to_s
551
- if rotate_x_labels
552
- text.attributes["transform"] =
553
- "rotate( 90 #{x} #{y-x_label_font_size} )"+
554
- " translate( 0 -#{x_label_font_size/4} )"
555
- text.attributes["style"] = "text-anchor: start"
556
- else
557
- text.attributes["style"] = "text-anchor: middle"
558
- end
559
- end
560
-
561
- draw_x_guidelines( label_width, count ) if show_x_guidelines
562
- count += 1
563
- end
564
- end
565
- end
566
-
567
-
568
- # Where in the Y area the label is drawn
569
- # Centered in the field, should be width/2. Start, 0.
570
- def y_label_offset( height )
571
- 0
572
- end
573
-
574
-
575
- def field_width
576
- (@graph_width.to_f - font_size*2*right_font) /
577
- (get_x_labels.length - right_align)
578
- end
579
-
580
-
581
- def field_height
582
- (@graph_height.to_f - font_size*2*top_font) /
583
- (get_y_labels.length - top_align)
584
- end
585
-
586
-
587
- # Draws the Y axis labels
588
- def draw_y_labels
589
- stagger = y_label_font_size + 5
590
- if show_y_labels
591
- label_height = field_height
592
-
593
- count = 0
594
- y_offset = @graph_height + y_label_offset( label_height )
595
- y_offset += font_size/1.2 unless rotate_y_labels
596
- for label in get_y_labels
597
- y = y_offset - (label_height * count)
598
- x = rotate_y_labels ? 0 : -3
599
-
600
- if stagger_y_labels and count % 2 == 1
601
- x -= stagger
602
- @graph.add_element( "path", {
603
- "d" => "M#{x} #{y} h#{stagger}",
604
- "class" => "staggerGuideLine"
605
- })
606
- end
607
-
608
- text = @graph.add_element( "text", {
609
- "x" => x.to_s,
610
- "y" => y.to_s,
611
- "class" => "yAxisLabels"
612
- })
613
- text.text = label.to_s
614
- if rotate_y_labels
615
- text.attributes["transform"] = "translate( -#{font_size} 0 ) "+
616
- "rotate( 90 #{x} #{y} ) "
617
- text.attributes["style"] = "text-anchor: middle"
618
- else
619
- text.attributes["y"] = (y - (y_label_font_size/2)).to_s
620
- text.attributes["style"] = "text-anchor: end"
621
- end
622
- draw_y_guidelines( label_height, count ) if show_y_guidelines
623
- count += 1
624
- end
625
- end
626
- end
627
-
628
-
629
- # Draws the X axis guidelines
630
- def draw_x_guidelines( label_height, count )
631
- if count != 0
632
- @graph.add_element( "path", {
633
- "d" => "M#{label_height*count} 0 v#@graph_height",
634
- "class" => "guideLines"
635
- })
636
- end
637
- end
638
-
639
-
640
- # Draws the Y axis guidelines
641
- def draw_y_guidelines( label_height, count )
642
- if count != 0
643
- @graph.add_element( "path", {
644
- "d" => "M0 #{@graph_height-(label_height*count)} h#@graph_width",
645
- "class" => "guideLines"
646
- })
647
- end
648
- end
649
-
650
-
651
- # Draws the graph title and subtitle
652
- def draw_titles
653
- if show_graph_title
654
- @root.add_element( "text", {
655
- "x" => (width / 2).to_s,
656
- "y" => (title_font_size).to_s,
657
- "class" => "mainTitle"
658
- }).text = graph_title.to_s
659
- end
660
-
661
- if show_graph_subtitle
662
- y_subtitle = show_graph_title ?
663
- title_font_size + 10 :
664
- subtitle_font_size
665
- @root.add_element("text", {
666
- "x" => (width / 2).to_s,
667
- "y" => (y_subtitle).to_s,
668
- "class" => "subTitle"
669
- }).text = graph_subtitle.to_s
670
- end
671
-
672
- if show_x_title
673
- y = @graph_height + @border_top + x_title_font_size
674
- if show_x_labels
675
- y += x_label_font_size + 5 if stagger_x_labels
676
- y += x_label_font_size + 5
677
- end
678
- x = width / 2
679
-
680
- @root.add_element("text", {
681
- "x" => x.to_s,
682
- "y" => y.to_s,
683
- "class" => "xAxisTitle",
684
- }).text = x_title.to_s
685
- end
686
-
687
- if show_y_title
688
- x = y_title_font_size + (y_title_text_direction==:bt ? 3 : -3)
689
- y = height / 2
690
-
691
- text = @root.add_element("text", {
692
- "x" => x.to_s,
693
- "y" => y.to_s,
694
- "class" => "yAxisTitle",
695
- })
696
- text.text = y_title.to_s
697
- if y_title_text_direction == :bt
698
- text.attributes["transform"] = "rotate( -90, #{x}, #{y} )"
699
- else
700
- text.attributes["transform"] = "rotate( 90, #{x}, #{y} )"
701
- end
702
- end
703
- end
704
-
705
- def keys
706
- return @data.collect{ |d| d[:title] }
707
- end
708
-
709
- # Draws the legend on the graph
710
- def draw_legend
711
- if key
712
- group = @root.add_element( "g" )
713
-
714
- key_count = 0
715
- for key_name in keys
716
- y_offset = (KEY_BOX_SIZE * key_count) + (key_count * 5)
717
- group.add_element( "rect", {
718
- "x" => 0.to_s,
719
- "y" => y_offset.to_s,
720
- "width" => KEY_BOX_SIZE.to_s,
721
- "height" => KEY_BOX_SIZE.to_s,
722
- "class" => "key#{key_count+1}"
723
- })
724
- group.add_element( "text", {
725
- "x" => (KEY_BOX_SIZE + 5).to_s,
726
- "y" => (y_offset + KEY_BOX_SIZE).to_s,
727
- "class" => "keyText"
728
- }).text = key_name.to_s
729
- key_count += 1
730
- end
731
-
732
- case key_position
733
- when :right
734
- x_offset = @graph_width + @border_left + 10
735
- y_offset = @border_top + 20
736
- when :bottom
737
- x_offset = @border_left + 20
738
- y_offset = @border_top + @graph_height + 5
739
- if show_x_labels
740
- max_x_label_height_px = (not rotate_x_labels) ?
741
- x_label_font_size :
742
- get_x_labels.max{|a,b|
743
- a.to_s.length<=>b.to_s.length
744
- }.to_s.length * x_label_font_size * 0.6
745
- x_label_font_size
746
- y_offset += max_x_label_height_px
747
- y_offset += max_x_label_height_px + 5 if stagger_x_labels
748
- end
749
- y_offset += x_title_font_size + 5 if show_x_title
750
- end
751
- group.attributes["transform"] = "translate(#{x_offset} #{y_offset})"
752
- end
753
- end
754
-
755
-
756
- private
757
-
758
- def sort_multiple( arrys, lo=0, hi=arrys[0].length-1 )
759
- if lo < hi
760
- p = partition(arrys,lo,hi)
761
- sort_multiple(arrys, lo, p-1)
762
- sort_multiple(arrys, p+1, hi)
763
- end
764
- arrys
765
- end
766
-
767
- def partition( arrys, lo, hi )
768
- p = arrys[0][lo]
769
- l = lo
770
- z = lo+1
771
- while z <= hi
772
- if arrys[0][z] < p
773
- l += 1
774
- arrys.each { |arry| arry[z], arry[l] = arry[l], arry[z] }
775
- end
776
- z += 1
777
- end
778
- arrys.each { |arry| arry[lo], arry[l] = arry[l], arry[lo] }
779
- l
780
- end
781
-
782
- def style
783
- if no_css
784
- styles = parse_css
785
- @root.elements.each("//*[@class]") { |el|
786
- cl = el.attributes["class"]
787
- style = styles[cl]
788
- style += el.attributes["style"] if el.attributes["style"]
789
- el.attributes["style"] = style
790
- }
791
- end
792
- end
793
-
794
- def parse_css
795
- css = get_style
796
- rv = {}
797
- while css =~ /^(\.(\w+)(?:\s*,\s*\.\w+)*)\s*\{/m
798
- names_orig = names = $1
799
- css = $'
800
- css =~ /([^}]+)\}/m
801
- content = $1
802
- css = $'
803
-
804
- nms = []
805
- while names =~ /^\s*,?\s*\.(\w+)/
806
- nms << $1
807
- names = $'
808
- end
809
-
810
- content = content.tr( "\n\t", " ")
811
- for name in nms
812
- current = rv[name]
813
- current = current ? current+"; "+content : content
814
- rv[name] = current.strip.squeeze(" ")
815
- end
816
- end
817
- return rv
818
- end
819
-
820
-
821
- # Override and place code to add defs here
822
- def add_defs defs
823
- end
824
-
825
-
826
- def start_svg
827
- # Base document
828
- @doc = Document.new
829
- @doc << XMLDecl.new
830
- @doc << DocType.new( %q{svg PUBLIC "-//W3C//DTD SVG 1.0//EN" } +
831
- %q{"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"} )
832
- if style_sheet && style_sheet != ''
833
- @doc << Instruction.new( "xml-stylesheet",
834
- %Q{href="#{style_sheet}" type="text/css"} )
835
- end
836
- @root = @doc.add_element( "svg", {
837
- "width" => width.to_s,
838
- "height" => height.to_s,
839
- "viewBox" => "0 0 #{width} #{height}",
840
- "xmlns" => "http://www.w3.org/2000/svg",
841
- "xmlns:xlink" => "http://www.w3.org/1999/xlink",
842
- "xmlns:a3" => "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/",
843
- "a3:scriptImplementation" => "Adobe"
844
- })
845
- @root << Comment.new( " "+"\\"*66 )
846
- @root << Comment.new( " Created with SVG::Graph " )
847
- @root << Comment.new( " SVG::Graph by Sean E. Russell " )
848
- @root << Comment.new( " Losely based on SVG::TT::Graph for Perl by"+
849
- " Leo Lapworth & Stephan Morgan " )
850
- @root << Comment.new( " "+"/"*66 )
851
-
852
- defs = @root.add_element( "defs" )
853
- add_defs defs
854
- if not(style_sheet && style_sheet != '') and !no_css
855
- @root << Comment.new(" include default stylesheet if none specified ")
856
- style = defs.add_element( "style", {"type"=>"text/css"} )
857
- style << CData.new( get_style )
858
- end
859
-
860
- @root << Comment.new( "SVG Background" )
861
- @root.add_element( "rect", {
862
- "width" => width.to_s,
863
- "height" => height.to_s,
864
- "x" => "0",
865
- "y" => "0",
866
- "class" => "svgBackground"
867
- })
868
- end
869
-
870
-
871
- def calculate_graph_dimensions
872
- calculate_left_margin
873
- calculate_right_margin
874
- calculate_bottom_margin
875
- calculate_top_margin
876
- @graph_width = width - @border_left - @border_right
877
- @graph_height = height - @border_top - @border_bottom
878
- end
879
-
880
- def get_style
881
- return <<EOL
882
- /* Copy from here for external style sheet */
883
- .svgBackground{
884
- fill:#ffffff;
885
- }
886
- .graphBackground{
887
- fill:#f0f0f0;
888
- }
889
-
890
- /* graphs titles */
891
- .mainTitle{
892
- text-anchor: middle;
893
- fill: #000000;
894
- font-size: #{title_font_size}px;
895
- font-family: "Arial", sans-serif;
896
- font-weight: normal;
897
- }
898
- .subTitle{
899
- text-anchor: middle;
900
- fill: #999999;
901
- font-size: #{subtitle_font_size}px;
902
- font-family: "Arial", sans-serif;
903
- font-weight: normal;
904
- }
905
-
906
- .axis{
907
- stroke: #000000;
908
- stroke-width: 1px;
909
- }
910
-
911
- .guideLines{
912
- stroke: #666666;
913
- stroke-width: 1px;
914
- stroke-dasharray: 5 5;
915
- }
916
-
917
- .xAxisLabels{
918
- text-anchor: middle;
919
- fill: #000000;
920
- font-size: #{x_label_font_size}px;
921
- font-family: "Arial", sans-serif;
922
- font-weight: normal;
923
- }
924
-
925
- .yAxisLabels{
926
- text-anchor: end;
927
- fill: #000000;
928
- font-size: #{y_label_font_size}px;
929
- font-family: "Arial", sans-serif;
930
- font-weight: normal;
931
- }
932
-
933
- .xAxisTitle{
934
- text-anchor: middle;
935
- fill: #ff0000;
936
- font-size: #{x_title_font_size}px;
937
- font-family: "Arial", sans-serif;
938
- font-weight: normal;
939
- }
940
-
941
- .yAxisTitle{
942
- fill: #ff0000;
943
- text-anchor: middle;
944
- font-size: #{y_title_font_size}px;
945
- font-family: "Arial", sans-serif;
946
- font-weight: normal;
947
- }
948
-
949
- .dataPointLabel{
950
- fill: #000000;
951
- text-anchor:middle;
952
- font-size: 10px;
953
- font-family: "Arial", sans-serif;
954
- font-weight: normal;
955
- }
956
-
957
- .staggerGuideLine{
958
- fill: none;
959
- stroke: #000000;
960
- stroke-width: 0.5px;
961
- }
962
-
963
- #{get_css}
964
-
965
- .keyText{
966
- fill: #000000;
967
- text-anchor:start;
968
- font-size: #{key_font_size}px;
969
- font-family: "Arial", sans-serif;
970
- font-weight: normal;
971
- }
972
- /* End copy for external style sheet */
973
- EOL
974
- end
975
-
976
- end
977
- end
978
- end