geokit-premier 0.0.7 → 0.1.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.
- data/.gitignore +1 -0
- data/geokit-premier.gemspec +1 -1
- data/lib/geokit/geocoders.rb +170 -157
- data/lib/geokit/geocoders_mine.rb +119 -119
- data/lib/geokit/version.rb +1 -1
- data/spec/geocoder_spec.rb +61 -1
- data/spec/spec_helper.rb +1 -1
- metadata +114 -118
- data/Gemfile.lock +0 -56
@@ -11,13 +11,13 @@ module Geokit
|
|
11
11
|
class TooManyQueriesError < StandardError; end
|
12
12
|
|
13
13
|
module Inflector
|
14
|
-
|
14
|
+
|
15
15
|
extend self
|
16
|
-
|
16
|
+
|
17
17
|
def titleize(word)
|
18
18
|
humanize(underscore(word)).gsub(/\b([a-z])/u) { $1.capitalize }
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def underscore(camel_cased_word)
|
22
22
|
camel_cased_word.to_s.gsub(/::/, '/').
|
23
23
|
gsub(/([A-Z]+)([A-Z][a-z])/u,'\1_\2').
|
@@ -25,52 +25,52 @@ module Geokit
|
|
25
25
|
tr("-", "_").
|
26
26
|
downcase
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
def humanize(lower_case_and_underscored_word)
|
30
30
|
lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def snake_case(s)
|
34
34
|
return s.downcase if s =~ /^[A-Z]+$/u
|
35
35
|
s.gsub(/([A-Z]+)(?=[A-Z][a-z]?)|\B[A-Z]/u, '_\&') =~ /_*(.*)/
|
36
36
|
return $+.downcase
|
37
|
-
|
37
|
+
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
def url_escape(s)
|
41
41
|
URI.escape(s)
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
def camelize(str)
|
45
45
|
str.split('_').map {|w| w.capitalize}.join
|
46
46
|
end
|
47
|
-
end
|
48
|
-
|
47
|
+
end
|
48
|
+
|
49
49
|
# Contains a range of geocoders:
|
50
|
-
#
|
51
|
-
# ### "regular" address geocoders
|
50
|
+
#
|
51
|
+
# ### "regular" address geocoders
|
52
52
|
# * Yahoo Geocoder - requires an API key.
|
53
53
|
# * Geocoder.us - may require authentication if performing more than the free request limit.
|
54
54
|
# * Geocoder.ca - for Canada; may require authentication as well.
|
55
55
|
# * Geonames - a free geocoder
|
56
56
|
#
|
57
|
-
# ### address geocoders that also provide reverse geocoding
|
57
|
+
# ### address geocoders that also provide reverse geocoding
|
58
58
|
# * Google Geocoder - requires an API key.
|
59
|
-
#
|
60
|
-
# ### IP address geocoders
|
59
|
+
#
|
60
|
+
# ### IP address geocoders
|
61
61
|
# * IP Geocoder - geocodes an IP address using hostip.info's web service.
|
62
62
|
# * Geoplugin.net -- another IP address geocoder
|
63
63
|
#
|
64
64
|
# ### The Multigeocoder
|
65
65
|
# * Multi Geocoder - provides failover for the physical location geocoders.
|
66
|
-
#
|
66
|
+
#
|
67
67
|
# Some of these geocoders require configuration. You don't have to provide it here. See the README.
|
68
68
|
module Geocoders
|
69
69
|
@@proxy_addr = nil
|
70
70
|
@@proxy_port = nil
|
71
71
|
@@proxy_user = nil
|
72
72
|
@@proxy_pass = nil
|
73
|
-
@@request_timeout = nil
|
73
|
+
@@request_timeout = nil
|
74
74
|
@@yahoo = 'REPLACE_WITH_YOUR_YAHOO_KEY'
|
75
75
|
@@google = 'REPLACE_WITH_YOUR_GOOGLE_KEY'
|
76
76
|
@@google_client_id = nil #only used for premier accounts
|
@@ -83,9 +83,9 @@ module Geokit
|
|
83
83
|
@@logger=Logger.new(STDOUT)
|
84
84
|
@@logger.level=Logger::INFO
|
85
85
|
@@domain = nil
|
86
|
-
|
86
|
+
|
87
87
|
def self.__define_accessors
|
88
|
-
class_variables.each do |v|
|
88
|
+
class_variables.each do |v|
|
89
89
|
sym = v.to_s.delete("@").to_sym
|
90
90
|
unless self.respond_to? sym
|
91
91
|
module_eval <<-EOS, __FILE__, __LINE__
|
@@ -100,7 +100,7 @@ module Geokit
|
|
100
100
|
end
|
101
101
|
value
|
102
102
|
end
|
103
|
-
|
103
|
+
|
104
104
|
def self.#{sym}=(obj)
|
105
105
|
@@#{sym} = obj
|
106
106
|
end
|
@@ -110,38 +110,38 @@ module Geokit
|
|
110
110
|
end
|
111
111
|
|
112
112
|
__define_accessors
|
113
|
-
|
113
|
+
|
114
114
|
# Error which is thrown in the event a geocoding error occurs.
|
115
115
|
class GeocodeError < StandardError; end
|
116
116
|
|
117
117
|
# -------------------------------------------------------------------------------------------
|
118
118
|
# Geocoder Base class -- every geocoder should inherit from this
|
119
|
-
# -------------------------------------------------------------------------------------------
|
120
|
-
|
119
|
+
# -------------------------------------------------------------------------------------------
|
120
|
+
|
121
121
|
# The Geocoder base class which defines the interface to be used by all
|
122
122
|
# other geocoders.
|
123
|
-
class Geocoder
|
123
|
+
class Geocoder
|
124
124
|
# Main method which calls the do_geocode template method which subclasses
|
125
125
|
# are responsible for implementing. Returns a populated GeoLoc or an
|
126
126
|
# empty one with a failed success code.
|
127
|
-
def self.geocode(address, options = {})
|
127
|
+
def self.geocode(address, options = {})
|
128
128
|
res = do_geocode(address, options)
|
129
129
|
return res.nil? ? GeoLoc.new : res
|
130
|
-
end
|
130
|
+
end
|
131
131
|
# Main method which calls the do_reverse_geocode template method which subclasses
|
132
132
|
# are responsible for implementing. Returns a populated GeoLoc or an
|
133
133
|
# empty one with a failed success code.
|
134
134
|
def self.reverse_geocode(latlng)
|
135
135
|
res = do_reverse_geocode(latlng)
|
136
|
-
return res.success? ? res : GeoLoc.new
|
136
|
+
return res.success? ? res : GeoLoc.new
|
137
137
|
end
|
138
|
-
|
138
|
+
|
139
139
|
# Call the geocoder service using the timeout if configured.
|
140
140
|
def self.call_geocoder_service(url)
|
141
|
-
Timeout::timeout(Geokit::Geocoders::request_timeout) { return self.do_get(url) } if Geokit::Geocoders::request_timeout
|
141
|
+
Timeout::timeout(Geokit::Geocoders::request_timeout) { return self.do_get(url) } if Geokit::Geocoders::request_timeout
|
142
142
|
return self.do_get(url)
|
143
143
|
rescue TimeoutError
|
144
|
-
return nil
|
144
|
+
return nil
|
145
145
|
end
|
146
146
|
|
147
147
|
# Not all geocoders can do reverse geocoding. So, unless the subclass explicitly overrides this method,
|
@@ -157,7 +157,7 @@ module Geokit
|
|
157
157
|
url_to_sign = uri.path + "?" + uri.query
|
158
158
|
decoded_key = Geocoder.urlsafe_decode64(private_key)
|
159
159
|
|
160
|
-
sha1_digest = OpenSSL::Digest
|
160
|
+
sha1_digest = OpenSSL::Digest.new('sha1')
|
161
161
|
signature = OpenSSL::HMAC.digest(sha1_digest,decoded_key,url_to_sign)
|
162
162
|
encoded_signature = Geocoder.urlsafe_encode64(signature)
|
163
163
|
signed_url = "#{uri.scheme}://#{uri.host}#{uri.path}?#{uri.query}&signature=#{encoded_signature}".strip!
|
@@ -181,14 +181,14 @@ module Geokit
|
|
181
181
|
|
182
182
|
protected
|
183
183
|
|
184
|
-
def self.logger()
|
184
|
+
def self.logger()
|
185
185
|
Geokit::Geocoders::logger
|
186
186
|
end
|
187
|
-
|
187
|
+
|
188
188
|
private
|
189
|
-
|
189
|
+
|
190
190
|
# Wraps the geocoder call around a proxy if necessary.
|
191
|
-
def self.do_get(url)
|
191
|
+
def self.do_get(url)
|
192
192
|
uri = URI.parse(url)
|
193
193
|
req = Net::HTTP::Get.new(url)
|
194
194
|
req.basic_auth(uri.user, uri.password) if uri.userinfo
|
@@ -198,8 +198,8 @@ module Geokit
|
|
198
198
|
GeoKit::Geocoders::proxy_pass).start(uri.host, uri.port) { |http| http.get(uri.path + "?" + uri.query) }
|
199
199
|
return res
|
200
200
|
end
|
201
|
-
|
202
|
-
# Adds subclass' geocode method making it conveniently available through
|
201
|
+
|
202
|
+
# Adds subclass' geocode method making it conveniently available through
|
203
203
|
# the base class.
|
204
204
|
def self.inherited(clazz)
|
205
205
|
class_name = clazz.name.split('::').last
|
@@ -214,10 +214,10 @@ module Geokit
|
|
214
214
|
|
215
215
|
# -------------------------------------------------------------------------------------------
|
216
216
|
# "Regular" Address geocoders
|
217
|
-
# -------------------------------------------------------------------------------------------
|
218
|
-
|
217
|
+
# -------------------------------------------------------------------------------------------
|
218
|
+
|
219
219
|
# Geocoder CA geocoder implementation. Requires the Geokit::Geocoders::GEOCODER_CA variable to
|
220
|
-
# contain true or false based upon whether authentication is to occur. Conforms to the
|
220
|
+
# contain true or false based upon whether authentication is to occur. Conforms to the
|
221
221
|
# interface set by the Geocoder class.
|
222
222
|
#
|
223
223
|
# Returns a response like:
|
@@ -239,15 +239,15 @@ module Geokit
|
|
239
239
|
xml = res.body
|
240
240
|
logger.debug "Geocoder.ca geocoding. Address: #{address}. Result: #{xml}"
|
241
241
|
# Parse the document.
|
242
|
-
doc = REXML::Document.new(xml)
|
242
|
+
doc = REXML::Document.new(xml)
|
243
243
|
address.lat = doc.elements['//latt'].text
|
244
244
|
address.lng = doc.elements['//longt'].text
|
245
245
|
address.success = true
|
246
246
|
return address
|
247
247
|
rescue
|
248
248
|
logger.error "Caught an error during Geocoder.ca geocoding call: "+$!
|
249
|
-
return GeoLoc.new
|
250
|
-
end
|
249
|
+
return GeoLoc.new
|
250
|
+
end
|
251
251
|
|
252
252
|
# Formats the request in the format acceptable by the CA geocoder.
|
253
253
|
def self.construct_request(location)
|
@@ -265,60 +265,60 @@ module Geokit
|
|
265
265
|
def self.add_ampersand(url)
|
266
266
|
url && url.length > 0 ? "&" : ""
|
267
267
|
end
|
268
|
-
end
|
269
|
-
|
268
|
+
end
|
269
|
+
|
270
270
|
# Geocoder Us geocoder implementation. Requires the Geokit::Geocoders::GEOCODER_US variable to
|
271
|
-
# contain true or false based upon whether authentication is to occur. Conforms to the
|
271
|
+
# contain true or false based upon whether authentication is to occur. Conforms to the
|
272
272
|
# interface set by the Geocoder class.
|
273
273
|
class UsGeocoder < Geocoder
|
274
274
|
|
275
275
|
private
|
276
276
|
def self.do_geocode(address, options = {})
|
277
277
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
278
|
-
|
278
|
+
|
279
279
|
query = (address_str =~ /^\d{5}(?:-\d{4})?$/ ? "zip" : "address") + "=#{Geokit::Inflector::url_escape(address_str)}"
|
280
|
-
url = if GeoKit::Geocoders::geocoder_us
|
280
|
+
url = if GeoKit::Geocoders::geocoder_us
|
281
281
|
"http://#{GeoKit::Geocoders::geocoder_us}@geocoder.us/member/service/csv/geocode"
|
282
282
|
else
|
283
283
|
"http://geocoder.us/service/csv/geocode"
|
284
284
|
end
|
285
|
-
|
286
|
-
url = "#{url}?#{query}"
|
285
|
+
|
286
|
+
url = "#{url}?#{query}"
|
287
287
|
res = self.call_geocoder_service(url)
|
288
|
-
|
288
|
+
|
289
289
|
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
290
290
|
data = res.body
|
291
291
|
logger.debug "Geocoder.us geocoding. Address: #{address}. Result: #{data}"
|
292
292
|
array = data.chomp.split(',')
|
293
|
-
|
293
|
+
|
294
294
|
if array.length == 5
|
295
295
|
res=GeoLoc.new
|
296
296
|
res.lat,res.lng,res.city,res.state,res.zip=array
|
297
297
|
res.country_code='US'
|
298
298
|
res.success=true
|
299
299
|
return res
|
300
|
-
elsif array.length == 6
|
301
|
-
res=GeoLoc.new
|
300
|
+
elsif array.length == 6
|
301
|
+
res=GeoLoc.new
|
302
302
|
res.lat,res.lng,res.street_address,res.city,res.state,res.zip=array
|
303
303
|
res.country_code='US'
|
304
|
-
res.success=true
|
304
|
+
res.success=true
|
305
305
|
return res
|
306
|
-
else
|
306
|
+
else
|
307
307
|
logger.info "geocoder.us was unable to geocode address: "+address
|
308
|
-
return GeoLoc.new
|
308
|
+
return GeoLoc.new
|
309
309
|
end
|
310
|
-
rescue
|
310
|
+
rescue
|
311
311
|
logger.error "Caught an error during geocoder.us geocoding call: "+$!
|
312
312
|
return GeoLoc.new
|
313
313
|
|
314
314
|
end
|
315
315
|
end
|
316
|
-
|
316
|
+
|
317
317
|
# Yahoo geocoder implementation. Requires the Geokit::Geocoders::YAHOO variable to
|
318
318
|
# contain a Yahoo API key. Conforms to the interface set by the Geocoder class.
|
319
319
|
class YahooGeocoder < Geocoder
|
320
320
|
|
321
|
-
private
|
321
|
+
private
|
322
322
|
|
323
323
|
# Template method which does the geocode lookup.
|
324
324
|
def self.do_geocode(address, options = {})
|
@@ -333,11 +333,11 @@ module Geokit
|
|
333
333
|
if doc.elements['//ResultSet']
|
334
334
|
res=GeoLoc.new
|
335
335
|
|
336
|
-
#basic
|
336
|
+
#basic
|
337
337
|
res.lat=doc.elements['//Latitude'].text
|
338
338
|
res.lng=doc.elements['//Longitude'].text
|
339
339
|
res.country_code=doc.elements['//Country'].text
|
340
|
-
res.provider='yahoo'
|
340
|
+
res.provider='yahoo'
|
341
341
|
|
342
342
|
#extended - false if not available
|
343
343
|
res.city=doc.elements['//City'].text if doc.elements['//City'] && doc.elements['//City'].text != nil
|
@@ -349,12 +349,12 @@ module Geokit
|
|
349
349
|
res.accuracy=%w{unknown country state state city zip zip+4 street address building}.index(res.precision)
|
350
350
|
res.success=true
|
351
351
|
return res
|
352
|
-
else
|
352
|
+
else
|
353
353
|
logger.info "Yahoo was unable to geocode address: "+address
|
354
354
|
return GeoLoc.new
|
355
|
-
end
|
355
|
+
end
|
356
356
|
|
357
|
-
rescue
|
357
|
+
rescue
|
358
358
|
logger.info "Caught an error during Yahoo geocoding call: "+$!
|
359
359
|
return GeoLoc.new
|
360
360
|
end
|
@@ -364,47 +364,47 @@ module Geokit
|
|
364
364
|
# http://www.geonames.org
|
365
365
|
class GeonamesGeocoder < Geocoder
|
366
366
|
|
367
|
-
private
|
368
|
-
|
367
|
+
private
|
368
|
+
|
369
369
|
# Template method which does the geocode lookup.
|
370
370
|
def self.do_geocode(address, options = {})
|
371
371
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
372
372
|
# geonames need a space seperated search string
|
373
373
|
address_str.gsub!(/,/, " ")
|
374
374
|
params = "/postalCodeSearch?placename=#{Geokit::Inflector::url_escape(address_str)}&maxRows=10"
|
375
|
-
|
375
|
+
|
376
376
|
if(GeoKit::Geocoders::geonames)
|
377
377
|
url = "http://ws.geonames.net#{params}&username=#{GeoKit::Geocoders::geonames}"
|
378
378
|
else
|
379
379
|
url = "http://ws.geonames.org#{params}"
|
380
380
|
end
|
381
|
-
|
381
|
+
|
382
382
|
res = self.call_geocoder_service(url)
|
383
|
-
|
383
|
+
|
384
384
|
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
385
|
-
|
385
|
+
|
386
386
|
xml=res.body
|
387
387
|
logger.debug "Geonames geocoding. Address: #{address}. Result: #{xml}"
|
388
388
|
doc=REXML::Document.new(xml)
|
389
|
-
|
389
|
+
|
390
390
|
if(doc.elements['//geonames/totalResultsCount'].text.to_i > 0)
|
391
391
|
res=GeoLoc.new
|
392
|
-
|
392
|
+
|
393
393
|
# only take the first result
|
394
394
|
res.lat=doc.elements['//code/lat'].text if doc.elements['//code/lat']
|
395
395
|
res.lng=doc.elements['//code/lng'].text if doc.elements['//code/lng']
|
396
396
|
res.country_code=doc.elements['//code/countryCode'].text if doc.elements['//code/countryCode']
|
397
|
-
res.provider='genomes'
|
397
|
+
res.provider='genomes'
|
398
398
|
res.city=doc.elements['//code/name'].text if doc.elements['//code/name']
|
399
399
|
res.state=doc.elements['//code/adminName1'].text if doc.elements['//code/adminName1']
|
400
400
|
res.zip=doc.elements['//code/postalcode'].text if doc.elements['//code/postalcode']
|
401
401
|
res.success=true
|
402
402
|
return res
|
403
|
-
else
|
403
|
+
else
|
404
404
|
logger.info "Geonames was unable to geocode address: "+address
|
405
405
|
return GeoLoc.new
|
406
406
|
end
|
407
|
-
|
407
|
+
|
408
408
|
rescue
|
409
409
|
logger.error "Caught an error during Geonames geocoding call: "+$!
|
410
410
|
end
|
@@ -418,18 +418,18 @@ module Geokit
|
|
418
418
|
# contain a Google API key. Conforms to the interface set by the Geocoder class.
|
419
419
|
class GoogleGeocoder < Geocoder
|
420
420
|
|
421
|
-
private
|
422
|
-
|
421
|
+
private
|
422
|
+
|
423
423
|
# Template method which does the reverse-geocode lookup.
|
424
|
-
def self.do_reverse_geocode(latlng)
|
424
|
+
def self.do_reverse_geocode(latlng)
|
425
425
|
latlng=LatLng.normalize(latlng)
|
426
426
|
res = self.call_geocoder_service("http://maps.google.com/maps/geo?ll=#{Geokit::Inflector::url_escape(latlng.ll)}&output=xml&key=#{Geokit::Geocoders::google}&oe=utf-8")
|
427
427
|
# res = Net::HTTP.get_response(URI.parse("http://maps.google.com/maps/geo?ll=#{Geokit::Inflector::url_escape(address_str)}&output=xml&key=#{Geokit::Geocoders::google}&oe=utf-8"))
|
428
428
|
return GeoLoc.new unless (res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPOK))
|
429
429
|
xml = res.body
|
430
430
|
logger.debug "Google reverse-geocoding. LL: #{latlng}. Result: #{xml}"
|
431
|
-
return self.xml2GeoLoc(xml)
|
432
|
-
end
|
431
|
+
return self.xml2GeoLoc(xml)
|
432
|
+
end
|
433
433
|
|
434
434
|
# Template method which does the geocode lookup.
|
435
435
|
#
|
@@ -438,7 +438,7 @@ module Geokit
|
|
438
438
|
# ==== OPTIONS
|
439
439
|
# * :bias - This option makes the Google Geocoder return results biased to a particular
|
440
440
|
# country or viewport. Country code biasing is achieved by passing the ccTLD
|
441
|
-
# ('uk' for .co.uk, for example) as a :bias value. For a list of ccTLD's,
|
441
|
+
# ('uk' for .co.uk, for example) as a :bias value. For a list of ccTLD's,
|
442
442
|
# look here: http://en.wikipedia.org/wiki/CcTLD. By default, the geocoder
|
443
443
|
# will be biased to results within the US (ccTLD .com).
|
444
444
|
#
|
@@ -461,9 +461,9 @@ module Geokit
|
|
461
461
|
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
462
462
|
xml = res.body
|
463
463
|
logger.debug "Google geocoding. Address: #{address}. Result: #{xml}"
|
464
|
-
return self.xml2GeoLoc(xml, address)
|
464
|
+
return self.xml2GeoLoc(xml, address)
|
465
465
|
end
|
466
|
-
|
466
|
+
|
467
467
|
# Determine the Google API url based on the google api key, or based on the client / private key for premier users
|
468
468
|
def self.geocode_url(address,options = {})
|
469
469
|
bias_str = options[:bias] ? construct_bias_string_from_options(options[:bias]) : ''
|
@@ -476,8 +476,8 @@ module Geokit
|
|
476
476
|
"http://maps.google.com/maps/geo?q=#{Geokit::Inflector::url_escape(address_str)}&output=xml#{bias_str}&key=#{Geokit::Geocoders::google}&oe=utf-8"
|
477
477
|
end
|
478
478
|
end
|
479
|
-
|
480
|
-
|
479
|
+
|
480
|
+
|
481
481
|
def self.construct_bias_string_from_options(bias)
|
482
482
|
if bias.is_a?(String) or bias.is_a?(Symbol)
|
483
483
|
# country code biasing
|
@@ -487,24 +487,24 @@ module Geokit
|
|
487
487
|
"&ll=#{bias.center.ll}&spn=#{bias.to_span.ll}"
|
488
488
|
end
|
489
489
|
end
|
490
|
-
|
490
|
+
|
491
491
|
def self.xml2GeoLoc(xml, address="")
|
492
492
|
doc=REXML::Document.new(xml)
|
493
493
|
|
494
494
|
if doc.elements['//kml/Response/Status/code'].text == '200'
|
495
495
|
geoloc = nil
|
496
|
-
# Google can return multiple results as //Placemark elements.
|
496
|
+
# Google can return multiple results as //Placemark elements.
|
497
497
|
# iterate through each and extract each placemark as a geoloc
|
498
498
|
doc.each_element('//Placemark') do |e|
|
499
499
|
extracted_geoloc = extract_placemark(e) # g is now an instance of GeoLoc
|
500
|
-
if geoloc.nil?
|
500
|
+
if geoloc.nil?
|
501
501
|
# first time through, geoloc is still nil, so we make it the geoloc we just extracted
|
502
|
-
geoloc = extracted_geoloc
|
502
|
+
geoloc = extracted_geoloc
|
503
503
|
else
|
504
|
-
# second (and subsequent) iterations, we push additional
|
505
|
-
# geolocs onto "geoloc.all"
|
506
|
-
geoloc.all.push(extracted_geoloc)
|
507
|
-
end
|
504
|
+
# second (and subsequent) iterations, we push additional
|
505
|
+
# geolocs onto "geoloc.all"
|
506
|
+
geoloc.all.push(extracted_geoloc)
|
507
|
+
end
|
508
508
|
end
|
509
509
|
return geoloc
|
510
510
|
elsif doc.elements['//kml/Response/Status/code'].text == '620'
|
@@ -520,7 +520,7 @@ module Geokit
|
|
520
520
|
rescue
|
521
521
|
logger.error "Caught an error during Google geocoding call: "+$!
|
522
522
|
return GeoLoc.new
|
523
|
-
end
|
523
|
+
end
|
524
524
|
|
525
525
|
# extracts a single geoloc from a //placemark element in the google results xml
|
526
526
|
def self.extract_placemark(doc)
|
@@ -547,14 +547,14 @@ module Geokit
|
|
547
547
|
address_details=doc.elements['.//*[local-name() = "AddressDetails"]']
|
548
548
|
res.accuracy = address_details ? address_details.attributes['Accuracy'].to_i : 0
|
549
549
|
res.precision=%w{unknown country state state city zip zip+4 street address building}[res.accuracy]
|
550
|
-
|
550
|
+
|
551
551
|
# google returns a set of suggested boundaries for the geocoded result
|
552
|
-
if suggested_bounds = doc.elements['//LatLonBox']
|
552
|
+
if suggested_bounds = doc.elements['//LatLonBox']
|
553
553
|
res.suggested_bounds = Bounds.normalize(
|
554
|
-
[suggested_bounds.attributes['south'], suggested_bounds.attributes['west']],
|
554
|
+
[suggested_bounds.attributes['south'], suggested_bounds.attributes['west']],
|
555
555
|
[suggested_bounds.attributes['north'], suggested_bounds.attributes['east']])
|
556
556
|
end
|
557
|
-
|
557
|
+
|
558
558
|
res.success=true
|
559
559
|
|
560
560
|
return res
|
@@ -565,11 +565,11 @@ module Geokit
|
|
565
565
|
# -------------------------------------------------------------------------------------------
|
566
566
|
# IP Geocoders
|
567
567
|
# -------------------------------------------------------------------------------------------
|
568
|
-
|
568
|
+
|
569
569
|
# Provides geocoding based upon an IP address. The underlying web service is geoplugin.net
|
570
570
|
class GeoPluginGeocoder < Geocoder
|
571
571
|
private
|
572
|
-
|
572
|
+
|
573
573
|
def self.do_geocode(ip, options = {})
|
574
574
|
return GeoLoc.new unless /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/.match(ip)
|
575
575
|
response = self.call_geocoder_service("http://www.geoplugin.net/xml.gp?ip=#{ip}")
|
@@ -596,7 +596,7 @@ module Geokit
|
|
596
596
|
# Provides geocoding based upon an IP address. The underlying web service is a hostip.info
|
597
597
|
# which sources their data through a combination of publicly available information as well
|
598
598
|
# as community contributions.
|
599
|
-
class IpGeocoder < Geocoder
|
599
|
+
class IpGeocoder < Geocoder
|
600
600
|
|
601
601
|
# A number of non-routable IP ranges.
|
602
602
|
#
|
@@ -619,11 +619,11 @@ module Geokit
|
|
619
619
|
IPAddr.new('240.0.0.0/4') # Reserved for future use
|
620
620
|
].freeze
|
621
621
|
|
622
|
-
private
|
622
|
+
private
|
623
623
|
|
624
624
|
# Given an IP address, returns a GeoLoc instance which contains latitude,
|
625
|
-
# longitude, city, and country code. Sets the success attribute to false if the ip
|
626
|
-
# parameter does not match an ip address.
|
625
|
+
# longitude, city, and country code. Sets the success attribute to false if the ip
|
626
|
+
# parameter does not match an ip address.
|
627
627
|
def self.do_geocode(ip, options = {})
|
628
628
|
return GeoLoc.new unless /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/.match(ip)
|
629
629
|
return GeoLoc.new if self.private_ip_address?(ip)
|
@@ -649,7 +649,7 @@ module Geokit
|
|
649
649
|
res.provider = 'hostip'
|
650
650
|
res.city, res.state = yaml['City'].split(', ')
|
651
651
|
country, res.country_code = yaml['Country'].split(' (')
|
652
|
-
res.lat = yaml['Latitude']
|
652
|
+
res.lat = yaml['Latitude']
|
653
653
|
res.lng = yaml['Longitude']
|
654
654
|
res.country_code.chop!
|
655
655
|
res.success = !(res.city =~ /\(.+\)/)
|
@@ -665,33 +665,33 @@ module Geokit
|
|
665
665
|
return NON_ROUTABLE_IP_RANGES.any? { |range| range.include?(ip) }
|
666
666
|
end
|
667
667
|
end
|
668
|
-
|
668
|
+
|
669
669
|
# -------------------------------------------------------------------------------------------
|
670
670
|
# The Multi Geocoder
|
671
|
-
# -------------------------------------------------------------------------------------------
|
672
|
-
|
671
|
+
# -------------------------------------------------------------------------------------------
|
672
|
+
|
673
673
|
# Provides methods to geocode with a variety of geocoding service providers, plus failover
|
674
674
|
# among providers in the order you configure. When 2nd parameter is set 'true', perform
|
675
675
|
# ip location lookup with 'address' as the ip address.
|
676
|
-
#
|
676
|
+
#
|
677
677
|
# Goal:
|
678
678
|
# - homogenize the results of multiple geocoders
|
679
|
-
#
|
679
|
+
#
|
680
680
|
# Limitations:
|
681
681
|
# - currently only provides the first result. Sometimes geocoders will return multiple results.
|
682
682
|
# - currently discards the "accuracy" component of the geocoding calls
|
683
|
-
class MultiGeocoder < Geocoder
|
683
|
+
class MultiGeocoder < Geocoder
|
684
684
|
|
685
685
|
private
|
686
|
-
# This method will call one or more geocoders in the order specified in the
|
686
|
+
# This method will call one or more geocoders in the order specified in the
|
687
687
|
# configuration until one of the geocoders work.
|
688
|
-
#
|
688
|
+
#
|
689
689
|
# The failover approach is crucial for production-grade apps, but is rarely used.
|
690
|
-
# 98% of your geocoding calls will be successful with the first call
|
690
|
+
# 98% of your geocoding calls will be successful with the first call
|
691
691
|
def self.do_geocode(address, options = {})
|
692
692
|
geocode_ip = /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.match(address)
|
693
693
|
provider_order = geocode_ip ? Geokit::Geocoders::ip_provider_order : Geokit::Geocoders::provider_order
|
694
|
-
|
694
|
+
|
695
695
|
provider_order.each do |provider|
|
696
696
|
begin
|
697
697
|
klass = Geokit::Geocoders.const_get "#{Geokit::Inflector::camelize(provider.to_s)}Geocoder"
|
@@ -704,8 +704,8 @@ module Geokit
|
|
704
704
|
# If we get here, we failed completely.
|
705
705
|
GeoLoc.new
|
706
706
|
end
|
707
|
-
|
708
|
-
# This method will call one or more geocoders in the order specified in the
|
707
|
+
|
708
|
+
# This method will call one or more geocoders in the order specified in the
|
709
709
|
# configuration until one of the geocoders work, only this time it's going
|
710
710
|
# to try to reverse geocode a geographical point.
|
711
711
|
def self.do_reverse_geocode(latlng)
|
@@ -721,6 +721,6 @@ module Geokit
|
|
721
721
|
# If we get here, we failed completely.
|
722
722
|
GeoLoc.new
|
723
723
|
end
|
724
|
-
end
|
724
|
+
end
|
725
725
|
end
|
726
726
|
end
|