Parent

Included Modules

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.

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[RW]

Add popups for the data points on some graphs

font_size[RW]

Set the font size (in points) of the data point labels

graph_subtitle[RW]

What the subtitle on the graph should be.

graph_title[RW]

What the title on the graph should be.

height[RW]

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.

key[RW]

Whether to show a key, defaults to false, set to true if you want to show it.

key_font_size[RW]

Set the key font size

key_position[RW]

Where the key should be positioned, defaults to :right, set to :bottom if you want to move it.

min_scale_value[RW]

The point at which the Y axis starts, defaults to '0', if set to nil it will default to the minimum data value.

no_css[RW]

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.

right_align[RW]
right_font[RW]
rotate_x_labels[RW]

This turns the X axis labels by 90 degrees. Default it false, to turn on set to true.

rotate_y_labels[RW]

This turns the Y axis labels by 90 degrees. Default it false, to turn on set to true.

scale_divisions[RW]

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.

scale_integers[RW]

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.

show_data_values[RW]

(Bool) Show the value of each element of data on the graph

show_graph_subtitle[RW]

Whether to show a subtitle on the graph, defaults to false, set to true to show.

show_graph_title[RW]

Whether to show a title on the graph, defaults to false, set to true to show.

show_x_guidelines[RW]

Show guidelines for the X axis

show_x_labels[RW]

Whether to show labels on the X axis or not, defaults to true, set to false if you want to turn them off.

show_x_title[RW]

Whether to show the title under the X axis labels, default is false, set to true to show.

show_y_guidelines[RW]

Show guidelines for the Y axis

show_y_labels[RW]

Whether to show labels on the Y axis or not, defaults to true, set to false if you want to turn them off.

show_y_title[RW]

Whether to show the title under the Y axis labels, default is false, set to true to show.

stagger_x_labels[RW]

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.

stagger_y_labels[RW]

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.

step_include_first_x_label[RW]

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).

step_x_labels[RW]

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.

style_sheet[RW]

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.

subtitle_font_size[RW]

Set the subtitle font size

title_font_size[RW]

Set the title font size

top_align[RW]
top_font[RW]
width[RW]

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.

x_label_font_size[RW]

Set the font size of the X axis labels

x_title[RW]

What the title under X axis should be, e.g. 'Months'.

x_title_font_size[RW]

Set the font size of the X axis title

y_label_font_size[RW]

Set the font size of the Y axis labels

y_title[RW]

What the title under Y axis should be, e.g. 'Sales in thousands'.

y_title_font_size[RW]

Set the font size of the Y axis title

y_title_text_direction[RW]

Aligns writing mode for Y axis label. Defaults to :bt (Bottom to Top). Change to :tb (Top to Bottom) to reverse.

Public Class Methods

new( config ) click to toggle source

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, line 100
      def initialize( config )
        @config = config
        @data = nil
        self.top_align = self.top_font = self.right_align = self.right_font = 0

        init_with({
          :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,
          :stagger_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 right

          :font_size            =>12,
          :title_font_size      =>16,
          :subtitle_font_size   =>14,
          :x_label_font_size    =>12,
          :y_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,
        })
        set_defaults if self.respond_to? :set_defaults
        init_with config
      end

Public Instance Methods

add_data(conf) click to toggle source

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, line 166
def add_data conf
    @data = [] unless (defined? @data and !@data.nil?) 

  if conf[:data] and conf[:data].kind_of? Array
    @data << conf
  else
    raise "No data provided by #{conf.inspect}"
  end
end
burn() click to toggle source

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, line 193
def burn
  raise "No data available" unless @data.size > 0
  
  calculations if methods.include? 'calculations'

  start_svg
  calculate_graph_dimensions
  @foreground = Element.new( "g" )
  draw_graph
  draw_titles
  draw_legend
  draw_data
  @graph.add_element( @foreground )
  style

  data = ""
  @doc.write( data, 0 )

  if @config[:compress]
    if @@__have_zlib
      inp, out = IO.pipe
      gz = Zlib::GzipWriter.new( out )
      gz.write data
      gz.close
      data = inp.read
    else
      data << "<!-- Ruby Zlib not available for SVGZ -->";
    end
  end
  
  return data
end
clear_data() click to toggle source

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, line 181
def clear_data 
  @data = []
end

Protected Instance Methods

add_popup( x, y, label ) click to toggle source

Adds pop-up point information to a graph.

# File lib/SVG/Graph/Graph.rb, line 414
def add_popup( x, y, label )
  txt_width = label.length * font_size * 0.6 + 10
  tx = (x+txt_width > width ? x-5 : x+5)
  t = @foreground.add_element( "text", {
    "x" => tx.to_s,
    "y" => (y - font_size).to_s,
    "visibility" => "hidden",
  })
  t.attributes["style"] = "fill: #000; "+
    (x+txt_width > width ? "text-anchor: end;" : "text-anchor: start;")
  t.text = label.to_s
  t.attributes["id"] = t.object_id.to_s

  @foreground.add_element( "circle", {
    "cx" => x.to_s,
    "cy" => y.to_s,
    "r" => "#{@popup_radius}",
    "style" => "opacity: 0",
    "onmouseover" => 
      "document.getElementById(#{t.object_id}).setAttribute('visibility', 'visible' )",
    "onmouseout" => 
      "document.getElementById(#{t.object_id}).setAttribute('visibility', 'hidden' )",
  })

end
calculate_bottom_margin() click to toggle source

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, line 443
def calculate_bottom_margin
  @border_bottom = 7
  if key and key_position == :bottom
    @border_bottom += @data.size * (font_size + 5)
    @border_bottom += 10
  end
  if show_x_labels
            max_x_label_height_px = (not rotate_x_labels) ? 
      x_label_font_size :
      get_x_labels.max{|a,b| 
        a.to_s.length<=>b.to_s.length
      }.to_s.length * x_label_font_size * 0.6
    @border_bottom += max_x_label_height_px
    @border_bottom += max_x_label_height_px + 10 if stagger_x_labels
  end
  @border_bottom += x_title_font_size + 5 if show_x_title
end
calculate_left_margin() click to toggle source

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, line 369
def calculate_left_margin
  @border_left = 7
  # Check for Y labels
  max_y_label_height_px = @rotate_y_labels ? 
    @y_label_font_size :
    get_y_labels.max{|a,b| 
      a.to_s.length<=>b.to_s.length
    }.to_s.length * @y_label_font_size * 0.6
  @border_left += max_y_label_height_px if @show_y_labels
  @border_left += max_y_label_height_px + 10 if @stagger_y_labels
  @border_left += y_title_font_size + 5 if @show_y_title
end
calculate_right_margin() click to toggle source

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, line 392
def calculate_right_margin
  @border_right = 7
  if key and key_position == :right
    val = keys.max { |a,b| a.length <=> b.length }
    @border_right += val.length * key_font_size * 0.6 
    @border_right += KEY_BOX_SIZE
    @border_right += 10    # Some padding around the box
  end
end
calculate_top_margin() click to toggle source

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, line 405
def calculate_top_margin
  @border_top = 5
  @border_top += title_font_size if show_graph_title
  @border_top += 5
  @border_top += subtitle_font_size if show_graph_subtitle
end
draw_graph() click to toggle source

Draws the background, axis, and labels.

# File lib/SVG/Graph/Graph.rb, line 463
def draw_graph
  @graph = @root.add_element( "g", {
    "transform" => "translate( #@border_left #@border_top )"
  })

  # Background
  @graph.add_element( "rect", {
    "x" => "0",
    "y" => "0",
    "width" => @graph_width.to_s,
    "height" => @graph_height.to_s,
    "class" => "graphBackground"
  })

  # Axis
  @graph.add_element( "path", {
    "d" => "M 0 0 v#@graph_height",
    "class" => "axis",
    "id" => "xAxis"
  })
  @graph.add_element( "path", {
    "d" => "M 0 #@graph_height h#@graph_width",
    "class" => "axis",
    "id" => "yAxis"
  })

  draw_x_labels
  draw_y_labels
end
draw_legend() click to toggle source

Draws the legend on the graph

# File lib/SVG/Graph/Graph.rb, line 712
def draw_legend
  if key
    group = @root.add_element( "g" )

    key_count = 0
    for key_name in keys
      y_offset = (KEY_BOX_SIZE * key_count) + (key_count * 5)
      group.add_element( "rect", {
        "x" => 0.to_s,
        "y" => y_offset.to_s,
        "width" => KEY_BOX_SIZE.to_s,
        "height" => KEY_BOX_SIZE.to_s,
        "class" => "key#{key_count+1}"
      })
      group.add_element( "text", {
        "x" => (KEY_BOX_SIZE + 5).to_s,
        "y" => (y_offset + KEY_BOX_SIZE).to_s,
        "class" => "keyText"
      }).text = key_name.to_s
      key_count += 1
    end

    case key_position
    when :right
      x_offset = @graph_width + @border_left + 10
      y_offset = @border_top + 20
    when :bottom
      x_offset = @border_left + 20
      y_offset = @border_top + @graph_height + 5
      if show_x_labels
                    max_x_label_height_px = (not rotate_x_labels) ? 
                          x_label_font_size :
                          get_x_labels.max{|a,b| 
                            a.to_s.length<=>b.to_s.length
                          }.to_s.length * x_label_font_size * 0.6
          x_label_font_size
        y_offset += max_x_label_height_px
        y_offset += max_x_label_height_px + 5 if stagger_x_labels
      end
      y_offset += x_title_font_size + 5 if show_x_title
    end
    group.attributes["transform"] = "translate(#{x_offset} #{y_offset})"
  end
end
draw_titles() click to toggle source

Draws the graph title and subtitle

# File lib/SVG/Graph/Graph.rb, line 653
def draw_titles
  if show_graph_title
    @root.add_element( "text", {
      "x" => (width / 2).to_s,
      "y" => (title_font_size).to_s,
      "class" => "mainTitle"
    }).text = graph_title.to_s
  end

  if show_graph_subtitle
    y_subtitle = show_graph_title ? 
      title_font_size + 10 :
      subtitle_font_size
    @root.add_element("text", {
      "x" => (width / 2).to_s,
      "y" => (y_subtitle).to_s,
      "class" => "subTitle"
    }).text = graph_subtitle.to_s
  end

  if show_x_title
    y = @graph_height + @border_top + x_title_font_size
    if show_x_labels
      y += x_label_font_size + 5 if stagger_x_labels
      y += x_label_font_size + 5
    end
    x = width / 2

    @root.add_element("text", {
      "x" => x.to_s,
      "y" => y.to_s,
      "class" => "xAxisTitle",
    }).text = x_title.to_s
  end

  if show_y_title
    x = y_title_font_size + (y_title_text_direction==:bt ? 3 : -3)
    y = height / 2

    text = @root.add_element("text", {
      "x" => x.to_s,
      "y" => y.to_s,
      "class" => "yAxisTitle",
    })
    text.text = y_title.to_s
    if y_title_text_direction == :bt
      text.attributes["transform"] = "rotate( -90, #{x}, #{y} )"
    else
      text.attributes["transform"] = "rotate( 90, #{x}, #{y} )"
    end
  end
end
draw_x_guidelines( label_height, count ) click to toggle source

Draws the X axis guidelines

# File lib/SVG/Graph/Graph.rb, line 631
def draw_x_guidelines( label_height, count )
  if count != 0
    @graph.add_element( "path", {
      "d" => "M#{label_height*count} 0 v#@graph_height",
      "class" => "guideLines"
    })
  end
end
draw_x_labels() click to toggle source

Draws the X axis labels

# File lib/SVG/Graph/Graph.rb, line 520
def draw_x_labels
  stagger = x_label_font_size + 5
  if show_x_labels
    label_width = field_width

    count = 0
    for label in get_x_labels
      if step_include_first_x_label == true then
        step = count % step_x_labels
      else
        step = (count + 1) % step_x_labels
      end

      if step == 0 then
        text = @graph.add_element( "text" )
        text.attributes["class"] = "xAxisLabels"
        text.text = label.to_s

        x = count * label_width + x_label_offset( label_width )
        y = @graph_height + x_label_font_size + 3
        #t = 0 - (font_size / 2)

        if stagger_x_labels and count % 2 == 1
          y += stagger
          @graph.add_element( "path", {
            "d" => "M#{x} #@graph_height v#{stagger}",
            "class" => "staggerGuideLine"
          })
        end

        text.attributes["x"] = x.to_s
        text.attributes["y"] = y.to_s
        if rotate_x_labels
          text.attributes["transform"] = 
            "rotate( 90 #{x} #{y-x_label_font_size} )"+
            " translate( 0 -#{x_label_font_size/4} )"
          text.attributes["style"] = "text-anchor: start"
        else
          text.attributes["style"] = "text-anchor: middle"
        end
      end

      draw_x_guidelines( label_width, count ) if show_x_guidelines
      count += 1
    end
  end
end
draw_y_guidelines( label_height, count ) click to toggle source

Draws the Y axis guidelines

# File lib/SVG/Graph/Graph.rb, line 642
def draw_y_guidelines( label_height, count )
  if count != 0
    @graph.add_element( "path", {
      "d" => "M0 #{@graph_height-(label_height*count)} h#@graph_width",
      "class" => "guideLines"
    })
  end
end
draw_y_labels() click to toggle source

Draws the Y axis labels

# File lib/SVG/Graph/Graph.rb, line 589
def draw_y_labels
  stagger = y_label_font_size + 5
  if show_y_labels
    label_height = field_height

    count = 0
    y_offset = @graph_height + y_label_offset( label_height )
    y_offset += font_size/1.2 unless rotate_y_labels
    for label in get_y_labels
      y = y_offset - (label_height * count)
      x = rotate_y_labels ? 0 : -3

      if stagger_y_labels and count % 2 == 1
        x -= stagger
        @graph.add_element( "path", {
          "d" => "M#{x} #{y} h#{stagger}",
          "class" => "staggerGuideLine"
        })
      end

      text = @graph.add_element( "text", {
        "x" => x.to_s,
        "y" => y.to_s,
        "class" => "yAxisLabels"
      })
      text.text = label.to_s
      if rotate_y_labels
        text.attributes["transform"] = "translate( -#{font_size} 0 ) "+
          "rotate( 90 #{x} #{y} ) "
        text.attributes["style"] = "text-anchor: middle"
      else
        text.attributes["y"] = (y - (y_label_font_size/2)).to_s
        text.attributes["style"] = "text-anchor: end"
      end
      draw_y_guidelines( label_height, count ) if show_y_guidelines
      count += 1
    end
  end
end
field_height() click to toggle source
# File lib/SVG/Graph/Graph.rb, line 582
def field_height
  (@graph_height.to_f - font_size*2*top_font) /
     (get_y_labels.length - top_align)
end
field_width() click to toggle source
# File lib/SVG/Graph/Graph.rb, line 576
def field_width
  (@graph_width.to_f - font_size*2*right_font) /
     (get_x_labels.length - right_align)
end
init_with(config) click to toggle source

Overwrite configuration options with supplied options. Used by subclasses.

# File lib/SVG/Graph/Graph.rb, line 356
def init_with config
  config.each { |key, value|
      self.send( key.to_s+"=", value ) if self.respond_to?  key
  }
  @popup_radius ||= 10
end
keys() click to toggle source
# File lib/SVG/Graph/Graph.rb, line 706
def keys 
  i = 0
  return @data.collect{ |d| i+=1; d[:title] || "Serie #{i}" }
end
make_datapoint_text( x, y, value, style="" ) click to toggle source
# File lib/SVG/Graph/Graph.rb, line 500
def make_datapoint_text( x, y, value, style="" )
  if show_data_values
    @foreground.add_element( "text", {
      "x" => x.to_s,
      "y" => y.to_s,
      "class" => "dataPointLabel",
      "style" => "#{style} stroke: #fff; stroke-width: 2;"
    }).text = value.to_s
    text = @foreground.add_element( "text", {
      "x" => x.to_s,
      "y" => y.to_s,
      "class" => "dataPointLabel"
    })
    text.text = value.to_s
    text.attributes["style"] = style if style.length > 0
  end
end
max_y_label_width_px() click to toggle source

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, line 385
def max_y_label_width_px
  return font_size if rotate_y_labels
end
sort( *arrys ) click to toggle source
# File lib/SVG/Graph/Graph.rb, line 350
def sort( *arrys )
  sort_multiple( arrys )
end
x_label_offset( width ) click to toggle source

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, line 496
def x_label_offset( width )
  0
end
y_label_offset( height ) click to toggle source

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, line 571
def y_label_offset( height )
  0
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.