geocoder 1.1.4 → 1.1.8
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/.travis.yml +4 -0
- data/{CHANGELOG.rdoc → CHANGELOG.md} +120 -39
- data/README.md +200 -54
- data/examples/autoexpire_cache_dalli.rb +62 -0
- data/examples/{autoexpire_cache.rb → autoexpire_cache_redis.rb} +4 -6
- data/lib/generators/geocoder/config/templates/initializer.rb +19 -23
- data/lib/geocoder/cache.rb +6 -1
- data/lib/geocoder/calculations.rb +45 -13
- data/lib/geocoder/cli.rb +10 -11
- data/lib/geocoder/configuration.rb +67 -43
- data/lib/geocoder/configuration_hash.rb +11 -0
- data/lib/geocoder/exceptions.rb +3 -0
- data/lib/geocoder/lookup.rb +3 -1
- data/lib/geocoder/lookups/base.rb +89 -27
- data/lib/geocoder/lookups/bing.rb +21 -11
- data/lib/geocoder/lookups/esri.rb +48 -0
- data/lib/geocoder/lookups/freegeoip.rb +8 -4
- data/lib/geocoder/lookups/geocoder_ca.rb +11 -7
- data/lib/geocoder/lookups/google.rb +18 -9
- data/lib/geocoder/lookups/google_premier.rb +16 -8
- data/lib/geocoder/lookups/mapquest.rb +33 -5
- data/lib/geocoder/lookups/maxmind.rb +88 -0
- data/lib/geocoder/lookups/nominatim.rb +13 -8
- data/lib/geocoder/lookups/ovi.rb +52 -0
- data/lib/geocoder/lookups/test.rb +10 -0
- data/lib/geocoder/lookups/yahoo.rb +51 -38
- data/lib/geocoder/lookups/yandex.rb +17 -9
- data/lib/geocoder/models/mongoid.rb +1 -1
- data/lib/geocoder/query.rb +20 -5
- data/lib/geocoder/request.rb +7 -1
- data/lib/geocoder/results/base.rb +8 -2
- data/lib/geocoder/results/esri.rb +51 -0
- data/lib/geocoder/results/google.rb +34 -0
- data/lib/geocoder/results/mapquest.rb +46 -2
- data/lib/geocoder/results/maxmind.rb +135 -0
- data/lib/geocoder/results/nominatim.rb +0 -10
- data/lib/geocoder/results/ovi.rb +62 -0
- data/lib/geocoder/results/test.rb +2 -1
- data/lib/geocoder/results/yahoo.rb +9 -2
- data/lib/geocoder/results/yandex.rb +12 -2
- data/lib/geocoder/sql.rb +6 -4
- data/lib/geocoder/stores/active_record.rb +57 -30
- data/lib/geocoder/stores/base.rb +3 -2
- data/lib/geocoder/stores/mongo_base.rb +2 -1
- data/lib/geocoder/version.rb +1 -1
- data/lib/geocoder.rb +2 -4
- data/lib/hash_recursive_merge.rb +74 -0
- data/lib/oauth_util.rb +112 -0
- data/test/cache_test.rb +19 -0
- data/test/calculations_test.rb +10 -4
- data/test/configuration_test.rb +26 -51
- data/test/error_handling_test.rb +8 -4
- data/test/fixtures/bing_invalid_key +1 -0
- data/test/fixtures/esri_madison_square_garden +59 -0
- data/test/fixtures/esri_no_results +8 -0
- data/test/fixtures/esri_reverse +21 -0
- data/test/fixtures/mapquest_madison_square_garden +52 -0
- data/test/fixtures/mapquest_no_results +7 -0
- data/test/fixtures/maxmind_24_24_24_21 +1 -0
- data/test/fixtures/maxmind_24_24_24_22 +1 -0
- data/test/fixtures/maxmind_24_24_24_23 +1 -0
- data/test/fixtures/maxmind_24_24_24_24 +1 -0
- data/test/fixtures/maxmind_74_200_247_59 +1 -0
- data/test/fixtures/maxmind_invalid_key +1 -0
- data/test/fixtures/maxmind_no_results +1 -0
- data/test/fixtures/ovi_madison_square_garden +72 -0
- data/test/fixtures/ovi_no_results +8 -0
- data/test/fixtures/yahoo_error +1 -0
- data/test/fixtures/yahoo_invalid_key +2 -0
- data/test/fixtures/yahoo_madison_square_garden +52 -0
- data/test/fixtures/yahoo_no_results +10 -0
- data/test/fixtures/yahoo_over_limit +2 -0
- data/test/fixtures/yandex_no_city_and_town +112 -0
- data/test/https_test.rb +3 -3
- data/test/integration/smoke_test.rb +2 -2
- data/test/lookup_test.rb +84 -11
- data/test/near_test.rb +43 -0
- data/test/oauth_util_test.rb +30 -0
- data/test/proxy_test.rb +2 -2
- data/test/query_test.rb +13 -0
- data/test/request_test.rb +29 -0
- data/test/result_test.rb +12 -2
- data/test/services_test.rb +208 -57
- data/test/test_helper.rb +66 -100
- data/test/test_mode_test.rb +35 -26
- metadata +58 -35
- data/test/fixtures/mapquest_madison_square_garden.json +0 -27
- data/test/fixtures/mapquest_no_results.json +0 -1
- data/test/fixtures/yahoo_madison_square_garden.json +0 -52
- data/test/fixtures/yahoo_no_results.json +0 -10
- data/test/fixtures/yahoo_v1_madison_square_garden.json +0 -46
- data/test/fixtures/yahoo_v1_no_results.json +0 -10
- /data/test/fixtures/{bing_madison_square_garden.json → bing_madison_square_garden} +0 -0
- /data/test/fixtures/{bing_no_results.json → bing_no_results} +0 -0
- /data/test/fixtures/{bing_reverse.json → bing_reverse} +0 -0
- /data/test/fixtures/{freegeoip_74_200_247_59.json → freegeoip_74_200_247_59} +0 -0
- /data/test/fixtures/{freegeoip_no_results.json → freegeoip_no_results} +0 -0
- /data/test/fixtures/{geocoder_ca_madison_square_garden.json → geocoder_ca_madison_square_garden} +0 -0
- /data/test/fixtures/{geocoder_ca_no_results.json → geocoder_ca_no_results} +0 -0
- /data/test/fixtures/{geocoder_ca_reverse.json → geocoder_ca_reverse} +0 -0
- /data/test/fixtures/{google_garbage.json → google_garbage} +0 -0
- /data/test/fixtures/{google_madison_square_garden.json → google_madison_square_garden} +0 -0
- /data/test/fixtures/{google_no_city_data.json → google_no_city_data} +0 -0
- /data/test/fixtures/{google_no_locality.json → google_no_locality} +0 -0
- /data/test/fixtures/{google_no_results.json → google_no_results} +0 -0
- /data/test/fixtures/{nominatim_madison_square_garden.json → nominatim_madison_square_garden} +0 -0
- /data/test/fixtures/{nominatim_no_results.json → nominatim_no_results} +0 -0
- /data/test/fixtures/{yandex_invalid_key.json → yandex_invalid_key} +0 -0
- /data/test/fixtures/{yandex_kremlin.json → yandex_kremlin} +0 -0
- /data/test/fixtures/{yandex_no_results.json → yandex_no_results} +0 -0
|
@@ -21,6 +21,11 @@ module Geocoder
|
|
|
21
21
|
#
|
|
22
22
|
KM_IN_MI = 0.621371192
|
|
23
23
|
|
|
24
|
+
##
|
|
25
|
+
# Conversion factor: multiply by nautical miles to get miles.
|
|
26
|
+
#
|
|
27
|
+
KM_IN_NM = 0.539957
|
|
28
|
+
|
|
24
29
|
# Not a number constant
|
|
25
30
|
NAN = defined?(::Float::NAN) ? ::Float::NAN : 0 / 0.0
|
|
26
31
|
|
|
@@ -40,7 +45,7 @@ module Geocoder
|
|
|
40
45
|
# Distance spanned by one degree of latitude in the given units.
|
|
41
46
|
#
|
|
42
47
|
def latitude_degree_distance(units = nil)
|
|
43
|
-
units ||= Geocoder
|
|
48
|
+
units ||= Geocoder.config.units
|
|
44
49
|
2 * Math::PI * earth_radius(units) / 360
|
|
45
50
|
end
|
|
46
51
|
|
|
@@ -49,7 +54,7 @@ module Geocoder
|
|
|
49
54
|
# This ranges from around 69 miles at the equator to zero at the poles.
|
|
50
55
|
#
|
|
51
56
|
def longitude_degree_distance(latitude, units = nil)
|
|
52
|
-
units ||= Geocoder
|
|
57
|
+
units ||= Geocoder.config.units
|
|
53
58
|
latitude_degree_distance(units) * Math.cos(to_radians(latitude))
|
|
54
59
|
end
|
|
55
60
|
|
|
@@ -67,12 +72,12 @@ module Geocoder
|
|
|
67
72
|
# The options hash supports:
|
|
68
73
|
#
|
|
69
74
|
# * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>
|
|
70
|
-
#
|
|
75
|
+
# Use Geocoder.configure(:units => ...) to configure default units.
|
|
71
76
|
#
|
|
72
77
|
def distance_between(point1, point2, options = {})
|
|
73
78
|
|
|
74
79
|
# set default options
|
|
75
|
-
options[:units] ||= Geocoder
|
|
80
|
+
options[:units] ||= Geocoder.config.units
|
|
76
81
|
|
|
77
82
|
# convert to coordinate arrays
|
|
78
83
|
point1 = extract_coordinates(point1)
|
|
@@ -103,14 +108,14 @@ module Geocoder
|
|
|
103
108
|
# the spherical method is "correct" in that it returns the shortest path
|
|
104
109
|
# (one along a great circle) but the linear method is less confusing
|
|
105
110
|
# (returns due east or west when given two points with the same latitude).
|
|
106
|
-
#
|
|
111
|
+
# Use Geocoder.configure(:distances => ...) to configure calculation method.
|
|
107
112
|
#
|
|
108
113
|
# Based on: http://www.movable-type.co.uk/scripts/latlong.html
|
|
109
114
|
#
|
|
110
115
|
def bearing_between(point1, point2, options = {})
|
|
111
116
|
|
|
112
117
|
# set default options
|
|
113
|
-
options[:method] ||= Geocoder
|
|
118
|
+
options[:method] ||= Geocoder.config.distances
|
|
114
119
|
options[:method] = :linear unless options[:method] == :spherical
|
|
115
120
|
|
|
116
121
|
# convert to coordinate arrays
|
|
@@ -197,12 +202,12 @@ module Geocoder
|
|
|
197
202
|
# ways of specifying the point. Also accepts an options hash:
|
|
198
203
|
#
|
|
199
204
|
# * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>.
|
|
200
|
-
#
|
|
205
|
+
# Use Geocoder.configure(:units => ...) to configure default units.
|
|
201
206
|
#
|
|
202
207
|
def bounding_box(point, radius, options = {})
|
|
203
208
|
lat,lon = extract_coordinates(point)
|
|
204
209
|
radius = radius.to_f
|
|
205
|
-
units = options[:units] || Geocoder
|
|
210
|
+
units = options[:units] || Geocoder.config.units
|
|
206
211
|
[
|
|
207
212
|
lat - (radius / latitude_degree_distance(units)),
|
|
208
213
|
lon - (radius / longitude_degree_distance(lat, units)),
|
|
@@ -240,12 +245,12 @@ module Geocoder
|
|
|
240
245
|
end
|
|
241
246
|
|
|
242
247
|
def distance_to_radians(distance, units = nil)
|
|
243
|
-
units ||= Geocoder
|
|
248
|
+
units ||= Geocoder.config.units
|
|
244
249
|
distance.to_f / earth_radius(units)
|
|
245
250
|
end
|
|
246
251
|
|
|
247
252
|
def radians_to_distance(radians, units = nil)
|
|
248
|
-
units ||= Geocoder
|
|
253
|
+
units ||= Geocoder.config.units
|
|
249
254
|
radians * earth_radius(units)
|
|
250
255
|
end
|
|
251
256
|
|
|
@@ -263,13 +268,24 @@ module Geocoder
|
|
|
263
268
|
km * km_in_mi
|
|
264
269
|
end
|
|
265
270
|
|
|
271
|
+
##
|
|
272
|
+
# Convert kilometers to nautical miles.
|
|
273
|
+
#
|
|
274
|
+
def to_nautical_miles(km)
|
|
275
|
+
km * km_in_nm
|
|
276
|
+
end
|
|
277
|
+
|
|
266
278
|
##
|
|
267
279
|
# Radius of the Earth in the given units (:mi or :km).
|
|
268
|
-
#
|
|
280
|
+
# Use Geocoder.configure(:units => ...) to configure default units.
|
|
269
281
|
#
|
|
270
282
|
def earth_radius(units = nil)
|
|
271
|
-
units ||= Geocoder
|
|
272
|
-
units
|
|
283
|
+
units ||= Geocoder.config.units
|
|
284
|
+
case units
|
|
285
|
+
when :km; EARTH_RADIUS
|
|
286
|
+
when :mi; to_miles(EARTH_RADIUS)
|
|
287
|
+
when :nm; to_nautical_miles(EARTH_RADIUS)
|
|
288
|
+
end
|
|
273
289
|
end
|
|
274
290
|
|
|
275
291
|
##
|
|
@@ -279,6 +295,15 @@ module Geocoder
|
|
|
279
295
|
KM_IN_MI
|
|
280
296
|
end
|
|
281
297
|
|
|
298
|
+
##
|
|
299
|
+
# Conversion factor: km to nm.
|
|
300
|
+
#
|
|
301
|
+
def km_in_nm
|
|
302
|
+
KM_IN_NM
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
|
|
282
307
|
##
|
|
283
308
|
# Conversion factor: mi to km.
|
|
284
309
|
#
|
|
@@ -286,6 +311,13 @@ module Geocoder
|
|
|
286
311
|
1.0 / KM_IN_MI
|
|
287
312
|
end
|
|
288
313
|
|
|
314
|
+
##
|
|
315
|
+
# Conversion factor: nm to km.
|
|
316
|
+
#
|
|
317
|
+
def nm_in_km
|
|
318
|
+
1.0 / KM_IN_NM
|
|
319
|
+
end
|
|
320
|
+
|
|
289
321
|
##
|
|
290
322
|
# Takes an object which is a [lat,lon] array, a geocodable string,
|
|
291
323
|
# or an object that implements +to_coordinates+ and returns a
|
data/lib/geocoder/cli.rb
CHANGED
|
@@ -13,34 +13,33 @@ module Geocoder
|
|
|
13
13
|
opts.separator "\nOptions: "
|
|
14
14
|
|
|
15
15
|
opts.on("-k <key>", "--key <key>",
|
|
16
|
-
"Key for geocoding API (optional
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Geocoder::Configuration.api_key = premier_key
|
|
16
|
+
"Key for geocoding API (usually optional). Enclose multi-part keys in quotes and separate parts by spaces") do |key|
|
|
17
|
+
if (key_parts = key.split(/\s+/)).size > 1
|
|
18
|
+
Geocoder.configure(:api_key => key_parts)
|
|
20
19
|
else
|
|
21
|
-
Geocoder
|
|
20
|
+
Geocoder.configure(:api_key => key)
|
|
22
21
|
end
|
|
23
22
|
end
|
|
24
23
|
|
|
25
24
|
opts.on("-l <language>", "--language <language>",
|
|
26
25
|
"Language of output (see API docs for valid choices)") do |language|
|
|
27
|
-
Geocoder
|
|
26
|
+
Geocoder.configure(:language => language)
|
|
28
27
|
end
|
|
29
28
|
|
|
30
29
|
opts.on("-p <proxy>", "--proxy <proxy>",
|
|
31
30
|
"HTTP proxy server to use (user:pass@host:port)") do |proxy|
|
|
32
|
-
Geocoder
|
|
31
|
+
Geocoder.configure(:http_proxy => proxy)
|
|
33
32
|
end
|
|
34
33
|
|
|
35
34
|
opts.on("-s <service>", Geocoder::Lookup.all_services_except_test, "--service <service>",
|
|
36
35
|
"Geocoding service: #{Geocoder::Lookup.all_services_except_test * ', '}") do |service|
|
|
37
|
-
Geocoder
|
|
38
|
-
Geocoder
|
|
36
|
+
Geocoder.configure(:lookup => service.to_sym)
|
|
37
|
+
Geocoder.configure(:ip_lookup => service.to_sym)
|
|
39
38
|
end
|
|
40
39
|
|
|
41
40
|
opts.on("-t <seconds>", "--timeout <seconds>",
|
|
42
41
|
"Maximum number of seconds to wait for API response") do |timeout|
|
|
43
|
-
Geocoder
|
|
42
|
+
Geocoder.configure(:timeout => timeout.to_i)
|
|
44
43
|
end
|
|
45
44
|
|
|
46
45
|
opts.on("-j", "--json", "Print API's raw JSON response") do
|
|
@@ -80,7 +79,7 @@ module Geocoder
|
|
|
80
79
|
|
|
81
80
|
if show_url
|
|
82
81
|
q = Geocoder::Query.new(query)
|
|
83
|
-
out << q.
|
|
82
|
+
out << q.url + "\n"
|
|
84
83
|
exit 0
|
|
85
84
|
end
|
|
86
85
|
|
|
@@ -1,39 +1,49 @@
|
|
|
1
1
|
require 'singleton'
|
|
2
|
+
require 'geocoder/configuration_hash'
|
|
2
3
|
|
|
3
4
|
module Geocoder
|
|
4
5
|
|
|
5
6
|
##
|
|
6
|
-
#
|
|
7
|
+
# Configuration options should be set by passing a hash:
|
|
7
8
|
#
|
|
8
|
-
|
|
9
|
+
# Geocoder.configure(
|
|
10
|
+
# :timeout => 5,
|
|
11
|
+
# :lookup => :yandex,
|
|
12
|
+
# :api_key => "2a9fsa983jaslfj982fjasd",
|
|
13
|
+
# :units => :km
|
|
14
|
+
# )
|
|
15
|
+
#
|
|
16
|
+
def self.configure(options = nil, &block)
|
|
9
17
|
if block_given?
|
|
18
|
+
warn "WARNING: Passing a block to Geocoder.configure is DEPRECATED. Please pass a hash instead (eg: Geocoder.configure(:units => ..., :api_key => ...))."
|
|
10
19
|
block.call(Configuration.instance)
|
|
20
|
+
elsif !options.nil?
|
|
21
|
+
Configuration.instance.configure(options)
|
|
11
22
|
else
|
|
23
|
+
warn "WARNING: Use of Geocoder.configure to read or write single config options is DEPRECATED. To write to the config please pass a hash (eg: Geocoder.configure(:units => ...)). To read config options please use the Geocoder.config object (eg: Geocoder.config.units)."
|
|
12
24
|
Configuration.instance
|
|
13
25
|
end
|
|
14
26
|
end
|
|
15
27
|
|
|
16
28
|
##
|
|
17
|
-
#
|
|
18
|
-
# (geocoding service provider, caching, units of measurement, etc).
|
|
19
|
-
# Configuration can be done in two ways:
|
|
20
|
-
#
|
|
21
|
-
# 1) Using Geocoder.configure and passing a block
|
|
22
|
-
# (useful for configuring multiple things at once):
|
|
23
|
-
#
|
|
24
|
-
# Geocoder.configure do |config|
|
|
25
|
-
# config.timeout = 5
|
|
26
|
-
# config.lookup = :yahoo
|
|
27
|
-
# config.api_key = "2a9fsa983jaslfj982fjasd"
|
|
28
|
-
# config.units = :km
|
|
29
|
-
# end
|
|
29
|
+
# Read-only access to the singleton's config data.
|
|
30
30
|
#
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
def self.config
|
|
32
|
+
Configuration.instance.data
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# Read-only access to lookup-specific config data.
|
|
36
37
|
#
|
|
38
|
+
def self.config_for_lookup(lookup_name)
|
|
39
|
+
data = config.clone
|
|
40
|
+
data.reject!{ |key,value| !Configuration::OPTIONS.include?(key) }
|
|
41
|
+
if config.has_key?(lookup_name)
|
|
42
|
+
data.merge!(config[lookup_name])
|
|
43
|
+
end
|
|
44
|
+
data
|
|
45
|
+
end
|
|
46
|
+
|
|
37
47
|
class Configuration
|
|
38
48
|
include Singleton
|
|
39
49
|
|
|
@@ -54,53 +64,67 @@ module Geocoder
|
|
|
54
64
|
:distances
|
|
55
65
|
]
|
|
56
66
|
|
|
57
|
-
attr_accessor
|
|
67
|
+
attr_accessor :data
|
|
68
|
+
|
|
69
|
+
def self.set_defaults
|
|
70
|
+
instance.set_defaults
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
OPTIONS.each do |o|
|
|
74
|
+
define_method o do
|
|
75
|
+
@data[o]
|
|
76
|
+
end
|
|
77
|
+
define_method "#{o}=" do |value|
|
|
78
|
+
@data[o] = value
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def configure(options)
|
|
83
|
+
@data.rmerge!(options)
|
|
84
|
+
end
|
|
58
85
|
|
|
59
86
|
def initialize # :nodoc
|
|
87
|
+
@data = Geocoder::ConfigurationHash.new
|
|
60
88
|
set_defaults
|
|
61
89
|
end
|
|
62
90
|
|
|
63
91
|
def set_defaults
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
@
|
|
67
|
-
@
|
|
68
|
-
@
|
|
69
|
-
@
|
|
70
|
-
@
|
|
71
|
-
@
|
|
72
|
-
@
|
|
73
|
-
@
|
|
74
|
-
@
|
|
92
|
+
|
|
93
|
+
# geocoding options
|
|
94
|
+
@data[:timeout] = 3 # geocoding service timeout (secs)
|
|
95
|
+
@data[:lookup] = :google # name of street address geocoding service (symbol)
|
|
96
|
+
@data[:ip_lookup] = :freegeoip # name of IP address geocoding service (symbol)
|
|
97
|
+
@data[:language] = :en # ISO-639 language code
|
|
98
|
+
@data[:http_headers] = {} # HTTP headers for lookup
|
|
99
|
+
@data[:use_https] = false # use HTTPS for lookup requests? (if supported)
|
|
100
|
+
@data[:http_proxy] = nil # HTTP proxy server (user:pass@host:port)
|
|
101
|
+
@data[:https_proxy] = nil # HTTPS proxy server (user:pass@host:port)
|
|
102
|
+
@data[:api_key] = nil # API key for geocoding service
|
|
103
|
+
@data[:cache] = nil # cache object (must respond to #[], #[]=, and #keys)
|
|
104
|
+
@data[:cache_prefix] = "geocoder:" # prefix (string) to use for all cache keys
|
|
75
105
|
|
|
76
106
|
# exceptions that should not be rescued by default
|
|
77
107
|
# (if you want to implement custom error handling);
|
|
78
108
|
# supports SocketError and TimeoutError
|
|
79
|
-
@always_raise = []
|
|
109
|
+
@data[:always_raise] = []
|
|
80
110
|
|
|
81
111
|
# calculation options
|
|
82
|
-
@units = :mi
|
|
83
|
-
@distances = :linear
|
|
112
|
+
@data[:units] = :mi # :mi or :km
|
|
113
|
+
@data[:distances] = :linear # :linear or :spherical
|
|
84
114
|
end
|
|
85
115
|
|
|
86
116
|
instance_eval(OPTIONS.map do |option|
|
|
87
117
|
o = option.to_s
|
|
88
118
|
<<-EOS
|
|
89
119
|
def #{o}
|
|
90
|
-
instance
|
|
120
|
+
instance.data[:#{o}]
|
|
91
121
|
end
|
|
92
122
|
|
|
93
123
|
def #{o}=(value)
|
|
94
|
-
instance
|
|
124
|
+
instance.data[:#{o}] = value
|
|
95
125
|
end
|
|
96
126
|
EOS
|
|
97
127
|
end.join("\n\n"))
|
|
98
128
|
|
|
99
|
-
class << self
|
|
100
|
-
def set_defaults
|
|
101
|
-
instance.set_defaults
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
129
|
end
|
|
106
130
|
end
|
data/lib/geocoder/exceptions.rb
CHANGED
data/lib/geocoder/lookup.rb
CHANGED
|
@@ -21,6 +21,7 @@ module Geocoder
|
|
|
21
21
|
#
|
|
22
22
|
def street_services
|
|
23
23
|
[
|
|
24
|
+
:esri,
|
|
24
25
|
:google,
|
|
25
26
|
:google_premier,
|
|
26
27
|
:yahoo,
|
|
@@ -29,6 +30,7 @@ module Geocoder
|
|
|
29
30
|
:yandex,
|
|
30
31
|
:nominatim,
|
|
31
32
|
:mapquest,
|
|
33
|
+
:ovi,
|
|
32
34
|
:test
|
|
33
35
|
]
|
|
34
36
|
end
|
|
@@ -37,7 +39,7 @@ module Geocoder
|
|
|
37
39
|
# All IP address lookup services, default first.
|
|
38
40
|
#
|
|
39
41
|
def ip_services
|
|
40
|
-
[:freegeoip]
|
|
42
|
+
[:freegeoip, :maxmind]
|
|
41
43
|
end
|
|
42
44
|
|
|
43
45
|
##
|
|
@@ -16,6 +16,21 @@ module Geocoder
|
|
|
16
16
|
|
|
17
17
|
class Base
|
|
18
18
|
|
|
19
|
+
##
|
|
20
|
+
# Human-readable name of the geocoding API.
|
|
21
|
+
#
|
|
22
|
+
def name
|
|
23
|
+
fail
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# Symbol which is used in configuration to refer to this Lookup.
|
|
28
|
+
#
|
|
29
|
+
def handle
|
|
30
|
+
str = self.class.to_s
|
|
31
|
+
str[str.rindex(':')+1..-1].gsub(/([a-z\d]+)([A-Z])/,'\1_\2').downcase.to_sym
|
|
32
|
+
end
|
|
33
|
+
|
|
19
34
|
##
|
|
20
35
|
# Query the geocoding API and return a Geocoder::Result object.
|
|
21
36
|
# Returns +nil+ on timeout or error.
|
|
@@ -43,16 +58,47 @@ module Geocoder
|
|
|
43
58
|
nil
|
|
44
59
|
end
|
|
45
60
|
|
|
61
|
+
##
|
|
62
|
+
# Array containing string descriptions of keys required by the API.
|
|
63
|
+
# Empty array if keys are optional or not required.
|
|
64
|
+
#
|
|
65
|
+
def required_api_key_parts
|
|
66
|
+
[]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
##
|
|
70
|
+
# URL to use for querying the geocoding engine.
|
|
71
|
+
#
|
|
72
|
+
def query_url(query)
|
|
73
|
+
fail
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
##
|
|
77
|
+
# The working Cache object.
|
|
78
|
+
#
|
|
79
|
+
def cache
|
|
80
|
+
if @cache.nil? and store = configuration.cache
|
|
81
|
+
@cache = Cache.new(store, configuration.cache_prefix)
|
|
82
|
+
end
|
|
83
|
+
@cache
|
|
84
|
+
end
|
|
46
85
|
|
|
47
86
|
private # -------------------------------------------------------------
|
|
48
87
|
|
|
88
|
+
##
|
|
89
|
+
# An object with configuration data for this particular lookup.
|
|
90
|
+
#
|
|
91
|
+
def configuration
|
|
92
|
+
Geocoder.config_for_lookup(handle)
|
|
93
|
+
end
|
|
94
|
+
|
|
49
95
|
##
|
|
50
96
|
# Object used to make HTTP requests.
|
|
51
97
|
#
|
|
52
98
|
def http_client
|
|
53
|
-
protocol = "http#{'s' if
|
|
99
|
+
protocol = "http#{'s' if configuration.use_https}"
|
|
54
100
|
proxy_name = "#{protocol}_proxy"
|
|
55
|
-
if proxy =
|
|
101
|
+
if proxy = configuration.send(proxy_name)
|
|
56
102
|
proxy_url = protocol + '://' + proxy
|
|
57
103
|
begin
|
|
58
104
|
uri = URI.parse(proxy_url)
|
|
@@ -84,10 +130,13 @@ module Geocoder
|
|
|
84
130
|
end
|
|
85
131
|
|
|
86
132
|
##
|
|
87
|
-
#
|
|
133
|
+
# Key to use for caching a geocoding result. Usually this will be the
|
|
134
|
+
# request URL, but in cases where OAuth is used and the nonce,
|
|
135
|
+
# timestamp, etc varies from one request to another, we need to use
|
|
136
|
+
# something else (like the URL before OAuth encoding).
|
|
88
137
|
#
|
|
89
|
-
def
|
|
90
|
-
|
|
138
|
+
def cache_key(query)
|
|
139
|
+
query_url(query)
|
|
91
140
|
end
|
|
92
141
|
|
|
93
142
|
##
|
|
@@ -102,7 +151,7 @@ module Geocoder
|
|
|
102
151
|
# Return false if exception not raised.
|
|
103
152
|
#
|
|
104
153
|
def raise_error(error, message = nil)
|
|
105
|
-
exceptions =
|
|
154
|
+
exceptions = configuration.always_raise
|
|
106
155
|
if exceptions == :all or exceptions.include?( error.is_a?(Class) ? error : error.class )
|
|
107
156
|
raise error, message
|
|
108
157
|
else
|
|
@@ -119,7 +168,7 @@ module Geocoder
|
|
|
119
168
|
raise_error(err) or warn "Geocoding API connection cannot be established."
|
|
120
169
|
rescue TimeoutError => err
|
|
121
170
|
raise_error(err) or warn "Geocoding API not responding fast enough " +
|
|
122
|
-
"(
|
|
171
|
+
"(use Geocoder.configure(:timeout => ...) to set limit)."
|
|
123
172
|
end
|
|
124
173
|
|
|
125
174
|
##
|
|
@@ -140,37 +189,50 @@ module Geocoder
|
|
|
140
189
|
# Set in configuration but not available for every service.
|
|
141
190
|
#
|
|
142
191
|
def protocol
|
|
143
|
-
"http" + (
|
|
192
|
+
"http" + (configuration.use_https ? "s" : "")
|
|
144
193
|
end
|
|
145
194
|
|
|
146
195
|
##
|
|
147
|
-
#
|
|
196
|
+
# Fetch a raw geocoding result (JSON string).
|
|
197
|
+
# The result might or might not be cached.
|
|
148
198
|
#
|
|
149
199
|
def fetch_raw_data(query)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
body = response.body
|
|
160
|
-
if cache and (200..399).include?(response.code.to_i)
|
|
161
|
-
cache[url] = body
|
|
162
|
-
end
|
|
163
|
-
@cache_hit = false
|
|
200
|
+
key = cache_key(query)
|
|
201
|
+
if cache and body = cache[key]
|
|
202
|
+
@cache_hit = true
|
|
203
|
+
else
|
|
204
|
+
check_api_key_configuration!(query)
|
|
205
|
+
response = make_api_request(query)
|
|
206
|
+
body = response.body
|
|
207
|
+
if cache and (200..399).include?(response.code.to_i)
|
|
208
|
+
cache[key] = body
|
|
164
209
|
end
|
|
165
|
-
|
|
210
|
+
@cache_hit = false
|
|
166
211
|
end
|
|
212
|
+
body
|
|
167
213
|
end
|
|
168
214
|
|
|
169
215
|
##
|
|
170
|
-
#
|
|
216
|
+
# Make an HTTP(S) request to a geocoding API and
|
|
217
|
+
# return the response object.
|
|
171
218
|
#
|
|
172
|
-
def
|
|
173
|
-
|
|
219
|
+
def make_api_request(query)
|
|
220
|
+
timeout(configuration.timeout) do
|
|
221
|
+
uri = URI.parse(query_url(query))
|
|
222
|
+
client = http_client.new(uri.host, uri.port)
|
|
223
|
+
client.use_ssl = true if configuration.use_https
|
|
224
|
+
client.get(uri.request_uri, configuration.http_headers)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def check_api_key_configuration!(query)
|
|
229
|
+
key_parts = query.lookup.required_api_key_parts
|
|
230
|
+
if key_parts.size > Array(configuration.api_key).size
|
|
231
|
+
parts_string = key_parts.size == 1 ? key_parts.first : key_parts
|
|
232
|
+
raise Geocoder::ConfigurationError,
|
|
233
|
+
"The #{query.lookup.name} API requires a key to be configured: " +
|
|
234
|
+
parts_string.inspect
|
|
235
|
+
end
|
|
174
236
|
end
|
|
175
237
|
|
|
176
238
|
##
|
|
@@ -4,34 +4,44 @@ require "geocoder/results/bing"
|
|
|
4
4
|
module Geocoder::Lookup
|
|
5
5
|
class Bing < Base
|
|
6
6
|
|
|
7
|
+
def name
|
|
8
|
+
"Bing"
|
|
9
|
+
end
|
|
10
|
+
|
|
7
11
|
def map_link_url(coordinates)
|
|
8
12
|
"http://www.bing.com/maps/default.aspx?cp=#{coordinates.join('~')}"
|
|
9
13
|
end
|
|
10
14
|
|
|
15
|
+
def required_api_key_parts
|
|
16
|
+
["key"]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def query_url(query)
|
|
20
|
+
"#{protocol}://dev.virtualearth.net/REST/v1/Locations" +
|
|
21
|
+
(query.reverse_geocode? ? "/#{query.sanitized_text}?" : "?") +
|
|
22
|
+
url_query_string(query)
|
|
23
|
+
end
|
|
24
|
+
|
|
11
25
|
private # ---------------------------------------------------------------
|
|
12
26
|
|
|
13
27
|
def results(query)
|
|
14
28
|
return [] unless doc = fetch_data(query)
|
|
15
29
|
|
|
16
|
-
if doc['
|
|
30
|
+
if doc['statusCode'] == 200
|
|
17
31
|
return doc['resourceSets'].first['estimatedTotal'] > 0 ? doc['resourceSets'].first['resources'] : []
|
|
32
|
+
elsif doc['statusCode'] == 401 and doc["authenticationResultCode"] == "InvalidCredentials"
|
|
33
|
+
raise_error(Geocoder::InvalidApiKey) || warn("Invalid Bing API key.")
|
|
18
34
|
else
|
|
19
35
|
warn "Bing Geocoding API error: #{doc['statusCode']} (#{doc['statusDescription']})."
|
|
20
|
-
return []
|
|
21
36
|
end
|
|
37
|
+
return []
|
|
22
38
|
end
|
|
23
39
|
|
|
24
40
|
def query_url_params(query)
|
|
25
|
-
|
|
26
|
-
:key =>
|
|
41
|
+
{
|
|
42
|
+
:key => configuration.api_key,
|
|
27
43
|
:query => query.reverse_geocode? ? nil : query.sanitized_text
|
|
28
|
-
)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def query_url(query)
|
|
32
|
-
"http://dev.virtualearth.net/REST/v1/Locations" +
|
|
33
|
-
(query.reverse_geocode? ? "/#{query.sanitized_text}?" : "?") +
|
|
34
|
-
url_query_string(query)
|
|
44
|
+
}.merge(super)
|
|
35
45
|
end
|
|
36
46
|
end
|
|
37
47
|
end
|