geokit 1.10.0 → 1.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.hound.yml +2 -0
- data/.rubocop.yml +358 -0
- data/.travis.yml +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile +3 -3
- data/README.markdown +19 -4
- data/Rakefile +11 -16
- data/fixtures/keys.yml +9 -0
- data/fixtures/vcr_cassettes/google_postal_town.yml +117 -0
- data/fixtures/vcr_cassettes/mapbox_forward_geocode.yml +30 -30
- data/fixtures/vcr_cassettes/mapbox_forward_geocode_city_only.yml +25 -25
- data/fixtures/vcr_cassettes/mapbox_forward_geocode_state_only.yml +71 -0
- data/fixtures/vcr_cassettes/mapbox_reverse_geocode.yml +25 -25
- data/fixtures/vcr_cassettes/test_component_filtering_off.yml +390 -0
- data/fixtures/vcr_cassettes/test_component_filtering_on.yml +164 -0
- data/fixtures/vcr_cassettes/test_component_filtering_on_without_filter.yml +404 -0
- data/geokit.gemspec +24 -23
- data/lib/geokit.rb +7 -7
- data/lib/geokit/core_ext.rb +1 -1
- data/lib/geokit/geo_loc.rb +25 -19
- data/lib/geokit/geocoders.rb +21 -30
- data/lib/geokit/geocoders/bing.rb +5 -4
- data/lib/geokit/geocoders/ca_geocoder.rb +10 -11
- data/lib/geokit/geocoders/fcc.rb +9 -9
- data/lib/geokit/geocoders/free_geo_ip.rb +8 -8
- data/lib/geokit/geocoders/geo_plugin.rb +7 -7
- data/lib/geokit/geocoders/geobytes.rb +10 -10
- data/lib/geokit/geocoders/geocodio.rb +14 -14
- data/lib/geokit/geocoders/geonames.rb +12 -12
- data/lib/geokit/geocoders/google.rb +89 -61
- data/lib/geokit/geocoders/ip.rb +9 -14
- data/lib/geokit/geocoders/mapbox.rb +30 -30
- data/lib/geokit/geocoders/mapquest.rb +12 -12
- data/lib/geokit/geocoders/maxmind.rb +1 -1
- data/lib/geokit/geocoders/opencage.rb +19 -19
- data/lib/geokit/geocoders/openstreetmap.rb +21 -19
- data/lib/geokit/geocoders/ripe.rb +7 -7
- data/lib/geokit/geocoders/us_geocoder.rb +5 -5
- data/lib/geokit/geocoders/yahoo.rb +46 -46
- data/lib/geokit/geocoders/yandex.rb +18 -17
- data/lib/geokit/inflectors.rb +5 -5
- data/lib/geokit/lat_lng.rb +5 -4
- data/lib/geokit/multi_geocoder.rb +4 -2
- data/lib/geokit/net_adapter/net_http.rb +3 -2
- data/lib/geokit/net_adapter/typhoeus.rb +2 -1
- data/lib/geokit/version.rb +1 -1
- data/test/coverage_loader.rb +25 -0
- data/test/helper.rb +18 -87
- data/test/test_base_geocoder.rb +44 -11
- data/test/test_bing_geocoder.rb +40 -48
- data/test/test_bounds.rb +1 -1
- data/test/test_ca_geocoder.rb +15 -15
- data/test/test_fcc_geocoder.rb +8 -9
- data/test/test_free_geo_ip_geocoder.rb +8 -10
- data/test/test_geo_plugin_geocoder.rb +21 -22
- data/test/test_geobytes_geocoder.rb +9 -11
- data/test/test_geocodio_geocoder.rb +12 -14
- data/test/test_geoloc.rb +48 -49
- data/test/test_geonames_geocoder.rb +19 -23
- data/test/test_google_geocoder.rb +197 -189
- data/test/test_inflector.rb +7 -7
- data/test/test_ipgeocoder.rb +32 -31
- data/test/test_latlng.rb +28 -28
- data/test/test_map_quest.rb +23 -27
- data/test/test_mapbox_geocoder.rb +38 -28
- data/test/test_mappable.rb +2 -2
- data/test/test_maxmind_geocoder.rb +16 -16
- data/test/test_multi_geocoder.rb +5 -5
- data/test/test_multi_ip_geocoder.rb +3 -3
- data/test/test_net_adapter.rb +4 -4
- data/test/test_opencage_geocoder.rb +58 -67
- data/test/test_openstreetmap_geocoder.rb +67 -65
- data/test/test_polygon.rb +4 -22
- data/test/test_ripe_geocoder.rb +21 -26
- data/test/test_us_geocoder.rb +21 -21
- data/test/test_useragent.rb +46 -0
- data/test/test_yahoo_geocoder.rb +35 -47
- data/test/test_yandex_geocoder.rb +29 -27
- data/test/vcr_loader.rb +18 -0
- metadata +20 -6
@@ -8,18 +8,18 @@ module Geokit
|
|
8
8
|
private
|
9
9
|
|
10
10
|
# Template method which does the geocode lookup.
|
11
|
-
def self.do_geocode(address)
|
11
|
+
def self.do_geocode(address, _=nil)
|
12
12
|
process :xml, submit_url(address)
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.submit_url(address)
|
16
16
|
if key.nil? || key.empty?
|
17
|
-
raise(Geokit::Geocoders::GeocodeError,
|
17
|
+
raise(Geokit::Geocoders::GeocodeError, 'Geonames requires a key to use their service.')
|
18
18
|
end
|
19
19
|
|
20
20
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
21
21
|
# geonames need a space seperated search string
|
22
|
-
address_str.gsub!(/,/,
|
22
|
+
address_str.gsub!(/,/, ' ')
|
23
23
|
params = "/postalCodeSearch?placename=#{Geokit::Inflector.url_escape(address_str)}&maxRows=10"
|
24
24
|
|
25
25
|
if premium
|
@@ -30,21 +30,21 @@ module Geokit
|
|
30
30
|
end
|
31
31
|
|
32
32
|
XML_MAPPINGS = {
|
33
|
-
city:
|
34
|
-
state_name:
|
35
|
-
state_code:
|
36
|
-
zip:
|
37
|
-
country_code:
|
38
|
-
lat:
|
39
|
-
lng:
|
33
|
+
city: 'name',
|
34
|
+
state_name: 'adminName1',
|
35
|
+
state_code: 'adminCode1',
|
36
|
+
zip: 'postalcode',
|
37
|
+
country_code: 'countryCode',
|
38
|
+
lat: 'lat',
|
39
|
+
lng: 'lng',
|
40
40
|
}
|
41
41
|
|
42
42
|
def self.parse_xml(xml)
|
43
|
-
count = xml.elements[
|
43
|
+
count = xml.elements['geonames/totalResultsCount']
|
44
44
|
return GeoLoc.new unless !count.nil? && count.text.to_i > 0
|
45
45
|
loc = new_loc
|
46
46
|
# only take the first result
|
47
|
-
set_mappings(loc, xml.elements[
|
47
|
+
set_mappings(loc, xml.elements['geonames/code'], XML_MAPPINGS)
|
48
48
|
loc.success = true
|
49
49
|
loc
|
50
50
|
end
|
@@ -8,9 +8,18 @@ module Geokit
|
|
8
8
|
|
9
9
|
# ==== OPTIONS
|
10
10
|
# * :language - See: https://developers.google.com/maps/documentation/geocoding
|
11
|
+
# * :result_type - This option allows restricting results by specific result types.
|
12
|
+
# See https://developers.google.com/maps/documentation/geocoding/intro#reverse-restricted
|
13
|
+
# Note: This parameter is available only for requests that include an API key or a client ID.
|
14
|
+
# * :location_type - This option allows restricting results by specific location type.
|
15
|
+
# See https://developers.google.com/maps/documentation/geocoding/intro#reverse-restricted
|
16
|
+
# Note: This parameter is available only for requests that include an API key or a client ID.
|
11
17
|
def self.do_reverse_geocode(latlng, options = {})
|
12
18
|
latlng = LatLng.normalize(latlng)
|
13
|
-
|
19
|
+
latlng_str = "latlng=#{Geokit::Inflector.url_escape(latlng.ll)}"
|
20
|
+
result_type_str = options[:result_type] ? "&result_type=#{options[:result_type]}" : ''
|
21
|
+
location_type_str = options[:location_type] ? "&location_type=#{options[:location_type]}" : ''
|
22
|
+
url = submit_url("#{latlng_str}#{result_type_str}#{location_type_str}", options)
|
14
23
|
process :json, url
|
15
24
|
end
|
16
25
|
|
@@ -24,6 +33,9 @@ module Geokit
|
|
24
33
|
#
|
25
34
|
# If you'd like the Google Geocoder to prefer results within a given viewport,
|
26
35
|
# you can pass a Geokit::Bounds object as the :bias value.
|
36
|
+
# * :components - This option allows restricting results by specific areas. See
|
37
|
+
# https://developers.google.com/maps/documentation/geocoding/intro#ComponentFiltering
|
38
|
+
# for details.
|
27
39
|
#
|
28
40
|
# ==== EXAMPLES
|
29
41
|
# # By default, the geocoder will return Toledo, OH
|
@@ -36,31 +48,40 @@ module Geokit
|
|
36
48
|
# # When biased to an bounding box around California, it will now return the Winnetka neighbourhood, CA
|
37
49
|
# bounds = Geokit::Bounds.normalize([34.074081, -118.694401], [34.321129, -118.399487])
|
38
50
|
# Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka', :bias => bounds).state # => 'CA'
|
51
|
+
#
|
52
|
+
# # By default, the geocoder will return several matches for Austin with
|
53
|
+
# the first one being in Texas
|
54
|
+
# Geokit::Geocoders::GoogleGeocoder.geocode('Austin').state # => 'TX'
|
55
|
+
# # Using Component Filtering the results can be restricted to a specific
|
56
|
+
# area, e.g. IL
|
57
|
+
# Geokit::Geocoders::GoogleGeocoder.geocode('Austin',
|
58
|
+
# :components => {administrative_area: 'IL', country: 'US'}).state # => 'IL'
|
39
59
|
def self.do_geocode(address, options = {})
|
40
|
-
bias_str = options[:bias] ? construct_bias_string_from_options(options[:bias]) :
|
60
|
+
bias_str = options[:bias] ? construct_bias_string_from_options(options[:bias]) : ''
|
61
|
+
components_str = options[:components] ? construct_components_string_from_options(options[:components]) : ''
|
41
62
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
42
|
-
url = submit_url("address=#{Geokit::Inflector.url_escape(address_str)}#{bias_str}", options)
|
63
|
+
url = submit_url("address=#{Geokit::Inflector.url_escape(address_str)}#{bias_str}#{components_str}", options)
|
43
64
|
process :json, url
|
44
65
|
end
|
45
66
|
|
46
67
|
# This code comes from Googles Examples
|
47
68
|
# http://gmaps-samples.googlecode.com/svn/trunk/urlsigning/urlsigner.rb
|
48
69
|
def self.sign_gmap_bus_api_url(urlToSign, google_cryptographic_key)
|
49
|
-
require
|
50
|
-
require
|
70
|
+
require 'base64'
|
71
|
+
require 'openssl'
|
51
72
|
# Decode the private key
|
52
|
-
rawKey = Base64.decode64(google_cryptographic_key.tr(
|
73
|
+
rawKey = Base64.decode64(google_cryptographic_key.tr('-_', '+/'))
|
53
74
|
# create a signature using the private key and the URL
|
54
|
-
rawSignature = OpenSSL::HMAC.digest(
|
75
|
+
rawSignature = OpenSSL::HMAC.digest('sha1', rawKey, urlToSign)
|
55
76
|
# encode the signature into base64 for url use form.
|
56
|
-
Base64.encode64(rawSignature).tr(
|
77
|
+
Base64.encode64(rawSignature).tr('+/', '-_').gsub(/\n/, '')
|
57
78
|
end
|
58
79
|
|
59
80
|
def self.submit_url(query_string, options = {})
|
60
|
-
language_str = options[:language] ? "&language=#{options[:language]}" :
|
81
|
+
language_str = options[:language] ? "&language=#{options[:language]}" : ''
|
61
82
|
query_string = "/maps/api/geocode/json?sensor=false&#{query_string}#{language_str}"
|
62
83
|
if client_id && cryptographic_key
|
63
|
-
channel_string = channel ? "&channel=#{channel}" :
|
84
|
+
channel_string = channel ? "&channel=#{channel}" : ''
|
64
85
|
urlToSign = query_string + "&client=#{client_id}" + channel_string
|
65
86
|
signature = sign_gmap_bus_api_url(urlToSign, cryptographic_key)
|
66
87
|
"#{protocol}://maps.googleapis.com" + urlToSign + "&signature=#{signature}"
|
@@ -84,21 +105,27 @@ module Geokit
|
|
84
105
|
end
|
85
106
|
end
|
86
107
|
|
108
|
+
def self.construct_components_string_from_options(components={})
|
109
|
+
unless components.empty?
|
110
|
+
"&components=#{components.to_a.map { |pair| pair.join(':').downcase }.join(CGI.escape('|'))}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
87
114
|
def self.parse_json(results)
|
88
|
-
case results[
|
89
|
-
when
|
90
|
-
raise Geokit::Geocoders::TooManyQueriesError, results[
|
91
|
-
when
|
92
|
-
raise Geokit::Geocoders::AccessDeniedError, results[
|
93
|
-
when
|
115
|
+
case results['status']
|
116
|
+
when 'OVER_QUERY_LIMIT'
|
117
|
+
raise Geokit::Geocoders::TooManyQueriesError, results['error_message']
|
118
|
+
when 'REQUEST_DENIED'
|
119
|
+
raise Geokit::Geocoders::AccessDeniedError, results['error_message']
|
120
|
+
when 'ZERO_RESULTS'
|
94
121
|
return GeoLoc.new
|
95
|
-
when
|
122
|
+
when 'OK'
|
96
123
|
# all good
|
97
124
|
else
|
98
|
-
raise Geokit::Geocoders::GeocodeError, results[
|
125
|
+
raise Geokit::Geocoders::GeocodeError, results['error_message']
|
99
126
|
end
|
100
127
|
|
101
|
-
unsorted = results[
|
128
|
+
unsorted = results['results'].map do |addr|
|
102
129
|
single_json_to_geoloc(addr)
|
103
130
|
end
|
104
131
|
|
@@ -125,10 +152,10 @@ module Geokit
|
|
125
152
|
# these do not map well. Perhaps we should guess better based on size
|
126
153
|
# of bounding box where it exists? Does it really matter?
|
127
154
|
ACCURACY = {
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
155
|
+
'ROOFTOP' => 9,
|
156
|
+
'RANGE_INTERPOLATED' => 8,
|
157
|
+
'GEOMETRIC_CENTER' => 5,
|
158
|
+
'APPROXIMATE' => 4,
|
132
159
|
}
|
133
160
|
|
134
161
|
PRECISIONS = %w(unknown country state state city zip zip+4 street address building)
|
@@ -136,17 +163,17 @@ module Geokit
|
|
136
163
|
def self.single_json_to_geoloc(addr)
|
137
164
|
loc = new_loc
|
138
165
|
loc.success = true
|
139
|
-
loc.full_address = addr[
|
166
|
+
loc.full_address = addr['formatted_address']
|
140
167
|
|
141
168
|
set_address_components(loc, addr)
|
142
169
|
set_precision(loc, addr)
|
143
170
|
if loc.street_name
|
144
|
-
loc.street_address = [loc.street_number, loc.street_name].join(
|
171
|
+
loc.street_address = [loc.street_number, loc.street_name].join(' ').strip
|
145
172
|
end
|
146
173
|
|
147
|
-
ll = addr[
|
148
|
-
loc.lat = ll[
|
149
|
-
loc.lng = ll[
|
174
|
+
ll = addr['geometry']['location']
|
175
|
+
loc.lat = ll['lat'].to_f
|
176
|
+
loc.lng = ll['lng'].to_f
|
150
177
|
|
151
178
|
set_bounds(loc, addr)
|
152
179
|
|
@@ -157,59 +184,60 @@ module Geokit
|
|
157
184
|
end
|
158
185
|
|
159
186
|
def self.set_bounds(loc, addr)
|
160
|
-
viewport = addr[
|
161
|
-
ne = Geokit::LatLng.from_json(viewport[
|
162
|
-
sw = Geokit::LatLng.from_json(viewport[
|
187
|
+
viewport = addr['geometry']['viewport']
|
188
|
+
ne = Geokit::LatLng.from_json(viewport['northeast'])
|
189
|
+
sw = Geokit::LatLng.from_json(viewport['southwest'])
|
163
190
|
loc.suggested_bounds = Geokit::Bounds.new(sw, ne)
|
164
191
|
end
|
165
192
|
|
166
193
|
def self.set_address_components(loc, addr)
|
167
|
-
addr[
|
168
|
-
types = comp[
|
194
|
+
addr['address_components'].each do |comp|
|
195
|
+
types = comp['types']
|
169
196
|
case
|
170
|
-
when types.include?(
|
171
|
-
loc.sub_premise = comp[
|
172
|
-
when types.include?(
|
173
|
-
loc.street_number = comp[
|
174
|
-
when types.include?(
|
175
|
-
loc.street_name = comp[
|
176
|
-
when types.include?(
|
177
|
-
loc.city = comp[
|
178
|
-
when types.include?(
|
179
|
-
loc.state_code = comp[
|
180
|
-
loc.state_name = comp[
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
loc.
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
197
|
+
when types.include?('subpremise')
|
198
|
+
loc.sub_premise = comp['short_name']
|
199
|
+
when types.include?('street_number')
|
200
|
+
loc.street_number = comp['short_name']
|
201
|
+
when types.include?('route')
|
202
|
+
loc.street_name = comp['long_name']
|
203
|
+
when types.include?('locality')
|
204
|
+
loc.city = comp['long_name']
|
205
|
+
when types.include?('administrative_area_level_1') # state
|
206
|
+
loc.state_code = comp['short_name']
|
207
|
+
loc.state_name = comp['long_name']
|
208
|
+
when types.include?('postal_town')
|
209
|
+
loc.city = comp['long_name']
|
210
|
+
when types.include?('postal_code')
|
211
|
+
loc.zip = comp['long_name']
|
212
|
+
when types.include?('country')
|
213
|
+
loc.country_code = comp['short_name']
|
214
|
+
loc.country = comp['long_name']
|
215
|
+
when types.include?('administrative_area_level_2')
|
216
|
+
loc.district = comp['long_name']
|
217
|
+
when types.include?('neighborhood')
|
218
|
+
loc.neighborhood = comp['short_name']
|
191
219
|
# Use either sublocality or admin area level 3 if google does not return a city
|
192
|
-
when types.include?(
|
193
|
-
loc.city = comp[
|
194
|
-
when types.include?(
|
195
|
-
loc.city = comp[
|
220
|
+
when types.include?('sublocality')
|
221
|
+
loc.city = comp['long_name'] if loc.city.nil?
|
222
|
+
when types.include?('administrative_area_level_3')
|
223
|
+
loc.city = comp['long_name'] if loc.city.nil?
|
196
224
|
end
|
197
225
|
end
|
198
226
|
end
|
199
227
|
|
200
228
|
def self.set_precision(loc, addr)
|
201
|
-
loc.accuracy = ACCURACY[addr[
|
229
|
+
loc.accuracy = ACCURACY[addr['geometry']['location_type']]
|
202
230
|
loc.precision = PRECISIONS[loc.accuracy]
|
203
231
|
# try a few overrides where we can
|
204
232
|
if loc.sub_premise
|
205
233
|
loc.precision = PRECISIONS[9]
|
206
234
|
loc.accuracy = 9
|
207
235
|
end
|
208
|
-
if loc.street_name && loc.precision ==
|
236
|
+
if loc.street_name && loc.precision == 'city'
|
209
237
|
loc.precision = PRECISIONS[7]
|
210
238
|
loc.accuracy = 7
|
211
239
|
end
|
212
|
-
if addr[
|
240
|
+
if addr['types'].include?('postal_code')
|
213
241
|
loc.precision = PRECISIONS[6]
|
214
242
|
loc.accuracy = 6
|
215
243
|
end
|
data/lib/geokit/geocoders/ip.rb
CHANGED
@@ -9,14 +9,14 @@ module Geokit
|
|
9
9
|
# Given an IP address, returns a GeoLoc instance which contains latitude,
|
10
10
|
# longitude, city, and country code. Sets the success attribute to false if the ip
|
11
11
|
# parameter does not match an ip address.
|
12
|
-
def self.do_geocode(ip)
|
12
|
+
def self.do_geocode(ip, _=nil)
|
13
13
|
return GeoLoc.new unless valid_ip?(ip)
|
14
14
|
url = submit_url(ip)
|
15
15
|
res = call_geocoder_service(url)
|
16
16
|
return GeoLoc.new unless net_adapter.success?(res)
|
17
17
|
ensure_utf8_encoding(res)
|
18
18
|
body = res.body
|
19
|
-
body = body.encode(
|
19
|
+
body = body.encode('UTF-8') if body.respond_to? :encode
|
20
20
|
parse :yaml, body
|
21
21
|
end
|
22
22
|
|
@@ -34,10 +34,10 @@ module Geokit
|
|
34
34
|
# then instantiates a GeoLoc instance to populate with location data.
|
35
35
|
def self.parse_yaml(yaml) # :nodoc:
|
36
36
|
loc = new_loc
|
37
|
-
loc.city, loc.state_code = yaml[
|
38
|
-
loc.country, loc.country_code = yaml[
|
39
|
-
loc.lat = yaml[
|
40
|
-
loc.lng = yaml[
|
37
|
+
loc.city, loc.state_code = yaml['City'].split(', ')
|
38
|
+
loc.country, loc.country_code = yaml['Country'].split(' (')
|
39
|
+
loc.lat = yaml['Latitude']
|
40
|
+
loc.lng = yaml['Longitude']
|
41
41
|
loc.country_code.chop!
|
42
42
|
loc.success = !(loc.city =~ /\(.+\)/)
|
43
43
|
loc
|
@@ -49,19 +49,14 @@ module Geokit
|
|
49
49
|
# thus extract encoding from headers and tell Rails about it by forcing it
|
50
50
|
def self.ensure_utf8_encoding(res)
|
51
51
|
if (enc_string = extract_charset(res))
|
52
|
-
|
53
|
-
|
54
|
-
res.body.encode("UTF-8")
|
55
|
-
else
|
56
|
-
require "iconv"
|
57
|
-
res.body.replace Iconv.conv("UTF8", "iso88591", res.body)
|
58
|
-
end
|
52
|
+
res.body.force_encoding(enc_string.upcase) if res.body.respond_to?(:force_encoding)
|
53
|
+
res.body.encode('UTF-8')
|
59
54
|
end
|
60
55
|
end
|
61
56
|
|
62
57
|
# Extracts charset out of the response headers
|
63
58
|
def self.extract_charset(res)
|
64
|
-
if (content_type = res[
|
59
|
+
if (content_type = res['content-type'])
|
65
60
|
capture = content_type.match(/charset=(.+)/)
|
66
61
|
capture && capture[1]
|
67
62
|
end
|
@@ -9,7 +9,7 @@ module Geokit
|
|
9
9
|
private
|
10
10
|
|
11
11
|
# Template method which does the reverse-geocode lookup.
|
12
|
-
def self.do_reverse_geocode(latlng,
|
12
|
+
def self.do_reverse_geocode(latlng, _options = nil)
|
13
13
|
latlng = LatLng.normalize(latlng)
|
14
14
|
url = "#{protocol}://api.tiles.mapbox.com/v4/geocode/mapbox.places-v1/"
|
15
15
|
url += "#{latlng.lng},#{latlng.lat}.json?access_token=#{key}"
|
@@ -17,7 +17,7 @@ module Geokit
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# Template method which does the geocode lookup.
|
20
|
-
def self.do_geocode(address,
|
20
|
+
def self.do_geocode(address, _options = nil)
|
21
21
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
22
22
|
url = "#{protocol}://api.tiles.mapbox.com/v4/geocode/mapbox.places-v1/"
|
23
23
|
url += "#{Geokit::Inflector.url_escape(address_str)}.json?access_token=#{key}"
|
@@ -25,9 +25,9 @@ module Geokit
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def self.parse_json(results)
|
28
|
-
return GeoLoc.new unless results[
|
28
|
+
return GeoLoc.new unless results['features'].count > 0
|
29
29
|
loc = nil
|
30
|
-
results[
|
30
|
+
results['features'].each do |feature|
|
31
31
|
extracted_geoloc = extract_geoloc(feature)
|
32
32
|
if loc.nil?
|
33
33
|
loc = extracted_geoloc
|
@@ -40,52 +40,52 @@ module Geokit
|
|
40
40
|
|
41
41
|
def self.extract_geoloc(result_json)
|
42
42
|
loc = new_loc
|
43
|
-
loc.lng = result_json[
|
44
|
-
loc.lat = result_json[
|
43
|
+
loc.lng = result_json['center'][0]
|
44
|
+
loc.lat = result_json['center'][1]
|
45
45
|
set_address_components(result_json, loc)
|
46
46
|
set_precision(loc)
|
47
|
-
set_bounds(result_json[
|
47
|
+
set_bounds(result_json['bbox'], loc)
|
48
48
|
loc.success = true
|
49
49
|
loc
|
50
50
|
end
|
51
51
|
|
52
52
|
def self.set_address_components(result_json, loc)
|
53
|
-
if result_json[
|
54
|
-
result_json[
|
55
|
-
if context[
|
56
|
-
loc.country = context[
|
57
|
-
elsif context[
|
58
|
-
loc.state = context[
|
59
|
-
elsif context[
|
60
|
-
loc.city = context[
|
61
|
-
elsif context[
|
62
|
-
loc.zip = context[
|
53
|
+
if result_json['context']
|
54
|
+
result_json['context'].each do |context|
|
55
|
+
if context['id'] =~ /^country\./
|
56
|
+
loc.country = context['text']
|
57
|
+
elsif context['id'] =~ /^province\./
|
58
|
+
loc.state = context['text']
|
59
|
+
elsif context['id'] =~ /^city\./
|
60
|
+
loc.city = context['text']
|
61
|
+
elsif context['id'] =~ /^postcode/
|
62
|
+
loc.zip = context['text']
|
63
63
|
end
|
64
64
|
end
|
65
65
|
loc.country = loc.country_code if loc.country_code && !loc.country
|
66
66
|
end
|
67
|
-
if result_json[
|
68
|
-
loc.full_address = result_json[
|
67
|
+
if result_json['place_name']
|
68
|
+
loc.full_address = result_json['place_name']
|
69
69
|
end
|
70
|
-
if !loc.city && result_json[
|
71
|
-
loc.city = result_json[
|
70
|
+
if !loc.city && result_json['id'] =~ /^city\./
|
71
|
+
loc.city = result_json['text']
|
72
|
+
end
|
73
|
+
if !loc.state && result_json['id'] =~ /^province\./
|
74
|
+
loc.state = result_json['text']
|
72
75
|
end
|
73
76
|
end
|
74
77
|
|
75
|
-
PRECISION_VALUES = %w(
|
78
|
+
PRECISION_VALUES = %w(country state city zip)
|
76
79
|
|
77
80
|
def self.set_precision(loc)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
else
|
82
|
-
break
|
83
|
-
end
|
81
|
+
PRECISION_VALUES.each do |precision|
|
82
|
+
break unless loc.send(precision) && loc.send(precision).length
|
83
|
+
loc.precision = precision
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
def self.set_bounds(
|
88
|
-
if bounds
|
87
|
+
def self.set_bounds(bounds, loc)
|
88
|
+
if bounds
|
89
89
|
loc.suggested_bounds = Bounds.normalize([bounds[1], bounds[0]], [bounds[3], bounds[2]])
|
90
90
|
end
|
91
91
|
end
|