rails-geocoder 0.9.8 → 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/CHANGELOG.rdoc +16 -1
- data/README.rdoc +123 -55
- data/Rakefile +2 -33
- data/VERSION +1 -0
- data/lib/geocoder.rb +79 -5
- data/lib/geocoder/configuration.rb +11 -3
- data/lib/geocoder/lookups/base.rb +110 -0
- data/lib/geocoder/lookups/freegeoip.rb +40 -0
- data/lib/geocoder/lookups/google.rb +31 -0
- data/lib/geocoder/lookups/yahoo.rb +29 -0
- data/lib/geocoder/{active_record.rb → orms/active_record.rb} +27 -82
- data/lib/geocoder/orms/active_record_legacy.rb +58 -0
- data/lib/geocoder/orms/base.rb +96 -0
- data/lib/geocoder/railtie.rb +50 -38
- data/lib/geocoder/request.rb +17 -0
- data/lib/geocoder/results/base.rb +58 -0
- data/lib/geocoder/results/freegeoip.rb +37 -0
- data/lib/geocoder/{result.rb → results/google.rb} +26 -8
- data/lib/geocoder/results/yahoo.rb +40 -0
- data/lib/tasks/geocoder.rake +1 -1
- data/rails-geocoder.gemspec +15 -0
- data/test/fixtures/freegeoip_74_200_247_59.json +12 -0
- data/test/fixtures/google_garbage.json +456 -0
- data/test/fixtures/google_madison_square_garden.json +57 -0
- data/test/fixtures/google_no_results.json +4 -0
- data/test/fixtures/yahoo_garbage.json +50 -0
- data/test/fixtures/yahoo_madison_square_garden.json +46 -0
- data/test/fixtures/yahoo_no_results.json +10 -0
- data/test/geocoder_test.rb +185 -12
- data/test/test_helper.rb +84 -17
- metadata +51 -17
- data/lib/geocoder/lookup.rb +0 -90
@@ -0,0 +1,57 @@
|
|
1
|
+
{
|
2
|
+
"status": "OK",
|
3
|
+
"results": [ {
|
4
|
+
"types": [ "street_address" ],
|
5
|
+
"formatted_address": "4 Penn Plaza, New York, NY 10001, USA",
|
6
|
+
"address_components": [ {
|
7
|
+
"long_name": "4",
|
8
|
+
"short_name": "4",
|
9
|
+
"types": [ "street_number" ]
|
10
|
+
}, {
|
11
|
+
"long_name": "Penn Plaza",
|
12
|
+
"short_name": "Penn Plaza",
|
13
|
+
"types": [ "route" ]
|
14
|
+
}, {
|
15
|
+
"long_name": "Manhattan",
|
16
|
+
"short_name": "Manhattan",
|
17
|
+
"types": [ "sublocality", "political" ]
|
18
|
+
}, {
|
19
|
+
"long_name": "New York",
|
20
|
+
"short_name": "New York",
|
21
|
+
"types": [ "locality", "political" ]
|
22
|
+
}, {
|
23
|
+
"long_name": "New York",
|
24
|
+
"short_name": "New York",
|
25
|
+
"types": [ "administrative_area_level_2", "political" ]
|
26
|
+
}, {
|
27
|
+
"long_name": "New York",
|
28
|
+
"short_name": "NY",
|
29
|
+
"types": [ "administrative_area_level_1", "political" ]
|
30
|
+
}, {
|
31
|
+
"long_name": "United States",
|
32
|
+
"short_name": "US",
|
33
|
+
"types": [ "country", "political" ]
|
34
|
+
}, {
|
35
|
+
"long_name": "10001",
|
36
|
+
"short_name": "10001",
|
37
|
+
"types": [ "postal_code" ]
|
38
|
+
} ],
|
39
|
+
"geometry": {
|
40
|
+
"location": {
|
41
|
+
"lat": 40.7503540,
|
42
|
+
"lng": -73.9933710
|
43
|
+
},
|
44
|
+
"location_type": "ROOFTOP",
|
45
|
+
"viewport": {
|
46
|
+
"southwest": {
|
47
|
+
"lat": 40.7473324,
|
48
|
+
"lng": -73.9965316
|
49
|
+
},
|
50
|
+
"northeast": {
|
51
|
+
"lat": 40.7536276,
|
52
|
+
"lng": -73.9902364
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
} ]
|
57
|
+
}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
{
|
2
|
+
"ResultSet":{
|
3
|
+
"version":"1.0",
|
4
|
+
"Error":0,
|
5
|
+
"ErrorMessage":"No error",
|
6
|
+
"Locale":"us_US",
|
7
|
+
"Quality":87,
|
8
|
+
"Found":1,
|
9
|
+
"Results":[{
|
10
|
+
"quality":9,
|
11
|
+
"latitude":"55.008390",
|
12
|
+
"longitude":"-5.822485",
|
13
|
+
"offsetlat":"54.314072",
|
14
|
+
"offsetlon":"-2.230010",
|
15
|
+
"radius":1145100,
|
16
|
+
"boundingbox":{
|
17
|
+
"north":"60.854691",
|
18
|
+
"south":"49.162090",
|
19
|
+
"east":"1.768960",
|
20
|
+
"west":"-13.413930"
|
21
|
+
},
|
22
|
+
"name":"",
|
23
|
+
"line1":"",
|
24
|
+
"line2":"",
|
25
|
+
"line3":"",
|
26
|
+
"line4":"United Kingdom",
|
27
|
+
"cross":"",
|
28
|
+
"house":"",
|
29
|
+
"street":"",
|
30
|
+
"xstreet":"",
|
31
|
+
"unittype":"",
|
32
|
+
"unit":"",
|
33
|
+
"postal":"",
|
34
|
+
"neighborhood":"",
|
35
|
+
"city":"",
|
36
|
+
"county":"",
|
37
|
+
"state":"",
|
38
|
+
"country":"United Kingdom",
|
39
|
+
"countrycode":"GB",
|
40
|
+
"statecode":"",
|
41
|
+
"countycode":"",
|
42
|
+
"timezone":"Europe/London",
|
43
|
+
"areacode":"",
|
44
|
+
"uzip":"",
|
45
|
+
"hash":"",
|
46
|
+
"woeid":23424975,
|
47
|
+
"woetype":12
|
48
|
+
}]
|
49
|
+
}
|
50
|
+
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
{
|
2
|
+
"ResultSet":{
|
3
|
+
"version":"1.0",
|
4
|
+
"Error":0,
|
5
|
+
"ErrorMessage":"No error",
|
6
|
+
"Locale":"us_US",
|
7
|
+
"Quality":90,
|
8
|
+
"Found":1,
|
9
|
+
"Results":[{
|
10
|
+
"quality":90,
|
11
|
+
"latitude":"40.750381",
|
12
|
+
"longitude":"-73.993988",
|
13
|
+
"offsetlat":"40.750381",
|
14
|
+
"offsetlon":"-73.993988",
|
15
|
+
"radius":100,
|
16
|
+
"name":"Madison Square Garden",
|
17
|
+
"line1":"Madison Square Garden",
|
18
|
+
"line2":"New York, NY 10001",
|
19
|
+
"line3":"",
|
20
|
+
"line4":"United States",
|
21
|
+
"house":"",
|
22
|
+
"street":"",
|
23
|
+
"xstreet":"",
|
24
|
+
"unittype":"",
|
25
|
+
"unit":"",
|
26
|
+
"postal":"10001",
|
27
|
+
"neighborhood":"",
|
28
|
+
"city":"New York",
|
29
|
+
"county":"New York County",
|
30
|
+
"state":"New York",
|
31
|
+
"country":"United States",
|
32
|
+
"countrycode":"US",
|
33
|
+
"statecode":"NY",
|
34
|
+
"countycode":"",
|
35
|
+
"uzip":"10001",
|
36
|
+
"hash":"",
|
37
|
+
"woeid":23617041,
|
38
|
+
"woetype":20,
|
39
|
+
"cross":"",
|
40
|
+
"timezone":"America/New_York",
|
41
|
+
"neighborhood":"Garment District|Midtown|Midtown West|Manhattan",
|
42
|
+
"areacode":"212",
|
43
|
+
"boundingbox":{"north":"40.750832","south":"40.749931","east":"-73.993393","west":"-73.994591"}
|
44
|
+
}]
|
45
|
+
}
|
46
|
+
}
|
data/test/geocoder_test.rb
CHANGED
@@ -2,18 +2,17 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class GeocoderTest < Test::Unit::TestCase
|
4
4
|
|
5
|
-
def
|
6
|
-
|
7
|
-
assert_equal [40.750354, -73.993371], v.fetch_coordinates
|
8
|
-
assert_equal [40.750354, -73.993371], [v.latitude, v.longitude]
|
5
|
+
def setup
|
6
|
+
Geocoder::Configuration.lookup = :google
|
9
7
|
end
|
10
8
|
|
11
|
-
|
9
|
+
|
10
|
+
# --- sanity checks ---
|
11
|
+
|
12
12
|
def test_distance_between
|
13
13
|
assert_equal 69, Geocoder::Calculations.distance_between(0,0, 0,1).round
|
14
14
|
end
|
15
15
|
|
16
|
-
# sanity check
|
17
16
|
def test_geographic_center
|
18
17
|
assert_equal [0.0, 0.5],
|
19
18
|
Geocoder::Calculations.geographic_center([[0,0], [0,1]])
|
@@ -21,16 +20,190 @@ class GeocoderTest < Test::Unit::TestCase
|
|
21
20
|
Geocoder::Calculations.geographic_center([[0,0], [0,1], [0,2]])
|
22
21
|
end
|
23
22
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
def test_does_not_choke_on_nil_address
|
24
|
+
v = Venue.new("Venue", nil)
|
25
|
+
assert_nothing_raised do
|
26
|
+
v.geocode
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
30
|
+
def test_distance_to_returns_float
|
31
|
+
v = Venue.new(*venue_params(:msg))
|
32
|
+
v.latitude, v.longitude = [40.750354, -73.993371]
|
33
|
+
assert (d = v.distance_to(30, -94)).is_a?(Float)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_distance_from_is_alias_for_distance_to
|
37
|
+
v = Venue.new(*venue_params(:msg))
|
38
|
+
v.latitude, v.longitude = [40.750354, -73.993371]
|
39
|
+
assert_equal v.distance_from(30, -94), v.distance_to(30, -94)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# --- general ---
|
44
|
+
|
45
|
+
def test_geocode_assigns_and_returns_coordinates
|
46
|
+
v = Venue.new(*venue_params(:msg))
|
47
|
+
coords = [40.750354, -73.993371]
|
48
|
+
assert_equal coords, v.geocode
|
49
|
+
assert_equal coords, [v.latitude, v.longitude]
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_reverse_geocode_assigns_and_returns_address
|
53
|
+
v = Landmark.new(*landmark_params(:msg))
|
54
|
+
address = "4 Penn Plaza, New York, NY 10001, USA"
|
55
|
+
assert_equal address, v.reverse_geocode
|
56
|
+
assert_equal address, v.address
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_geocode_with_block_runs_block
|
60
|
+
e = Event.new(*venue_params(:msg))
|
61
|
+
coords = [40.750354, -73.993371]
|
62
|
+
e.geocode
|
63
|
+
assert_equal coords.map{ |c| c.to_s }.join(','), e.coordinates
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_geocode_with_block_doesnt_auto_assign_coordinates
|
67
|
+
e = Event.new(*venue_params(:msg))
|
68
|
+
e.geocode
|
69
|
+
assert_nil e.latitude
|
70
|
+
assert_nil e.longitude
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_reverse_geocode_with_block_runs_block
|
74
|
+
e = Party.new(*landmark_params(:msg))
|
75
|
+
e.reverse_geocode
|
76
|
+
assert_equal "US", e.country
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_reverse_geocode_with_block_doesnt_auto_assign_address
|
80
|
+
e = Party.new(*landmark_params(:msg))
|
81
|
+
e.reverse_geocode
|
82
|
+
assert_nil e.address
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_forward_and_reverse_geocoding_on_same_model
|
86
|
+
g = GasStation.new("Exxon")
|
87
|
+
g.address = "404 New St, Middletown, CT"
|
88
|
+
g.geocode
|
89
|
+
assert_not_nil g.lat
|
90
|
+
assert_not_nil g.lon
|
91
|
+
|
92
|
+
assert_nil g.location
|
93
|
+
g.reverse_geocode
|
94
|
+
assert_not_nil g.location
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_fetch_coordinates_is_alias_for_geocode
|
98
|
+
v = Venue.new(*venue_params(:msg))
|
99
|
+
coords = [40.750354, -73.993371]
|
100
|
+
assert_equal coords, v.fetch_coordinates
|
101
|
+
assert_equal coords, [v.latitude, v.longitude]
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_fetch_address_is_alias_for_reverse_geocode
|
105
|
+
v = Landmark.new(*landmark_params(:msg))
|
106
|
+
address = "4 Penn Plaza, New York, NY 10001, USA"
|
107
|
+
assert_equal address, v.fetch_address
|
108
|
+
assert_equal address, v.address
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
# --- Google ---
|
113
|
+
|
31
114
|
def test_result_address_components_of_type
|
32
|
-
|
115
|
+
result = Geocoder.search("Madison Square Garden, New York, NY")
|
33
116
|
assert_equal "Manhattan",
|
34
|
-
|
117
|
+
result.address_components_of_type(:sublocality).first['long_name']
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_google_result_has_required_attributes
|
121
|
+
result = Geocoder.search("Madison Square Garden, New York, NY")
|
122
|
+
assert_result_has_required_attributes(result)
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_google_returns_nil_when_no_results
|
126
|
+
assert_nil Geocoder.search("no results")
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
# --- Yahoo ---
|
131
|
+
|
132
|
+
def test_yahoo_result_components
|
133
|
+
Geocoder::Configuration.lookup = :yahoo
|
134
|
+
result = Geocoder.search("Madison Square Garden, New York, NY")
|
135
|
+
assert_equal "10001", result.postal
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_yahoo_address_formatting
|
139
|
+
Geocoder::Configuration.lookup = :yahoo
|
140
|
+
result = Geocoder.search("Madison Square Garden, New York, NY")
|
141
|
+
assert_equal "Madison Square Garden, New York, NY 10001, United States",
|
142
|
+
result.address
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_yahoo_result_has_required_attributes
|
146
|
+
Geocoder::Configuration.lookup = :yahoo
|
147
|
+
result = Geocoder.search("Madison Square Garden, New York, NY")
|
148
|
+
assert_result_has_required_attributes(result)
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_yahoo_returns_nil_when_no_results
|
152
|
+
Geocoder::Configuration.lookup = :yahoo
|
153
|
+
assert_nil Geocoder.search("no results")
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
# --- FreeGeoIp ---
|
158
|
+
|
159
|
+
def test_freegeoip_result_on_ip_address_search
|
160
|
+
result = Geocoder.search("74.200.247.59")
|
161
|
+
assert result.is_a?(Geocoder::Result::Freegeoip)
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_freegeoip_result_components
|
165
|
+
result = Geocoder.search("74.200.247.59")
|
166
|
+
assert_equal "Plano, TX 75093, United States", result.address
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_freegeoip_result_has_required_attributes
|
170
|
+
result = Geocoder.search("74.200.247.59")
|
171
|
+
assert_result_has_required_attributes(result)
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
# --- search queries ---
|
176
|
+
|
177
|
+
def test_ip_address_detection
|
178
|
+
assert Geocoder.send(:ip_address?, "232.65.123.94")
|
179
|
+
assert Geocoder.send(:ip_address?, "666.65.123.94") # technically invalid
|
180
|
+
assert !Geocoder.send(:ip_address?, "232.65.123.94.43")
|
181
|
+
assert !Geocoder.send(:ip_address?, "232.65.123")
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_blank_query_detection
|
185
|
+
assert Geocoder.send(:blank_query?, nil)
|
186
|
+
assert Geocoder.send(:blank_query?, "")
|
187
|
+
assert Geocoder.send(:blank_query?, ", , (-)")
|
188
|
+
assert !Geocoder.send(:blank_query?, "a")
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_hash_to_query
|
192
|
+
g = Geocoder::Lookup::Google.new
|
193
|
+
assert_equal "a=1&b=2", g.send(:hash_to_query, {:a => 1, :b => 2})
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
private # ------------------------------------------------------------------
|
198
|
+
|
199
|
+
def assert_result_has_required_attributes(result)
|
200
|
+
assert result.coordinates.is_a?(Array)
|
201
|
+
assert result.latitude.is_a?(Float)
|
202
|
+
assert result.longitude.is_a?(Float)
|
203
|
+
assert result.city.is_a?(String)
|
204
|
+
assert result.postal_code.is_a?(String)
|
205
|
+
assert result.country.is_a?(String)
|
206
|
+
assert result.country_code.is_a?(String)
|
207
|
+
assert_not_nil result.address
|
35
208
|
end
|
36
209
|
end
|
data/test/test_helper.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'test/unit'
|
3
|
-
require 'active_support/core_ext'
|
4
3
|
|
5
4
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
5
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
@@ -28,21 +27,47 @@ module ActiveRecord
|
|
28
27
|
end
|
29
28
|
|
30
29
|
def self.scope(*args); end
|
30
|
+
|
31
|
+
def method_missing(name, *args, &block)
|
32
|
+
if name.to_s[-1..-1] == "="
|
33
|
+
write_attribute name.to_s[0...-1], *args
|
34
|
+
else
|
35
|
+
read_attribute name
|
36
|
+
end
|
37
|
+
end
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
34
41
|
# Require Geocoder after ActiveRecord simulator.
|
35
42
|
require 'geocoder'
|
43
|
+
require "geocoder/lookups/base"
|
36
44
|
|
37
45
|
##
|
38
46
|
# Mock HTTP request to geocoding service.
|
39
47
|
#
|
40
48
|
module Geocoder
|
41
49
|
module Lookup
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
50
|
+
class Google < Base
|
51
|
+
private #-----------------------------------------------------------------
|
52
|
+
def fetch_raw_data(query, reverse = false)
|
53
|
+
file = query == "no results" ? :no_results : :madison_square_garden
|
54
|
+
File.read(File.join("test", "fixtures", "google_#{file}.json"))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Yahoo < Base
|
59
|
+
private #-----------------------------------------------------------------
|
60
|
+
def fetch_raw_data(query, reverse = false)
|
61
|
+
file = query == "no results" ? :no_results : :madison_square_garden
|
62
|
+
File.read(File.join("test", "fixtures", "yahoo_#{file}.json"))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Freegeoip < Base
|
67
|
+
private #-----------------------------------------------------------------
|
68
|
+
def fetch_raw_data(query, reverse = false)
|
69
|
+
File.read(File.join("test", "fixtures", "freegeoip_74_200_247_59.json"))
|
70
|
+
end
|
46
71
|
end
|
47
72
|
end
|
48
73
|
end
|
@@ -58,13 +83,6 @@ class Venue < ActiveRecord::Base
|
|
58
83
|
write_attribute :name, name
|
59
84
|
write_attribute :address, address
|
60
85
|
end
|
61
|
-
|
62
|
-
##
|
63
|
-
# If method not found, assume it's an ActiveRecord attribute reader.
|
64
|
-
#
|
65
|
-
def method_missing(name, *args, &block)
|
66
|
-
@attributes[name]
|
67
|
-
end
|
68
86
|
end
|
69
87
|
|
70
88
|
##
|
@@ -79,19 +97,68 @@ class Landmark < ActiveRecord::Base
|
|
79
97
|
write_attribute :latitude, latitude
|
80
98
|
write_attribute :longitude, longitude
|
81
99
|
end
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Geocoded model with block.
|
104
|
+
#
|
105
|
+
class Event < ActiveRecord::Base
|
106
|
+
geocoded_by :address do |obj,result|
|
107
|
+
if result
|
108
|
+
obj.coordinates = "#{result.latitude},#{result.longitude}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def initialize(name, address)
|
113
|
+
super()
|
114
|
+
write_attribute :name, name
|
115
|
+
write_attribute :address, address
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Reverse geocoded model with block.
|
121
|
+
#
|
122
|
+
class Party < ActiveRecord::Base
|
123
|
+
reverse_geocoded_by :latitude, :longitude do |obj,result|
|
124
|
+
if result
|
125
|
+
obj.country = result.country_code
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def initialize(name, latitude, longitude)
|
130
|
+
super()
|
131
|
+
write_attribute :name, name
|
132
|
+
write_attribute :latitude, latitude
|
133
|
+
write_attribute :longitude, longitude
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
# Forward and reverse geocoded model.
|
139
|
+
# Should fill in whatever's missing (coords or address).
|
140
|
+
#
|
141
|
+
class GasStation < ActiveRecord::Base
|
142
|
+
geocoded_by :address, :latitude => :lat, :longitude => :lon
|
143
|
+
reverse_geocoded_by :lat, :lon, :address => :location
|
82
144
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
def method_missing(name, *args, &block)
|
87
|
-
@attributes[name]
|
145
|
+
def initialize(name)
|
146
|
+
super()
|
147
|
+
write_attribute :name, name
|
88
148
|
end
|
89
149
|
end
|
90
150
|
|
151
|
+
|
91
152
|
class Test::Unit::TestCase
|
92
153
|
def venue_params(abbrev)
|
93
154
|
{
|
94
155
|
:msg => ["Madison Square Garden", "4 Penn Plaza, New York, NY"]
|
95
156
|
}[abbrev]
|
96
157
|
end
|
158
|
+
|
159
|
+
def landmark_params(abbrev)
|
160
|
+
{
|
161
|
+
:msg => ["Madison Square Garden", 40.750354, -73.993371]
|
162
|
+
}[abbrev]
|
163
|
+
end
|
97
164
|
end
|