glennr-seer 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/seer/chart.rb ADDED
@@ -0,0 +1,68 @@
1
+ module Seer
2
+
3
+ module Chart #:nodoc:
4
+
5
+ attr_accessor :chart_element, :colors
6
+
7
+ DEFAULT_COLORS = ['#324F69','#919E4B', '#A34D4D', '#BEC8BE']
8
+ DEFAULT_LEGEND_LOCATION = 'bottom'
9
+ DEFAULT_HEIGHT = 350
10
+ DEFAULT_WIDTH = 550
11
+
12
+ def hash_options
13
+ []
14
+ end
15
+
16
+ def in_element=(elem)
17
+ @chart_element = elem
18
+ end
19
+
20
+ def colors=(colors_list)
21
+ unless colors_list.include?('darker')
22
+ raise ArgumentError, "Invalid color option: #{colors_list}" unless colors_list.is_a?(Array)
23
+ colors_list.each do |color|
24
+ raise ArgumentError, "Invalid color option: #{colors_list}" unless Seer.valid_hex_number?(color)
25
+ end
26
+ end
27
+ @colors = colors_list
28
+ end
29
+
30
+ def formatted_colors
31
+ if @colors.include?('darker')
32
+ @colors
33
+ else
34
+ "[#{@colors.map{|color| "'#{color.gsub(/\#/,'')}'"} * ','}]"
35
+ end
36
+ end
37
+
38
+ def data_columns
39
+ _data_columns = " data.addRows(#{data_table.size});\r"
40
+ _data_columns << " data.addColumn('string', '#{label_method}');\r"
41
+ _data_columns << " data.addColumn('number', '#{data_method}');\r"
42
+ _data_columns
43
+ end
44
+
45
+ def options
46
+ _options = ""
47
+ nonstring_options.each do |opt|
48
+ next unless self.send(opt)
49
+ if opt == :colors
50
+ _options << " options['#{opt.to_s.camelize(:lower)}'] = #{self.send(:formatted_colors)};\r"
51
+ else
52
+ _options << " options['#{opt.to_s.camelize(:lower)}'] = #{self.send(opt)};\r"
53
+ end
54
+ end
55
+ string_options.each do |opt|
56
+ next unless self.send(opt)
57
+ _options << " options['#{opt.to_s.camelize(:lower)}'] = '#{self.send(opt)}';\r"
58
+ end
59
+ hash_options.each do |opt|
60
+ next unless self.send(opt)
61
+ _options << " options['#{opt.to_s.camelize(:lower)}'] = #{Seer.options_for_javascript(self.send(opt))};\r"
62
+ end
63
+ _options
64
+ end
65
+ end
66
+
67
+
68
+ end
@@ -0,0 +1,124 @@
1
+ module Seer
2
+
3
+ # =USAGE
4
+ #
5
+ # In your controller:
6
+ #
7
+ # @data = Widgets.all # Must be an array, and must respond
8
+ # # to the data method specified below (in this example, 'quantity')
9
+ #
10
+ # In your view:
11
+ #
12
+ # <div id="chart"></div>
13
+ #
14
+ # <%= Seer::visualize(
15
+ # @widgets,
16
+ # :as => :column_chart,
17
+ # :in_element => 'chart',
18
+ # :series => {:series_label => 'name', :data_method => 'quantity'},
19
+ # :chart_options => {
20
+ # :height => 300,
21
+ # :width => 300,
22
+ # :is_3_d => true,
23
+ # :legend => 'none',
24
+ # :colors => "[{color:'#990000', darker:'#660000'}]",
25
+ # :title => "Widget Quantities",
26
+ # :title_x => 'Widgets',
27
+ # :title_y => 'Quantities'
28
+ # }
29
+ # )
30
+ # -%>
31
+ #
32
+ # Colors are treated differently for 2d and 3d graphs. If you set is_3_d to false, set the
33
+ # graph color like this:
34
+ #
35
+ # :colors => "#990000"
36
+ #
37
+ # For details on the chart options, see the Google API docs at
38
+ # http://code.google.com/apis/visualization/documentation/gallery/columnchart.html
39
+ #
40
+ class ColumnChart
41
+
42
+ include Seer::Chart
43
+
44
+ # Chart options accessors
45
+ attr_accessor :axis_color, :axis_background_color, :axis_font_size, :background_color, :border_color, :enable_tooltip, :focus_border_color, :height, :is_3_d, :is_stacked, :legend, :legend_background_color, :legend_font_size, :legend_text_color, :log_scale, :max, :min, :reverse_axis, :show_categories, :title, :title_x, :title_y, :title_color, :title_font_size, :tooltip_font_size, :tooltip_height, :tooltip_width, :width
46
+
47
+ # Graph data
48
+ attr_accessor :data, :data_method, :data_table, :label_method
49
+
50
+ def initialize(args={}) #:nodoc:
51
+
52
+ # Standard options
53
+ args.each{ |method,arg| self.send("#{method}=",arg) if self.respond_to?(method) }
54
+
55
+ # Chart options
56
+ args[:chart_options].each{ |method, arg| self.send("#{method}=",arg) if self.respond_to?(method) }
57
+
58
+ # Handle defaults
59
+ @colors ||= args[:chart_options][:colors] || DEFAULT_COLORS
60
+ @legend ||= args[:chart_options][:legend] || DEFAULT_LEGEND_LOCATION
61
+ @height ||= args[:chart_options][:height] || DEFAULT_HEIGHT
62
+ @width ||= args[:chart_options][:width] || DEFAULT_WIDTH
63
+ @is_3_d ||= args[:chart_options][:is_3_d]
64
+
65
+ @data_table = []
66
+
67
+ end
68
+
69
+ def data_table #:nodoc:
70
+ data.each_with_index do |datum, column|
71
+ @data_table << [
72
+ " data.setValue(#{column}, 0,'#{datum.send(label_method)}');\r",
73
+ " data.setValue(#{column}, 1, #{datum.send(data_method)});\r"
74
+ ]
75
+ end
76
+ @data_table
77
+ end
78
+
79
+ def is_3_d #:nodoc:
80
+ @is_3_d.blank? ? false : @is_3_d
81
+ end
82
+
83
+ def nonstring_options #:nodoc:
84
+ [:axis_font_size, :colors, :enable_tooltip, :height, :is_3_d, :is_stacked, :legend_font_size, :log_scale, :max, :min, :reverse_axis, :show_categories, :title_font_size, :tooltip_font_size, :tooltip_width, :width]
85
+ end
86
+
87
+ def string_options #:nodoc:
88
+ [:axis_color, :axis_background_color, :background_color, :border_color, :focus_border_color, :legend, :legend_background_color, :legend_text_color, :title, :title_x, :title_y, :title_color]
89
+ end
90
+
91
+ def to_js #:nodoc:
92
+
93
+ %{
94
+ <script type="text/javascript">
95
+ google.load('visualization', '1', {'packages':['corechart']});
96
+ google.setOnLoadCallback(drawChart);
97
+ function drawChart() {
98
+ var data = new google.visualization.DataTable();
99
+ #{data_columns}
100
+ #{data_table.to_s}
101
+ var options = {};
102
+ #{options}
103
+ var container = document.getElementById('#{self.chart_element}');
104
+ var chart = new google.visualization.ColumnChart(container);
105
+ chart.draw(data, options);
106
+ }
107
+ </script>
108
+ }
109
+ end
110
+
111
+ def self.render(data, args) #:nodoc:
112
+ graph = Seer::ColumnChart.new(
113
+ :data => data,
114
+ :label_method => args[:series][:series_label],
115
+ :data_method => args[:series][:data_method],
116
+ :chart_options => args[:chart_options],
117
+ :chart_element => args[:in_element] || 'chart'
118
+ )
119
+ graph.to_js
120
+ end
121
+
122
+ end
123
+
124
+ end
data/lib/seer/gauge.rb ADDED
@@ -0,0 +1,119 @@
1
+ module Seer
2
+
3
+ # =USAGE
4
+ #
5
+ # In your controller:
6
+ #
7
+ # @data = Widgets.all # Must be an array, and must respond
8
+ # # to the data method specified below (in this example, 'quantity')
9
+ #
10
+ # In your view:
11
+ #
12
+ # <div id="chart"></div>
13
+ #
14
+ # <%= Seer::visualize(
15
+ # @data,
16
+ # :as => :gauge,
17
+ # :in_element => 'chart',
18
+ # :series => {:series_label => 'name', :data_method => 'quantity'},
19
+ # :chart_options => {
20
+ # :green_from => 0,
21
+ # :green_to => 50,
22
+ # :height => 300,
23
+ # :max => 100,
24
+ # :min => 0,
25
+ # :minor_ticks => 5,
26
+ # :red_from => 76,
27
+ # :red_to => 100,
28
+ # :width => 600,
29
+ # :yellow_from => 51,
30
+ # :yellow_to => 75
31
+ # }
32
+ # )
33
+ # -%>
34
+ #
35
+ # For details on the chart options, see the Google API docs at
36
+ # http://code.google.com/apis/visualization/documentation/gallery/gauge.html
37
+ #
38
+ class Gauge
39
+
40
+ include Seer::Chart
41
+
42
+ # Chart options accessors
43
+ attr_accessor :green_from, :green_to, :height, :major_ticks, :max, :min, :minor_ticks, :red_from, :red_to, :width, :yellow_from, :yellow_to
44
+
45
+ # Graph data
46
+ attr_accessor :data, :data_method, :data_table, :label_method
47
+
48
+ def initialize(args={}) #:nodoc:
49
+
50
+ # Standard options
51
+ args.each{ |method,arg| self.send("#{method}=",arg) if self.respond_to?(method) }
52
+
53
+ # Chart options
54
+ args[:chart_options].each{ |method, arg| self.send("#{method}=",arg) if self.respond_to?(method) }
55
+
56
+ # Handle defaults
57
+ @height ||= args[:chart_options][:height] || DEFAULT_HEIGHT
58
+ @width ||= args[:chart_options][:width] || DEFAULT_WIDTH
59
+
60
+ @data_table = []
61
+
62
+ end
63
+
64
+ def data_table #:nodoc:
65
+ data.each_with_index do |datum, column|
66
+ @data_table << [
67
+ " data.setValue(#{column}, 0,'#{datum.send(label_method)}');\r",
68
+ " data.setValue(#{column}, 1, #{datum.send(data_method)});\r"
69
+ ]
70
+ end
71
+ @data_table
72
+ end
73
+
74
+ def is_3_d #:nodoc:
75
+ @is_3_d.blank? ? false : @is_3_d
76
+ end
77
+
78
+ def nonstring_options #:nodoc:
79
+ [:green_from, :green_to, :height, :major_ticks, :max, :min, :minor_ticks, :red_from, :red_to, :width, :yellow_from, :yellow_to]
80
+ end
81
+
82
+ def string_options #:nodoc:
83
+ []
84
+ end
85
+
86
+ def to_js #:nodoc:
87
+
88
+ %{
89
+ <script type="text/javascript">
90
+ google.load('visualization', '1', {'packages':['gauge']});
91
+ google.setOnLoadCallback(drawChart);
92
+ function drawChart() {
93
+ var data = new google.visualization.DataTable();
94
+ #{data_columns}
95
+ #{data_table.join}
96
+ var options = {};
97
+ #{options}
98
+ var container = document.getElementById('#{self.chart_element}');
99
+ var chart = new google.visualization.Gauge(container);
100
+ chart.draw(data, options);
101
+ }
102
+ </script>
103
+ }
104
+ end
105
+
106
+ def self.render(data, args) #:nodoc:
107
+ graph = Seer::Gauge.new(
108
+ :data => data,
109
+ :label_method => args[:series][:series_label],
110
+ :data_method => args[:series][:data_method],
111
+ :chart_options => args[:chart_options],
112
+ :chart_element => args[:in_element] || 'chart'
113
+ )
114
+ graph.to_js
115
+ end
116
+
117
+ end
118
+
119
+ end
@@ -0,0 +1,166 @@
1
+ module Seer
2
+
3
+ # Geomap creates a map of a country, continent, or region, with colors and values assigned to
4
+ # specific regions. Values are displayed as a color scale, and you can specify optional hovertext
5
+ # for regions.
6
+ #
7
+ # =USAGE=
8
+ #
9
+ # In your view:
10
+ #
11
+ # <div id="my_geomap_container" class="chart"></div>
12
+ #
13
+ # <%= Seer::visualize(
14
+ # @widgets,
15
+ # :as => :geomap,
16
+ # :in_element => 'my_geomap_container',
17
+ # :series => {
18
+ # :series_label => 'name',
19
+ # :data_label => '# widgets',
20
+ # :data_method => 'quantity'
21
+ # },
22
+ # :chart_options => {
23
+ # :data_mode => 'regions',
24
+ # :region => 'US',
25
+ # }
26
+ # )
27
+ # -%>
28
+ #
29
+ # ==@widgets==
30
+ #
31
+ # A collection of objects (ActiveRecord or otherwise) that must respond to the
32
+ # following methods:
33
+ #
34
+ # latitude # => returns the latitude in decimal format
35
+ # longitude # => returns the longitude in decimal format
36
+ # geocoded? # => result of latitude && longitude
37
+ #
38
+ # For details on the chart options, see the Google API docs at
39
+ # http://code.google.com/apis/visualization/documentation/gallery/geomap.html
40
+ #
41
+ class Geomap
42
+
43
+ include Seer::Chart
44
+
45
+ COUNTRY_CODES = ['world', 'AX', 'AF', 'AL', 'DZ', 'AS', 'AD', 'AO', 'AI', 'AQ', 'AG', 'AR', 'AM', 'AW', 'AU', 'AT', 'AZ', 'BS', 'BH', 'BD', 'BB', 'BY', 'BE', 'BZ', 'BJ', 'BM', 'BT', 'BO', 'BA', 'BW', 'BV', 'BR', 'IO', 'BN', 'BG', 'BF', 'BI', 'KH', 'CM', 'CA', 'CV', 'KY', 'CF', 'TD', 'CL', 'CN', 'CX', 'CC', 'CO', 'KM', 'CD', 'CG', 'CK', 'CR', 'CI', 'HR', 'CU', 'CY', 'CZ', 'DK', 'DJ', 'DM', 'DO', 'EC', 'EG', 'SV', 'GQ', 'ER', 'EE', 'ET', 'FK', 'FO', 'FJ', 'FI', 'FR', 'GF', 'PF', 'TF', 'GA', 'GM', 'GE', 'DE', 'GH', 'GI', 'GR', 'GL', 'GD', 'GP', 'GU', 'GT', 'GN', 'GW', 'GY', 'HT', 'HM', 'HN', 'HK', 'HU', 'IS', 'IN', 'ID', 'IR', 'IQ', 'IE', 'IL', 'IT', 'JM', 'JP', 'JO', 'KZ', 'KE', 'KI', 'KP', 'KR', 'KW', 'KG', 'LA', 'LV', 'LB', 'LS', 'LR', 'LY', 'LI', 'LT', 'LU', 'MO', 'MK', 'MG', 'MW', 'MY', 'MV', 'ML', 'MT', 'MH', 'MQ', 'MR', 'MU', 'YT', 'MX', 'FM', 'MD', 'MC', 'MN', 'MS', 'MA', 'MZ', 'MM', 'NA', 'NR', 'NP', 'NL', 'AN', 'NC', 'NZ', 'NI', 'NE', 'NG', 'NU', 'NF', 'MP', 'NO', 'OM', 'PK', 'PW', 'PS', 'PA', 'PG', 'PY', 'PE', 'PH', 'PN', 'PL', 'PT', 'PR', 'QA', 'RE', 'RO', 'RU', 'RW', 'SH', 'KN', 'LC', 'PM', 'VC', 'WS', 'SM', 'ST', 'SA', 'SN', 'CS', 'SC', 'SL', 'SG', 'SK', 'SI', 'SB', 'SO', 'ZA', 'GS', 'ES', 'LK', 'SD', 'SR', 'SJ', 'SZ', 'SE', 'CH', 'SY', 'TW', 'TJ', 'TZ', 'TH', 'TL', 'TG', 'TK', 'TO', 'TT', 'TN', 'TR', 'TM', 'TC', 'TV', 'UG', 'UA', 'AE', 'GB', 'US', 'us_metro', 'UM', 'UY', 'UZ', 'VU', 'VA', 'VE', 'VN', 'VG', 'VI', 'WF', 'EH', 'YE', 'ZM', 'ZW', '005', '013', '021', '002', '017', '015', '018', '030', '034', '035', '143', '145', '151', '154', '155', '039']
46
+
47
+ # Chart options accessors
48
+ attr_accessor :data_mode, :enable_tooltip, :height, :legend_background_color, :legend_font_size, :legend_text_color, :legend, :region, :show_legend, :show_zoom_out, :title_color, :title_font_size, :title_x, :title_y, :title, :tooltip_font_size, :tooltip_height, :tooltip_width, :width, :zoom_out_label
49
+
50
+ # Graph data
51
+ attr_accessor :data, :data_label, :data_method, :data_table, :label_method
52
+
53
+ def initialize(args={}) #:nodoc:
54
+
55
+ # Standard options
56
+ args.each{ |method,arg| self.send("#{method}=",arg) if self.respond_to?(method) }
57
+
58
+ # Chart options
59
+ args[:chart_options].each{ |method, arg| self.send("#{method}=",arg) if self.respond_to?(method) }
60
+
61
+ # Handle defaults
62
+ @colors ||= args[:chart_options][:colors] || DEFAULT_COLORS
63
+ @legend ||= args[:chart_options][:legend] || DEFAULT_LEGEND_LOCATION
64
+ @height ||= args[:chart_options][:height] || DEFAULT_HEIGHT
65
+ @width ||= args[:chart_options][:width] || DEFAULT_WIDTH
66
+
67
+ end
68
+
69
+ def data_columns #:nodoc:
70
+ _data_columns = "data.addRows(#{data_table.size});"
71
+ if self.data_mode == 'markers'
72
+ _data_columns << %{
73
+ data.addColumn('number', 'LATITUDE');
74
+ data.addColumn('number', 'LONGITUDE');
75
+ data.addColumn('number', '#{data_method}');
76
+ data.addColumn('string', '#{label_method}');
77
+ }
78
+ else
79
+ _data_columns << " data.addColumn('string', '#{label_method}');"
80
+ _data_columns << " data.addColumn('number', '#{data_method}');"
81
+ _data_columns
82
+ end
83
+ _data_columns
84
+ end
85
+
86
+ def data_mode=(mode) #:nodoc:
87
+ raise ArgumentError, "Invalid data mode option: #{mode}. Must be one of 'regions' or 'markers'." unless ['regions', 'markers'].include?(mode)
88
+ @data_mode = mode
89
+ end
90
+
91
+ def data_table #:nodoc:
92
+ @data_table = []
93
+ data.each_with_index do |datum, column|
94
+ next unless datum.geocoded?
95
+ if data_mode == "markers"
96
+ @data_table << [
97
+ " data.setValue(#{column}, 0, #{datum.latitude});\r",
98
+ " data.setValue(#{column}, 1, #{datum.longitude});\r",
99
+ " data.setValue(#{column}, 2, #{datum.send(data_method)});\r",
100
+ " data.setValue(#{column}, 3, '#{datum.send(label_method)}');\r"
101
+ ]
102
+ else # Regions
103
+ @data_table << [
104
+ " data.setValue(#{column}, 0, '#{datum.name}');\r",
105
+ " data.setValue(#{column}, 1, #{datum.send(data_method)});\r"
106
+ ]
107
+ end
108
+ end
109
+ @data_table
110
+ end
111
+
112
+ # Because Google is not consistent in their @#!$ API...
113
+ def formatted_colors
114
+ "[#{@colors.map{|color| "'#{color.gsub(/\#/,'0x')}'"} * ','}]"
115
+ end
116
+
117
+ def nonstring_options #:nodoc:
118
+ [:colors, :enable_tooltip, :height, :legend_font_size, :title_font_size, :tooltip_font_size, :tooltip_width, :width]
119
+ end
120
+
121
+ def string_options #:nodoc:
122
+ [:data_mode, :legend, :legend_background_color, :legend_text_color, :title, :title_x, :title_y, :title_color, :region]
123
+ end
124
+
125
+ def to_js
126
+ %{
127
+ <script type="text/javascript">
128
+ google.load('visualization', '1', {'packages':['geomap']});
129
+ google.setOnLoadCallback(drawChart);
130
+ function drawChart() {
131
+ var data = new google.visualization.DataTable();
132
+ #{data_columns}
133
+ #{data_table.to_s}
134
+ var options = {};
135
+ #{options}
136
+ var container = document.getElementById('#{self.chart_element}');
137
+ var geomap = new google.visualization.GeoMap(container);
138
+ geomap.draw(data, options);
139
+ }
140
+ </script>
141
+ }
142
+ end
143
+
144
+ def region=(desired_region)
145
+ raise ArgumentError, "Invalid region: #{desired_region}" unless COUNTRY_CODES.include?(desired_region)
146
+ @region = desired_region
147
+ end
148
+
149
+ # ====================================== Class Methods =========================================
150
+
151
+ def self.render(data, args)
152
+ map = Seer::Geomap.new(
153
+ :region => args[:chart_options][:region],
154
+ :data_mode => args[:chart_options][:data_mode],
155
+ :data => data,
156
+ :label_method => args[:series][:series_label],
157
+ :data_method => args[:series][:data_method],
158
+ :chart_options => args[:chart_options],
159
+ :chart_element => args[:in_element] || 'chart'
160
+ )
161
+ map.to_js
162
+ end
163
+
164
+ end
165
+
166
+ end