seer 0.7.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -0
- data/lib/seer.rb +6 -1
- data/lib/seer/geomap.rb +166 -0
- data/spec/geomap_spec.rb +67 -0
- data/spec/spec_helper.rb +1 -1
- metadata +23 -4
data/Rakefile
CHANGED
data/lib/seer.rb
CHANGED
@@ -5,10 +5,11 @@ module Seer
|
|
5
5
|
require 'seer/bar_chart'
|
6
6
|
require 'seer/column_chart'
|
7
7
|
require 'seer/gauge'
|
8
|
+
require 'seer/geomap'
|
8
9
|
require 'seer/line_chart'
|
9
10
|
require 'seer/pie_chart'
|
10
11
|
|
11
|
-
VISUALIZERS = [:area_chart, :bar_chart, :column_chart, :gauge, :line_chart, :pie_chart]
|
12
|
+
VISUALIZERS = [:area_chart, :bar_chart, :column_chart, :gauge, :geomap, :line_chart, :pie_chart]
|
12
13
|
|
13
14
|
def self.valid_hex_number?(val) #:nodoc:
|
14
15
|
return false unless val.is_a?(String) && ! val.empty?
|
@@ -52,6 +53,10 @@ module Seer
|
|
52
53
|
Gauge.render(data, args)
|
53
54
|
end
|
54
55
|
|
56
|
+
def self.geomap(data, args)
|
57
|
+
Geomap.render(data, args)
|
58
|
+
end
|
59
|
+
|
55
60
|
def self.line_chart(data, args)
|
56
61
|
LineChart.render(data, args)
|
57
62
|
end
|
data/lib/seer/geomap.rb
ADDED
@@ -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', '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
|
data/spec/geomap_spec.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Seer::Geomap" do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
|
7
|
+
class GeoThing
|
8
|
+
def initialize; end
|
9
|
+
def name; 'foo'; end
|
10
|
+
def latitude; -90; end
|
11
|
+
def longitude; -90; end
|
12
|
+
def count; 8; end
|
13
|
+
def geocoded?; true; end
|
14
|
+
end
|
15
|
+
|
16
|
+
@chart = Seer::Geomap.new(
|
17
|
+
:data => [GeoThing.new, GeoThing.new, GeoThing.new],
|
18
|
+
:label_method => 'name',
|
19
|
+
:data_method => 'count',
|
20
|
+
:chart_options => {},
|
21
|
+
:chart_element => 'geochart'
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'defaults' do
|
26
|
+
|
27
|
+
it 'height' do
|
28
|
+
@chart.height.should == Seer::Chart::DEFAULT_HEIGHT
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'width' do
|
32
|
+
@chart.width.should == Seer::Chart::DEFAULT_WIDTH
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'graph options' do
|
38
|
+
|
39
|
+
[:show_zoom_out, :zoom_out_label].each do |accessor|
|
40
|
+
it "sets its #{accessor} value" do
|
41
|
+
@chart.send("#{accessor}=", 'foo')
|
42
|
+
@chart.send(accessor).should == 'foo'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it_should_behave_like 'it has colors attribute'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'renders as JavaScript' do
|
50
|
+
(@chart.to_js =~ /javascript/).should be_true
|
51
|
+
(@chart.to_js =~ /geomap/).should be_true
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'sets its data columns' do
|
55
|
+
@chart.data_columns.should =~ /addRows\(3\)/
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'sets its data table' do
|
59
|
+
@chart.data_table.to_s.should set_value(0, 0,'foo')
|
60
|
+
@chart.data_table.to_s.should set_value(0, 1, 8)
|
61
|
+
@chart.data_table.to_s.should set_value(1, 0,'foo')
|
62
|
+
@chart.data_table.to_s.should set_value(1, 1, 8)
|
63
|
+
@chart.data_table.to_s.should set_value(2, 0,'foo')
|
64
|
+
@chart.data_table.to_s.should set_value(2, 1, 8)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
2
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
3
|
require 'rubygems'
|
4
|
-
require 'active_support'
|
5
4
|
require 'action_pack'
|
5
|
+
require 'active_support'
|
6
6
|
require 'spec'
|
7
7
|
require 'spec/autorun'
|
8
8
|
require 'seer'
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: seer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 59
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
|
-
-
|
8
|
+
- 9
|
8
9
|
- 0
|
9
|
-
version: 0.
|
10
|
+
version: 0.9.0
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Corey Ehmke / SEO Logic
|
@@ -14,16 +15,18 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-09-08 00:00:00 -05:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: rspec
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
24
26
|
requirements:
|
25
27
|
- - ">="
|
26
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 13
|
27
30
|
segments:
|
28
31
|
- 1
|
29
32
|
- 2
|
@@ -52,11 +55,22 @@ files:
|
|
52
55
|
- lib/seer/chart.rb
|
53
56
|
- lib/seer/column_chart.rb
|
54
57
|
- lib/seer/gauge.rb
|
58
|
+
- lib/seer/geomap.rb
|
55
59
|
- lib/seer/line_chart.rb
|
56
60
|
- lib/seer/pie_chart.rb
|
57
61
|
- spec/seer_spec.rb
|
58
62
|
- spec/spec.opts
|
59
63
|
- spec/spec_helper.rb
|
64
|
+
- spec/area_chart_spec.rb
|
65
|
+
- spec/bar_chart_spec.rb
|
66
|
+
- spec/chart_spec.rb
|
67
|
+
- spec/column_chart_spec.rb
|
68
|
+
- spec/custom_matchers.rb
|
69
|
+
- spec/gauge_spec.rb
|
70
|
+
- spec/geomap_spec.rb
|
71
|
+
- spec/helpers.rb
|
72
|
+
- spec/line_chart_spec.rb
|
73
|
+
- spec/pie_chart_spec.rb
|
60
74
|
has_rdoc: true
|
61
75
|
homepage: http://github.com/Bantik/seer
|
62
76
|
licenses: []
|
@@ -67,23 +81,27 @@ rdoc_options:
|
|
67
81
|
require_paths:
|
68
82
|
- lib
|
69
83
|
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
70
85
|
requirements:
|
71
86
|
- - ">="
|
72
87
|
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
73
89
|
segments:
|
74
90
|
- 0
|
75
91
|
version: "0"
|
76
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
77
94
|
requirements:
|
78
95
|
- - ">="
|
79
96
|
- !ruby/object:Gem::Version
|
97
|
+
hash: 3
|
80
98
|
segments:
|
81
99
|
- 0
|
82
100
|
version: "0"
|
83
101
|
requirements: []
|
84
102
|
|
85
103
|
rubyforge_project:
|
86
|
-
rubygems_version: 1.3.
|
104
|
+
rubygems_version: 1.3.7
|
87
105
|
signing_key:
|
88
106
|
specification_version: 3
|
89
107
|
summary: Seer is a lightweight, semantically rich wrapper for the Google Visualization API.
|
@@ -94,6 +112,7 @@ test_files:
|
|
94
112
|
- spec/column_chart_spec.rb
|
95
113
|
- spec/custom_matchers.rb
|
96
114
|
- spec/gauge_spec.rb
|
115
|
+
- spec/geomap_spec.rb
|
97
116
|
- spec/helpers.rb
|
98
117
|
- spec/line_chart_spec.rb
|
99
118
|
- spec/pie_chart_spec.rb
|