geocoder 0.9.8 → 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of geocoder might be problematic. Click here for more details.
- data/.gitignore +3 -0
- data/CHANGELOG.rdoc +19 -4
- data/README.rdoc +115 -76
- data/Rakefile +2 -33
- data/VERSION +1 -0
- data/lib/geocoder.rb +78 -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/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 +50 -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
|