geocoder 1.1.8 → 1.1.9
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of geocoder might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +13 -1
- data/README.md +100 -30
- data/Rakefile +1 -1
- data/examples/autoexpire_cache_dalli.rb +2 -2
- data/examples/cache_bypass.rb +48 -0
- data/lib/geocoder/calculations.rb +38 -0
- data/lib/geocoder/cli.rb +8 -1
- data/lib/geocoder/lookup.rb +3 -0
- data/lib/geocoder/lookups/baidu.rb +54 -0
- data/lib/geocoder/lookups/base.rb +26 -11
- data/lib/geocoder/lookups/dstk.rb +20 -0
- data/lib/geocoder/lookups/freegeoip.rb +2 -6
- data/lib/geocoder/lookups/geocoder_us.rb +39 -0
- data/lib/geocoder/lookups/google.rb +5 -0
- data/lib/geocoder/lookups/mapquest.rb +21 -5
- data/lib/geocoder/lookups/maxmind.rb +1 -1
- data/lib/geocoder/lookups/nominatim.rb +0 -1
- data/lib/geocoder/lookups/ovi.rb +14 -4
- data/lib/geocoder/query.rb +5 -1
- data/lib/geocoder/results/baidu.rb +79 -0
- data/lib/geocoder/results/dstk.rb +6 -0
- data/lib/geocoder/results/geocoder_us.rb +39 -0
- data/lib/geocoder/results/yandex.rb +1 -1
- data/lib/geocoder/stores/active_record.rb +12 -6
- data/lib/geocoder/stores/mongo_base.rb +5 -2
- data/lib/geocoder/version.rb +1 -1
- data/lib/tasks/geocoder.rake +2 -0
- data/test/cache_test.rb +16 -0
- data/test/calculations_test.rb +31 -21
- data/test/fixtures/baidu_invalid_key +1 -0
- data/test/fixtures/baidu_no_results +1 -0
- data/test/fixtures/baidu_reverse +1 -0
- data/test/fixtures/baidu_shanghai_pearl_tower +12 -0
- data/test/fixtures/geocoder_us_madison_square_garden +1 -0
- data/test/fixtures/geocoder_us_no_results +1 -0
- data/test/fixtures/google_over_limit +4 -0
- data/test/fixtures/mapquest_error +16 -0
- data/test/fixtures/mapquest_invalid_api_key +16 -0
- data/test/fixtures/mapquest_invalid_request +16 -0
- data/test/fixtures/mapquest_no_results +10 -1
- data/test/mongoid_test.rb +7 -0
- data/test/near_test.rb +18 -0
- data/test/proxy_test.rb +13 -0
- data/test/query_test.rb +5 -0
- data/test/services_test.rb +75 -2
- data/test/test_helper.rb +16 -6
- metadata +24 -9
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/baidu"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Baidu < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Baidu"
|
9
|
+
end
|
10
|
+
|
11
|
+
def required_api_key_parts
|
12
|
+
["key"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def query_url(query)
|
16
|
+
"http://api.map.baidu.com/geocoder/v2/?" + url_query_string(query)
|
17
|
+
end
|
18
|
+
|
19
|
+
private # ---------------------------------------------------------------
|
20
|
+
|
21
|
+
def results(query, reverse = false)
|
22
|
+
return [] unless doc = fetch_data(query)
|
23
|
+
case doc['status']; when 0
|
24
|
+
return [doc['result']] unless doc['result'].blank?
|
25
|
+
when 1, 3, 4
|
26
|
+
raise_error(Geocoder::Error, messages) ||
|
27
|
+
warn("Baidu Geocoding API error: server error.")
|
28
|
+
when 2
|
29
|
+
raise_error(Geocoder::InvalidRequest, messages) ||
|
30
|
+
warn("Baidu Geocoding API error: invalid request.")
|
31
|
+
when 5
|
32
|
+
raise_error(Geocoder::InvalidApiKey, messages) ||
|
33
|
+
warn("Baidu Geocoding API error: invalid api key.")
|
34
|
+
when 101, 102, 200..299
|
35
|
+
raise_error(Geocoder::RequestDenied) ||
|
36
|
+
warn("Baidu Geocoding API error: request denied.")
|
37
|
+
when 300..399
|
38
|
+
raise_error(Geocoder::OverQueryLimitError) ||
|
39
|
+
warn("Baidu Geocoding API error: over query limit.")
|
40
|
+
end
|
41
|
+
return []
|
42
|
+
end
|
43
|
+
|
44
|
+
def query_url_params(query)
|
45
|
+
{
|
46
|
+
(query.reverse_geocode? ? :location : :address) => query.sanitized_text,
|
47
|
+
:ak => configuration.api_key,
|
48
|
+
:output => "json"
|
49
|
+
}.merge(super)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -72,7 +72,7 @@ module Geocoder
|
|
72
72
|
def query_url(query)
|
73
73
|
fail
|
74
74
|
end
|
75
|
-
|
75
|
+
|
76
76
|
##
|
77
77
|
# The working Cache object.
|
78
78
|
#
|
@@ -99,7 +99,7 @@ module Geocoder
|
|
99
99
|
protocol = "http#{'s' if configuration.use_https}"
|
100
100
|
proxy_name = "#{protocol}_proxy"
|
101
101
|
if proxy = configuration.send(proxy_name)
|
102
|
-
proxy_url = protocol + '://' + proxy
|
102
|
+
proxy_url = !!(proxy =~ /^#{protocol}/) ? proxy : protocol + '://' + proxy
|
103
103
|
begin
|
104
104
|
uri = URI.parse(proxy_url)
|
105
105
|
rescue URI::InvalidURIError
|
@@ -171,15 +171,19 @@ module Geocoder
|
|
171
171
|
"(use Geocoder.configure(:timeout => ...) to set limit)."
|
172
172
|
end
|
173
173
|
|
174
|
+
def parse_json(data)
|
175
|
+
if defined?(ActiveSupport::JSON)
|
176
|
+
ActiveSupport::JSON.decode(data)
|
177
|
+
else
|
178
|
+
JSON.parse(data)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
174
182
|
##
|
175
183
|
# Parses a raw search result (returns hash or array).
|
176
184
|
#
|
177
185
|
def parse_raw_data(raw_data)
|
178
|
-
|
179
|
-
ActiveSupport::JSON.decode(raw_data)
|
180
|
-
else
|
181
|
-
JSON.parse(raw_data)
|
182
|
-
end
|
186
|
+
parse_json(raw_data)
|
183
187
|
rescue
|
184
188
|
warn "Geocoding API's response was not valid JSON."
|
185
189
|
end
|
@@ -192,6 +196,10 @@ module Geocoder
|
|
192
196
|
"http" + (configuration.use_https ? "s" : "")
|
193
197
|
end
|
194
198
|
|
199
|
+
def valid_response?(response)
|
200
|
+
(200..399).include?(response.code.to_i)
|
201
|
+
end
|
202
|
+
|
195
203
|
##
|
196
204
|
# Fetch a raw geocoding result (JSON string).
|
197
205
|
# The result might or might not be cached.
|
@@ -204,7 +212,7 @@ module Geocoder
|
|
204
212
|
check_api_key_configuration!(query)
|
205
213
|
response = make_api_request(query)
|
206
214
|
body = response.body
|
207
|
-
if cache and
|
215
|
+
if cache and valid_response?(response)
|
208
216
|
cache[key] = body
|
209
217
|
end
|
210
218
|
@cache_hit = false
|
@@ -219,9 +227,16 @@ module Geocoder
|
|
219
227
|
def make_api_request(query)
|
220
228
|
timeout(configuration.timeout) do
|
221
229
|
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)
|
230
|
+
# client = http_client.new(uri.host, uri.port)
|
231
|
+
# client.use_ssl = true if configuration.use_https
|
232
|
+
# client.get(uri.request_uri, configuration.http_headers)
|
233
|
+
|
234
|
+
http_client.start(uri.host, uri.port) do |client|
|
235
|
+
client.use_ssl = true if configuration.use_https
|
236
|
+
req = Net::HTTP::Get.new(uri.request_uri, configuration.http_headers)
|
237
|
+
req.basic_auth(uri.user, uri.password) if uri.user and uri.password
|
238
|
+
client.request(req)
|
239
|
+
end
|
225
240
|
end
|
226
241
|
end
|
227
242
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# More information about the Data Science Toolkit can be found at:
|
2
|
+
# http://www.datasciencetoolkit.org/. The provided APIs mimic the
|
3
|
+
# Google geocoding api.
|
4
|
+
|
5
|
+
require 'geocoder/lookups/google'
|
6
|
+
require 'geocoder/results/dstk'
|
7
|
+
|
8
|
+
module Geocoder::Lookup
|
9
|
+
class Dstk < Google
|
10
|
+
|
11
|
+
def name
|
12
|
+
"Data Science Toolkit"
|
13
|
+
end
|
14
|
+
|
15
|
+
def query_url(query)
|
16
|
+
host = configuration[:host] || "www.datasciencetoolkit.org"
|
17
|
+
"#{protocol}://#{host}/maps/api/geocode/json?" + url_query_string(query)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -21,12 +21,8 @@ module Geocoder::Lookup
|
|
21
21
|
def results(query)
|
22
22
|
# don't look up a loopback address, just return the stored result
|
23
23
|
return [reserved_result(query.text)] if query.loopback_ip_address?
|
24
|
-
|
25
|
-
|
26
|
-
rescue StandardError => err # Freegeoip.net returns HTML on bad request
|
27
|
-
raise_error(err)
|
28
|
-
return []
|
29
|
-
end
|
24
|
+
# note: Freegeoip.net returns plain text "Not Found" on bad request
|
25
|
+
(doc = fetch_data(query)) ? [doc] : []
|
30
26
|
end
|
31
27
|
|
32
28
|
def reserved_result(ip)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/geocoder_us"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class GeocoderUs < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Geocoder.us"
|
9
|
+
end
|
10
|
+
|
11
|
+
def query_url(query)
|
12
|
+
if configuration.api_key
|
13
|
+
"http://#{configuration.api_key}@geocoder.us/member/service/csv/geocode?" + url_query_string(query)
|
14
|
+
else
|
15
|
+
"http://geocoder.us/service/csv/geocode?" + url_query_string(query)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def results(query)
|
22
|
+
return [] unless doc = fetch_data(query)
|
23
|
+
if doc[0].to_s =~ /^(\d+)\:/
|
24
|
+
return []
|
25
|
+
else
|
26
|
+
return [doc.size == 5 ? ((doc[0..1] << nil) + doc[2..4]) : doc]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def query_url_params(query)
|
31
|
+
(query.text =~ /^\d{5}(?:-\d{4})?$/ ? {:zip => query} : {:address => query.sanitized_text}).merge(super)
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_raw_data(raw_data)
|
35
|
+
raw_data.chomp.split(',')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -18,6 +18,11 @@ module Geocoder::Lookup
|
|
18
18
|
|
19
19
|
private # ---------------------------------------------------------------
|
20
20
|
|
21
|
+
def valid_response?(response)
|
22
|
+
status = parse_json(response.body)["status"]
|
23
|
+
super(response) and ['OK', 'ZERO_RESULTS'].include?(status)
|
24
|
+
end
|
25
|
+
|
21
26
|
def results(query)
|
22
27
|
return [] unless doc = fetch_data(query)
|
23
28
|
case doc['status']; when "OK" # OK status implies >0 results
|
@@ -10,13 +10,13 @@ module Geocoder::Lookup
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def required_api_key_parts
|
13
|
-
[]
|
13
|
+
["key"]
|
14
14
|
end
|
15
15
|
|
16
16
|
def query_url(query)
|
17
|
-
|
18
|
-
|
19
|
-
url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/
|
17
|
+
domain = configuration[:licensed] ? "www" : "open"
|
18
|
+
version = configuration[:version] || 1
|
19
|
+
url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v#{version}/#{search_type(query)}?"
|
20
20
|
url + url_query_string(query)
|
21
21
|
end
|
22
22
|
|
@@ -34,9 +34,25 @@ module Geocoder::Lookup
|
|
34
34
|
params.merge(super)
|
35
35
|
end
|
36
36
|
|
37
|
+
# http://www.mapquestapi.com/geocoding/status_codes.html
|
38
|
+
# http://open.mapquestapi.com/geocoding/status_codes.html
|
37
39
|
def results(query)
|
38
40
|
return [] unless doc = fetch_data(query)
|
39
|
-
doc["results"][0]['locations']
|
41
|
+
return doc["results"][0]['locations'] if doc['info']['statuscode'] == 0 # A successful geocode call
|
42
|
+
|
43
|
+
messages = doc['info']['messages'].join
|
44
|
+
|
45
|
+
case doc['info']['statuscode']
|
46
|
+
when 400 # Error with input
|
47
|
+
raise_error(Geocoder::InvalidRequest, messages) ||
|
48
|
+
warn("Mapquest Geocoding API error: #{messages}")
|
49
|
+
when 403 # Key related error
|
50
|
+
raise_error(Geocoder::InvalidApiKey, messages) ||
|
51
|
+
warn("Mapquest Geocoding API error: #{messages}")
|
52
|
+
when 500 # Unknown error
|
53
|
+
raise_error(Geocoder::Error, messages) ||
|
54
|
+
warn("Mapquest Geocoding API error: #{messages}")
|
55
|
+
end
|
40
56
|
end
|
41
57
|
|
42
58
|
end
|
data/lib/geocoder/lookups/ovi.rb
CHANGED
@@ -13,7 +13,7 @@ module Geocoder::Lookup
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def query_url(query)
|
16
|
-
"#{protocol}://lbs.ovi.com/search/6.2
|
16
|
+
"#{protocol}://lbs.ovi.com/search/6.2/#{if query.reverse_geocode? then 'reverse' end}geocode.json?" + url_query_string(query)
|
17
17
|
end
|
18
18
|
|
19
19
|
private # ---------------------------------------------------------------
|
@@ -29,12 +29,22 @@ module Geocoder::Lookup
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def query_url_params(query)
|
32
|
-
|
33
|
-
:searchtext=>query.sanitized_text,
|
32
|
+
options = {
|
34
33
|
:gen=>1,
|
35
34
|
:app_id=>api_key,
|
36
35
|
:app_code=>api_code
|
37
|
-
|
36
|
+
}
|
37
|
+
|
38
|
+
if query.reverse_geocode?
|
39
|
+
super.merge(options).merge(
|
40
|
+
:prox=>query.sanitized_text,
|
41
|
+
:mode=>:retrieveAddresses
|
42
|
+
)
|
43
|
+
else
|
44
|
+
super.merge(options).merge(
|
45
|
+
:searchtext=>query.sanitized_text
|
46
|
+
)
|
47
|
+
end
|
38
48
|
end
|
39
49
|
|
40
50
|
def api_key
|
data/lib/geocoder/query.rb
CHANGED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'geocoder/results/base'
|
2
|
+
|
3
|
+
module Geocoder::Result
|
4
|
+
class Baidu < Base
|
5
|
+
|
6
|
+
def coordinates
|
7
|
+
['lat', 'lng'].map{ |i| @data['location'][i] }
|
8
|
+
end
|
9
|
+
|
10
|
+
def address
|
11
|
+
@data['formatted_address']
|
12
|
+
end
|
13
|
+
|
14
|
+
def state
|
15
|
+
province
|
16
|
+
end
|
17
|
+
|
18
|
+
def province
|
19
|
+
@data['addressComponent']['province']
|
20
|
+
end
|
21
|
+
|
22
|
+
def city
|
23
|
+
@data['addressComponent']['city']
|
24
|
+
end
|
25
|
+
|
26
|
+
def district
|
27
|
+
@data['addressComponent']['district']
|
28
|
+
end
|
29
|
+
|
30
|
+
def street
|
31
|
+
@data['addressComponent']['street']
|
32
|
+
end
|
33
|
+
|
34
|
+
def street_number
|
35
|
+
@data['addressComponent']['street_number']
|
36
|
+
end
|
37
|
+
|
38
|
+
def formatted_address
|
39
|
+
@data['formatted_address']
|
40
|
+
end
|
41
|
+
|
42
|
+
def address_components
|
43
|
+
@data['addressComponent']
|
44
|
+
end
|
45
|
+
|
46
|
+
def state_code
|
47
|
+
""
|
48
|
+
end
|
49
|
+
|
50
|
+
def postal_code
|
51
|
+
""
|
52
|
+
end
|
53
|
+
|
54
|
+
def country
|
55
|
+
"China"
|
56
|
+
end
|
57
|
+
|
58
|
+
def country_code
|
59
|
+
"CN"
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Get address components of a given type. Valid types are defined in
|
64
|
+
# Baidu's Geocoding API documentation and include (among others):
|
65
|
+
#
|
66
|
+
# :business
|
67
|
+
# :cityCode
|
68
|
+
#
|
69
|
+
def self.response_attributes
|
70
|
+
%w[business cityCode]
|
71
|
+
end
|
72
|
+
|
73
|
+
response_attributes.each do |a|
|
74
|
+
define_method a do
|
75
|
+
@data[a]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'geocoder/results/base'
|
2
|
+
|
3
|
+
module Geocoder::Result
|
4
|
+
class GeocoderUs < Base
|
5
|
+
def coordinates
|
6
|
+
[@data[0].to_f, @data[1].to_f]
|
7
|
+
end
|
8
|
+
|
9
|
+
def address(format = :full)
|
10
|
+
"#{street_address}, #{city}, #{state} #{postal_code}, #{country}".sub(/^[ ,]*/, "")
|
11
|
+
end
|
12
|
+
|
13
|
+
def street_address
|
14
|
+
@data[2]
|
15
|
+
end
|
16
|
+
|
17
|
+
def city
|
18
|
+
@data[3]
|
19
|
+
end
|
20
|
+
|
21
|
+
def state
|
22
|
+
@data[4]
|
23
|
+
end
|
24
|
+
|
25
|
+
alias_method :state_code, :state
|
26
|
+
|
27
|
+
def postal_code
|
28
|
+
@data[5]
|
29
|
+
end
|
30
|
+
|
31
|
+
def country
|
32
|
+
'United States'
|
33
|
+
end
|
34
|
+
|
35
|
+
def country_code
|
36
|
+
'US'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|