geocoder 0.9.13 → 1.0.0
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 +16 -7
- data/README.rdoc +116 -51
- data/bin/geocode +5 -0
- data/lib/geocoder.rb +30 -43
- data/lib/geocoder/calculations.rb +4 -21
- data/lib/geocoder/cli.rb +111 -0
- data/lib/geocoder/configuration.rb +6 -7
- data/lib/geocoder/lookups/base.rb +36 -9
- data/lib/geocoder/lookups/bing.rb +33 -0
- data/lib/geocoder/lookups/google.rb +4 -0
- data/lib/geocoder/lookups/yahoo.rb +5 -2
- data/lib/geocoder/lookups/yandex.rb +39 -0
- data/lib/geocoder/models/base.rb +5 -1
- data/lib/geocoder/results/base.rb +16 -0
- data/lib/geocoder/results/bing.rb +48 -0
- data/lib/geocoder/results/freegeoip.rb +10 -3
- data/lib/geocoder/results/geocoder_ca.rb +14 -12
- data/lib/geocoder/results/google.rb +15 -2
- data/lib/geocoder/results/yahoo.rb +10 -2
- data/lib/geocoder/results/yandex.rb +48 -0
- data/lib/geocoder/stores/active_record.rb +2 -7
- data/lib/geocoder/stores/base.rb +2 -10
- data/lib/geocoder/version.rb +3 -0
- data/test/fixtures/bing_madison_square_garden.json +40 -0
- data/test/fixtures/bing_no_results.json +16 -0
- data/test/fixtures/bing_reverse.json +42 -0
- data/test/fixtures/yandex_invalid_key.json +1 -0
- data/test/fixtures/yandex_kremlin.json +48 -0
- data/test/fixtures/yandex_no_results.json +16 -0
- data/test/geocoder_test.rb +58 -8
- data/test/test_helper.rb +39 -3
- metadata +69 -58
- data/VERSION +0 -1
- data/lib/geocoder/stores/active_record_legacy.rb +0 -62
@@ -15,6 +15,12 @@ module Geocoder
|
|
15
15
|
# use HTTPS for lookup requests? (if supported)
|
16
16
|
[:use_https, false],
|
17
17
|
|
18
|
+
# HTTP proxy server (user:pass@host:port)
|
19
|
+
[:http_proxy, nil],
|
20
|
+
|
21
|
+
# HTTPS proxy server (user:pass@host:port)
|
22
|
+
[:https_proxy, nil],
|
23
|
+
|
18
24
|
# API key for geocoding service
|
19
25
|
[:api_key, nil],
|
20
26
|
|
@@ -32,13 +38,6 @@ module Geocoder
|
|
32
38
|
eval("def self.#{o}=(obj); @@#{o} = obj; end")
|
33
39
|
end
|
34
40
|
|
35
|
-
# legacy support
|
36
|
-
def self.yahoo_app_id=(value)
|
37
|
-
warn "DEPRECATION WARNING: Geocoder's 'yahoo_app_id' setting has been replaced by 'api_key'. " +
|
38
|
-
"This method will be removed in Geocoder v1.0."
|
39
|
-
@@api_key = value
|
40
|
-
end
|
41
|
-
|
42
41
|
##
|
43
42
|
# Set all values to default.
|
44
43
|
#
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
2
4
|
unless defined?(ActiveSupport::JSON)
|
3
5
|
begin
|
4
6
|
require 'rubygems' # for Ruby 1.8
|
@@ -20,12 +22,7 @@ module Geocoder
|
|
20
22
|
# "205.128.54.202") for geocoding, or coordinates (latitude, longitude)
|
21
23
|
# for reverse geocoding. Returns an array of <tt>Geocoder::Result</tt>s.
|
22
24
|
#
|
23
|
-
def search(query
|
24
|
-
# convert coordinates as separate arguments to an array
|
25
|
-
if query.is_a?(Numeric) and args.first.is_a?(Numeric)
|
26
|
-
warn "DEPRECATION WARNING: Instead of passing latitude/longitude as separate arguments to the search method, please pass an array: [#{query},#{args.first}]. The old argument format will not be supported in Geocoder v.1.0."
|
27
|
-
query = [query, args.first]
|
28
|
-
end
|
25
|
+
def search(query)
|
29
26
|
|
30
27
|
# if coordinates given as string, turn into array
|
31
28
|
query = query.split(/\s*,\s*/) if coordinates?(query)
|
@@ -39,9 +36,39 @@ module Geocoder
|
|
39
36
|
results(query, reverse).map{ |r| result_class.new(r) }
|
40
37
|
end
|
41
38
|
|
39
|
+
##
|
40
|
+
# Return the URL for a map of the given coordinates.
|
41
|
+
#
|
42
|
+
# Not necessarily implemented by all subclasses as only some lookups
|
43
|
+
# also provide maps.
|
44
|
+
#
|
45
|
+
def map_link_url(coordinates)
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
42
49
|
|
43
50
|
private # -------------------------------------------------------------
|
44
51
|
|
52
|
+
##
|
53
|
+
# Object used to make HTTP requests.
|
54
|
+
#
|
55
|
+
def http_client
|
56
|
+
protocol = "http#{'s' if Geocoder::Configuration.use_https}"
|
57
|
+
proxy_name = "#{protocol}_proxy"
|
58
|
+
if proxy = Geocoder::Configuration.send(proxy_name)
|
59
|
+
proxy_url = protocol + '://' + proxy
|
60
|
+
begin
|
61
|
+
uri = URI.parse(proxy_url)
|
62
|
+
rescue URI::InvalidURIError
|
63
|
+
raise ConfigurationError,
|
64
|
+
"Error parsing #{protocol.upcase} proxy URL: '#{proxy_url}'"
|
65
|
+
end
|
66
|
+
Net::HTTP::Proxy(uri.host, uri.port, uri.user, uri.password)
|
67
|
+
else
|
68
|
+
Net::HTTP
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
45
72
|
##
|
46
73
|
# Geocoder::Result object or nil on timeout or other error.
|
47
74
|
#
|
@@ -74,7 +101,7 @@ module Geocoder
|
|
74
101
|
rescue TimeoutError
|
75
102
|
warn "Geocoding API not responding fast enough " +
|
76
103
|
"(see Geocoder::Configuration.timeout to set limit)."
|
77
|
-
|
104
|
+
end
|
78
105
|
end
|
79
106
|
|
80
107
|
##
|
@@ -107,7 +134,7 @@ module Geocoder
|
|
107
134
|
timeout(Geocoder::Configuration.timeout) do
|
108
135
|
url = query_url(query, reverse)
|
109
136
|
unless cache and response = cache[url]
|
110
|
-
response =
|
137
|
+
response = http_client.get_response(URI.parse(url)).body
|
111
138
|
if cache
|
112
139
|
cache[url] = response
|
113
140
|
end
|
@@ -134,7 +161,7 @@ module Geocoder
|
|
134
161
|
# Does the given string look like latitude/longitude coordinates?
|
135
162
|
#
|
136
163
|
def coordinates?(value)
|
137
|
-
!!value.to_s.match(
|
164
|
+
value.is_a?(String) and !!value.to_s.match(/^-?[0-9\.]+, *-?[0-9\.]+$/)
|
138
165
|
end
|
139
166
|
|
140
167
|
##
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/bing"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Bing < Base
|
6
|
+
|
7
|
+
def map_link_url(coordinates)
|
8
|
+
"http://www.bing.com/maps/default.aspx?cp=#{coordinates.join('~')}"
|
9
|
+
end
|
10
|
+
|
11
|
+
private # ---------------------------------------------------------------
|
12
|
+
|
13
|
+
def results(query, reverse = false)
|
14
|
+
return [] unless doc = fetch_data(query, reverse)
|
15
|
+
|
16
|
+
if doc['statusDescription'] == "OK"
|
17
|
+
return doc['resourceSets'].first['estimatedTotal'] > 0 ? doc['resourceSets'].first['resources'] : []
|
18
|
+
else
|
19
|
+
warn "Bing Geocoding API error: #{doc['statusCode']} (#{doc['statusDescription']})."
|
20
|
+
return []
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def query_url(query, reverse = false)
|
25
|
+
params = {:key => Geocoder::Configuration.api_key}
|
26
|
+
params[:query] = query unless reverse
|
27
|
+
|
28
|
+
base_url = "http://dev.virtualearth.net/REST/v1/Locations"
|
29
|
+
url_tail = reverse ? "/#{query}?" : "?"
|
30
|
+
base_url + url_tail + hash_to_query(params)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -4,6 +4,10 @@ require "geocoder/results/google"
|
|
4
4
|
module Geocoder::Lookup
|
5
5
|
class Google < Base
|
6
6
|
|
7
|
+
def map_link_url(coordinates)
|
8
|
+
"http://maps.google.com/maps?q=#{coordinates.join(',')}"
|
9
|
+
end
|
10
|
+
|
7
11
|
private # ---------------------------------------------------------------
|
8
12
|
|
9
13
|
def results(query, reverse = false)
|
@@ -4,6 +4,10 @@ require "geocoder/results/yahoo"
|
|
4
4
|
module Geocoder::Lookup
|
5
5
|
class Yahoo < Base
|
6
6
|
|
7
|
+
def map_link_url(coordinates)
|
8
|
+
"http://maps.yahoo.com/#lat=#{coordinates[0]}&lon=#{coordinates[1]}"
|
9
|
+
end
|
10
|
+
|
7
11
|
private # ---------------------------------------------------------------
|
8
12
|
|
9
13
|
def results(query, reverse = false)
|
@@ -18,7 +22,7 @@ module Geocoder::Lookup
|
|
18
22
|
|
19
23
|
def query_url(query, reverse = false)
|
20
24
|
params = {
|
21
|
-
:location =>
|
25
|
+
:location => query,
|
22
26
|
:flags => "JXTSR",
|
23
27
|
:gflags => "AC#{'R' if reverse}",
|
24
28
|
:locale => "#{Geocoder::Configuration.language}_US",
|
@@ -28,4 +32,3 @@ module Geocoder::Lookup
|
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
31
|
-
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/yandex"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Yandex < Base
|
6
|
+
|
7
|
+
def map_link_url(coordinates)
|
8
|
+
"http://maps.yandex.ru/?ll=#{coordinates.reverse.join(',')}"
|
9
|
+
end
|
10
|
+
|
11
|
+
private # ---------------------------------------------------------------
|
12
|
+
|
13
|
+
def results(query, reverse = false)
|
14
|
+
return [] unless doc = fetch_data(query, reverse)
|
15
|
+
if err = doc['error']
|
16
|
+
warn "Yandex Geocoding API error: #{err['status']} (#{err['message']})."
|
17
|
+
return []
|
18
|
+
end
|
19
|
+
if doc = doc['response']['GeoObjectCollection']
|
20
|
+
meta = doc['metaDataProperty']['GeocoderResponseMetaData']
|
21
|
+
return meta['found'].to_i > 0 ? doc['featureMember'] : []
|
22
|
+
else
|
23
|
+
warn "Yandex Geocoding API error: unexpected response format."
|
24
|
+
return []
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def query_url(query, reverse = false)
|
29
|
+
query = query.split(",").reverse.join(",") if reverse
|
30
|
+
params = {
|
31
|
+
:geocode => query,
|
32
|
+
:format => "json",
|
33
|
+
:plng => "#{Geocoder::Configuration.language}", # supports ru, uk, be
|
34
|
+
:key => Geocoder::Configuration.api_key
|
35
|
+
}
|
36
|
+
"http://geocode-maps.yandex.ru/1.x/?" + hash_to_query(params)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/geocoder/models/base.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'geocoder/results/base'
|
2
|
+
|
3
|
+
module Geocoder::Result
|
4
|
+
class Bing < Base
|
5
|
+
|
6
|
+
def address(format = :full)
|
7
|
+
@data['address']['formattedAddress']
|
8
|
+
end
|
9
|
+
|
10
|
+
def city
|
11
|
+
@data['address']['locality']
|
12
|
+
end
|
13
|
+
|
14
|
+
def state_code
|
15
|
+
@data['address']['adminDistrict']
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :state, :state_code
|
19
|
+
|
20
|
+
def country
|
21
|
+
@data['address']['countryRegion']
|
22
|
+
end
|
23
|
+
|
24
|
+
alias_method :country_code, :country
|
25
|
+
|
26
|
+
def postal_code
|
27
|
+
@data['address']['postalCode']
|
28
|
+
end
|
29
|
+
|
30
|
+
def coordinates
|
31
|
+
@data['point']['coordinates']
|
32
|
+
end
|
33
|
+
|
34
|
+
def address_data
|
35
|
+
@data['address']
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.response_attributes
|
39
|
+
%w[bbox name confidence entityType]
|
40
|
+
end
|
41
|
+
|
42
|
+
response_attributes.each do |a|
|
43
|
+
define_method a do
|
44
|
+
@data[a]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -4,13 +4,21 @@ module Geocoder::Result
|
|
4
4
|
class Freegeoip < Base
|
5
5
|
|
6
6
|
def address(format = :full)
|
7
|
-
"#{city}#{', ' +
|
7
|
+
"#{city}#{', ' + state_code unless state_code == ''} #{postal_code}, #{country}".sub(/^[ ,]*/, "")
|
8
8
|
end
|
9
9
|
|
10
10
|
def city
|
11
11
|
@data['city']
|
12
12
|
end
|
13
13
|
|
14
|
+
def state
|
15
|
+
@data['region_name']
|
16
|
+
end
|
17
|
+
|
18
|
+
def state_code
|
19
|
+
@data['region_code']
|
20
|
+
end
|
21
|
+
|
14
22
|
def country
|
15
23
|
@data['country_name']
|
16
24
|
end
|
@@ -24,8 +32,7 @@ module Geocoder::Result
|
|
24
32
|
end
|
25
33
|
|
26
34
|
def self.response_attributes
|
27
|
-
%w[
|
28
|
-
zipcode country_name country_code ip]
|
35
|
+
%w[metrocode ip]
|
29
36
|
end
|
30
37
|
|
31
38
|
response_attributes.each do |a|
|
@@ -8,7 +8,7 @@ module Geocoder::Result
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def address(format = :full)
|
11
|
-
"#{street_address}, #{city}, #{state} #{postal_code}, #{country}"
|
11
|
+
"#{street_address}, #{city}, #{state} #{postal_code}, #{country}".sub(/^[ ,]*/, "")
|
12
12
|
end
|
13
13
|
|
14
14
|
def street_address
|
@@ -23,7 +23,7 @@ module Geocoder::Result
|
|
23
23
|
@data['prov']
|
24
24
|
end
|
25
25
|
|
26
|
-
alias_method :
|
26
|
+
alias_method :state_code, :state
|
27
27
|
|
28
28
|
def postal_code
|
29
29
|
@data['postal']
|
@@ -34,19 +34,14 @@ module Geocoder::Result
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def country_code
|
37
|
-
|
38
|
-
|
39
|
-
canadian_province_abbreviations.include?(@data['prov']) ? "CA" : "US"
|
40
|
-
end
|
41
|
-
|
42
|
-
def canadian_province_abbreviations
|
43
|
-
%w[ON QC NS NB MB BC PE SK AB NL]
|
37
|
+
return nil if state.nil? || state == ""
|
38
|
+
canadian_province_abbreviations.include?(state) ? "CA" : "US"
|
44
39
|
end
|
45
40
|
|
46
41
|
def self.response_attributes
|
47
|
-
%w[latt longt inlatt inlongt
|
48
|
-
|
49
|
-
|
42
|
+
%w[latt longt inlatt inlongt distance stnumber staddress prov
|
43
|
+
NearRoad NearRoadDistance betweenRoad1 betweenRoad2
|
44
|
+
intersection major_intersection]
|
50
45
|
end
|
51
46
|
|
52
47
|
response_attributes.each do |a|
|
@@ -54,5 +49,12 @@ module Geocoder::Result
|
|
54
49
|
@data[a]
|
55
50
|
end
|
56
51
|
end
|
52
|
+
|
53
|
+
|
54
|
+
private # ----------------------------------------------------------------
|
55
|
+
|
56
|
+
def canadian_province_abbreviations
|
57
|
+
%w[ON QC NS NB MB BC PE SK AB NL]
|
58
|
+
end
|
57
59
|
end
|
58
60
|
end
|
@@ -12,8 +12,9 @@ module Geocoder::Result
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def city
|
15
|
-
fields = [:locality, :sublocality,
|
16
|
-
:
|
15
|
+
fields = [:locality, :sublocality,
|
16
|
+
:administrative_area_level_3,
|
17
|
+
:administrative_area_level_2]
|
17
18
|
fields.each do |f|
|
18
19
|
if entity = address_components_of_type(f).first
|
19
20
|
return entity['long_name']
|
@@ -21,6 +22,18 @@ module Geocoder::Result
|
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
25
|
+
def state
|
26
|
+
if state = address_components_of_type(:administrative_area_level_1).first
|
27
|
+
state['long_name']
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def state_code
|
32
|
+
if state = address_components_of_type(:administrative_area_level_1).first
|
33
|
+
state['short_name']
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
24
37
|
def country
|
25
38
|
if country = address_components_of_type(:country).first
|
26
39
|
country['long_name']
|
@@ -11,6 +11,14 @@ module Geocoder::Result
|
|
11
11
|
@data['city']
|
12
12
|
end
|
13
13
|
|
14
|
+
def state
|
15
|
+
@data['state']
|
16
|
+
end
|
17
|
+
|
18
|
+
def state_code
|
19
|
+
@data['statecode']
|
20
|
+
end
|
21
|
+
|
14
22
|
def country
|
15
23
|
@data['country']
|
16
24
|
end
|
@@ -25,8 +33,8 @@ module Geocoder::Result
|
|
25
33
|
|
26
34
|
def self.response_attributes
|
27
35
|
%w[quality offsetlat offsetlon radius boundingbox name
|
28
|
-
line1 line2 line3 line4 cross house street xstreet unittype unit
|
29
|
-
neighborhood
|
36
|
+
line1 line2 line3 line4 cross house street xstreet unittype unit
|
37
|
+
neighborhood county countycode
|
30
38
|
level0 level1 level2 level3 level4 level0code level1code level2code
|
31
39
|
timezone areacode uzip hash woeid woetype]
|
32
40
|
end
|