Class | SVG::Graph::Plot |
In: |
lib/SVG/Graph/Plot.rb
|
Parent: | Graph |
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()
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.
www.germane-software/repositories/public/SVG/test/plot.rb
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)
Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
Copyright 2004 Sean E. Russell This software is available under the Ruby license
X | = | 0 |
Y | = | 1 |
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 |
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, line 148 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
In addition to the defaults set by Graph::initialize, sets
# File lib/SVG/Graph/Plot.rb, line 96 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
# File lib/SVG/Graph/Plot.rb, line 180 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
# File lib/SVG/Graph/Plot.rb, line 186 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
# File lib/SVG/Graph/Plot.rb, line 297 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
# File lib/SVG/Graph/Plot.rb, line 285 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
# File lib/SVG/Graph/Plot.rb, line 233 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
# File lib/SVG/Graph/Plot.rb, line 350 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
# File lib/SVG/Graph/Plot.rb, line 358 358: def get_css 359: return "/* default line styles */\n.line1{\n fill: none;\n stroke: #ff0000;\n stroke-width: 1px; \n}\n.line2{\n fill: none;\n stroke: #0000ff;\n stroke-width: 1px; \n}\n.line3{\n fill: none;\n stroke: #00ff00;\n stroke-width: 1px; \n}\n.line4{\n fill: none;\n stroke: #ffcc00;\n stroke-width: 1px; \n}\n.line5{\n fill: none;\n stroke: #00ccff;\n stroke-width: 1px; \n}\n.line6{\n fill: none;\n stroke: #ff00ff;\n stroke-width: 1px; \n}\n.line7{\n fill: none;\n stroke: #00ffff;\n stroke-width: 1px; \n}\n.line8{\n fill: none;\n stroke: #ffff00;\n stroke-width: 1px; \n}\n.line9{\n fill: none;\n stroke: #ccc6666;\n stroke-width: 1px; \n}\n.line10{\n fill: none;\n stroke: #663399;\n stroke-width: 1px; \n}\n.line11{\n fill: none;\n stroke: #339900;\n stroke-width: 1px; \n}\n.line12{\n fill: none;\n stroke: #9966FF;\n stroke-width: 1px; \n}\n/* default fill styles */\n.fill1{\n fill: #cc0000;\n fill-opacity: 0.2;\n stroke: none;\n}\n.fill2{\n fill: #0000cc;\n fill-opacity: 0.2;\n stroke: none;\n}\n.fill3{\n fill: #00cc00;\n fill-opacity: 0.2;\n stroke: none;\n}\n.fill4{\n fill: #ffcc00;\n fill-opacity: 0.2;\n stroke: none;\n}\n.fill5{\n fill: #00ccff;\n fill-opacity: 0.2;\n stroke: none;\n}\n.fill6{\n fill: #ff00ff;\n fill-opacity: 0.2;\n stroke: none;\n}\n.fill7{\n fill: #00ffff;\n fill-opacity: 0.2;\n stroke: none;\n}\n.fill8{\n fill: #ffff00;\n fill-opacity: 0.2;\n stroke: none;\n}\n.fill9{\n fill: #cc6666;\n fill-opacity: 0.2;\n stroke: none;\n}\n.fill10{\n fill: #663399;\n fill-opacity: 0.2;\n stroke: none;\n}\n.fill11{\n fill: #339900;\n fill-opacity: 0.2;\n stroke: none;\n}\n.fill12{\n fill: #9966FF;\n fill-opacity: 0.2;\n stroke: none;\n}\n/* default line styles */\n.key1,.dataPoint1{\n fill: #ff0000;\n stroke: none;\n stroke-width: 1px; \n}\n.key2,.dataPoint2{\n fill: #0000ff;\n stroke: none;\n stroke-width: 1px; \n}\n.key3,.dataPoint3{\n fill: #00ff00;\n stroke: none;\n stroke-width: 1px; \n}\n.key4,.dataPoint4{\n fill: #ffcc00;\n stroke: none;\n stroke-width: 1px; \n}\n.key5,.dataPoint5{\n fill: #00ccff;\n stroke: none;\n stroke-width: 1px; \n}\n.key6,.dataPoint6{\n fill: #ff00ff;\n stroke: none;\n stroke-width: 1px; \n}\n.key7,.dataPoint7{\n fill: #00ffff;\n stroke: none;\n stroke-width: 1px; \n}\n.key8,.dataPoint8{\n fill: #ffff00;\n stroke: none;\n stroke-width: 1px; \n}\n.key9,.dataPoint9{\n fill: #cc6666;\n stroke: none;\n stroke-width: 1px; \n}\n.key10,.dataPoint10{\n fill: #663399;\n stroke: none;\n stroke-width: 1px; \n}\n.key11,.dataPoint11{\n fill: #339900;\n stroke: none;\n stroke-width: 1px; \n}\n.key12,.dataPoint12{\n fill: #9966FF;\n stroke: none;\n stroke-width: 1px; \n}\n" 360: end
# File lib/SVG/Graph/Plot.rb, line 225 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
# File lib/SVG/Graph/Plot.rb, line 271 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
# File lib/SVG/Graph/Plot.rb, line 196 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
# File lib/SVG/Graph/Plot.rb, line 242 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
# File lib/SVG/Graph/Plot.rb, line 202 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
# File lib/SVG/Graph/Plot.rb, line 248 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
# File lib/SVG/Graph/Plot.rb, line 208 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
# File lib/SVG/Graph/Plot.rb, line 254 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