ruport 0.4.99 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,494 +0,0 @@
1
- require 'SVG/Graph/Graph'
2
-
3
- module SVG
4
- module Graph
5
- # === For creating SVG plots of scalar data
6
- #
7
- # = Synopsis
8
- #
9
- # require 'SVG/Graph/Plot'
10
- #
11
- # # Data sets are x,y pairs
12
- # # Note that multiple data sets can differ in length, and that the
13
- # # data in the datasets needn't be in order; they will be ordered
14
- # # by the plot along the X-axis.
15
- # projection = [
16
- # 6, 11, 0, 5, 18, 7, 1, 11, 13, 9, 1, 2, 19, 0, 3, 13,
17
- # 7, 9
18
- # ]
19
- # actual = [
20
- # 0, 18, 8, 15, 9, 4, 18, 14, 10, 2, 11, 6, 14, 12,
21
- # 15, 6, 4, 17, 2, 12
22
- # ]
23
- #
24
- # graph = SVG::Graph::Plot.new({
25
- # :height => 500,
26
- # :width => 300,
27
- # :key => true,
28
- # :scale_x_integers => true,
29
- # :scale_y_integerrs => true,
30
- # })
31
- #
32
- # graph.add_data({
33
- # :data => projection
34
- # :title => 'Projected',
35
- # })
36
- #
37
- # graph.add_data({
38
- # :data => actual,
39
- # :title => 'Actual',
40
- # })
41
- #
42
- # print graph.burn()
43
- #
44
- # = Description
45
- #
46
- # Produces a graph of scalar data.
47
- #
48
- # This object aims to allow you to easily create high quality
49
- # SVG[http://www.w3c.org/tr/svg] scalar plots. You can either use the
50
- # default style sheet or supply your own. Either way there are many options
51
- # which can be configured to give you control over how the graph is
52
- # generated - with or without a key, data elements at each point, title,
53
- # subtitle etc.
54
- #
55
- # = Examples
56
- #
57
- # http://www.germane-software/repositories/public/SVG/test/plot.rb
58
- #
59
- # = Notes
60
- #
61
- # The default stylesheet handles upto 10 data sets, if you
62
- # use more you must create your own stylesheet and add the
63
- # additional settings for the extra data sets. You will know
64
- # if you go over 10 data sets as they will have no style and
65
- # be in black.
66
- #
67
- # Unlike the other types of charts, data sets must contain x,y pairs:
68
- #
69
- # [ 1, 2 ] # A data set with 1 point: (1,2)
70
- # [ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
71
- #
72
- # = See also
73
- #
74
- # * SVG::Graph::Graph
75
- # * SVG::Graph::BarHorizontal
76
- # * SVG::Graph::Bar
77
- # * SVG::Graph::Line
78
- # * SVG::Graph::Pie
79
- # * SVG::Graph::TimeSeries
80
- #
81
- # == Author
82
- #
83
- # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
84
- #
85
- # Copyright 2004 Sean E. Russell
86
- # This software is available under the Ruby license[LICENSE.txt]
87
- #
88
- class Plot < Graph
89
-
90
- # In addition to the defaults set by Graph::initialize, sets
91
- # [show_data_points] true
92
- # [area_fill] false
93
- # [stacked] false
94
- def set_defaults
95
- init_with(
96
- :show_data_points => true,
97
- :area_fill => false,
98
- :stacked => false
99
- )
100
- self.top_align = self.right_align = self.top_font = self.right_font = 1
101
- end
102
-
103
- # Determines the scaling for the X axis divisions.
104
- #
105
- # graph.scale_x_divisions = 2
106
- #
107
- # would cause the graph to attempt to generate labels stepped by 2; EG:
108
- # 0,2,4,6,8...
109
- attr_accessor :scale_x_divisions
110
- # Determines the scaling for the Y axis divisions.
111
- #
112
- # graph.scale_y_divisions = 0.5
113
- #
114
- # would cause the graph to attempt to generate labels stepped by 0.5; EG:
115
- # 0, 0.5, 1, 1.5, 2, ...
116
- attr_accessor :scale_y_divisions
117
- # Make the X axis labels integers
118
- attr_accessor :scale_x_integers
119
- # Make the Y axis labels integers
120
- attr_accessor :scale_y_integers
121
- # Fill the area under the line
122
- attr_accessor :area_fill
123
- # Show a small circle on the graph where the line
124
- # goes from one point to the next.
125
- attr_accessor :show_data_points
126
- # Set the minimum value of the X axis
127
- attr_accessor :min_x_value
128
- # Set the minimum value of the Y axis
129
- attr_accessor :min_y_value
130
-
131
-
132
- # Adds data to the plot. The data must be in X,Y pairs; EG
133
- # [ 1, 2 ] # A data set with 1 point: (1,2)
134
- # [ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
135
- def add_data data
136
- @data = [] unless @data
137
-
138
- raise "No data provided by #{conf.inspect}" unless data[:data] and
139
- data[:data].kind_of? Array
140
- raise "Data supplied must be x,y pairs! "+
141
- "The data provided contained an odd set of "+
142
- "data points" unless data[:data].length % 2 == 0
143
- return if data[:data].length == 0
144
-
145
- x = []
146
- y = []
147
- data[:data].each_index {|i|
148
- (i%2 == 0 ? x : y) << data[:data][i]
149
- }
150
- sort( x, y )
151
- data[:data] = [x,y]
152
- @data << data
153
- end
154
-
155
-
156
- protected
157
-
158
- def keys
159
- @data.collect{ |x| x[:title] }
160
- end
161
-
162
- def calculate_left_margin
163
- super
164
- label_left = get_x_labels[0].to_s.length / 2 * font_size * 0.6
165
- @border_left = label_left if label_left > @border_left
166
- end
167
-
168
- def calculate_right_margin
169
- super
170
- label_right = get_x_labels[-1].to_s.length / 2 * font_size * 0.6
171
- @border_right = label_right if label_right > @border_right
172
- end
173
-
174
-
175
- X = 0
176
- Y = 1
177
- def x_range
178
- max_value = @data.collect{|x| x[:data][X][-1] }.max
179
- min_value = @data.collect{|x| x[:data][X][0] }.min
180
- min_value = min_value<min_x_value ? min_value : min_x_value if min_x_value
181
-
182
- range = max_value - min_value
183
- right_pad = range == 0 ? 10 : range / 20.0
184
- scale_range = (max_value + right_pad) - min_value
185
-
186
- scale_division = scale_x_divisions || (scale_range / 10.0)
187
-
188
- if scale_x_integers
189
- scale_division = scale_division < 1 ? 1 : scale_division.round
190
- end
191
-
192
- [min_value, max_value, scale_division]
193
- end
194
-
195
- def get_x_values
196
- min_value, max_value, scale_division = x_range
197
- rv = []
198
- min_value.step( max_value, scale_division ) {|v| rv << v}
199
- return rv
200
- end
201
- alias :get_x_labels :get_x_values
202
-
203
- def field_width
204
- values = get_x_values
205
- max = @data.collect{|x| x[:data][X][-1]}.max
206
- dx = (max - values[-1]).to_f / (values[-1] - values[-2])
207
- (@graph_width.to_f - font_size*2*right_font) /
208
- (values.length + dx - right_align)
209
- end
210
-
211
-
212
- def y_range
213
- max_value = @data.collect{|x| x[:data][Y].max }.max
214
- min_value = @data.collect{|x| x[:data][Y].min }.min
215
- min_value = min_value<min_y_value ? min_value : min_y_value if min_y_value
216
-
217
- range = max_value - min_value
218
- top_pad = range == 0 ? 10 : range / 20.0
219
- scale_range = (max_value + top_pad) - min_value
220
-
221
- scale_division = scale_y_divisions || (scale_range / 10.0)
222
-
223
- if scale_y_integers
224
- scale_division = scale_division < 1 ? 1 : scale_division.round
225
- end
226
-
227
- return [min_value, max_value, scale_division]
228
- end
229
-
230
- def get_y_values
231
- min_value, max_value, scale_division = y_range
232
- rv = []
233
- min_value.step( max_value, scale_division ) {|v| rv << v}
234
- return rv
235
- end
236
- alias :get_y_labels :get_y_values
237
-
238
- def field_height
239
- values = get_y_values
240
- max = @data.collect{|x| x[:data][Y].max }.max
241
- dx = (max - values[-1]).to_f / (values[-1] - values[-2])
242
- (@graph_height.to_f - font_size*2*top_font) /
243
- (values.length + dx - top_align)
244
- end
245
-
246
- def draw_data
247
- line = 1
248
-
249
- x_min, x_max, x_div = x_range
250
- y_min, y_max, y_div = y_range
251
- x_step = (@graph_width.to_f - font_size*2) / (x_max-x_min)
252
- y_step = (@graph_height.to_f - font_size*2) / (y_max-y_min)
253
-
254
- for data in @data
255
- x_points = data[:data][X]
256
- y_points = data[:data][Y]
257
-
258
- lpath = "L"
259
- x_start = 0
260
- y_start = 0
261
- x_points.each_index { |idx|
262
- x = (x_points[idx] - x_min) * x_step
263
- y = @graph_height - (y_points[idx] - y_min) * y_step
264
- x_start, y_start = x,y if idx == 0
265
- lpath << "#{x} #{y} "
266
- }
267
-
268
- if area_fill
269
- @graph.add_element( "path", {
270
- "d" => "M#{x_start} #@graph_height #{lpath} V#@graph_height Z",
271
- "class" => "fill#{line}"
272
- })
273
- end
274
-
275
- @graph.add_element( "path", {
276
- "d" => "M#{x_start} #{y_start} #{lpath}",
277
- "class" => "line#{line}"
278
- })
279
-
280
- if show_data_points || show_data_values
281
- x_points.each_index { |idx|
282
- x = (x_points[idx] - x_min) * x_step
283
- y = @graph_height - (y_points[idx] - y_min) * y_step
284
- if show_data_points
285
- @graph.add_element( "circle", {
286
- "cx" => x.to_s,
287
- "cy" => y.to_s,
288
- "r" => "2.5",
289
- "class" => "dataPoint#{line}"
290
- })
291
- add_popup(x, y, format( x_points[idx], y_points[idx] )) if add_popups
292
- end
293
- make_datapoint_text( x, y-6, y_points[idx] )
294
- }
295
- end
296
- line += 1
297
- end
298
- end
299
-
300
- def format x, y
301
- "(#{(x * 100).to_i / 100}, #{(y * 100).to_i / 100})"
302
- end
303
-
304
- def get_css
305
- return <<EOL
306
- /* default line styles */
307
- .line1{
308
- fill: none;
309
- stroke: #ff0000;
310
- stroke-width: 1px;
311
- }
312
- .line2{
313
- fill: none;
314
- stroke: #0000ff;
315
- stroke-width: 1px;
316
- }
317
- .line3{
318
- fill: none;
319
- stroke: #00ff00;
320
- stroke-width: 1px;
321
- }
322
- .line4{
323
- fill: none;
324
- stroke: #ffcc00;
325
- stroke-width: 1px;
326
- }
327
- .line5{
328
- fill: none;
329
- stroke: #00ccff;
330
- stroke-width: 1px;
331
- }
332
- .line6{
333
- fill: none;
334
- stroke: #ff00ff;
335
- stroke-width: 1px;
336
- }
337
- .line7{
338
- fill: none;
339
- stroke: #00ffff;
340
- stroke-width: 1px;
341
- }
342
- .line8{
343
- fill: none;
344
- stroke: #ffff00;
345
- stroke-width: 1px;
346
- }
347
- .line9{
348
- fill: none;
349
- stroke: #ccc6666;
350
- stroke-width: 1px;
351
- }
352
- .line10{
353
- fill: none;
354
- stroke: #663399;
355
- stroke-width: 1px;
356
- }
357
- .line11{
358
- fill: none;
359
- stroke: #339900;
360
- stroke-width: 1px;
361
- }
362
- .line12{
363
- fill: none;
364
- stroke: #9966FF;
365
- stroke-width: 1px;
366
- }
367
- /* default fill styles */
368
- .fill1{
369
- fill: #cc0000;
370
- fill-opacity: 0.2;
371
- stroke: none;
372
- }
373
- .fill2{
374
- fill: #0000cc;
375
- fill-opacity: 0.2;
376
- stroke: none;
377
- }
378
- .fill3{
379
- fill: #00cc00;
380
- fill-opacity: 0.2;
381
- stroke: none;
382
- }
383
- .fill4{
384
- fill: #ffcc00;
385
- fill-opacity: 0.2;
386
- stroke: none;
387
- }
388
- .fill5{
389
- fill: #00ccff;
390
- fill-opacity: 0.2;
391
- stroke: none;
392
- }
393
- .fill6{
394
- fill: #ff00ff;
395
- fill-opacity: 0.2;
396
- stroke: none;
397
- }
398
- .fill7{
399
- fill: #00ffff;
400
- fill-opacity: 0.2;
401
- stroke: none;
402
- }
403
- .fill8{
404
- fill: #ffff00;
405
- fill-opacity: 0.2;
406
- stroke: none;
407
- }
408
- .fill9{
409
- fill: #cc6666;
410
- fill-opacity: 0.2;
411
- stroke: none;
412
- }
413
- .fill10{
414
- fill: #663399;
415
- fill-opacity: 0.2;
416
- stroke: none;
417
- }
418
- .fill11{
419
- fill: #339900;
420
- fill-opacity: 0.2;
421
- stroke: none;
422
- }
423
- .fill12{
424
- fill: #9966FF;
425
- fill-opacity: 0.2;
426
- stroke: none;
427
- }
428
- /* default line styles */
429
- .key1,.dataPoint1{
430
- fill: #ff0000;
431
- stroke: none;
432
- stroke-width: 1px;
433
- }
434
- .key2,.dataPoint2{
435
- fill: #0000ff;
436
- stroke: none;
437
- stroke-width: 1px;
438
- }
439
- .key3,.dataPoint3{
440
- fill: #00ff00;
441
- stroke: none;
442
- stroke-width: 1px;
443
- }
444
- .key4,.dataPoint4{
445
- fill: #ffcc00;
446
- stroke: none;
447
- stroke-width: 1px;
448
- }
449
- .key5,.dataPoint5{
450
- fill: #00ccff;
451
- stroke: none;
452
- stroke-width: 1px;
453
- }
454
- .key6,.dataPoint6{
455
- fill: #ff00ff;
456
- stroke: none;
457
- stroke-width: 1px;
458
- }
459
- .key7,.dataPoint7{
460
- fill: #00ffff;
461
- stroke: none;
462
- stroke-width: 1px;
463
- }
464
- .key8,.dataPoint8{
465
- fill: #ffff00;
466
- stroke: none;
467
- stroke-width: 1px;
468
- }
469
- .key9,.dataPoint9{
470
- fill: #cc6666;
471
- stroke: none;
472
- stroke-width: 1px;
473
- }
474
- .key10,.dataPoint10{
475
- fill: #663399;
476
- stroke: none;
477
- stroke-width: 1px;
478
- }
479
- .key11,.dataPoint11{
480
- fill: #339900;
481
- stroke: none;
482
- stroke-width: 1px;
483
- }
484
- .key12,.dataPoint12{
485
- fill: #9966FF;
486
- stroke: none;
487
- stroke-width: 1px;
488
- }
489
- EOL
490
- end
491
-
492
- end
493
- end
494
- end