class SVG::Graph::Plot

For creating SVG plots of scalar data

Synopsis

require 'SVG/Graph/Plot'

# Data sets are x,y pairs
# Note that multiple data sets can differ in length, and that the
# data in the datasets needn't be in order; they will be ordered
# by the plot along the X-axis.
projection = [
  6, 11,    0, 5,   18, 7,   1, 11,   13, 9,   1, 2,   19, 0,   3, 13,
  7, 9 
]
actual = [
  0, 18,    8, 15,    9, 4,   18, 14,   10, 2,   11, 6,  14, 12,   
  15, 6,   4, 17,   2, 12
]

graph = SVG::Graph::Plot.new({
        :height => 500,
        :width => 300,
  :key => true,
  :scale_x_integers => true,
  :scale_y_integerrs => true,
})

graph.add_data({
        :data => projection
  :title => 'Projected',
})

graph.add_data({
        :data => actual,
  :title => 'Actual',
})

print graph.burn()

Description

Produces a graph of scalar data.

This object aims to allow you to easily create high quality SVG scalar plots. You can either use the default style sheet or supply your own. Either way there are many options which can be configured to give you control over how the graph is generated - with or without a key, data elements at each point, title, subtitle etc.

Examples

www.germane-software/repositories/public/SVG/test/plot.rb

Notes

The default stylesheet handles upto 10 data sets, if you use more you must create your own stylesheet and add the additional settings for the extra data sets. You will know if you go over 10 data sets as they will have no style and be in black.

Unlike the other types of charts, data sets must contain x,y pairs:

[ 1, 2 ]    # A data set with 1 point: (1,2)
[ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)

See also

Author

Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>

Copyright 2004 Sean E. Russell This software is available under the Ruby license

Constants

X
Y

Attributes

area_fill[RW]

Fill the area under the line

max_x_value[RW]

Set the maximum value of the X axis

max_y_value[RW]

Set the maximum value of the Y axis

min_x_value[RW]

Set the minimum value of the X axis

min_y_value[RW]

Set the minimum value of the Y axis

round_popups[RW]

Round value of data points in popups to integer

scale_x_divisions[RW]

Determines the scaling for the X axis divisions.

graph.scale_x_divisions = 2

would cause the graph to attempt to generate labels stepped by 2; EG: 0,2,4,6,8…

scale_x_integers[RW]

Make the X axis labels integers

scale_y_divisions[RW]

Determines the scaling for the Y axis divisions.

graph.scale_y_divisions = 0.5

would cause the graph to attempt to generate labels stepped by 0.5; EG: 0, 0.5, 1, 1.5, 2, …

scale_y_integers[RW]

Make the Y axis labels integers

show_data_points[RW]

Show a small circle on the graph where the line goes from one point to the next.

show_lines[RW]

Show lines connecting data points

Public Instance Methods

add_data(data) click to toggle source

Adds data to the plot. The data must be in X,Y pairs; EG

[ 1, 2 ]    # A data set with 1 point: (1,2)
[ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
    # File lib/SVG/Graph/Plot.rb
148 def add_data(data)
149         
150   @data = [] unless @data
151 
152   raise "No data provided by #{conf.inspect}" unless data[:data] and
153   data[:data].kind_of? Array
154   raise "Data supplied must be x,y pairs!  "+
155     "The data provided contained an odd set of "+
156     "data points" unless data[:data].length % 2 == 0
157   return if data[:data].length == 0
158 
159   data[:description] ||= Array.new(data[:data].size/2)
160   if data[:description].size != data[:data].size/2
161     raise "Description for popups does not have same size as provided data: #{data[:description].size} vs #{data[:data].size/2}"
162   end
163 
164   x = []
165   y = []
166   data[:data].each_index {|i|
167     (i%2 == 0 ? x : y) << data[:data][i]
168   }
169   sort( x, y, data[:description] )
170   data[:data] = [x,y]
171   @data << data
172 end
set_defaults() click to toggle source

In addition to the defaults set by Graph::initialize, sets

show_data_values

true

show_data_points

true

area_fill

false

stacked

false

    # File lib/SVG/Graph/Plot.rb
 96 def set_defaults
 97   init_with(
 98             :show_data_values  => true,
 99             :show_data_points  => true,
100             :area_fill         => false,
101             :stacked           => false,
102             :show_lines        => true,
103             :round_popups      => true
104            )
105            self.top_align = self.right_align = self.top_font = self.right_font = 1
106 end

Protected Instance Methods

calculate_left_margin() click to toggle source
Calls superclass method SVG::Graph::Graph#calculate_left_margin
    # File lib/SVG/Graph/Plot.rb
180 def calculate_left_margin
181   super
182   label_left = get_x_labels[0].to_s.length / 2 * font_size * 0.6
183   @border_left = label_left if label_left > @border_left
184 end
calculate_right_margin() click to toggle source
    # File lib/SVG/Graph/Plot.rb
186 def calculate_right_margin
187   super
188   label_right = get_x_labels[-1].to_s.length / 2 * font_size * 0.6
189   @border_right = label_right if label_right > @border_right
190 end
draw_data() click to toggle source
    # File lib/SVG/Graph/Plot.rb
297 def draw_data
298   line = 1
299   
300   x_min, x_max = x_range
301   y_min, y_max = y_range
302   x_step = (@graph_width.to_f - font_size*2) / (x_max-x_min)
303   y_step = (@graph_height.to_f -  font_size*2) / (y_max-y_min)
304 
305   for data in @data
306     x_points = data[:data][X]
307     y_points = data[:data][Y]
308 
309     lpath = "L"
310     x_start = 0
311     y_start = 0
312     x_points.each_index { |idx|
313       x = (x_points[idx] -  x_min) * x_step
314       y = @graph_height - (y_points[idx] -  y_min) * y_step
315       x_start, y_start = x,y if idx == 0
316       lpath << "#{x} #{y} "
317     }
318 
319     if area_fill
320       @graph.add_element( "path", {
321         "d" => "M#{x_start} #@graph_height #{lpath} V#@graph_height Z",
322         "class" => "fill#{line}"
323       })
324     end
325 
326     if show_lines
327       @graph.add_element( "path", {
328         "d" => "M#{x_start} #{y_start} #{lpath}",
329         "class" => "line#{line}"
330       })
331     end
332 
333     if show_data_points || show_data_values
334       x_points.each_index { |idx|
335         x = (x_points[idx] -  x_min) * x_step
336         y = @graph_height - (y_points[idx] -  y_min) * y_step
337         if show_data_points
338           DataPoint.new(x, y, line).shape(data[:description][idx]).each{|s|
339             @graph.add_element( *s )
340           }
341           add_popup(x, y, format( x_points[idx], y_points[idx], data[:description][idx])) if add_popups
342         end
343         make_datapoint_text( x, y-6, y_points[idx] ) if show_data_values
344       }
345     end
346     line += 1
347   end
348 end
field_height() click to toggle source
    # File lib/SVG/Graph/Plot.rb
285 def field_height
286   values = get_y_values
287   max = max_y_range
288   if values.length == 1
289     dx = values[-1]
290   else
291     dx = (max - values[-1]).to_f / (values[-1] - values[-2])
292   end
293   (@graph_height.to_f - font_size*2*top_font) /
294     (values.length + dx - top_align)
295 end
field_width() click to toggle source
    # File lib/SVG/Graph/Plot.rb
233 def field_width
234   values = get_x_values
235   max = max_x_range
236   dx = (max - values[-1]).to_f / (values[-1] - values[-2])
237   (@graph_width.to_f - font_size*2*right_font) /
238     (values.length + dx - right_align)
239 end
format(x, y, desc) click to toggle source
    # File lib/SVG/Graph/Plot.rb
350 def format x, y, desc
351   info = []
352   info << (round_popups ? (x * 100).to_i / 100 : x)
353   info << (round_popups ? (y * 100).to_i / 100 : y)
354   info << desc
355   "(#{info.compact.join(', ')})"
356 end
get_css() click to toggle source
    # File lib/SVG/Graph/Plot.rb
358       def get_css
359         return <<EOL
360 /* default line styles */
361 .line1{
362         fill: none;
363         stroke: #ff0000;
364         stroke-width: 1px;     
365 }
366 .line2{
367         fill: none;
368         stroke: #0000ff;
369         stroke-width: 1px;     
370 }
371 .line3{
372         fill: none;
373         stroke: #00ff00;
374         stroke-width: 1px;     
375 }
376 .line4{
377         fill: none;
378         stroke: #ffcc00;
379         stroke-width: 1px;     
380 }
381 .line5{
382         fill: none;
383         stroke: #00ccff;
384         stroke-width: 1px;     
385 }
386 .line6{
387         fill: none;
388         stroke: #ff00ff;
389         stroke-width: 1px;     
390 }
391 .line7{
392         fill: none;
393         stroke: #00ffff;
394         stroke-width: 1px;     
395 }
396 .line8{
397         fill: none;
398         stroke: #ffff00;
399         stroke-width: 1px;     
400 }
401 .line9{
402         fill: none;
403         stroke: #ccc6666;
404         stroke-width: 1px;     
405 }
406 .line10{
407         fill: none;
408         stroke: #663399;
409         stroke-width: 1px;     
410 }
411 .line11{
412         fill: none;
413         stroke: #339900;
414         stroke-width: 1px;     
415 }
416 .line12{
417         fill: none;
418         stroke: #9966FF;
419         stroke-width: 1px;     
420 }
421 /* default fill styles */
422 .fill1{
423         fill: #cc0000;
424         fill-opacity: 0.2;
425         stroke: none;
426 }
427 .fill2{
428         fill: #0000cc;
429         fill-opacity: 0.2;
430         stroke: none;
431 }
432 .fill3{
433         fill: #00cc00;
434         fill-opacity: 0.2;
435         stroke: none;
436 }
437 .fill4{
438         fill: #ffcc00;
439         fill-opacity: 0.2;
440         stroke: none;
441 }
442 .fill5{
443         fill: #00ccff;
444         fill-opacity: 0.2;
445         stroke: none;
446 }
447 .fill6{
448         fill: #ff00ff;
449         fill-opacity: 0.2;
450         stroke: none;
451 }
452 .fill7{
453         fill: #00ffff;
454         fill-opacity: 0.2;
455         stroke: none;
456 }
457 .fill8{
458         fill: #ffff00;
459         fill-opacity: 0.2;
460         stroke: none;
461 }
462 .fill9{
463         fill: #cc6666;
464         fill-opacity: 0.2;
465         stroke: none;
466 }
467 .fill10{
468         fill: #663399;
469         fill-opacity: 0.2;
470         stroke: none;
471 }
472 .fill11{
473         fill: #339900;
474         fill-opacity: 0.2;
475         stroke: none;
476 }
477 .fill12{
478         fill: #9966FF;
479         fill-opacity: 0.2;
480         stroke: none;
481 }
482 /* default line styles */
483 .key1,.dataPoint1{
484         fill: #ff0000;
485         stroke: none;
486         stroke-width: 1px;     
487 }
488 .key2,.dataPoint2{
489         fill: #0000ff;
490         stroke: none;
491         stroke-width: 1px;     
492 }
493 .key3,.dataPoint3{
494         fill: #00ff00;
495         stroke: none;
496         stroke-width: 1px;     
497 }
498 .key4,.dataPoint4{
499         fill: #ffcc00;
500         stroke: none;
501         stroke-width: 1px;     
502 }
503 .key5,.dataPoint5{
504         fill: #00ccff;
505         stroke: none;
506         stroke-width: 1px;     
507 }
508 .key6,.dataPoint6{
509         fill: #ff00ff;
510         stroke: none;
511         stroke-width: 1px;     
512 }
513 .key7,.dataPoint7{
514         fill: #00ffff;
515         stroke: none;
516         stroke-width: 1px;     
517 }
518 .key8,.dataPoint8{
519         fill: #ffff00;
520         stroke: none;
521         stroke-width: 1px;     
522 }
523 .key9,.dataPoint9{
524         fill: #cc6666;
525         stroke: none;
526         stroke-width: 1px;     
527 }
528 .key10,.dataPoint10{
529         fill: #663399;
530         stroke: none;
531         stroke-width: 1px;     
532 }
533 .key11,.dataPoint11{
534         fill: #339900;
535         stroke: none;
536         stroke-width: 1px;     
537 }
538 .key12,.dataPoint12{
539         fill: #9966FF;
540         stroke: none;
541         stroke-width: 1px;     
542 }
543 EOL
544       end
get_x_labels()
Alias for: get_x_values
get_x_values() click to toggle source
    # File lib/SVG/Graph/Plot.rb
225 def get_x_values
226   min_value, max_value, scale_division = x_range
227   rv = []
228   min_value.step( max_value, scale_division ) {|v| rv << v}
229   return rv
230 end
Also aliased as: get_x_labels
get_y_labels()
Alias for: get_y_values
get_y_values() click to toggle source
    # File lib/SVG/Graph/Plot.rb
271 def get_y_values
272   min_value, max_value, scale_division = y_range
273   if max_value != min_value
274     while (max_value - min_value) < scale_division
275       scale_division /= 10.0
276     end
277   end
278   rv = []
279   min_value.step( max_value, scale_division ) {|v| rv << v}
280   rv << rv[0] + 1 if rv.length == 1
281   return rv
282 end
Also aliased as: get_y_labels
keys() click to toggle source
    # File lib/SVG/Graph/Plot.rb
176 def keys
177   @data.collect{ |x| x[:title] }
178 end
max_x_range() click to toggle source
    # File lib/SVG/Graph/Plot.rb
196 def max_x_range
197   max_value = @data.collect{|x| x[:data][X][-1] }.max
198   max_value = max_value > max_x_value ? max_value : max_x_value if max_x_value
199   max_value
200 end
max_y_range() click to toggle source
    # File lib/SVG/Graph/Plot.rb
242 def max_y_range
243   max_value = @data.collect{|x| x[:data][Y].max }.max
244   max_value = max_value > max_y_value ? max_value : max_y_value if max_y_value
245   max_value
246 end
min_x_range() click to toggle source
    # File lib/SVG/Graph/Plot.rb
202 def min_x_range
203   min_value = @data.collect{|x| x[:data][X][0] }.min
204   min_value = min_value < min_x_value ? min_value : min_x_value if min_x_value
205   min_value
206 end
min_y_range() click to toggle source
    # File lib/SVG/Graph/Plot.rb
248 def min_y_range
249   min_value = @data.collect{|x| x[:data][Y].min }.min
250   min_value = min_value < min_y_value ? min_value : min_y_value if min_y_value
251   min_value
252 end
x_range() click to toggle source
    # File lib/SVG/Graph/Plot.rb
208 def x_range
209   max_value = max_x_range
210   min_value = min_x_range
211 
212   range = max_value - min_value
213   right_pad = range == 0 ? 10 : range / 20.0
214   scale_range = (max_value + right_pad) - min_value
215 
216   scale_division = scale_x_divisions || (scale_range / 10.0)
217 
218   if scale_x_integers
219     scale_division = scale_division < 1 ? 1 : scale_division.round
220   end
221 
222   [min_value, max_value, scale_division]
223 end
y_range() click to toggle source
    # File lib/SVG/Graph/Plot.rb
254 def y_range
255   max_value = max_y_range
256   min_value = min_y_range
257 
258   range = max_value - min_value
259   top_pad = range == 0 ? 10 : range / 20.0
260   scale_range = (max_value + top_pad) - min_value
261 
262   scale_division = scale_y_divisions || (scale_range / 10.0)
263 
264   if scale_y_integers
265     scale_division = scale_division < 1 ? 1 : scale_division.round
266   end
267 
268   return [min_value, max_value, scale_division]
269 end