geocoder 1.1.3 → 1.1.6
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} +109 -35
- data/README.md +645 -0
- data/examples/autoexpire_cache.rb +28 -0
- data/lib/generators/geocoder/config/templates/initializer.rb +18 -22
- data/lib/geocoder/cache.rb +2 -1
- data/lib/geocoder/calculations.rb +13 -13
- data/lib/geocoder/cli.rb +17 -19
- data/lib/geocoder/configuration.rb +68 -42
- data/lib/geocoder/configuration_hash.rb +11 -0
- data/lib/geocoder/exceptions.rb +3 -0
- data/lib/geocoder/lookup.rb +81 -0
- data/lib/geocoder/lookups/base.rb +106 -56
- data/lib/geocoder/lookups/bing.rb +25 -11
- data/lib/geocoder/lookups/freegeoip.rb +11 -7
- data/lib/geocoder/lookups/geocoder_ca.rb +17 -9
- data/lib/geocoder/lookups/google.rb +29 -8
- data/lib/geocoder/lookups/google_premier.rb +20 -11
- data/lib/geocoder/lookups/mapquest.rb +29 -19
- data/lib/geocoder/lookups/maxmind.rb +72 -0
- data/lib/geocoder/lookups/nominatim.rb +21 -13
- data/lib/geocoder/lookups/test.rb +12 -6
- data/lib/geocoder/lookups/yahoo.rb +63 -13
- data/lib/geocoder/lookups/yandex.rb +26 -11
- data/lib/geocoder/models/mongoid.rb +1 -2
- data/lib/geocoder/query.rb +103 -0
- data/lib/geocoder/request.rb +7 -1
- data/lib/geocoder/results/base.rb +8 -2
- data/lib/geocoder/results/google.rb +6 -0
- data/lib/geocoder/results/mapquest.rb +19 -34
- data/lib/geocoder/results/maxmind.rb +136 -0
- data/lib/geocoder/results/nominatim.rb +14 -3
- data/lib/geocoder/results/yahoo.rb +9 -2
- data/lib/geocoder/sql.rb +106 -0
- data/lib/geocoder/stores/active_record.rb +75 -142
- data/lib/geocoder/stores/base.rb +3 -2
- data/lib/geocoder/stores/mongo_base.rb +2 -2
- data/lib/geocoder/version.rb +1 -1
- data/lib/geocoder.rb +11 -89
- data/lib/hash_recursive_merge.rb +74 -0
- data/lib/oauth_util.rb +112 -0
- data/test/active_record_test.rb +15 -0
- data/test/cache_test.rb +19 -0
- data/test/calculations_test.rb +11 -4
- data/test/configuration_test.rb +26 -51
- data/test/error_handling_test.rb +15 -11
- data/test/fixtures/bing_invalid_key +1 -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/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/https_test.rb +3 -3
- data/test/integration/smoke_test.rb +8 -6
- data/test/lookup_test.rb +92 -13
- data/test/near_test.rb +11 -0
- data/test/oauth_util_test.rb +30 -0
- data/test/proxy_test.rb +2 -2
- data/test/query_test.rb +42 -0
- data/test/result_test.rb +4 -3
- data/test/services_test.rb +164 -26
- data/test/test_helper.rb +93 -112
- data/test/test_mode_test.rb +3 -4
- metadata +52 -35
- data/README.rdoc +0 -552
- data/test/fixtures/mapquest_madison_square_garden.json +0 -27
- data/test/fixtures/mapquest_no_results.json +0 -1
- data/test/fixtures/yahoo_garbage.json +0 -50
- data/test/fixtures/yahoo_madison_square_garden.json +0 -46
- data/test/fixtures/yahoo_no_results.json +0 -10
- data/test/input_handling_test.rb +0 -43
- /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
|
@@ -13,8 +13,24 @@ end
|
|
|
13
13
|
|
|
14
14
|
module Geocoder
|
|
15
15
|
module Lookup
|
|
16
|
+
|
|
16
17
|
class Base
|
|
17
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
|
+
|
|
18
34
|
##
|
|
19
35
|
# Query the geocoding API and return a Geocoder::Result object.
|
|
20
36
|
# Returns +nil+ on timeout or error.
|
|
@@ -23,18 +39,9 @@ module Geocoder
|
|
|
23
39
|
# "205.128.54.202") for geocoding, or coordinates (latitude, longitude)
|
|
24
40
|
# for reverse geocoding. Returns an array of <tt>Geocoder::Result</tt>s.
|
|
25
41
|
#
|
|
26
|
-
def search(query)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
query = query.split(/\s*,\s*/) if coordinates?(query)
|
|
30
|
-
|
|
31
|
-
if query.is_a?(Array)
|
|
32
|
-
reverse = true
|
|
33
|
-
query = query.join(',')
|
|
34
|
-
else
|
|
35
|
-
reverse = false
|
|
36
|
-
end
|
|
37
|
-
results(query, reverse).map{ |r|
|
|
42
|
+
def search(query, options = {})
|
|
43
|
+
query = Geocoder::Query.new(query, options) unless query.is_a?(Geocoder::Query)
|
|
44
|
+
results(query).map{ |r|
|
|
38
45
|
result = result_class.new(r)
|
|
39
46
|
result.cache_hit = @cache_hit if cache
|
|
40
47
|
result
|
|
@@ -51,16 +58,37 @@ module Geocoder
|
|
|
51
58
|
nil
|
|
52
59
|
end
|
|
53
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
|
|
54
75
|
|
|
55
76
|
private # -------------------------------------------------------------
|
|
56
77
|
|
|
78
|
+
##
|
|
79
|
+
# An object with configuration data for this particular lookup.
|
|
80
|
+
#
|
|
81
|
+
def configuration
|
|
82
|
+
Geocoder.config_for_lookup(handle)
|
|
83
|
+
end
|
|
84
|
+
|
|
57
85
|
##
|
|
58
86
|
# Object used to make HTTP requests.
|
|
59
87
|
#
|
|
60
88
|
def http_client
|
|
61
|
-
protocol = "http#{'s' if
|
|
89
|
+
protocol = "http#{'s' if configuration.use_https}"
|
|
62
90
|
proxy_name = "#{protocol}_proxy"
|
|
63
|
-
if proxy =
|
|
91
|
+
if proxy = configuration.send(proxy_name)
|
|
64
92
|
proxy_url = protocol + '://' + proxy
|
|
65
93
|
begin
|
|
66
94
|
uri = URI.parse(proxy_url)
|
|
@@ -77,15 +105,28 @@ module Geocoder
|
|
|
77
105
|
##
|
|
78
106
|
# Geocoder::Result object or nil on timeout or other error.
|
|
79
107
|
#
|
|
80
|
-
def results(query
|
|
108
|
+
def results(query)
|
|
81
109
|
fail
|
|
82
110
|
end
|
|
83
111
|
|
|
112
|
+
def query_url_params(query)
|
|
113
|
+
query.options[:params] || {}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def url_query_string(query)
|
|
117
|
+
hash_to_query(
|
|
118
|
+
query_url_params(query).reject{ |key,value| value.nil? }
|
|
119
|
+
)
|
|
120
|
+
end
|
|
121
|
+
|
|
84
122
|
##
|
|
85
|
-
#
|
|
123
|
+
# Key to use for caching a geocoding result. Usually this will be the
|
|
124
|
+
# request URL, but in cases where OAuth is used and the nonce,
|
|
125
|
+
# timestamp, etc varies from one request to another, we need to use
|
|
126
|
+
# something else (like the URL before OAuth encoding).
|
|
86
127
|
#
|
|
87
|
-
def
|
|
88
|
-
|
|
128
|
+
def cache_key(query)
|
|
129
|
+
query_url(query)
|
|
89
130
|
end
|
|
90
131
|
|
|
91
132
|
##
|
|
@@ -100,7 +141,7 @@ module Geocoder
|
|
|
100
141
|
# Return false if exception not raised.
|
|
101
142
|
#
|
|
102
143
|
def raise_error(error, message = nil)
|
|
103
|
-
exceptions =
|
|
144
|
+
exceptions = configuration.always_raise
|
|
104
145
|
if exceptions == :all or exceptions.include?( error.is_a?(Class) ? error : error.class )
|
|
105
146
|
raise error, message
|
|
106
147
|
else
|
|
@@ -111,13 +152,13 @@ module Geocoder
|
|
|
111
152
|
##
|
|
112
153
|
# Returns a parsed search result (Ruby hash).
|
|
113
154
|
#
|
|
114
|
-
def fetch_data(query
|
|
115
|
-
parse_raw_data fetch_raw_data(query
|
|
155
|
+
def fetch_data(query)
|
|
156
|
+
parse_raw_data fetch_raw_data(query)
|
|
116
157
|
rescue SocketError => err
|
|
117
158
|
raise_error(err) or warn "Geocoding API connection cannot be established."
|
|
118
159
|
rescue TimeoutError => err
|
|
119
160
|
raise_error(err) or warn "Geocoding API not responding fast enough " +
|
|
120
|
-
"(
|
|
161
|
+
"(use Geocoder.configure(:timeout => ...) to set limit)."
|
|
121
162
|
end
|
|
122
163
|
|
|
123
164
|
##
|
|
@@ -138,51 +179,60 @@ module Geocoder
|
|
|
138
179
|
# Set in configuration but not available for every service.
|
|
139
180
|
#
|
|
140
181
|
def protocol
|
|
141
|
-
"http" + (
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
##
|
|
145
|
-
# Fetches a raw search result (JSON string).
|
|
146
|
-
#
|
|
147
|
-
def fetch_raw_data(query, reverse = false)
|
|
148
|
-
timeout(Geocoder::Configuration.timeout) do
|
|
149
|
-
url = query_url(query, reverse)
|
|
150
|
-
uri = URI.parse(url)
|
|
151
|
-
if cache and body = cache[url]
|
|
152
|
-
@cache_hit = true
|
|
153
|
-
else
|
|
154
|
-
client = http_client.new(uri.host, uri.port)
|
|
155
|
-
client.use_ssl = true if Geocoder::Configuration.use_https
|
|
156
|
-
response = client.get(uri.request_uri, Geocoder::Configuration.http_headers)
|
|
157
|
-
body = response.body
|
|
158
|
-
if cache and (200..399).include?(response.code.to_i)
|
|
159
|
-
cache[url] = body
|
|
160
|
-
end
|
|
161
|
-
@cache_hit = false
|
|
162
|
-
end
|
|
163
|
-
body
|
|
164
|
-
end
|
|
182
|
+
"http" + (configuration.use_https ? "s" : "")
|
|
165
183
|
end
|
|
166
184
|
|
|
167
185
|
##
|
|
168
|
-
#
|
|
186
|
+
# Fetch a raw geocoding result (JSON string).
|
|
187
|
+
# The result might or might not be cached.
|
|
169
188
|
#
|
|
170
|
-
def
|
|
171
|
-
|
|
189
|
+
def fetch_raw_data(query)
|
|
190
|
+
key = cache_key(query)
|
|
191
|
+
if cache and body = cache[key]
|
|
192
|
+
@cache_hit = true
|
|
193
|
+
else
|
|
194
|
+
check_api_key_configuration!(query)
|
|
195
|
+
response = make_api_request(query)
|
|
196
|
+
body = response.body
|
|
197
|
+
if cache and (200..399).include?(response.code.to_i)
|
|
198
|
+
cache[key] = body
|
|
199
|
+
end
|
|
200
|
+
@cache_hit = false
|
|
201
|
+
end
|
|
202
|
+
body
|
|
172
203
|
end
|
|
173
204
|
|
|
174
205
|
##
|
|
175
|
-
#
|
|
176
|
-
#
|
|
177
|
-
|
|
178
|
-
|
|
206
|
+
# Make an HTTP(S) request to a geocoding API and
|
|
207
|
+
# return the response object.
|
|
208
|
+
#
|
|
209
|
+
def make_api_request(query)
|
|
210
|
+
timeout(configuration.timeout) do
|
|
211
|
+
uri = URI.parse(query_url(query))
|
|
212
|
+
client = http_client.new(uri.host, uri.port)
|
|
213
|
+
client.use_ssl = true if configuration.use_https
|
|
214
|
+
client.get(uri.request_uri, configuration.http_headers)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def check_api_key_configuration!(query)
|
|
219
|
+
key_parts = query.lookup.required_api_key_parts
|
|
220
|
+
if key_parts.size > Array(configuration.api_key).size
|
|
221
|
+
parts_string = key_parts.size == 1 ? key_parts.first : key_parts
|
|
222
|
+
raise Geocoder::ConfigurationError,
|
|
223
|
+
"The #{query.lookup.name} API requires a key to be configured: " +
|
|
224
|
+
parts_string.inspect
|
|
225
|
+
end
|
|
179
226
|
end
|
|
180
227
|
|
|
181
228
|
##
|
|
182
|
-
#
|
|
229
|
+
# The working Cache object.
|
|
183
230
|
#
|
|
184
|
-
def
|
|
185
|
-
|
|
231
|
+
def cache
|
|
232
|
+
if @cache.nil? and store = configuration.cache
|
|
233
|
+
@cache = Cache.new(store, configuration.cache_prefix)
|
|
234
|
+
end
|
|
235
|
+
@cache
|
|
186
236
|
end
|
|
187
237
|
|
|
188
238
|
##
|
|
@@ -4,30 +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
|
-
def results(query
|
|
14
|
-
return [] unless doc = fetch_data(query
|
|
27
|
+
def results(query)
|
|
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
|
-
def
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
url_tail = reverse ? "/#{query}?" : "?"
|
|
30
|
-
base_url + url_tail + hash_to_query(params)
|
|
40
|
+
def query_url_params(query)
|
|
41
|
+
{
|
|
42
|
+
:key => configuration.api_key,
|
|
43
|
+
:query => query.reverse_geocode? ? nil : query.sanitized_text
|
|
44
|
+
}.merge(super)
|
|
31
45
|
end
|
|
32
46
|
end
|
|
33
47
|
end
|
|
@@ -4,17 +4,25 @@ require 'geocoder/results/freegeoip'
|
|
|
4
4
|
module Geocoder::Lookup
|
|
5
5
|
class Freegeoip < Base
|
|
6
6
|
|
|
7
|
+
def name
|
|
8
|
+
"FreeGeoIP"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def query_url(query)
|
|
12
|
+
"#{protocol}://freegeoip.net/json/#{query.sanitized_text}"
|
|
13
|
+
end
|
|
14
|
+
|
|
7
15
|
private # ---------------------------------------------------------------
|
|
8
16
|
|
|
9
17
|
def parse_raw_data(raw_data)
|
|
10
18
|
raw_data.match(/^<html><title>404/) ? nil : super(raw_data)
|
|
11
19
|
end
|
|
12
20
|
|
|
13
|
-
def results(query
|
|
21
|
+
def results(query)
|
|
14
22
|
# don't look up a loopback address, just return the stored result
|
|
15
|
-
return [reserved_result(query)] if
|
|
23
|
+
return [reserved_result(query.text)] if query.loopback_ip_address?
|
|
16
24
|
begin
|
|
17
|
-
return (doc = fetch_data(query
|
|
25
|
+
return (doc = fetch_data(query)) ? [doc] : []
|
|
18
26
|
rescue StandardError => err # Freegeoip.net returns HTML on bad request
|
|
19
27
|
raise_error(err)
|
|
20
28
|
return []
|
|
@@ -35,9 +43,5 @@ module Geocoder::Lookup
|
|
|
35
43
|
"country_code" => "RD"
|
|
36
44
|
}
|
|
37
45
|
end
|
|
38
|
-
|
|
39
|
-
def query_url(query, reverse = false)
|
|
40
|
-
"http://freegeoip.net/json/#{query}"
|
|
41
|
-
end
|
|
42
46
|
end
|
|
43
47
|
end
|
|
@@ -4,10 +4,18 @@ require "geocoder/results/geocoder_ca"
|
|
|
4
4
|
module Geocoder::Lookup
|
|
5
5
|
class GeocoderCa < Base
|
|
6
6
|
|
|
7
|
+
def name
|
|
8
|
+
"Geocoder.ca"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def query_url(query)
|
|
12
|
+
"#{protocol}://geocoder.ca/?" + url_query_string(query)
|
|
13
|
+
end
|
|
14
|
+
|
|
7
15
|
private # ---------------------------------------------------------------
|
|
8
16
|
|
|
9
|
-
def results(query
|
|
10
|
-
return [] unless doc = fetch_data(query
|
|
17
|
+
def results(query)
|
|
18
|
+
return [] unless doc = fetch_data(query)
|
|
11
19
|
if doc['error'].nil?
|
|
12
20
|
return [doc]
|
|
13
21
|
elsif doc['error']['code'] == "005"
|
|
@@ -18,24 +26,24 @@ module Geocoder::Lookup
|
|
|
18
26
|
return []
|
|
19
27
|
end
|
|
20
28
|
|
|
21
|
-
def
|
|
29
|
+
def query_url_params(query)
|
|
22
30
|
params = {
|
|
23
31
|
:geoit => "xml",
|
|
24
32
|
:jsonp => 1,
|
|
25
33
|
:callback => "test",
|
|
26
|
-
:auth =>
|
|
27
|
-
}
|
|
28
|
-
if
|
|
29
|
-
lat,lon = query.
|
|
34
|
+
:auth => configuration.api_key
|
|
35
|
+
}.merge(super)
|
|
36
|
+
if query.reverse_geocode?
|
|
37
|
+
lat,lon = query.coordinates
|
|
30
38
|
params[:latt] = lat
|
|
31
39
|
params[:longt] = lon
|
|
32
40
|
params[:corner] = 1
|
|
33
41
|
params[:reverse] = 1
|
|
34
42
|
else
|
|
35
|
-
params[:locate] = query
|
|
43
|
+
params[:locate] = query.sanitized_text
|
|
36
44
|
params[:showpostal] = 1
|
|
37
45
|
end
|
|
38
|
-
|
|
46
|
+
params
|
|
39
47
|
end
|
|
40
48
|
|
|
41
49
|
def parse_raw_data(raw_data)
|
|
@@ -4,14 +4,22 @@ require "geocoder/results/google"
|
|
|
4
4
|
module Geocoder::Lookup
|
|
5
5
|
class Google < Base
|
|
6
6
|
|
|
7
|
+
def name
|
|
8
|
+
"Google"
|
|
9
|
+
end
|
|
10
|
+
|
|
7
11
|
def map_link_url(coordinates)
|
|
8
12
|
"http://maps.google.com/maps?q=#{coordinates.join(',')}"
|
|
9
13
|
end
|
|
10
14
|
|
|
15
|
+
def query_url(query)
|
|
16
|
+
"#{protocol}://maps.googleapis.com/maps/api/geocode/json?" + url_query_string(query)
|
|
17
|
+
end
|
|
18
|
+
|
|
11
19
|
private # ---------------------------------------------------------------
|
|
12
20
|
|
|
13
|
-
def results(query
|
|
14
|
-
return [] unless doc = fetch_data(query
|
|
21
|
+
def results(query)
|
|
22
|
+
return [] unless doc = fetch_data(query)
|
|
15
23
|
case doc['status']; when "OK" # OK status implies >0 results
|
|
16
24
|
return doc['results']
|
|
17
25
|
when "OVER_QUERY_LIMIT"
|
|
@@ -27,15 +35,28 @@ module Geocoder::Lookup
|
|
|
27
35
|
return []
|
|
28
36
|
end
|
|
29
37
|
|
|
30
|
-
def
|
|
38
|
+
def query_url_google_params(query)
|
|
31
39
|
params = {
|
|
32
|
-
(
|
|
40
|
+
(query.reverse_geocode? ? :latlng : :address) => query.sanitized_text,
|
|
33
41
|
:sensor => "false",
|
|
34
|
-
:language =>
|
|
35
|
-
:key => Geocoder::Configuration.api_key
|
|
42
|
+
:language => configuration.language
|
|
36
43
|
}
|
|
37
|
-
|
|
44
|
+
unless (bounds = query.options[:bounds]).nil?
|
|
45
|
+
params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join('|')
|
|
46
|
+
end
|
|
47
|
+
unless (region = query.options[:region]).nil?
|
|
48
|
+
params[:region] = region
|
|
49
|
+
end
|
|
50
|
+
unless (components = query.options[:components]).nil?
|
|
51
|
+
params[:components] = components.is_a?(Array) ? components.join("|") : components
|
|
52
|
+
end
|
|
53
|
+
params
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def query_url_params(query)
|
|
57
|
+
query_url_google_params(query).merge(
|
|
58
|
+
:key => configuration.api_key
|
|
59
|
+
).merge(super)
|
|
38
60
|
end
|
|
39
61
|
end
|
|
40
62
|
end
|
|
41
|
-
|
|
@@ -6,22 +6,31 @@ require 'geocoder/results/google_premier'
|
|
|
6
6
|
module Geocoder::Lookup
|
|
7
7
|
class GooglePremier < Google
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
def name
|
|
10
|
+
"Google Premier"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def required_api_key_parts
|
|
14
|
+
["private key", "client", "channel"]
|
|
15
|
+
end
|
|
10
16
|
|
|
11
|
-
def query_url(query
|
|
12
|
-
|
|
13
|
-
(reverse ? :latlng : :address) => query,
|
|
14
|
-
:sensor => 'false',
|
|
15
|
-
:language => Geocoder::Configuration.language,
|
|
16
|
-
:client => Geocoder::Configuration.api_key[1],
|
|
17
|
-
:channel => Geocoder::Configuration.api_key[2]
|
|
18
|
-
}.reject{ |key, value| value.nil? }
|
|
19
|
-
path = "/maps/api/geocode/json?#{hash_to_query(params)}"
|
|
17
|
+
def query_url(query)
|
|
18
|
+
path = "/maps/api/geocode/json?" + url_query_string(query)
|
|
20
19
|
"#{protocol}://maps.googleapis.com#{path}&signature=#{sign(path)}"
|
|
21
20
|
end
|
|
22
21
|
|
|
22
|
+
private # ---------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
def query_url_params(query)
|
|
25
|
+
query_url_google_params(query).merge(super).merge(
|
|
26
|
+
:key => nil, # don't use param inherited from Google lookup
|
|
27
|
+
:client => configuration.api_key[1],
|
|
28
|
+
:channel => configuration.api_key[2]
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
23
32
|
def sign(string)
|
|
24
|
-
raw_private_key = url_safe_base64_decode(
|
|
33
|
+
raw_private_key = url_safe_base64_decode(configuration.api_key[0])
|
|
25
34
|
digest = OpenSSL::Digest::Digest.new('sha1')
|
|
26
35
|
raw_signature = OpenSSL::HMAC.digest(digest, raw_private_key, string)
|
|
27
36
|
url_safe_base64_encode(raw_signature)
|
|
@@ -1,33 +1,43 @@
|
|
|
1
|
+
require 'cgi'
|
|
1
2
|
require 'geocoder/lookups/base'
|
|
2
3
|
require "geocoder/results/mapquest"
|
|
3
4
|
|
|
4
5
|
module Geocoder::Lookup
|
|
5
6
|
class Mapquest < Base
|
|
6
7
|
|
|
8
|
+
def name
|
|
9
|
+
"Mapquest"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def required_api_key_parts
|
|
13
|
+
["key"]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def query_url(query)
|
|
17
|
+
key = configuration.api_key
|
|
18
|
+
domain = key ? "www" : "open"
|
|
19
|
+
url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v1/#{search_type(query)}?"
|
|
20
|
+
url + url_query_string(query)
|
|
21
|
+
end
|
|
22
|
+
|
|
7
23
|
private # ---------------------------------------------------------------
|
|
8
24
|
|
|
9
|
-
def
|
|
10
|
-
|
|
11
|
-
doc.is_a?(Array) ? doc : [doc]
|
|
25
|
+
def search_type(query)
|
|
26
|
+
query.reverse_geocode? ? "reverse" : "address"
|
|
12
27
|
end
|
|
13
28
|
|
|
14
|
-
def
|
|
15
|
-
params = {
|
|
16
|
-
|
|
17
|
-
:
|
|
18
|
-
:addressdetails => "1",
|
|
19
|
-
:"accept-language" => Geocoder::Configuration.language
|
|
20
|
-
}
|
|
21
|
-
if (reverse)
|
|
22
|
-
method = 'reverse'
|
|
23
|
-
parts = query.split(/\s*,\s*/);
|
|
24
|
-
params[:lat] = parts[0]
|
|
25
|
-
params[:lon] = parts[1]
|
|
26
|
-
else
|
|
27
|
-
method = 'search'
|
|
28
|
-
params[:q] = query
|
|
29
|
+
def query_url_params(query)
|
|
30
|
+
params = { :location => query.sanitized_text }
|
|
31
|
+
if key = configuration.api_key
|
|
32
|
+
params[:key] = CGI.unescape(key)
|
|
29
33
|
end
|
|
30
|
-
|
|
34
|
+
params.merge(super)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def results(query)
|
|
38
|
+
return [] unless doc = fetch_data(query)
|
|
39
|
+
doc["results"][0]['locations']
|
|
31
40
|
end
|
|
41
|
+
|
|
32
42
|
end
|
|
33
43
|
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'geocoder/lookups/base'
|
|
2
|
+
require 'geocoder/results/maxmind'
|
|
3
|
+
require 'csv'
|
|
4
|
+
|
|
5
|
+
module Geocoder::Lookup
|
|
6
|
+
class Maxmind < Base
|
|
7
|
+
|
|
8
|
+
def name
|
|
9
|
+
"MaxMind"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def query_url(query)
|
|
13
|
+
"#{protocol}://geoip.maxmind.com/#{service_code}?" + url_query_string(query)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private # ---------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
def service_code
|
|
19
|
+
if s = configuration[:service] and services.keys.include?(s)
|
|
20
|
+
services[s]
|
|
21
|
+
else
|
|
22
|
+
raise(
|
|
23
|
+
Geocoder::ConfigurationError,
|
|
24
|
+
"When using MaxMind you MUST specify a service name: " +
|
|
25
|
+
"Geocoder.configure(:maxmind => {:service => ...}), " +
|
|
26
|
+
"where '...' is one of: #{services.keys.inspect}"
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# Service names mapped to code used in URL.
|
|
33
|
+
#
|
|
34
|
+
def services
|
|
35
|
+
{
|
|
36
|
+
:country => "a",
|
|
37
|
+
:city => "b",
|
|
38
|
+
:city_isp_org => "f",
|
|
39
|
+
:omni => "e"
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def results(query)
|
|
44
|
+
# don't look up a loopback address, just return the stored result
|
|
45
|
+
return [reserved_result] if query.loopback_ip_address?
|
|
46
|
+
doc = fetch_data(query)
|
|
47
|
+
if doc and doc.is_a?(Array)
|
|
48
|
+
if doc.last.nil?
|
|
49
|
+
return [doc]
|
|
50
|
+
elsif doc.last == "INVALID_LICENSE_KEY"
|
|
51
|
+
raise_error(Geocoder::InvalidApiKey) || warn("Invalid MaxMind API key.")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
return []
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def parse_raw_data(raw_data)
|
|
58
|
+
CSV.parse_line raw_data
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def reserved_result
|
|
62
|
+
",,,,0,0,0,0,,,"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def query_url_params(query)
|
|
66
|
+
{
|
|
67
|
+
:l => configuration.api_key,
|
|
68
|
+
:i => query.sanitized_text
|
|
69
|
+
}.merge(super)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -4,34 +4,42 @@ require "geocoder/results/nominatim"
|
|
|
4
4
|
module Geocoder::Lookup
|
|
5
5
|
class Nominatim < Base
|
|
6
6
|
|
|
7
|
+
def name
|
|
8
|
+
"Nominatim"
|
|
9
|
+
end
|
|
10
|
+
|
|
7
11
|
def map_link_url(coordinates)
|
|
8
12
|
"http://www.openstreetmap.org/?lat=#{coordinates[0]}&lon=#{coordinates[1]}&zoom=15&layers=M"
|
|
9
13
|
end
|
|
10
14
|
|
|
15
|
+
def query_url(query)
|
|
16
|
+
method = query.reverse_geocode? ? "reverse" : "search"
|
|
17
|
+
host = configuration[:host] || "nominatim.openstreetmap.org"
|
|
18
|
+
"#{protocol}://#{host}/#{method}?" + url_query_string(query)
|
|
19
|
+
end
|
|
20
|
+
|
|
11
21
|
private # ---------------------------------------------------------------
|
|
12
22
|
|
|
13
|
-
def results(query
|
|
14
|
-
return [] unless doc = fetch_data(query
|
|
23
|
+
def results(query)
|
|
24
|
+
return [] unless doc = fetch_data(query)
|
|
15
25
|
doc.is_a?(Array) ? doc : [doc]
|
|
16
26
|
end
|
|
17
27
|
|
|
18
|
-
def
|
|
28
|
+
def query_url_params(query)
|
|
19
29
|
params = {
|
|
20
30
|
:format => "json",
|
|
21
31
|
:polygon => "1",
|
|
22
32
|
:addressdetails => "1",
|
|
23
|
-
:"accept-language" =>
|
|
24
|
-
}
|
|
25
|
-
if
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
params[:
|
|
29
|
-
params[:lon] = parts[1]
|
|
33
|
+
:"accept-language" => configuration.language
|
|
34
|
+
}.merge(super)
|
|
35
|
+
if query.reverse_geocode?
|
|
36
|
+
lat,lon = query.coordinates
|
|
37
|
+
params[:lat] = lat
|
|
38
|
+
params[:lon] = lon
|
|
30
39
|
else
|
|
31
|
-
|
|
32
|
-
params[:q] = query
|
|
40
|
+
params[:q] = query.sanitized_text
|
|
33
41
|
end
|
|
34
|
-
|
|
42
|
+
params
|
|
35
43
|
end
|
|
36
44
|
end
|
|
37
45
|
end
|