geokit 1.8.5 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +0 -1
- data/CHANGELOG.md +7 -0
- data/README.markdown +31 -14
- data/fixtures/vcr_cassettes/geonames_geocode.yml +1 -1
- data/fixtures/vcr_cassettes/geonames_geocode_premium.yml +42 -0
- data/fixtures/vcr_cassettes/google_country_code_biased_result_orly.yml +160 -0
- data/fixtures/vcr_cassettes/google_country_code_biased_result_toledo.yml +111 -0
- data/fixtures/vcr_cassettes/google_result_toledo_default_bias.yml +275 -0
- data/fixtures/vcr_cassettes/google_sublocality.yml +126 -0
- data/fixtures/vcr_cassettes/mapbox_forward_geocode.yml +59 -0
- data/fixtures/vcr_cassettes/mapbox_reverse_geocode.yml +63 -0
- data/fixtures/vcr_cassettes/opencage_city.yml +51 -0
- data/fixtures/vcr_cassettes/opencage_full.yml +65 -0
- data/fixtures/vcr_cassettes/opencage_language_response_es.yml +66 -0
- data/fixtures/vcr_cassettes/opencage_reverse_madrid.yml +51 -0
- data/fixtures/vcr_cassettes/opencage_reverse_prilep.yml +53 -0
- data/geokit.gemspec +5 -3
- data/lib/geokit/bounds.rb +40 -31
- data/lib/geokit/core_ext.rb +1 -1
- data/lib/geokit/geo_loc.rb +63 -41
- data/lib/geokit/geocoders.rb +13 -13
- data/lib/geokit/geocoders/bing.rb +9 -9
- data/lib/geokit/geocoders/ca_geocoder.rb +29 -29
- data/lib/geokit/geocoders/fcc.rb +4 -4
- data/lib/geokit/geocoders/free_geo_ip.rb +6 -7
- data/lib/geokit/geocoders/geo_plugin.rb +5 -6
- data/lib/geokit/geocoders/geocodio.rb +2 -2
- data/lib/geokit/geocoders/geonames.rb +18 -12
- data/lib/geokit/geocoders/google.rb +31 -30
- data/lib/geokit/geocoders/ip.rb +3 -4
- data/lib/geokit/geocoders/mapbox.rb +94 -0
- data/lib/geokit/geocoders/mapquest.rb +5 -5
- data/lib/geokit/geocoders/opencage.rb +93 -0
- data/lib/geokit/geocoders/openstreetmap.rb +9 -6
- data/lib/geokit/geocoders/ripe.rb +3 -3
- data/lib/geokit/geocoders/us_geocoder.rb +10 -9
- data/lib/geokit/geocoders/yahoo.rb +33 -34
- data/lib/geokit/geocoders/yandex.rb +17 -17
- data/lib/geokit/inflectors.rb +4 -10
- data/lib/geokit/lat_lng.rb +50 -26
- data/lib/geokit/mappable.rb +83 -83
- data/lib/geokit/multi_geocoder.rb +25 -20
- data/lib/geokit/net_adapter/net_http.rb +7 -4
- data/lib/geokit/polygon.rb +36 -4
- data/lib/geokit/version.rb +1 -1
- data/test/helper.rb +15 -13
- data/test/test_base_geocoder.rb +6 -7
- data/test/test_bing_geocoder.rb +20 -21
- data/test/test_bounds.rb +26 -28
- data/test/test_ca_geocoder.rb +9 -10
- data/test/test_fcc_geocoder.rb +1 -1
- data/test/test_free_geo_ip_geocoder.rb +1 -1
- data/test/test_geo_plugin_geocoder.rb +9 -9
- data/test/test_geoloc.rb +7 -6
- data/test/test_geonames_geocoder.rb +28 -6
- data/test/test_google_geocoder.rb +210 -176
- data/test/test_inflector.rb +0 -1
- data/test/test_ipgeocoder.rb +17 -18
- data/test/test_latlng.rb +105 -85
- data/test/test_map_quest.rb +18 -21
- data/test/test_mapbox_geocoder.rb +31 -0
- data/test/test_mappable.rb +46 -0
- data/test/test_maxmind_geocoder.rb +1 -3
- data/test/test_multi_geocoder.rb +8 -9
- data/test/test_multi_ip_geocoder.rb +3 -5
- data/test/test_net_adapter.rb +4 -4
- data/test/test_opencage_geocoder.rb +108 -0
- data/test/test_openstreetmap_geocoder.rb +62 -44
- data/test/{test_polygon_contains.rb → test_polygon.rb} +30 -20
- data/test/test_ripe_geocoder.rb +2 -0
- data/test/test_us_geocoder.rb +7 -8
- data/test/test_yahoo_geocoder.rb +20 -21
- data/test/test_yandex_geocoder.rb +34 -35
- metadata +79 -56
- data/fixtures/vcr_cassettes/google_country_code_biased_result.yml +0 -401
@@ -3,7 +3,7 @@ module Geokit
|
|
3
3
|
# Another geocoding web service
|
4
4
|
# http://www.geonames.org
|
5
5
|
class GeonamesGeocoder < Geocoder
|
6
|
-
config :key
|
6
|
+
config :key, :premium
|
7
7
|
|
8
8
|
private
|
9
9
|
|
@@ -13,29 +13,35 @@ module Geokit
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.submit_url(address)
|
16
|
+
if key.nil? || key.empty?
|
17
|
+
raise Geokit::Geocoders::GeocodeError.new('Geonames requires a key to use their service.')
|
18
|
+
end
|
19
|
+
|
16
20
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
17
21
|
# geonames need a space seperated search string
|
18
|
-
address_str.gsub!(/,/,
|
19
|
-
params = "/postalCodeSearch?placename=#{Geokit::Inflector
|
22
|
+
address_str.gsub!(/,/, ' ')
|
23
|
+
params = "/postalCodeSearch?placename=#{Geokit::Inflector.url_escape(address_str)}&maxRows=10"
|
20
24
|
|
21
|
-
if
|
25
|
+
if premium
|
22
26
|
"http://ws.geonames.net#{params}&username=#{key}"
|
23
27
|
else
|
24
|
-
"http://
|
28
|
+
"http://api.geonames.org#{params}&username=#{key}"
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
28
32
|
XML_MAPPINGS = {
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
33
|
+
city: 'name',
|
34
|
+
state_name: 'adminName1',
|
35
|
+
state_code: 'adminCode1',
|
36
|
+
zip: 'postalcode',
|
37
|
+
country_code: 'countryCode',
|
38
|
+
lat: 'lat',
|
39
|
+
lng: 'lng'
|
35
40
|
}
|
36
41
|
|
37
42
|
def self.parse_xml(xml)
|
38
|
-
|
43
|
+
count = xml.elements['geonames/totalResultsCount']
|
44
|
+
return GeoLoc.new unless !count.nil? && count.text.to_i > 0
|
39
45
|
loc = new_loc
|
40
46
|
# only take the first result
|
41
47
|
set_mappings(loc, xml.elements['geonames/code'], XML_MAPPINGS)
|
@@ -5,11 +5,12 @@ module Geokit
|
|
5
5
|
self.secure = true
|
6
6
|
|
7
7
|
private
|
8
|
+
|
8
9
|
# ==== OPTIONS
|
9
10
|
# * :language - See: https://developers.google.com/maps/documentation/geocoding
|
10
11
|
def self.do_reverse_geocode(latlng, options = {})
|
11
|
-
latlng=LatLng.normalize(latlng)
|
12
|
-
url = submit_url("latlng=#{Geokit::Inflector
|
12
|
+
latlng = LatLng.normalize(latlng)
|
13
|
+
url = submit_url("latlng=#{Geokit::Inflector.url_escape(latlng.ll)}", options)
|
13
14
|
process :json, url
|
14
15
|
end
|
15
16
|
|
@@ -25,10 +26,10 @@ module Geokit
|
|
25
26
|
# you can pass a Geokit::Bounds object as the :bias value.
|
26
27
|
#
|
27
28
|
# ==== EXAMPLES
|
28
|
-
# # By default, the geocoder will return
|
29
|
-
# Geokit::Geocoders::GoogleGeocoder.geocode('
|
30
|
-
# # With country code biasing, it returns
|
31
|
-
# Geokit::Geocoders::GoogleGeocoder.geocode('
|
29
|
+
# # By default, the geocoder will return Toledo, OH
|
30
|
+
# Geokit::Geocoders::GoogleGeocoder.geocode('Toledo').country_code # => 'US'
|
31
|
+
# # With country code biasing, it returns Toledo (spannish city), Spain
|
32
|
+
# Geokit::Geocoders::GoogleGeocoder.geocode('Toledo', :bias => :es).country_code # => 'Es'
|
32
33
|
#
|
33
34
|
# # By default, the geocoder will return Winnetka, IL
|
34
35
|
# Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka').state # => 'IL'
|
@@ -38,7 +39,7 @@ module Geokit
|
|
38
39
|
def self.do_geocode(address, options = {})
|
39
40
|
bias_str = options[:bias] ? construct_bias_string_from_options(options[:bias]) : ''
|
40
41
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
41
|
-
url = submit_url("address=#{Geokit::Inflector
|
42
|
+
url = submit_url("address=#{Geokit::Inflector.url_escape(address_str)}#{bias_str}", options)
|
42
43
|
process :json, url
|
43
44
|
end
|
44
45
|
|
@@ -48,14 +49,13 @@ module Geokit
|
|
48
49
|
require 'base64'
|
49
50
|
require 'openssl'
|
50
51
|
# Decode the private key
|
51
|
-
rawKey = Base64.decode64(google_cryptographic_key.tr('-_','+/'))
|
52
|
+
rawKey = Base64.decode64(google_cryptographic_key.tr('-_', '+/'))
|
52
53
|
# create a signature using the private key and the URL
|
53
54
|
rawSignature = OpenSSL::HMAC.digest('sha1', rawKey, urlToSign)
|
54
55
|
# encode the signature into base64 for url use form.
|
55
|
-
Base64.encode64(rawSignature).tr('+/','-_').gsub(/\n/, '')
|
56
|
+
Base64.encode64(rawSignature).tr('+/', '-_').gsub(/\n/, '')
|
56
57
|
end
|
57
58
|
|
58
|
-
|
59
59
|
def self.submit_url(query_string, options = {})
|
60
60
|
language_str = options[:language] ? "&language=#{options[:language]}" : ''
|
61
61
|
query_string = "/maps/api/geocode/json?sensor=false&#{query_string}#{language_str}"
|
@@ -72,7 +72,6 @@ module Geokit
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
|
76
75
|
def self.construct_bias_string_from_options(bias)
|
77
76
|
case bias
|
78
77
|
when String, Symbol
|
@@ -80,7 +79,7 @@ module Geokit
|
|
80
79
|
"®ion=#{bias.to_s.downcase}"
|
81
80
|
when Bounds
|
82
81
|
# viewport biasing
|
83
|
-
url_escaped_string = Geokit::Inflector
|
82
|
+
url_escaped_string = Geokit::Inflector.url_escape("#{bias.sw}|#{bias.ne}")
|
84
83
|
"&bounds=#{url_escaped_string}"
|
85
84
|
end
|
86
85
|
end
|
@@ -99,13 +98,12 @@ module Geokit
|
|
99
98
|
single_json_to_geoloc(addr)
|
100
99
|
end
|
101
100
|
|
102
|
-
all = unsorted.
|
101
|
+
all = unsorted.sort {|a, b| b.accuracy <=> a.accuracy }
|
103
102
|
encoded = all.first
|
104
103
|
encoded.all = all
|
105
104
|
encoded
|
106
105
|
end
|
107
106
|
|
108
|
-
|
109
107
|
# location_type stores additional data about the specified location.
|
110
108
|
# The following values are currently supported:
|
111
109
|
# "ROOFTOP" indicates that the returned result is a precise geocode
|
@@ -123,10 +121,10 @@ module Geokit
|
|
123
121
|
# these do not map well. Perhaps we should guess better based on size
|
124
122
|
# of bounding box where it exists? Does it really matter?
|
125
123
|
ACCURACY = {
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
124
|
+
'ROOFTOP' => 9,
|
125
|
+
'RANGE_INTERPOLATED' => 8,
|
126
|
+
'GEOMETRIC_CENTER' => 5,
|
127
|
+
'APPROXIMATE' => 4
|
130
128
|
}
|
131
129
|
|
132
130
|
def self.single_json_to_geoloc(addr)
|
@@ -137,7 +135,7 @@ module Geokit
|
|
137
135
|
set_address_components(loc, addr)
|
138
136
|
set_precision(loc, addr)
|
139
137
|
if loc.street_name
|
140
|
-
loc.street_address=[loc.street_number, loc.street_name].join(' ').strip
|
138
|
+
loc.street_address = [loc.street_number, loc.street_name].join(' ').strip
|
141
139
|
end
|
142
140
|
|
143
141
|
ll = addr['geometry']['location']
|
@@ -160,39 +158,42 @@ module Geokit
|
|
160
158
|
addr['address_components'].each do |comp|
|
161
159
|
types = comp['types']
|
162
160
|
case
|
163
|
-
when types.include?(
|
161
|
+
when types.include?('subpremise')
|
164
162
|
loc.sub_premise = comp['short_name']
|
165
|
-
when types.include?(
|
163
|
+
when types.include?('street_number')
|
166
164
|
loc.street_number = comp['short_name']
|
167
|
-
when types.include?(
|
165
|
+
when types.include?('route')
|
168
166
|
loc.street_name = comp['long_name']
|
169
|
-
when types.include?(
|
167
|
+
when types.include?('locality')
|
170
168
|
loc.city = comp['long_name']
|
171
|
-
when types.include?(
|
172
|
-
loc.
|
169
|
+
when types.include?('administrative_area_level_1')
|
170
|
+
loc.state_code = comp['short_name']
|
171
|
+
loc.state_name = comp['long_name']
|
173
172
|
loc.province = comp['short_name']
|
174
|
-
when types.include?(
|
173
|
+
when types.include?('postal_code')
|
175
174
|
loc.zip = comp['long_name']
|
176
|
-
when types.include?(
|
175
|
+
when types.include?('country')
|
177
176
|
loc.country_code = comp['short_name']
|
178
177
|
loc.country = comp['long_name']
|
179
|
-
when types.include?(
|
178
|
+
when types.include?('administrative_area_level_2')
|
180
179
|
loc.district = comp['long_name']
|
181
180
|
when types.include?('neighborhood')
|
182
181
|
loc.neighborhood = comp['short_name']
|
182
|
+
when types.include?('sublocality')
|
183
|
+
loc.city = comp['long_name'] if loc.city.nil?
|
183
184
|
end
|
184
185
|
end
|
185
186
|
end
|
186
187
|
|
187
188
|
def self.set_precision(loc, addr)
|
188
189
|
loc.accuracy = ACCURACY[addr['geometry']['location_type']]
|
189
|
-
loc.precision
|
190
|
+
loc.precision = %w{unknown country state state city zip zip+4 street address building}[loc.accuracy]
|
190
191
|
# try a few overrides where we can
|
191
192
|
if loc.sub_premise
|
192
193
|
loc.accuracy = 9
|
193
194
|
loc.precision = 'building'
|
194
195
|
end
|
195
|
-
if loc.street_name && loc.precision=='city'
|
196
|
+
if loc.street_name && loc.precision == 'city'
|
196
197
|
loc.precision = 'street'
|
197
198
|
loc.accuracy = 7
|
198
199
|
end
|
data/lib/geokit/geocoders/ip.rb
CHANGED
@@ -34,7 +34,7 @@ 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.
|
37
|
+
loc.city, loc.state_code = yaml['City'].split(', ')
|
38
38
|
loc.country, loc.country_code = yaml['Country'].split(' (')
|
39
39
|
loc.lat = yaml['Latitude']
|
40
40
|
loc.lng = yaml['Longitude']
|
@@ -51,10 +51,10 @@ module Geokit
|
|
51
51
|
if (enc_string = extract_charset(res))
|
52
52
|
if defined?(Encoding) && Encoding.aliases.values.include?(enc_string.upcase)
|
53
53
|
res.body.force_encoding(enc_string.upcase) if res.body.respond_to?(:force_encoding)
|
54
|
-
res.body.encode(
|
54
|
+
res.body.encode('UTF-8')
|
55
55
|
else
|
56
56
|
require 'iconv'
|
57
|
-
res.body.replace Iconv.conv(
|
57
|
+
res.body.replace Iconv.conv('UTF8', 'iso88591', res.body)
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
@@ -69,4 +69,3 @@ module Geokit
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
end
|
72
|
-
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Geokit
|
2
|
+
module Geocoders
|
3
|
+
# Mapbox geocoder implementation. Requires the Geokit::Geocoders::MapboxGeocoder:key variable to
|
4
|
+
# contain a Mapbox access token. Conforms to the interface set by the Geocoder class.
|
5
|
+
class MapboxGeocoder < Geocoder
|
6
|
+
config :key
|
7
|
+
self.secure = true
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# Template method which does the reverse-geocode lookup.
|
12
|
+
def self.do_reverse_geocode(latlng)
|
13
|
+
latlng = LatLng.normalize(latlng)
|
14
|
+
url = "#{protocol}://api.tiles.mapbox.com/v4/geocode/mapbox.places-v1/"
|
15
|
+
url += "#{latlng.lng},#{latlng.lat}.json?access_token=#{key}"
|
16
|
+
process :json, url
|
17
|
+
end
|
18
|
+
|
19
|
+
# Template method which does the geocode lookup.
|
20
|
+
def self.do_geocode(address)
|
21
|
+
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
22
|
+
url = "#{protocol}://api.tiles.mapbox.com/v4/geocode/mapbox.places-v1/"
|
23
|
+
url += "#{Geokit::Inflector.url_escape(address_str)}.json?access_token=#{key}"
|
24
|
+
process :json, url
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.parse_json(results)
|
28
|
+
return GeoLoc.new unless results['features'].count > 0
|
29
|
+
loc = nil
|
30
|
+
results['features'].each do |feature|
|
31
|
+
extracted_geoloc = extract_geoloc(feature)
|
32
|
+
if loc.nil?
|
33
|
+
loc = extracted_geoloc
|
34
|
+
else
|
35
|
+
loc.all.push(extracted_geoloc)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
loc
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.extract_geoloc(result_json)
|
42
|
+
loc = new_loc
|
43
|
+
loc.lng = result_json['center'][0]
|
44
|
+
loc.lat = result_json['center'][1]
|
45
|
+
set_address_components(result_json, loc)
|
46
|
+
set_precision(loc)
|
47
|
+
set_bounds(result_json['bbox'], loc)
|
48
|
+
loc.success = true
|
49
|
+
loc
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.set_address_components(result_json, loc)
|
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
|
+
loc.country_code = context['id'].split('.')[0].gsub(/^postcode-/, '').upcase
|
64
|
+
end
|
65
|
+
end
|
66
|
+
if loc.country_code && !loc.country
|
67
|
+
loc.country = loc.country_code
|
68
|
+
end
|
69
|
+
end
|
70
|
+
if result_json['place_name']
|
71
|
+
loc.full_address = result_json['place_name']
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
PRECISION_VALUES = %w{unknown country state city zip full_address}
|
76
|
+
|
77
|
+
def self.set_precision(loc)
|
78
|
+
for i in 1...PRECISION_VALUES.length - 1
|
79
|
+
if loc.send(PRECISION_VALUES[i]) && loc.send(PRECISION_VALUES[i]).length
|
80
|
+
loc.precision = PRECISION_VALUES[i]
|
81
|
+
else
|
82
|
+
break
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.set_bounds(result_json, loc)
|
88
|
+
if bounds = result_json
|
89
|
+
loc.suggested_bounds = Bounds.normalize([bounds[1], bounds[0]], [bounds[3], bounds[2]])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Geokit
|
2
2
|
module Geocoders
|
3
|
-
# MapQuest geocoder implementation. Requires the Geokit::Geocoders::
|
4
|
-
# contain a MapQuest API key. Conforms to the interface set by the Geocoder class.
|
3
|
+
# MapQuest geocoder implementation. Requires the Geokit::Geocoders::MapQuestGeocoder:key
|
4
|
+
# variable to contain a MapQuest API key. Conforms to the interface set by the Geocoder class.
|
5
5
|
class MapQuestGeocoder < Geocoder
|
6
6
|
config :key
|
7
7
|
self.secure = true
|
@@ -10,7 +10,7 @@ module Geokit
|
|
10
10
|
|
11
11
|
# Template method which does the reverse-geocode lookup.
|
12
12
|
def self.do_reverse_geocode(latlng)
|
13
|
-
latlng=LatLng.normalize(latlng)
|
13
|
+
latlng = LatLng.normalize(latlng)
|
14
14
|
url = "#{protocol}://www.mapquestapi.com/geocoding/v1/reverse?key=#{key}&location=#{latlng.lat},#{latlng.lng}"
|
15
15
|
process :json, url
|
16
16
|
end
|
@@ -18,7 +18,7 @@ module Geokit
|
|
18
18
|
# Template method which does the geocode lookup.
|
19
19
|
def self.do_geocode(address)
|
20
20
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
21
|
-
url = "#{protocol}://www.mapquestapi.com/geocoding/v1/address?key=#{key}&location=#{Geokit::Inflector
|
21
|
+
url = "#{protocol}://www.mapquestapi.com/geocoding/v1/address?key=#{key}&location=#{Geokit::Inflector.url_escape(address_str)}"
|
22
22
|
process :json, url
|
23
23
|
end
|
24
24
|
|
@@ -34,7 +34,7 @@ module Geokit
|
|
34
34
|
loc.all.push(extracted_geoloc)
|
35
35
|
end
|
36
36
|
end
|
37
|
-
end
|
37
|
+
end
|
38
38
|
loc
|
39
39
|
end
|
40
40
|
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Geokit
|
2
|
+
module Geocoders
|
3
|
+
# OpenCage geocoder implementation. Requires the Geokit::Geocoders::OpencageGeocoder.key
|
4
|
+
# variable to contain an API key. Conforms to the interface set by the Geocoder class.
|
5
|
+
class OpencageGeocoder < Geocoder
|
6
|
+
config :key
|
7
|
+
self.secure = true
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# Template method which does the geocode lookup.
|
12
|
+
def self.do_geocode(address, options = {})
|
13
|
+
options_str = generate_param_for_option(:language, options)
|
14
|
+
options_str << generate_param_for_option(:bounds, options)
|
15
|
+
options_str << generate_param_for_option(:min_confidence, options)
|
16
|
+
|
17
|
+
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
18
|
+
url = "#{protocol}://api.opencagedata.com/geocode/v1/json?"
|
19
|
+
url += "key=#{key}#{options_str}&"
|
20
|
+
url += "query=#{Geokit::Inflector.url_escape(address_str)}&"
|
21
|
+
url += 'no_annotations=1'
|
22
|
+
process :json, url
|
23
|
+
end
|
24
|
+
# Template method which does the reverse-geocode lookup.
|
25
|
+
def self.do_reverse_geocode(latlng)
|
26
|
+
latlng = LatLng.normalize(latlng)
|
27
|
+
url = "#{protocol}://api.opencagedata.com/geocode/v1/json?"
|
28
|
+
url += "key=#{key}&query=#{latlng.lat},#{latlng.lng}"
|
29
|
+
process :json, url
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.generate_param_for(param, value)
|
33
|
+
"&#{param}=#{Geokit::Inflector.url_escape(value.to_s)}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.generate_param_for_option(param, options)
|
37
|
+
options[param] ? "&#{param}=#{Geokit::Inflector.url_escape(options[param])}" : ''
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.generate_bool_param_for_option(param, options)
|
41
|
+
options[param] ? "&#{param}=1" : "&#{param}=0"
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.parse_json(results)
|
45
|
+
return GeoLoc.new if results.empty?
|
46
|
+
if results.is_a?(Hash)
|
47
|
+
return GeoLoc.new unless results['status']['message'] == 'OK'
|
48
|
+
end
|
49
|
+
|
50
|
+
loc = nil
|
51
|
+
results['results'].each do |result|
|
52
|
+
extracted_geoloc = extract_geoloc(result)
|
53
|
+
if loc.nil?
|
54
|
+
loc = extracted_geoloc
|
55
|
+
else
|
56
|
+
loc.all.push(extracted_geoloc)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
loc
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.extract_geoloc(result_json)
|
63
|
+
loc = new_loc
|
64
|
+
loc.lat = result_json['geometry']['lat']
|
65
|
+
loc.lng = result_json['geometry']['lng']
|
66
|
+
set_address_components(result_json['components'], loc)
|
67
|
+
set_precision(result_json, loc)
|
68
|
+
loc.success = true
|
69
|
+
loc
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.set_address_components(address_data, loc)
|
73
|
+
return unless address_data
|
74
|
+
loc.country = address_data['country']
|
75
|
+
loc.country_code = address_data['country_code'].upcase if address_data['country_code']
|
76
|
+
loc.state_name = address_data['state']
|
77
|
+
loc.city = address_data['city']
|
78
|
+
loc.city = address_data['county'] if loc.city.nil? && address_data['county']
|
79
|
+
loc.zip = address_data['postcode']
|
80
|
+
loc.district = address_data['city_district']
|
81
|
+
loc.district = address_data['state_district'] if loc.district.nil? && address_data['state_district']
|
82
|
+
loc.street_address = "#{address_data['road']} #{address_data['house_number']}".strip if address_data['road']
|
83
|
+
loc.street_name = address_data['road']
|
84
|
+
loc.street_number = address_data['house_number']
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.set_precision(result_json, loc)
|
88
|
+
# a value between 1 (worse) and 10 (best). 0 means unknown
|
89
|
+
loc.precision = result_json['confidence']
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|