File: ErrBar.rb

package info (click to toggle)
ruby-svg-graph 2.2.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,312 kB
  • sloc: javascript: 23,548; ruby: 4,234; xml: 224; makefile: 2
file content (198 lines) | stat: -rw-r--r-- 6,016 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
require 'rexml/document'
require_relative 'Graph'
require_relative 'BarBase'

module SVG
  module Graph
    # === Create presentation quality SVG bar graphs easily
    #
    # = Synopsis
    #
    #   require 'SVG/Graph/ErrBar'
    #
    #   fields = %w(Jan Feb);
    #   myarr1_mean = 10
    #   myarr1_confidence = 1
    #
    #   myarr2_mean = 20
    #   myarr2_confidence = 2
    #
    #   data= [myarr1_mean, myarr2_mean]
    #
    #   err_mesure = [myarr1_confidence, myarr2_confidence]
    #
    #   graph = SVG::Graph::ErrBar.new(
    #     :height => 500,
    #     :width => 600,
    #     :fields => fields,
    #     :errorBars => err_mesure,
    #     :scale_integers => true,
    #   )
    #
    #   graph.add_data(
    #     :data => data,
    #     :title => 'Sales 2002'
    #   )
    #
    #   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[http://www.w3c.org/tr/svg bar 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.
    #
    # = Notes
    #
    # The default stylesheet handles upto 12 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 12 data sets as they will have no style and
    # be in black.
    #
    # = Examples
    #
    # * http://germane-software.com/repositories/public/SVG/test/test.rb
    #
    # = See also
    #
    # * SVG::Graph::Graph
    # * SVG::Graph::BarHorizontal
    # * SVG::Graph::Line
    # * SVG::Graph::Pie
    # * SVG::Graph::Plot
    # * SVG::Graph::TimeSeries
    class ErrBar < BarBase
      include REXML

      def initialize config
          raise "fields was not supplied or is empty" unless config[:errorBars] &&
          config[:errorBars].kind_of?(Array) &&
          config[:errorBars].length > 0
          super
      end
      # Array of confidence values for each item in :fields. A range from
      # value[i]-errorBars[i] to value[i]+errorBars[i] is drawn into the graph.
      attr_accessor :errorBars

      protected

      def get_x_labels
        @config[:fields]
      end

      def get_y_labels
        maxvalue = max_value
        minvalue = min_value
        range = maxvalue - minvalue
        # add some padding on top of the graph
        if range == 0
          maxvalue += 10
        else
          maxvalue += range / 20.0
        end
        scale_range = maxvalue - minvalue

        @y_scale_division = scale_divisions || (scale_range / 10.0)

        if scale_integers
          @y_scale_division = @y_scale_division < 1 ? 1 : @y_scale_division.round
        end

        rv = []
        maxvalue = maxvalue%@y_scale_division == 0 ?
          maxvalue : maxvalue + @y_scale_division
        minvalue.step( maxvalue, @y_scale_division ) {|v| rv << v}
        return rv
      end

      def x_label_offset( width )
        width / 2.0
      end

      def draw_data
        minvalue = min_value
        fieldwidth = field_width

        unit_size = field_height
        bargap = bar_gap ? (fieldwidth < 10 ? fieldwidth / 2 : 10) : 0

        bar_width = fieldwidth - (bargap *2)
        bar_width /= @data.length if stack == :side

        bottom = @graph_height

        field_count = 0
        @config[:fields].each_index { |i|
          dataset_count = 0
          for dataset in @data

            # cases (assume 0 = +ve):
            #   value  min  length
            #    +ve   +ve  value - min
            #    +ve   -ve  value - 0
            #    -ve   -ve  value.abs - 0

            value = dataset[:data][i].to_f/@y_scale_division

            left = (fieldwidth * field_count)
            left += bargap


            length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
            # top is 0 if value is negative
            top = bottom - (((value < 0 ? 0 : value) - minvalue) * unit_size)
            left += bar_width * dataset_count if stack == :side

            @graph.add_element( "rect", {
              "x" => left.to_s,
              "y" => top.to_s,
              "width" => bar_width.to_s,
              "height" => length.to_s,
              "class" => "fill#{dataset_count+1}"
            })

            threshold = @config[:errorBars][i].to_f/@y_scale_division * unit_size
            middlePointErr = left+bar_width/2
            upperErr = top+threshold
            bottomErr = top-threshold
            withthErr = bar_width/4

            @graph.add_element( "line", {
              "x1" => middlePointErr.to_s,
              "y1" => upperErr.to_s,
              "x2" => middlePointErr.to_s,
              "y2" => bottomErr.to_s,
              "style" => "stroke:rgb(0,0,0);stroke-width:1"
            })

            @graph.add_element( "line", {
              "x1" => (middlePointErr-withthErr).to_s,
              "y1" => upperErr.to_s,
              "x2" => (middlePointErr+withthErr).to_s,
              "y2" => upperErr.to_s,
              "style" => "stroke:rgb(0,0,0);stroke-width:1"
            })
            @graph.add_element( "line", {
              "x1" => (middlePointErr-withthErr).to_s,
              "y1" => bottomErr.to_s,
              "x2" => (middlePointErr+withthErr).to_s,
              "y2" => bottomErr.to_s,
              "style" => "stroke:rgb(0,0,0);stroke-width:1"
            })

            make_datapoint_text(left + bar_width/2.0, top - 6, dataset[:data][i])
            # number format shall not apply to popup (use .to_s conversion)
            add_popup(left + bar_width/2.0, top , dataset[:data][i].to_s)
            dataset_count += 1
          end
          field_count += 1
        } # config[:fields].each_index
      end # draw_data

    end # ErrBar
  end
end