class SVG::Graph::Graph
Base object for generating SVG
Graphs¶ ↑
Synopsis¶ ↑
This class is only used as a superclass of specialized charts. Do not attempt to use this class directly, unless creating a new chart type.
For examples of how to subclass this class, see the existing specific subclasses, such as SVG::Graph::Pie
.
Examples¶ ↑
For examples of how to use this package, see either the test files, or the documentation for the specific class you want to use.
-
file:test/plot.rb
-
file:test/single.rb
-
file:test/test.rb
-
file:test/timeseries.rb
Description¶ ↑
This package should be used as a base for creating SVG
graphs.
Acknowledgements¶ ↑
Leo Lapworth for creating the SVG::TT::Graph package which this Ruby port is based on.
Stephen Morgan for creating the TT template and SVG
.
See¶ ↑
Author¶ ↑
Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
Copyright 2004 Sean E. Russell This software is available under the Ruby license
Constants
- KEY_BOX_SIZE
Attributes
Add popups for the data points on some graphs
Set the font size (in points) of the data point labels
What the subtitle on the graph should be.
What the title on the graph should be.
Set the height of the graph box, this is the total height of the SVG
box created - not the graph it self which auto scales to fix the space.
Whether to show a key, defaults to false, set to true if you want to show it.
Set the key font size
Where the key should be positioned, defaults to :right, set to :bottom if you want to move it.
The point at which the Y axis starts, defaults to '0', if set to nil it will default to the minimum data value.
Do not use CSS if set to true. Many SVG
viewers do not support CSS, but not using CSS can result in larger SVGs as well as making it impossible to change colors after the chart is generated. Defaults to false.
Customize popup radius
This turns the X axis labels by 90 degrees. Default it false, to turn on set to true.
This turns the Y axis labels by 90 degrees. Default it false, to turn on set to true.
This defines the gap between markers on the Y axis, default is a 10th of the max_value, e.g. you will have 10 markers on the Y axis. NOTE: do not set this too low - you are limited to 999 markers, after that the graph won't generate.
Ensures only whole numbers are used as the scale divisions. Default it false, to turn on set to true. This has no effect if scale divisions are less than 1.
(Bool) Show the value of each element of data on the graph
Whether to show a subtitle on the graph, defaults to false, set to true to show.
Whether to show a title on the graph, defaults to false, set to true to show.
Show guidelines for the X axis
Whether to show labels on the X axis or not, defaults to true, set to false if you want to turn them off.
Whether to show the title under the X axis labels, default is false, set to true to show.
Show guidelines for the Y axis
Whether to show labels on the Y axis or not, defaults to true, set to false if you want to turn them off.
Whether to show the title under the Y axis labels, default is false, set to true to show.
This puts the X labels at alternative levels so if they are long field names they will not overlap so easily. Default it false, to turn on set to true.
This puts the Y labels at alternative levels so if they are long field names they will not overlap so easily. Default it false, to turn on set to true.
Whether to (when taking “steps” between X axis labels) step from the first label (i.e. always include the first label) or step from the X axis origin (i.e. start with a gap if step_x_labels
is greater than one).
How many “steps” to use between displayed X axis labels, a step of one means display every label, a step of two results in every other label being displayed (label <gap> label <gap> label), a step of three results in every third label being displayed (label <gap> <gap> label <gap> <gap> label) and so on.
Set the path to an external stylesheet, set to '' if you want to revert back to using the defaut internal version.
To create an external stylesheet create a graph using the default internal version and copy the stylesheet section to an external file and edit from there.
Set the subtitle font size
Set the title font size
Set the width of the graph box, this is the total width of the SVG
box created - not the graph it self which auto scales to fix the space.
Set the font size of the X axis labels
What the title under X axis should be, e.g. 'Months'.
Set the font size of the X axis title
Set the font size of the Y axis labels
What the title under Y axis should be, e.g. 'Sales in thousands'.
Set the font size of the Y axis title
Aligns writing mode for Y axis label. Defaults to :bt (Bottom to Top). Change to :tb (Top to Bottom) to reverse.
Public Class Methods
Initialize the graph object with the graph settings. You won't instantiate this class directly; see the subclass for options.
- width
-
500
- height
-
300
show_x_guidelines
-
false
show_y_guidelines
-
true
show_data_values
-
true
min_scale_value
-
0
show_x_labels
-
true
stagger_x_labels
-
false
rotate_x_labels
-
false
step_x_labels
-
1
step_include_first_x_label
-
true
show_y_labels
-
true
rotate_y_labels
-
false
scale_integers
-
false
show_x_title
-
false
x_title
-
'X Field names'
show_y_title
-
false
y_title_text_direction
-
:bt
y_title
-
'Y Scale'
show_graph_title
-
false
graph_title
-
'Graph Title'
show_graph_subtitle
-
false
graph_subtitle
-
'Graph Sub Title'
- key
-
true,
key_position
-
:right, # bottom or righ
font_size
-
12
title_font_size
-
16
subtitle_font_size
-
14
x_label_font_size
-
12
x_title_font_size
-
14
y_label_font_size
-
12
y_title_font_size
-
14
key_font_size
-
10
no_css
-
false
add_popups
-
false
# File lib/SVG/Graph/Graph.rb 100 def initialize( config ) 101 @config = config 102 @data = nil 103 self.top_align = self.top_font = self.right_align = self.right_font = 0 104 105 init_with({ 106 :width => 500, 107 :height => 300, 108 :show_x_guidelines => false, 109 :show_y_guidelines => true, 110 :show_data_values => true, 111 112 # :min_scale_value => 0, 113 114 :show_x_labels => true, 115 :stagger_x_labels => false, 116 :rotate_x_labels => false, 117 :step_x_labels => 1, 118 :step_include_first_x_label => true, 119 120 :show_y_labels => true, 121 :rotate_y_labels => false, 122 :stagger_y_labels => false, 123 :scale_integers => false, 124 125 :show_x_title => false, 126 :x_title => 'X Field names', 127 128 :show_y_title => false, 129 :y_title_text_direction => :bt, 130 :y_title => 'Y Scale', 131 132 :show_graph_title => false, 133 :graph_title => 'Graph Title', 134 :show_graph_subtitle => false, 135 :graph_subtitle => 'Graph Sub Title', 136 :key => true, 137 :key_position => :right, # bottom or right 138 139 :font_size =>12, 140 :title_font_size =>16, 141 :subtitle_font_size =>14, 142 :x_label_font_size =>12, 143 :y_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 set_defaults if self.respond_to? :set_defaults 153 init_with config 154 end
Public Instance Methods
This method allows you do add data to the graph object. It can be called several times to add more data sets in.
data_sales_02 = [12, 45, 21]; graph.add_data({ :data => data_sales_02, :title => 'Sales 2002' })
# File lib/SVG/Graph/Graph.rb 166 def add_data conf 167 @data = [] unless (defined? @data and !@data.nil?) 168 169 if conf[:data] and conf[:data].kind_of? Array 170 @data << conf 171 else 172 raise "No data provided by #{conf.inspect}" 173 end 174 end
This method processes the template with the data and config which has been set and returns the resulting SVG
.
This method will croak unless at least one data set has been added to the graph object.
print graph.burn
# File lib/SVG/Graph/Graph.rb 193 def burn 194 raise "No data available" unless @data.size > 0 195 196 calculations if methods.include? 'calculations' 197 198 start_svg 199 calculate_graph_dimensions 200 @foreground = Element.new( "g" ) 201 draw_graph 202 draw_titles 203 draw_legend 204 draw_data 205 @graph.add_element( @foreground ) 206 style 207 208 data = "" 209 @doc.write( data, 0 ) 210 211 if @config[:compress] 212 if @@__have_zlib 213 inp, out = IO.pipe 214 gz = Zlib::GzipWriter.new( out ) 215 gz.write data 216 gz.close 217 data = inp.read 218 else 219 data << "<!-- Ruby Zlib not available for SVGZ -->"; 220 end 221 end 222 223 return data 224 end
This method removes all data from the object so that you can reuse it to create a new graph but with the same config options.
graph.clear_data
# File lib/SVG/Graph/Graph.rb 181 def clear_data 182 @data = [] 183 end
Protected Instance Methods
Adds pop-up point information to a graph.
# File lib/SVG/Graph/Graph.rb 414 def add_popup( x, y, label ) 415 txt_width = label.length * font_size * 0.6 + 10 416 tx = (x+txt_width > width ? x-5 : x+5) 417 t = @foreground.add_element( "text", { 418 "x" => tx.to_s, 419 "y" => (y - font_size).to_s, 420 "visibility" => "hidden", 421 }) 422 t.attributes["style"] = "fill: #000; "+ 423 (x+txt_width > width ? "text-anchor: end;" : "text-anchor: start;") 424 t.text = label.to_s 425 t.attributes["id"] = t.object_id.to_s 426 427 @foreground.add_element( "circle", { 428 "cx" => x.to_s, 429 "cy" => y.to_s, 430 "r" => "#{@popup_radius}", 431 "style" => "opacity: 0", 432 "onmouseover" => 433 "document.getElementById(#{t.object_id}).setAttribute('visibility', 'visible' )", 434 "onmouseout" => 435 "document.getElementById(#{t.object_id}).setAttribute('visibility', 'hidden' )", 436 }) 437 438 end
Override this (and call super) to change the margin to the bottom of the plot area. Results in @border_bottom being set.
# File lib/SVG/Graph/Graph.rb 443 def calculate_bottom_margin 444 @border_bottom = 7 445 if key and key_position == :bottom 446 @border_bottom += @data.size * (font_size + 5) 447 @border_bottom += 10 448 end 449 if show_x_labels 450 max_x_label_height_px = (not rotate_x_labels) ? 451 x_label_font_size : 452 get_x_labels.max{|a,b| 453 a.to_s.length<=>b.to_s.length 454 }.to_s.length * x_label_font_size * 0.6 455 @border_bottom += max_x_label_height_px 456 @border_bottom += max_x_label_height_px + 10 if stagger_x_labels 457 end 458 @border_bottom += x_title_font_size + 5 if show_x_title 459 end
Override this (and call super) to change the margin to the left of the plot area. Results in @border_left being set.
# File lib/SVG/Graph/Graph.rb 369 def calculate_left_margin 370 @border_left = 7 371 # Check for Y labels 372 max_y_label_height_px = @rotate_y_labels ? 373 @y_label_font_size : 374 get_y_labels.max{|a,b| 375 a.to_s.length<=>b.to_s.length 376 }.to_s.length * @y_label_font_size * 0.6 377 @border_left += max_y_label_height_px if @show_y_labels 378 @border_left += max_y_label_height_px + 10 if @stagger_y_labels 379 @border_left += y_title_font_size + 5 if @show_y_title 380 end
Override this (and call super) to change the margin to the right of the plot area. Results in @border_right being set.
# File lib/SVG/Graph/Graph.rb 392 def calculate_right_margin 393 @border_right = 7 394 if key and key_position == :right 395 val = keys.max { |a,b| a.length <=> b.length } 396 @border_right += val.length * key_font_size * 0.6 397 @border_right += KEY_BOX_SIZE 398 @border_right += 10 # Some padding around the box 399 end 400 end
Override this (and call super) to change the margin to the top of the plot area. Results in @border_top being set.
# File lib/SVG/Graph/Graph.rb 405 def calculate_top_margin 406 @border_top = 5 407 @border_top += title_font_size if show_graph_title 408 @border_top += 5 409 @border_top += subtitle_font_size if show_graph_subtitle 410 end
Draws the background, axis, and labels.
# File lib/SVG/Graph/Graph.rb 463 def draw_graph 464 @graph = @root.add_element( "g", { 465 "transform" => "translate( #@border_left #@border_top )" 466 }) 467 468 # Background 469 @graph.add_element( "rect", { 470 "x" => "0", 471 "y" => "0", 472 "width" => @graph_width.to_s, 473 "height" => @graph_height.to_s, 474 "class" => "graphBackground" 475 }) 476 477 # Axis 478 @graph.add_element( "path", { 479 "d" => "M 0 0 v#@graph_height", 480 "class" => "axis", 481 "id" => "xAxis" 482 }) 483 @graph.add_element( "path", { 484 "d" => "M 0 #@graph_height h#@graph_width", 485 "class" => "axis", 486 "id" => "yAxis" 487 }) 488 489 draw_x_labels 490 draw_y_labels 491 end
Draws the legend on the graph
# File lib/SVG/Graph/Graph.rb 712 def draw_legend 713 if key 714 group = @root.add_element( "g" ) 715 716 key_count = 0 717 for key_name in keys 718 y_offset = (KEY_BOX_SIZE * key_count) + (key_count * 5) 719 group.add_element( "rect", { 720 "x" => 0.to_s, 721 "y" => y_offset.to_s, 722 "width" => KEY_BOX_SIZE.to_s, 723 "height" => KEY_BOX_SIZE.to_s, 724 "class" => "key#{key_count+1}" 725 }) 726 group.add_element( "text", { 727 "x" => (KEY_BOX_SIZE + 5).to_s, 728 "y" => (y_offset + KEY_BOX_SIZE).to_s, 729 "class" => "keyText" 730 }).text = key_name.to_s 731 key_count += 1 732 end 733 734 case key_position 735 when :right 736 x_offset = @graph_width + @border_left + 10 737 y_offset = @border_top + 20 738 when :bottom 739 x_offset = @border_left + 20 740 y_offset = @border_top + @graph_height + 5 741 if show_x_labels 742 max_x_label_height_px = (not rotate_x_labels) ? 743 x_label_font_size : 744 get_x_labels.max{|a,b| 745 a.to_s.length<=>b.to_s.length 746 }.to_s.length * x_label_font_size * 0.6 747 x_label_font_size 748 y_offset += max_x_label_height_px 749 y_offset += max_x_label_height_px + 5 if stagger_x_labels 750 end 751 y_offset += x_title_font_size + 5 if show_x_title 752 end 753 group.attributes["transform"] = "translate(#{x_offset} #{y_offset})" 754 end 755 end
Draws the graph title and subtitle
# File lib/SVG/Graph/Graph.rb 653 def draw_titles 654 if show_graph_title 655 @root.add_element( "text", { 656 "x" => (width / 2).to_s, 657 "y" => (title_font_size).to_s, 658 "class" => "mainTitle" 659 }).text = graph_title.to_s 660 end 661 662 if show_graph_subtitle 663 y_subtitle = show_graph_title ? 664 title_font_size + 10 : 665 subtitle_font_size 666 @root.add_element("text", { 667 "x" => (width / 2).to_s, 668 "y" => (y_subtitle).to_s, 669 "class" => "subTitle" 670 }).text = graph_subtitle.to_s 671 end 672 673 if show_x_title 674 y = @graph_height + @border_top + x_title_font_size 675 if show_x_labels 676 y += x_label_font_size + 5 if stagger_x_labels 677 y += x_label_font_size + 5 678 end 679 x = width / 2 680 681 @root.add_element("text", { 682 "x" => x.to_s, 683 "y" => y.to_s, 684 "class" => "xAxisTitle", 685 }).text = x_title.to_s 686 end 687 688 if show_y_title 689 x = y_title_font_size + (y_title_text_direction==:bt ? 3 : -3) 690 y = height / 2 691 692 text = @root.add_element("text", { 693 "x" => x.to_s, 694 "y" => y.to_s, 695 "class" => "yAxisTitle", 696 }) 697 text.text = y_title.to_s 698 if y_title_text_direction == :bt 699 text.attributes["transform"] = "rotate( -90, #{x}, #{y} )" 700 else 701 text.attributes["transform"] = "rotate( 90, #{x}, #{y} )" 702 end 703 end 704 end
Draws the X axis guidelines
# File lib/SVG/Graph/Graph.rb 631 def draw_x_guidelines( label_height, count ) 632 if count != 0 633 @graph.add_element( "path", { 634 "d" => "M#{label_height*count} 0 v#@graph_height", 635 "class" => "guideLines" 636 }) 637 end 638 end
Draws the X axis labels
# File lib/SVG/Graph/Graph.rb 520 def draw_x_labels 521 stagger = x_label_font_size + 5 522 if show_x_labels 523 label_width = field_width 524 525 count = 0 526 for label in get_x_labels 527 if step_include_first_x_label == true then 528 step = count % step_x_labels 529 else 530 step = (count + 1) % step_x_labels 531 end 532 533 if step == 0 then 534 text = @graph.add_element( "text" ) 535 text.attributes["class"] = "xAxisLabels" 536 text.text = label.to_s 537 538 x = count * label_width + x_label_offset( label_width ) 539 y = @graph_height + x_label_font_size + 3 540 #t = 0 - (font_size / 2) 541 542 if stagger_x_labels and count % 2 == 1 543 y += stagger 544 @graph.add_element( "path", { 545 "d" => "M#{x} #@graph_height v#{stagger}", 546 "class" => "staggerGuideLine" 547 }) 548 end 549 550 text.attributes["x"] = x.to_s 551 text.attributes["y"] = y.to_s 552 if rotate_x_labels 553 text.attributes["transform"] = 554 "rotate( 90 #{x} #{y-x_label_font_size} )"+ 555 " translate( 0 -#{x_label_font_size/4} )" 556 text.attributes["style"] = "text-anchor: start" 557 else 558 text.attributes["style"] = "text-anchor: middle" 559 end 560 end 561 562 draw_x_guidelines( label_width, count ) if show_x_guidelines 563 count += 1 564 end 565 end 566 end
Draws the Y axis guidelines
# File lib/SVG/Graph/Graph.rb 642 def draw_y_guidelines( label_height, count ) 643 if count != 0 644 @graph.add_element( "path", { 645 "d" => "M0 #{@graph_height-(label_height*count)} h#@graph_width", 646 "class" => "guideLines" 647 }) 648 end 649 end
Draws the Y axis labels
# File lib/SVG/Graph/Graph.rb 589 def draw_y_labels 590 stagger = y_label_font_size + 5 591 if show_y_labels 592 label_height = field_height 593 594 count = 0 595 y_offset = @graph_height + y_label_offset( label_height ) 596 y_offset += font_size/1.2 unless rotate_y_labels 597 for label in get_y_labels 598 y = y_offset - (label_height * count) 599 x = rotate_y_labels ? 0 : -3 600 601 if stagger_y_labels and count % 2 == 1 602 x -= stagger 603 @graph.add_element( "path", { 604 "d" => "M#{x} #{y} h#{stagger}", 605 "class" => "staggerGuideLine" 606 }) 607 end 608 609 text = @graph.add_element( "text", { 610 "x" => x.to_s, 611 "y" => y.to_s, 612 "class" => "yAxisLabels" 613 }) 614 text.text = label.to_s 615 if rotate_y_labels 616 text.attributes["transform"] = "translate( -#{font_size} 0 ) "+ 617 "rotate( 90 #{x} #{y} ) " 618 text.attributes["style"] = "text-anchor: middle" 619 else 620 text.attributes["y"] = (y - (y_label_font_size/2)).to_s 621 text.attributes["style"] = "text-anchor: end" 622 end 623 draw_y_guidelines( label_height, count ) if show_y_guidelines 624 count += 1 625 end 626 end 627 end
# File lib/SVG/Graph/Graph.rb 582 def field_height 583 (@graph_height.to_f - font_size*2*top_font) / 584 (get_y_labels.length - top_align) 585 end
# File lib/SVG/Graph/Graph.rb 576 def field_width 577 (@graph_width.to_f - font_size*2*right_font) / 578 (get_x_labels.length - right_align) 579 end
Overwrite configuration options with supplied options. Used by subclasses.
# File lib/SVG/Graph/Graph.rb 356 def init_with config 357 config.each { |key, value| 358 self.send( key.to_s+"=", value ) if self.respond_to? key 359 } 360 @popup_radius ||= 10 361 end
# File lib/SVG/Graph/Graph.rb 706 def keys 707 i = 0 708 return @data.collect{ |d| i+=1; d[:title] || "Serie #{i}" } 709 end
# File lib/SVG/Graph/Graph.rb 500 def make_datapoint_text( x, y, value, style="" ) 501 if show_data_values 502 @foreground.add_element( "text", { 503 "x" => x.to_s, 504 "y" => y.to_s, 505 "class" => "dataPointLabel", 506 "style" => "#{style} stroke: #fff; stroke-width: 2;" 507 }).text = value.to_s 508 text = @foreground.add_element( "text", { 509 "x" => x.to_s, 510 "y" => y.to_s, 511 "class" => "dataPointLabel" 512 }) 513 text.text = value.to_s 514 text.attributes["style"] = style if style.length > 0 515 end 516 end
Calculates the width of the widest Y label. This will be the character height if the Y labels are rotated
# File lib/SVG/Graph/Graph.rb 385 def max_y_label_width_px 386 return font_size if rotate_y_labels 387 end
# File lib/SVG/Graph/Graph.rb 350 def sort( *arrys ) 351 sort_multiple( arrys ) 352 end
Where in the X area the label is drawn Centered in the field, should be width/2. Start, 0.
# File lib/SVG/Graph/Graph.rb 496 def x_label_offset( width ) 497 0 498 end
Where in the Y area the label is drawn Centered in the field, should be width/2. Start, 0.
# File lib/SVG/Graph/Graph.rb 571 def y_label_offset( height ) 572 0 573 end