geokit 1.10.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|