class DYI::Chart::PieChart
+PieChart+ creates the image of pie chart.
Basic Usage¶ ↑
Using +PieChart+ and ArrayReader (or sub class of ArrayReader), you can create the pie chart as the following: require 'rubygems' require 'dyi' # Nominal GDP of Asian Countries (2010) chart_data = [['China', 5878], ['Japan', 5459], ['India', 1538], ['South Koria', 1007], ['Other Countries', 2863]] reader = DYI::Chart::ArrayReader.read(chart_data, :schema => [:name, :value]) # Creates the Pie Chart chart = DYI::Chart::PieChart.new(450,250) chart.load_data(reader) chart.save('asian_gdp.svg') See {ArrayReader} about how to set the chart data. The chart options of +PieChart+ are specified at the constractor. See <em>Instance Attribute</em> of this class, {Base} and {Legend} for the attributes that can be specified. The specified attributes can be refered and set. # Creates the Pie Chart chart = DYI::Chart::PieChart.new(500,250, :center_point => [130, 100], :legend_point => [250, 50], :represent_3d => true, :_3d_settings => {:dy => 20}, :legend_format => "{?name}\t{!e}{?value:#,0}\t{!e}({?percent:0.0%})", :chart_stroke_color => 'white') puts chart.represent_3d? # => true chart.show_baloon = false puts chart.show_baloon? # => false chart.load_data(reader) chart.save('asian_gdp.svg')
Adds Custom Elements to Chart
¶ ↑
Using {#canvas canvas} attribute, you can add arbitrary graphical elements. # Creates the Pie Chart chart = DYI::Chart::PieChart.new(500,250, :center_point => [130, 100], :legend_point => [250, 50], :represent_3d => true, :_3d_settings => {:dy => 20}, :legend_format => "{?name}\t{!e}{?value:#,0}\t{!e}({?percent:0.0%})", :chart_stroke_color => 'white') DYI::Drawing::Pen.black_pen.draw_text(chart.canvas, [250, 20], 'Nominal GDP of Asian Countries (2010)', :text_anchor => 'middle') chart.load_data(reader) chart.save('asian_gdp.svg') @see LineChart @see ArrayReader @since 0.0.0
Attributes
chart_canvas[R]
Returns the container element which body of chart is drawn on. @return [Shape::ShapeGroup] the container element which chart parts is
drawn on
data_label_canvas[R]
Returns the container element which data labels is drawn on. @return [Shape::ShapeGroup] the container element which chart parts is
drawn on
legend_canvas[R]
Returns the container element which legend is drawn on. @return [Shape::ShapeGroup] the container element which chart parts is
drawn on
Public Instance Methods
back_translate_value()
click to toggle source
# File lib/dyi/chart/pie_chart.rb, line 222 def back_translate_value {:dy => (Length.new_or_nil(_3d_settings[:dy]) || chart_radius_y.quo(2))} end
get_baloon_background_color(index)
click to toggle source
@since 1.0.0
# File lib/dyi/chart/pie_chart.rb, line 227 def get_baloon_background_color(index) (baloon_background_colors && baloon_background_colors[index]) || baloon_background_color || chart_color(index).merge('white', 0.7) end
get_baloon_border_color(index)
click to toggle source
@since 1.0.0
# File lib/dyi/chart/pie_chart.rb, line 234 def get_baloon_border_color(index) (baloon_border_colors && baloon_border_colors[index]) || baloon_border_color || chart_color(index).merge('black', 0.3) end
Private Instance Methods
create_vector_image()
click to toggle source
Calls superclass method
DYI::Chart::Base#create_vector_image
# File lib/dyi/chart/pie_chart.rb, line 267 def create_vector_image super if represent_3d? brush = Drawing::ColumnBrush.new(back_translate_value.merge(chart_stroke_color ? {:stroke_width => chart_stroke_width, :stroke => chart_stroke_color} : {})) else brush = Drawing::Brush.new(chart_stroke_color ? {:stroke_width => chart_stroke_width, :stroke => chart_stroke_color, :stroke_miterlimit => chart_stroke_width} : {}) end attrs = if pie_css_class && !pie_css_class.empty? {:css_class => pie_css_class} else {} end @chart_canvas = Shape::ShapeGroup.draw_on(@canvas) attrs = if data_label_css_class && !data_label_css_class.empty? {:css_class => data_label_css_class} else {} end @data_label_canvas = Shape::ShapeGroup.draw_on(@canvas, attrs) attrs = if legend_css_class && !legend_css_class.empty? {:css_class => legend_css_class} else {} end @legend_canvas = Shape::ShapeGroup.draw_on(@canvas, attrs) @legends = [] @sectors = [] draw_legend(data.records, nil) total_value = data.inject(0.0) {|sum, record| sum + (record.value || 0)} accumulation = 0.0 accumulations = [] stop_index = -1 data.each_with_index do |record, i| accumulations.push(accumulation) value = record.value if value && total_value > (accumulation + value) * 2 && value != 0.0 brush.color = chart_color(i) draw_chart(brush, record, accumulation, total_value, i) stop_index = i end accumulation += value if value end (data.records_size - 1).downto(stop_index + 1) do |i| value = (record = data.records[i]).value if value && value != 0.0 brush.color = chart_color(i) draw_chart(brush, record, accumulations[i], total_value, i) end end @sectors.each_with_index do |sector, i| @legends.each_with_index do |legend, j| if i != j sector.parent.add_painting_animation(:to => {:opacity => 0.35}, :duration => animation_duration, :fill => 'freeze', :begin_event => Event.mouseover(legend), :end_event => Event.mouseout(legend)) sector.parent.add_painting_animation(:to => {:opacity => 1}, :fill => 'freeze', :begin_event => Event.mouseout(legend)) end end end end
default_center_point()
click to toggle source
# File lib/dyi/chart/pie_chart.rb, line 242 def default_center_point margin = [width - chart_radius_x * 2, height - chart_radius_y * 2].min.quo(2) Coordinate.new(margin + chart_radius_x, margin + chart_radius_y) end
default_chart_radius_x()
click to toggle source
# File lib/dyi/chart/pie_chart.rb, line 247 def default_chart_radius_x [width, height].min * 0.4 end
default_chart_radius_y()
click to toggle source
# File lib/dyi/chart/pie_chart.rb, line 251 def default_chart_radius_y represent_3d? ? chart_radius_x.quo(2) : chart_radius_x end
default_legend_format()
click to toggle source
# File lib/dyi/chart/pie_chart.rb, line 263 def default_legend_format "{?name}\t{?percent}" end
default_legend_point()
click to toggle source
# File lib/dyi/chart/pie_chart.rb, line 255 def default_legend_point if width - chart_radius_x * 2 < height - chart_radius_y * 2 Coordinate.new(width * 0.1, chart_radius_y * 2 + (width - chart_radius_x * 2) * 0.8) else Coordinate.new(chart_radius_x * 2 + (height - chart_radius_y * 2) * 0.8, height * 0.1 + Length.new(10)) end end
draw_baloon(brush, record, accumulation, total_value, index, ratio, pie_sector)
click to toggle source
@since 1.0.0
# File lib/dyi/chart/pie_chart.rb, line 370 def draw_baloon(brush, record, accumulation, total_value, index, ratio, pie_sector) value = record.value if show_baloon? dr = moved_elements && moved_elements[index] baloon_point = Coordinate.new( chart_radius_x * (baloon_position + (dr || 0)) * Math.cos(((accumulation * 2.0 + value) / total_value - 0.5) * Math::PI), chart_radius_y * (baloon_position + (dr || 0)) * Math.sin(((accumulation * 2.0 + value) / total_value - 0.5) * Math::PI)) baloon_options = {:text_anchor => 'middle', :show_border => true} if data.has_field?(:css_class) && (css_class = record.css_class) baloon_options[:css_class] = record.css_class end baloon_options[:vertical_padding] = baloon_padding[:vertical_padding] || 2 baloon_options[:horizontal_padding] = baloon_padding[:horizontal_padding] || 5 baloon_options[:background_color] = get_baloon_background_color(index) baloon_options[:border_color] = get_baloon_border_color(index) baloon_options[:border_width] = baloon_border_width baloon_options[:border_rx] = baloon_round if baloon_css_class && baloon_css_class.empty? @data_label_canvas.add_css_class(baloon_css_class) end text = Drawing::Pen.black_pen(:font => baloon_font, :opacity => 0.0).draw_text( @data_label_canvas, center_point + baloon_point, format_string(baloon_format, record, total_value), baloon_options) text.add_painting_animation(:to => {:opacity => 1}, :duration => animation_duration, :fill => 'freeze', :begin_event => Event.mouseover(pie_sector)) text.add_painting_animation(:to => {:opacity => 0}, :duration => animation_duration, :fill => 'freeze', :begin_event => Event.mouseout(pie_sector)) if @legends text.add_painting_animation(:to => {:opacity => 1}, :duration => animation_duration, :fill => 'freeze', :begin_event => Event.mouseover(@legends[index])) text.add_painting_animation(:to => {:opacity => 0}, :duration => animation_duration, :fill => 'freeze', :begin_event => Event.mouseout(@legends[index])) end end end
draw_chart(brush, record, accumulation, total_value, index)
click to toggle source
# File lib/dyi/chart/pie_chart.rb, line 332 def draw_chart(brush, record, accumulation, total_value, index) canvas = Shape::ShapeGroup.draw_on(@chart_canvas) attrs = {} if data.has_field?(:css_class) && (css_class = record.css_class) attrs[:css_class] = record.css_class end value = record.value pie_sector = brush.draw_sector( canvas, center_point, chart_radius_x, chart_radius_y, accumulation * 360.0 / total_value - 90, value * 360.0 / total_value, attrs.merge(:inner_radius => inner_radius)) @sectors[index] = pie_sector if moved_elements && (dr = moved_elements[index]) canvas.translate( chart_radius_x * dr * Math.cos(((accumulation * 2.0 + value) / total_value - 0.5) * Math::PI), chart_radius_y * dr * Math.sin(((accumulation * 2.0 + value) / total_value - 0.5) * Math::PI)) end ratio = value.to_f.quo(total_value) if show_data_label? label_point = Coordinate.new( chart_radius_x * (data_label_position + (dr || 0)) * Math.cos(((accumulation * 2.0 + value) / total_value - 0.5) * Math::PI), chart_radius_y * (data_label_position + (dr || 0)) * Math.sin(((accumulation * 2.0 + value) / total_value - 0.5) * Math::PI)) Drawing::Pen.black_pen(:font => data_label_font).draw_text( @data_label_canvas, center_point + label_point, format_string(data_label_format, record, total_value), attrs.merge(:text_anchor => 'middle')) end if hide_data_label_ratio < ratio draw_baloon(brush, record, accumulation, total_value, index, ratio, pie_sector) end
draw_legend(records, shapes=nil)
click to toggle source
@since 1.0.0
# File lib/dyi/chart/pie_chart.rb, line 419 def draw_legend(records, shapes=nil) legend_canvas.translate(legend_point.x, legend_point.y) if show_legend? pen = Drawing::Pen.black_pen(:font => legend_font) brush = Drawing::Brush.new toatal = records.inject(0.0){|sum, record| sum + record.value} formats = legend_format.split("\t") legend_labels = [] records.each_with_index do |record, i| legend_labels << formats.map do |format| format_string(format, record, toatal) end end max_lengths = legend_labels.inject(Array.new(formats.size, 0)) do |maxs, labels| (0...formats.size).each do |i| maxs[i] = labels[i].bytesize if maxs[i] < labels[i].bytesize end maxs end canvas.add_initialize_script(Script::EcmaScript::DomLevel2.form_legend_labels(legend_canvas)) records.each_with_index do |record, i| y = legend_font_size * (1.2 * (i + 1)) attrs = {} if data.has_field?(:css_class) && (css_class = record.css_class) attrs[:css_class] = record.css_class end group = Shape::ShapeGroup.draw_on(legend_canvas, attrs) @legends << group case shapes && shapes[i] when Shape::Base shapes[i].draw_on(group) when NilClass brush.color = chart_color(i) brush.draw_rectangle( group, Coordinate.new(legend_font_size * 0.2, y - legend_font_size * 0.8), legend_font_size * 0.8, legend_font_size * 0.8) end x = legend_font_size * 0.2 + legend_font_size legend_labels[i].each_with_index do |label, j| formats[j] =~ /\A\{!(\w)\}/ case $1 when 's' attrs = {:text_anchor => 'start'} pen.draw_text(group, Coordinate.new(x, y), label, attrs) x += legend_font_size * max_lengths[j] * 0.5 when 'm' attrs = {:text_anchor => 'middle'} x += legend_font_size * max_lengths[j] * 0.25 pen.draw_text(group, Coordinate.new(x, y), label, attrs) x += legend_font_size * max_lengths[j] * 0.25 when 'e' attrs = {:text_anchor => 'end'} x += legend_font_size * max_lengths[j] * 0.5 pen.draw_text(group, Coordinate.new(x, y), label, attrs) else attrs = {} pen.draw_text(group, Coordinate.new(x, y), label, attrs) x += legend_font_size * max_lengths[j] * 0.5 end end end end end
format_string(format, record, total_value)
click to toggle source
@since 1.0.0
# File lib/dyi/chart/pie_chart.rb, line 486 def format_string(format, record, total_value) format = format.gsub(/\A\{!(\w)\}/, '') format.gsub(/\{\?((?![0-9])\w+)(:[^}]*)?\}/){|m| fmt = $2 ? $2[1..-1] : nil if $1 == 'percent' value = record.value.quo(total_value) fmt ||= '0.0%' else value = record.__send__($1) end if fmt case value when Numeric then value.strfnum(fmt) when DateTime, Time then value.strftime(fmt) else fmt % value end else value end } end