geocoder 1.1.3 → 1.1.4
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.
- data/CHANGELOG.rdoc +12 -0
- data/README.md +580 -0
- data/examples/autoexpire_cache.rb +30 -0
- data/lib/geocoder.rb +9 -85
- data/lib/geocoder/calculations.rb +1 -1
- data/lib/geocoder/cli.rb +9 -10
- data/lib/geocoder/configuration.rb +3 -1
- data/lib/geocoder/lookup.rb +81 -0
- data/lib/geocoder/lookups/base.rb +20 -32
- data/lib/geocoder/lookups/bing.rb +12 -8
- data/lib/geocoder/lookups/freegeoip.rb +5 -5
- data/lib/geocoder/lookups/geocoder_ca.rb +13 -9
- data/lib/geocoder/lookups/google.rb +19 -7
- data/lib/geocoder/lookups/google_premier.rb +8 -7
- data/lib/geocoder/lookups/mapquest.rb +5 -23
- data/lib/geocoder/lookups/nominatim.rb +16 -13
- data/lib/geocoder/lookups/test.rb +8 -6
- data/lib/geocoder/lookups/yahoo.rb +49 -10
- data/lib/geocoder/lookups/yandex.rb +15 -8
- data/lib/geocoder/models/mongoid.rb +0 -1
- data/lib/geocoder/query.rb +88 -0
- data/lib/geocoder/results/mapquest.rb +2 -61
- data/lib/geocoder/results/nominatim.rb +24 -3
- data/lib/geocoder/sql.rb +104 -0
- data/lib/geocoder/stores/active_record.rb +68 -140
- data/lib/geocoder/stores/mongo_base.rb +2 -2
- data/lib/geocoder/version.rb +1 -1
- data/test/active_record_test.rb +15 -0
- data/test/calculations_test.rb +7 -0
- data/test/error_handling_test.rb +7 -7
- data/test/fixtures/yahoo_madison_square_garden.json +49 -43
- data/test/fixtures/yahoo_v1_madison_square_garden.json +46 -0
- data/test/fixtures/yahoo_v1_no_results.json +10 -0
- data/test/https_test.rb +2 -2
- data/test/integration/smoke_test.rb +6 -4
- data/test/lookup_test.rb +13 -6
- data/test/query_test.rb +34 -0
- data/test/result_test.rb +1 -1
- data/test/services_test.rb +48 -7
- data/test/test_helper.rb +64 -49
- data/test/test_mode_test.rb +0 -1
- metadata +13 -7
- data/README.rdoc +0 -552
- data/test/fixtures/yahoo_garbage.json +0 -50
- data/test/input_handling_test.rb +0 -43
@@ -10,11 +10,11 @@ module Geocoder::Lookup
|
|
10
10
|
raw_data.match(/^<html><title>404/) ? nil : super(raw_data)
|
11
11
|
end
|
12
12
|
|
13
|
-
def results(query
|
13
|
+
def results(query)
|
14
14
|
# don't look up a loopback address, just return the stored result
|
15
|
-
return [reserved_result(query)] if
|
15
|
+
return [reserved_result(query.text)] if query.loopback_ip_address?
|
16
16
|
begin
|
17
|
-
return (doc = fetch_data(query
|
17
|
+
return (doc = fetch_data(query)) ? [doc] : []
|
18
18
|
rescue StandardError => err # Freegeoip.net returns HTML on bad request
|
19
19
|
raise_error(err)
|
20
20
|
return []
|
@@ -36,8 +36,8 @@ module Geocoder::Lookup
|
|
36
36
|
}
|
37
37
|
end
|
38
38
|
|
39
|
-
def query_url(query
|
40
|
-
"http://freegeoip.net/json/#{query}"
|
39
|
+
def query_url(query)
|
40
|
+
"http://freegeoip.net/json/#{query.sanitized_text}"
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
@@ -6,8 +6,8 @@ module Geocoder::Lookup
|
|
6
6
|
|
7
7
|
private # ---------------------------------------------------------------
|
8
8
|
|
9
|
-
def results(query
|
10
|
-
return [] unless doc = fetch_data(query
|
9
|
+
def results(query)
|
10
|
+
return [] unless doc = fetch_data(query)
|
11
11
|
if doc['error'].nil?
|
12
12
|
return [doc]
|
13
13
|
elsif doc['error']['code'] == "005"
|
@@ -18,24 +18,28 @@ module Geocoder::Lookup
|
|
18
18
|
return []
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
params =
|
21
|
+
def query_url_params(query)
|
22
|
+
params = super.merge(
|
23
23
|
:geoit => "xml",
|
24
24
|
:jsonp => 1,
|
25
25
|
:callback => "test",
|
26
26
|
:auth => Geocoder::Configuration.api_key
|
27
|
-
|
28
|
-
if
|
29
|
-
lat,lon = query.
|
27
|
+
)
|
28
|
+
if query.reverse_geocode?
|
29
|
+
lat,lon = query.coordinates
|
30
30
|
params[:latt] = lat
|
31
31
|
params[:longt] = lon
|
32
32
|
params[:corner] = 1
|
33
33
|
params[:reverse] = 1
|
34
34
|
else
|
35
|
-
params[:locate] = query
|
35
|
+
params[:locate] = query.sanitized_text
|
36
36
|
params[:showpostal] = 1
|
37
37
|
end
|
38
|
-
|
38
|
+
params
|
39
|
+
end
|
40
|
+
|
41
|
+
def query_url(query)
|
42
|
+
"http://geocoder.ca/?" + url_query_string(query)
|
39
43
|
end
|
40
44
|
|
41
45
|
def parse_raw_data(raw_data)
|
@@ -10,8 +10,8 @@ module Geocoder::Lookup
|
|
10
10
|
|
11
11
|
private # ---------------------------------------------------------------
|
12
12
|
|
13
|
-
def results(query
|
14
|
-
return [] unless doc = fetch_data(query
|
13
|
+
def results(query)
|
14
|
+
return [] unless doc = fetch_data(query)
|
15
15
|
case doc['status']; when "OK" # OK status implies >0 results
|
16
16
|
return doc['results']
|
17
17
|
when "OVER_QUERY_LIMIT"
|
@@ -27,14 +27,26 @@ module Geocoder::Lookup
|
|
27
27
|
return []
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
30
|
+
def query_url_google_params(query)
|
31
31
|
params = {
|
32
|
-
(
|
32
|
+
(query.reverse_geocode? ? :latlng : :address) => query.sanitized_text,
|
33
33
|
:sensor => "false",
|
34
|
-
:language => Geocoder::Configuration.language
|
35
|
-
:key => Geocoder::Configuration.api_key
|
34
|
+
:language => Geocoder::Configuration.language
|
36
35
|
}
|
37
|
-
|
36
|
+
unless (bounds = query.options[:bounds]).nil?
|
37
|
+
params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join('|')
|
38
|
+
end
|
39
|
+
params
|
40
|
+
end
|
41
|
+
|
42
|
+
def query_url_params(query)
|
43
|
+
super.merge(query_url_google_params(query)).merge(
|
44
|
+
:key => Geocoder::Configuration.api_key
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def query_url(query)
|
49
|
+
"#{protocol}://maps.googleapis.com/maps/api/geocode/json?" + url_query_string(query)
|
38
50
|
end
|
39
51
|
end
|
40
52
|
end
|
@@ -8,15 +8,16 @@ module Geocoder::Lookup
|
|
8
8
|
|
9
9
|
private # ---------------------------------------------------------------
|
10
10
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
:sensor => 'false',
|
15
|
-
:language => Geocoder::Configuration.language,
|
11
|
+
def query_url_params(query)
|
12
|
+
super.merge(query_url_google_params(query)).merge(
|
13
|
+
:key => nil, # don't use param inherited from Google lookup
|
16
14
|
:client => Geocoder::Configuration.api_key[1],
|
17
15
|
:channel => Geocoder::Configuration.api_key[2]
|
18
|
-
|
19
|
-
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def query_url(query)
|
20
|
+
path = "/maps/api/geocode/json?" + url_query_string(query)
|
20
21
|
"#{protocol}://maps.googleapis.com#{path}&signature=#{sign(path)}"
|
21
22
|
end
|
22
23
|
|
@@ -1,33 +1,15 @@
|
|
1
1
|
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/lookups/nominatim"
|
2
3
|
require "geocoder/results/mapquest"
|
3
4
|
|
4
5
|
module Geocoder::Lookup
|
5
|
-
class Mapquest <
|
6
|
+
class Mapquest < Nominatim
|
6
7
|
|
7
8
|
private # ---------------------------------------------------------------
|
8
9
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def query_url(query, reverse = false)
|
15
|
-
params = {
|
16
|
-
:format => "json",
|
17
|
-
:polygon => "1",
|
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
|
-
end
|
30
|
-
"http://open.mapquestapi.com/#{method}?" + hash_to_query(params)
|
10
|
+
def query_url(query)
|
11
|
+
method = query.reverse_geocode? ? "reverse" : "search"
|
12
|
+
"http://open.mapquestapi.com/#{method}?" + url_query_string(query)
|
31
13
|
end
|
32
14
|
end
|
33
15
|
end
|
@@ -10,28 +10,31 @@ module Geocoder::Lookup
|
|
10
10
|
|
11
11
|
private # ---------------------------------------------------------------
|
12
12
|
|
13
|
-
def results(query
|
14
|
-
return [] unless doc = fetch_data(query
|
13
|
+
def results(query)
|
14
|
+
return [] unless doc = fetch_data(query)
|
15
15
|
doc.is_a?(Array) ? doc : [doc]
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
params =
|
18
|
+
def query_url_params(query)
|
19
|
+
params = super.merge(
|
20
20
|
:format => "json",
|
21
21
|
:polygon => "1",
|
22
22
|
:addressdetails => "1",
|
23
23
|
:"accept-language" => Geocoder::Configuration.language
|
24
|
-
|
25
|
-
if
|
26
|
-
|
27
|
-
|
28
|
-
params[:
|
29
|
-
params[:lon] = parts[1]
|
24
|
+
)
|
25
|
+
if query.reverse_geocode?
|
26
|
+
lat,lon = query.coordinates
|
27
|
+
params[:lat] = lat
|
28
|
+
params[:lon] = lon
|
30
29
|
else
|
31
|
-
|
32
|
-
params[:q] = query
|
30
|
+
params[:q] = query.sanitized_text
|
33
31
|
end
|
34
|
-
|
32
|
+
params
|
33
|
+
end
|
34
|
+
|
35
|
+
def query_url(query)
|
36
|
+
method = query.reverse_geocode? ? "reverse" : "search"
|
37
|
+
"http://nominatim.openstreetmap.org/#{method}?" + url_query_string(query)
|
35
38
|
end
|
36
39
|
end
|
37
40
|
end
|
@@ -5,12 +5,14 @@ module Geocoder
|
|
5
5
|
module Lookup
|
6
6
|
class Test < Base
|
7
7
|
|
8
|
-
def self.add_stub(
|
9
|
-
stubs[
|
8
|
+
def self.add_stub(query_text, results)
|
9
|
+
stubs[query_text] = results
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.read_stub(
|
13
|
-
stubs.fetch(
|
12
|
+
def self.read_stub(query_text)
|
13
|
+
stubs.fetch(query_text) {
|
14
|
+
raise ArgumentError, "unknown stub request #{query_text}"
|
15
|
+
}
|
14
16
|
end
|
15
17
|
|
16
18
|
def self.stubs
|
@@ -23,8 +25,8 @@ module Geocoder
|
|
23
25
|
|
24
26
|
private
|
25
27
|
|
26
|
-
def results(query
|
27
|
-
Geocoder::Lookup::Test.read_stub(query)
|
28
|
+
def results(query)
|
29
|
+
Geocoder::Lookup::Test.read_stub(query.text)
|
28
30
|
end
|
29
31
|
|
30
32
|
end
|
@@ -10,25 +10,64 @@ module Geocoder::Lookup
|
|
10
10
|
|
11
11
|
private # ---------------------------------------------------------------
|
12
12
|
|
13
|
-
def results(query
|
14
|
-
return [] unless doc = fetch_data(query
|
15
|
-
|
16
|
-
|
13
|
+
def results(query)
|
14
|
+
return [] unless doc = fetch_data(query)
|
15
|
+
doc = doc['ResultSet']
|
16
|
+
if api_version(doc).to_i == 1
|
17
|
+
return version_1_results(doc)
|
18
|
+
elsif api_version(doc).to_i == 2
|
19
|
+
return version_2_results(doc)
|
17
20
|
else
|
18
21
|
warn "Yahoo Geocoding API error: #{doc['Error']} (#{doc['ErrorMessage']})."
|
19
22
|
return []
|
20
23
|
end
|
21
24
|
end
|
22
25
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
+
def api_version(doc)
|
27
|
+
if doc.include?('version')
|
28
|
+
return doc['version'].to_f
|
29
|
+
elsif doc.include?('@version')
|
30
|
+
return doc['@version'].to_f
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def version_1_results(doc)
|
35
|
+
if doc['Error'] == 0
|
36
|
+
if doc['Found'] > 0
|
37
|
+
return doc['Results']
|
38
|
+
else
|
39
|
+
return []
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Return array of results, or nil if an error.
|
46
|
+
#
|
47
|
+
def version_2_results(doc)
|
48
|
+
# seems to have Error == 7 when no results, though this is not documented
|
49
|
+
if [0, 7].include?(doc['Error'].to_i)
|
50
|
+
if doc['Found'].to_i > 0
|
51
|
+
r = doc['Result']
|
52
|
+
return r.is_a?(Array) ? r : [r]
|
53
|
+
else
|
54
|
+
return []
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def query_url_params(query)
|
60
|
+
super.merge(
|
61
|
+
:location => query.sanitized_text,
|
26
62
|
:flags => "JXTSR",
|
27
|
-
:gflags => "AC#{'R' if
|
63
|
+
:gflags => "AC#{'R' if query.reverse_geocode?}",
|
28
64
|
:locale => "#{Geocoder::Configuration.language}_US",
|
29
65
|
:appid => Geocoder::Configuration.api_key
|
30
|
-
|
31
|
-
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def query_url(query)
|
70
|
+
"http://where.yahooapis.com/geocode?" + url_query_string(query)
|
32
71
|
end
|
33
72
|
end
|
34
73
|
end
|
@@ -10,8 +10,8 @@ module Geocoder::Lookup
|
|
10
10
|
|
11
11
|
private # ---------------------------------------------------------------
|
12
12
|
|
13
|
-
def results(query
|
14
|
-
return [] unless doc = fetch_data(query
|
13
|
+
def results(query)
|
14
|
+
return [] unless doc = fetch_data(query)
|
15
15
|
if err = doc['error']
|
16
16
|
warn "Yandex Geocoding API error: #{err['status']} (#{err['message']})."
|
17
17
|
return []
|
@@ -25,15 +25,22 @@ module Geocoder::Lookup
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
def query_url_params(query)
|
29
|
+
if query.reverse_geocode?
|
30
|
+
q = query.coordinates.reverse.join(",")
|
31
|
+
else
|
32
|
+
q = query.sanitized_text
|
33
|
+
end
|
34
|
+
super.merge(
|
35
|
+
:geocode => q,
|
32
36
|
:format => "json",
|
33
37
|
:plng => "#{Geocoder::Configuration.language}", # supports ru, uk, be
|
34
38
|
:key => Geocoder::Configuration.api_key
|
35
|
-
|
36
|
-
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def query_url(query)
|
43
|
+
"http://geocode-maps.yandex.ru/1.x/?" + url_query_string(query)
|
37
44
|
end
|
38
45
|
end
|
39
46
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Geocoder
|
2
|
+
class Query
|
3
|
+
attr_accessor :text, :options
|
4
|
+
|
5
|
+
def initialize(text, options = {})
|
6
|
+
self.text = text
|
7
|
+
self.options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
lookup.search(text, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
text
|
16
|
+
end
|
17
|
+
|
18
|
+
def sanitized_text
|
19
|
+
if coordinates?
|
20
|
+
text.split(/\s*,\s*/).join(',')
|
21
|
+
else
|
22
|
+
text
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Get a Lookup object (which communicates with the remote geocoding API)
|
28
|
+
# appropriate to the Query text.
|
29
|
+
#
|
30
|
+
def lookup
|
31
|
+
if ip_address?
|
32
|
+
name = Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
|
33
|
+
else
|
34
|
+
name = Configuration.lookup || Geocoder::Lookup.street_services.first
|
35
|
+
end
|
36
|
+
Lookup.get(name)
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Is the Query text blank? (ie, should we not bother searching?)
|
41
|
+
#
|
42
|
+
def blank?
|
43
|
+
!!text.to_s.match(/^\s*$/)
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Does the Query text look like an IP address?
|
48
|
+
#
|
49
|
+
# Does not check for actual validity, just the appearance of four
|
50
|
+
# dot-delimited numbers.
|
51
|
+
#
|
52
|
+
def ip_address?
|
53
|
+
!!text.to_s.match(/^(::ffff:)?(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Is the Query text a loopback IP address?
|
58
|
+
#
|
59
|
+
def loopback_ip_address?
|
60
|
+
!!(text == "0.0.0.0" or text.to_s.match(/^127/))
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Does the given string look like latitude/longitude coordinates?
|
65
|
+
#
|
66
|
+
def coordinates?
|
67
|
+
text.is_a?(Array) or (
|
68
|
+
text.is_a?(String) and
|
69
|
+
!!text.to_s.match(/^-?[0-9\.]+, *-?[0-9\.]+$/)
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Return the latitude/longitude coordinates specified in the query,
|
75
|
+
# or nil if none.
|
76
|
+
#
|
77
|
+
def coordinates
|
78
|
+
sanitized_text.split(',') if coordinates?
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Should reverse geocoding be performed for this query?
|
83
|
+
#
|
84
|
+
def reverse_geocode?
|
85
|
+
coordinates?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|