class SVG::Graph::Pie

Create presentation quality SVG pie graphs easily

Synopsis

require 'SVG/Graph/Pie'

fields = %w(Jan Feb Mar)
data_sales_02 = [12, 45, 21]

graph = SVG::Graph::Pie.new({
        :height => 500,
  :width  => 300,
  :fields => fields,
})

graph.add_data({
        :data => data_sales_02,
  :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 pie 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, display percent on pie chart, title, subtitle etc.

Examples

www.germane-software/repositories/public/SVG/test/single.rb

See also

Author

Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>

Copyright 2004 Sean E. Russell This software is available under the Ruby license

Constants

RADIANS

Attributes

datapoint_font_size[RW]

The font size of the data point labels

expand_gap[RW]

The amount of space between expanded wedges

expand_greatest[RW]

If true, expand the largest pie wedge

expanded[RW]

If true, “explode” the pie (put space between the wedges)

shadow_offset[RW]

Sets the offset of the shadow from the pie chart

show_actual_values[RW]

If true, display the actual field values in the data labels

show_data_labels[RW]

If true, display the data labels on the chart

show_key_actual_values[RW]

If true, display the actual value of the field in the key

show_key_data_labels[RW]

If true, display the labels in the key

show_key_percent[RW]

If true, display the percentage value of the wedges in the key

show_percent[RW]

If true, display the percentage value of each pie wedge in the data labels

show_shadow[RW]

If true, displays a drop shadow for the chart

Public Instance Methods

add_data(arg) click to toggle source

Adds a data set to the graph.

graph.add_data( { :data => [1,2,3,4] } )

Note that the :title is not necessary. If multiple data sets are added to the graph, the pie chart will display the sums of the data. EG:

graph.add_data( { :data => [1,2,3,4] } )
graph.add_data( { :data => [2,3,5,9] } )

is the same as:

graph.add_data( { :data => [3,5,8,13] } )
    # File lib/SVG/Graph/Pie.rb
111 def add_data arg
112   arg[:data].each_index {|idx|
113     @data[idx] = 0 unless @data[idx]
114     @data[idx] += arg[:data][idx]
115   }
116 end
set_defaults() click to toggle source

Defaults are those set by Graph::initialize, and

show_shadow

true

shadow_offset

10

show_data_labels

false

show_actual_values

false

show_percent

true

show_key_data_labels

true

show_key_actual_values

true

show_key_percent

false

expanded

false

expand_greatest

false

expand_gap

10

show_x_labels

false

show_y_labels

false

datapoint_font_size

12

   # File lib/SVG/Graph/Pie.rb
73 def set_defaults
74   init_with(
75     :show_shadow                  => true,
76     :shadow_offset                => 10, 
77     
78     :show_data_labels           => false,
79     :show_actual_values     => false,
80     :show_percent                 => true,
81 
82     :show_key_data_labels   => true,
83     :show_key_actual_values => true,
84     :show_key_percent                 => false,
85     
86     :expanded                                     => false,
87     :expand_greatest                  => false,
88     :expand_gap             => 10,
89     
90     :show_x_labels          => false,
91     :show_y_labels          => false,
92     :datapoint_font_size    => 12
93   )
94   @data = []
95 end

Protected Instance Methods

add_defs(defs) click to toggle source
    # File lib/SVG/Graph/Pie.rb
147 def add_defs defs
148   gradient = defs.add_element( "filter", {
149     "id"=>"dropshadow",
150     "width" => "1.2",
151     "height" => "1.2",
152   } )
153   gradient.add_element( "feGaussianBlur", {
154     "stdDeviation" => "4",
155     "result" => "blur"
156   })
157 end
draw_data() click to toggle source
    # File lib/SVG/Graph/Pie.rb
187 def draw_data
188   @graph = @root.add_element( "g" )
189   background = @graph.add_element("g")
190   midground = @graph.add_element("g")
191 
192   diameter = @graph_height > @graph_width ? @graph_width : @graph_height
193   diameter -= expand_gap if expanded or expand_greatest
194   diameter -= datapoint_font_size if show_data_labels
195   diameter -= 10 if show_shadow
196   radius = diameter / 2.0
197 
198   xoff = (width - diameter) / 2
199   yoff = (height - @border_bottom - diameter)
200   yoff -= 10 if show_shadow
201   @graph.attributes['transform'] = "translate( #{xoff} #{yoff} )"
202 
203   wedge_text_pad = 5
204   wedge_text_pad = 20 if show_percent and show_data_labels
205 
206   total = 0
207   max_value = 0
208   @data.each {|x| 
209     max_value = max_value < x ? x : max_value
210     total += x 
211   }
212   percent_scale = 100.0 / total
213 
214   prev_percent = 0
215   rad_mult = 3.6 * RADIANS
216   @config[:fields].each_index { |count|
217     value = @data[count]
218     percent = percent_scale * value
219 
220     radians = prev_percent * rad_mult
221     x_start = radius+(Math.sin(radians) * radius)
222     y_start = radius-(Math.cos(radians) * radius)
223     radians = (prev_percent+percent) * rad_mult
224     x_end = radius+(Math.sin(radians) * radius)
225     x_end -= 0.00001 if @data.length == 1
226     y_end = radius-(Math.cos(radians) * radius)
227     path = "M#{radius},#{radius} L#{x_start},#{y_start} "+
228       "A#{radius},#{radius} "+
229       "0, #{percent >= 50 ? '1' : '0'},1, "+
230       "#{x_end} #{y_end} Z"
231 
232 
233     wedge = @foreground.add_element( "path", {
234       "d" => path,
235       "class" => "fill#{count+1}"
236     })
237 
238     translate = nil
239     tx = 0
240     ty = 0
241     half_percent = prev_percent + percent / 2
242     radians = half_percent * rad_mult
243 
244     if show_shadow
245       shadow = background.add_element( "path", {
246         "d" => path,
247         "filter" => "url(#dropshadow)",
248         "style" => "fill: #ccc; stroke: none;"
249       })
250       clear = midground.add_element( "path", {
251         "d" => path,
252         "style" => "fill: #fff; stroke: none;"
253       })
254     end
255 
256     if expanded or (expand_greatest && value == max_value)
257       tx = (Math.sin(radians) * expand_gap)
258       ty = -(Math.cos(radians) * expand_gap)
259       translate = "translate( #{tx} #{ty} )"
260       wedge.attributes["transform"] = translate
261       clear.attributes["transform"] = translate if clear
262     end
263 
264     if show_shadow
265       shadow.attributes["transform"] = 
266         "translate( #{tx+shadow_offset} #{ty+shadow_offset} )"
267     end
268     
269     if show_data_labels and value != 0
270       label = ""
271       label += @config[:fields][count] if show_key_data_labels
272       label += " ["+value.to_s+"]" if show_actual_values
273       label += " "+percent.round.to_s+"%" if show_percent
274 
275       msr = Math.sin(radians)
276       mcr = Math.cos(radians)
277       tx = radius + (msr * radius)
278       ty = radius - (mcr * radius)
279 
280       if expanded or (expand_greatest && value == max_value)
281         tx += (msr * expand_gap)
282         ty -= (mcr * expand_gap)
283       end
284       @foreground.add_element( "text", {
285         "x" => tx.to_s,
286         "y" => ty.to_s,
287         "class" => "dataPointLabel",
288         "style" => "stroke: #fff; stroke-width: 2;"
289       }).text = label.to_s
290       @foreground.add_element( "text", {
291         "x" => tx.to_s,
292         "y" => ty.to_s,
293         "class" => "dataPointLabel",
294       }).text = label.to_s
295     end
296 
297     prev_percent += percent
298   }
299 end
draw_graph() click to toggle source

We don't need the graph

    # File lib/SVG/Graph/Pie.rb
160 def draw_graph
161 end
get_css() click to toggle source
    # File lib/SVG/Graph/Pie.rb
308       def get_css
309         return <<EOL
310 .dataPointLabel{
311         fill: #000000;
312         text-anchor:middle;
313         font-size: #{datapoint_font_size}px;
314         font-family: "Arial", sans-serif;
315         font-weight: normal;
316 }
317 
318 /* key - MUST match fill styles */
319 .key1,.fill1{
320         fill: #ff0000;
321         fill-opacity: 0.7;
322         stroke: none;
323         stroke-width: 1px;     
324 }
325 .key2,.fill2{
326         fill: #0000ff;
327         fill-opacity: 0.7;
328         stroke: none;
329         stroke-width: 1px;     
330 }
331 .key3,.fill3{
332         fill-opacity: 0.7;
333         fill: #00ff00;
334         stroke: none;
335         stroke-width: 1px;     
336 }
337 .key4,.fill4{
338         fill-opacity: 0.7;
339         fill: #ffcc00;
340         stroke: none;
341         stroke-width: 1px;     
342 }
343 .key5,.fill5{
344         fill-opacity: 0.7;
345         fill: #00ccff;
346         stroke: none;
347         stroke-width: 1px;     
348 }
349 .key6,.fill6{
350         fill-opacity: 0.7;
351         fill: #ff00ff;
352         stroke: none;
353         stroke-width: 1px;     
354 }
355 .key7,.fill7{
356         fill-opacity: 0.7;
357         fill: #00ff99;
358         stroke: none;
359         stroke-width: 1px;     
360 }
361 .key8,.fill8{
362         fill-opacity: 0.7;
363         fill: #ffff00;
364         stroke: none;
365         stroke-width: 1px;     
366 }
367 .key9,.fill9{
368         fill-opacity: 0.7;
369         fill: #cc6666;
370         stroke: none;
371         stroke-width: 1px;     
372 }
373 .key10,.fill10{
374         fill-opacity: 0.7;
375         fill: #663399;
376         stroke: none;
377         stroke-width: 1px;     
378 }
379 .key11,.fill11{
380         fill-opacity: 0.7;
381         fill: #339900;
382         stroke: none;
383         stroke-width: 1px;     
384 }
385 .key12,.fill12{
386         fill-opacity: 0.7;
387         fill: #9966FF;
388         stroke: none;
389         stroke-width: 1px;     
390 }
391 EOL
392       end
get_x_labels() click to toggle source
    # File lib/SVG/Graph/Pie.rb
167 def get_x_labels
168   [""]
169 end
get_y_labels() click to toggle source
    # File lib/SVG/Graph/Pie.rb
163 def get_y_labels
164   [""]
165 end
keys() click to toggle source
    # File lib/SVG/Graph/Pie.rb
171 def keys
172   total = 0
173   #max_value = 0
174   @data.each {|x| total += x }
175   percent_scale = 100.0 / total
176   count = -1
177   @config[:fields].collect{ |x|
178     count += 1
179     v = @data[count]
180     perc = show_key_percent ? " "+(v * percent_scale).round.to_s+"%" : ""
181     x + " [" + v.to_s + "]" + perc
182   }
183 end
round(val, to) click to toggle source
    # File lib/SVG/Graph/Pie.rb
302 def round val, to
303   up = 10**to.to_f
304   (val * up).to_i / up
305 end