class SVG::Graph::Line
Create presentation quality SVG
line graphs easily¶ ↑
Synopsis¶ ↑
require 'SVG/Graph/Line' fields = %w(Jan Feb Mar); data_sales_02 = [12, 45, 21] data_sales_03 = [15, 30, 40] graph = SVG::Graph::Line.new({ :height => 500, :width => 300, :fields => fields, }) graph.add_data({ :data => data_sales_02, :title => 'Sales 2002', }) graph.add_data({ :data => data_sales_03, :title => 'Sales 2003', }) print "Content-type: image/svg+xml\r\n\r\n"; print graph.burn();
Description¶ ↑
This object aims to allow you to easily create high quality SVG
line graphs. 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/single.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.
See also¶ ↑
Author¶ ↑
Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
Copyright 2004 Sean E. Russell This software is available under the Ruby license
Attributes
Fill in the area under the plot if true
Show a small circle on the graph where the line goes from one point to the next.
Accumulates each data set. (i.e. Each point increased by sum of
all previous series at same point). Default is 0, set to '1' to show.
Public Class Methods
The constructor takes a hash reference, fields (the names for each field on the X axis) MUST be set, all other values are defaulted to those shown above - with the exception of style_sheet which defaults to using the internal style sheet.
SVG::Graph::Graph::new
# File lib/SVG/Graph/Line.rb 85 def initialize config 86 raise "fields was not supplied or is empty" unless config[:fields] && 87 config[:fields].kind_of?(Array) && 88 config[:fields].length > 0 89 super 90 end
Public Instance Methods
In addition to the defaults set in Graph::initialize, sets
show_data_points
-
true
- show_data_values
-
true
- stacked
-
false
area_fill
-
false
# File lib/SVG/Graph/Line.rb 97 def set_defaults 98 init_with( 99 :show_data_points => true, 100 :show_data_values => true, 101 :stacked => false, 102 :area_fill => false 103 ) 104 105 106 self.top_align = self.top_font = self.right_align = self.right_font = 1 107 end
Protected Instance Methods
# File lib/SVG/Graph/Line.rb 175 def calc_coords(field, value, width = field_width, height = field_height) 176 coords = {:x => 0, :y => 0} 177 coords[:x] = width * field 178 coords[:y] = @graph_height - value * height 179 180 return coords 181 end
SVG::Graph::Graph#calculate_left_margin
# File lib/SVG/Graph/Line.rb 149 def calculate_left_margin 150 super 151 label_left = @config[:fields][0].length / 2 * font_size * 0.6 152 @border_left = label_left if label_left > @border_left 153 end
# File lib/SVG/Graph/Line.rb 183 def draw_data 184 minvalue = min_value 185 fieldheight = (@graph_height.to_f - font_size*2*top_font) / 186 (get_y_labels.max - get_y_labels.min) 187 fieldwidth = field_width 188 line = @data.length 189 190 prev_sum = Array.new(@config[:fields].length).fill(0) 191 cum_sum = Array.new(@config[:fields].length).fill(-minvalue) 192 193 for data in @data.reverse 194 lpath = "" 195 apath = "" 196 197 if not stacked then cum_sum.fill(-minvalue) end 198 199 data[:data].each_index do |i| 200 cum_sum[i] += data[:data][i] 201 202 c = calc_coords(i, cum_sum[i], fieldwidth, fieldheight) 203 204 lpath << "#{c[:x]} #{c[:y]} " 205 end 206 207 if area_fill 208 if stacked then 209 (prev_sum.length - 1).downto 0 do |i| 210 c = calc_coords(i, prev_sum[i], fieldwidth, fieldheight) 211 212 apath << "#{c[:x]} #{c[:y]} " 213 end 214 215 c = calc_coords(0, prev_sum[0], fieldwidth, fieldheight) 216 else 217 apath = "V#@graph_height" 218 c = calc_coords(0, 0, fieldwidth, fieldheight) 219 end 220 221 @graph.add_element("path", { 222 "d" => "M#{c[:x]} #{c[:y]} L" + lpath + apath + "Z", 223 "class" => "fill#{line}" 224 }) 225 end 226 227 @graph.add_element("path", { 228 "d" => "M0 #@graph_height L" + lpath, 229 "class" => "line#{line}" 230 }) 231 232 if show_data_points || show_data_values 233 cum_sum.each_index do |i| 234 if show_data_points 235 @graph.add_element( "circle", { 236 "cx" => (fieldwidth * i).to_s, 237 "cy" => (@graph_height - cum_sum[i] * fieldheight).to_s, 238 "r" => "2.5", 239 "class" => "dataPoint#{line}" 240 }) 241 end 242 make_datapoint_text( 243 fieldwidth * i, 244 @graph_height - cum_sum[i] * fieldheight - 6, 245 cum_sum[i] + minvalue 246 ) 247 end 248 end 249 250 prev_sum = cum_sum.dup 251 line -= 1 252 end 253 end
# File lib/SVG/Graph/Line.rb 256 def get_css 257 return <<EOL 258 /* default line styles */ 259 .line1{ 260 fill: none; 261 stroke: #ff0000; 262 stroke-width: 1px; 263 } 264 .line2{ 265 fill: none; 266 stroke: #0000ff; 267 stroke-width: 1px; 268 } 269 .line3{ 270 fill: none; 271 stroke: #00ff00; 272 stroke-width: 1px; 273 } 274 .line4{ 275 fill: none; 276 stroke: #ffcc00; 277 stroke-width: 1px; 278 } 279 .line5{ 280 fill: none; 281 stroke: #00ccff; 282 stroke-width: 1px; 283 } 284 .line6{ 285 fill: none; 286 stroke: #ff00ff; 287 stroke-width: 1px; 288 } 289 .line7{ 290 fill: none; 291 stroke: #00ffff; 292 stroke-width: 1px; 293 } 294 .line8{ 295 fill: none; 296 stroke: #ffff00; 297 stroke-width: 1px; 298 } 299 .line9{ 300 fill: none; 301 stroke: #ccc6666; 302 stroke-width: 1px; 303 } 304 .line10{ 305 fill: none; 306 stroke: #663399; 307 stroke-width: 1px; 308 } 309 .line11{ 310 fill: none; 311 stroke: #339900; 312 stroke-width: 1px; 313 } 314 .line12{ 315 fill: none; 316 stroke: #9966FF; 317 stroke-width: 1px; 318 } 319 /* default fill styles */ 320 .fill1{ 321 fill: #cc0000; 322 fill-opacity: 0.2; 323 stroke: none; 324 } 325 .fill2{ 326 fill: #0000cc; 327 fill-opacity: 0.2; 328 stroke: none; 329 } 330 .fill3{ 331 fill: #00cc00; 332 fill-opacity: 0.2; 333 stroke: none; 334 } 335 .fill4{ 336 fill: #ffcc00; 337 fill-opacity: 0.2; 338 stroke: none; 339 } 340 .fill5{ 341 fill: #00ccff; 342 fill-opacity: 0.2; 343 stroke: none; 344 } 345 .fill6{ 346 fill: #ff00ff; 347 fill-opacity: 0.2; 348 stroke: none; 349 } 350 .fill7{ 351 fill: #00ffff; 352 fill-opacity: 0.2; 353 stroke: none; 354 } 355 .fill8{ 356 fill: #ffff00; 357 fill-opacity: 0.2; 358 stroke: none; 359 } 360 .fill9{ 361 fill: #cc6666; 362 fill-opacity: 0.2; 363 stroke: none; 364 } 365 .fill10{ 366 fill: #663399; 367 fill-opacity: 0.2; 368 stroke: none; 369 } 370 .fill11{ 371 fill: #339900; 372 fill-opacity: 0.2; 373 stroke: none; 374 } 375 .fill12{ 376 fill: #9966FF; 377 fill-opacity: 0.2; 378 stroke: none; 379 } 380 /* default line styles */ 381 .key1,.dataPoint1{ 382 fill: #ff0000; 383 stroke: none; 384 stroke-width: 1px; 385 } 386 .key2,.dataPoint2{ 387 fill: #0000ff; 388 stroke: none; 389 stroke-width: 1px; 390 } 391 .key3,.dataPoint3{ 392 fill: #00ff00; 393 stroke: none; 394 stroke-width: 1px; 395 } 396 .key4,.dataPoint4{ 397 fill: #ffcc00; 398 stroke: none; 399 stroke-width: 1px; 400 } 401 .key5,.dataPoint5{ 402 fill: #00ccff; 403 stroke: none; 404 stroke-width: 1px; 405 } 406 .key6,.dataPoint6{ 407 fill: #ff00ff; 408 stroke: none; 409 stroke-width: 1px; 410 } 411 .key7,.dataPoint7{ 412 fill: #00ffff; 413 stroke: none; 414 stroke-width: 1px; 415 } 416 .key8,.dataPoint8{ 417 fill: #ffff00; 418 stroke: none; 419 stroke-width: 1px; 420 } 421 .key9,.dataPoint9{ 422 fill: #cc6666; 423 stroke: none; 424 stroke-width: 1px; 425 } 426 .key10,.dataPoint10{ 427 fill: #663399; 428 stroke: none; 429 stroke-width: 1px; 430 } 431 .key11,.dataPoint11{ 432 fill: #339900; 433 stroke: none; 434 stroke-width: 1px; 435 } 436 .key12,.dataPoint12{ 437 fill: #9966FF; 438 stroke: none; 439 stroke-width: 1px; 440 } 441 EOL 442 end
# File lib/SVG/Graph/Line.rb 145 def get_x_labels 146 @config[:fields] 147 end
# File lib/SVG/Graph/Line.rb 155 def get_y_labels 156 maxvalue = max_value 157 minvalue = min_value 158 range = maxvalue - minvalue 159 top_pad = range == 0 ? 10 : range / 20.0 160 scale_range = (maxvalue + top_pad) - minvalue 161 162 scale_division = scale_divisions || (scale_range / 10.0) 163 164 if scale_integers 165 scale_division = scale_division < 1 ? 1 : scale_division.round 166 end 167 168 rv = [] 169 maxvalue = maxvalue%scale_division == 0 ? 170 maxvalue : maxvalue + scale_division 171 minvalue.step( maxvalue, scale_division ) {|v| rv << v} 172 return rv 173 end
# File lib/SVG/Graph/Line.rb 111 def max_value 112 max = 0 113 114 if (stacked == true) then 115 sums = Array.new(@config[:fields].length).fill(0) 116 117 @data.each do |data| 118 sums.each_index do |i| 119 sums[i] += data[:data][i].to_f 120 end 121 end 122 123 max = sums.max 124 else 125 max = @data.collect{|x| x[:data].max}.max 126 end 127 128 return max 129 end
# File lib/SVG/Graph/Line.rb 131 def min_value 132 min = 0 133 134 if (min_scale_value.nil? == false) then 135 min = min_scale_value 136 elsif (stacked == true) then 137 min = @data[-1][:data].min 138 else 139 min = @data.collect{|x| x[:data].min}.min 140 end 141 142 return min 143 end