geokit 1.8.5 → 1.9.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 +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
|