| Class | SVG::Graph::Pie |
| In: |
SVG/Graph/Pie.rb
|
| Parent: | Graph |
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();
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.
www.germane-software/repositories/public/SVG/test/single.rb
Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
Copyright 2004 Sean E. Russell This software is available under the Ruby license
| RADIANS | = | Math::PI/180 |
| 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 |
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 SVG/Graph/Pie.rb, line 111
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
Defaults are those set by Graph::initialize, and
# File SVG/Graph/Pie.rb, line 73
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
# File SVG/Graph/Pie.rb, line 147
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
# File SVG/Graph/Pie.rb, line 187
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
# File SVG/Graph/Pie.rb, line 308
308: def get_css
309: return ".dataPointLabel{\n fill: #000000;\n text-anchor:middle;\n font-size: \#{datapoint_font_size}px;\n font-family: \"Arial\", sans-serif;\n font-weight: normal;\n}\n\n/* key - MUST match fill styles */\n.key1,.fill1{\n fill: #ff0000;\n fill-opacity: 0.7;\n stroke: none;\n stroke-width: 1px; \n}\n.key2,.fill2{\n fill: #0000ff;\n fill-opacity: 0.7;\n stroke: none;\n stroke-width: 1px; \n}\n.key3,.fill3{\n fill-opacity: 0.7;\n fill: #00ff00;\n stroke: none;\n stroke-width: 1px; \n}\n.key4,.fill4{\n fill-opacity: 0.7;\n fill: #ffcc00;\n stroke: none;\n stroke-width: 1px; \n}\n.key5,.fill5{\n fill-opacity: 0.7;\n fill: #00ccff;\n stroke: none;\n stroke-width: 1px; \n}\n.key6,.fill6{\n fill-opacity: 0.7;\n fill: #ff00ff;\n stroke: none;\n stroke-width: 1px; \n}\n.key7,.fill7{\n fill-opacity: 0.7;\n fill: #00ff99;\n stroke: none;\n stroke-width: 1px; \n}\n.key8,.fill8{\n fill-opacity: 0.7;\n fill: #ffff00;\n stroke: none;\n stroke-width: 1px; \n}\n.key9,.fill9{\n fill-opacity: 0.7;\n fill: #cc6666;\n stroke: none;\n stroke-width: 1px; \n}\n.key10,.fill10{\n fill-opacity: 0.7;\n fill: #663399;\n stroke: none;\n stroke-width: 1px; \n}\n.key11,.fill11{\n fill-opacity: 0.7;\n fill: #339900;\n stroke: none;\n stroke-width: 1px; \n}\n.key12,.fill12{\n fill-opacity: 0.7;\n fill: #9966FF;\n stroke: none;\n stroke-width: 1px; \n}\n"
310: end
# File SVG/Graph/Pie.rb, line 171
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: a = @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