geokit 1.7.1 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +6 -14
- data/.travis.yml +1 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +2 -1
- data/MIT-LICENSE +20 -0
- data/README.markdown +44 -39
- data/Rakefile +15 -0
- data/fixtures/vcr_cassettes/bing_full.yml +102 -0
- data/fixtures/vcr_cassettes/bing_full_au.yml +91 -0
- data/fixtures/vcr_cassettes/bing_full_de.yml +91 -0
- data/fixtures/vcr_cassettes/fcc_reverse_geocode.yml +37 -0
- data/fixtures/vcr_cassettes/free_geo_ip_geocode.yml +36 -0
- data/fixtures/vcr_cassettes/geo_plugin_geocode.yml +38 -0
- data/fixtures/vcr_cassettes/geonames_geocode.yml +304 -0
- data/fixtures/vcr_cassettes/{google3_city.yml → google_city.yml} +0 -0
- data/fixtures/vcr_cassettes/{google3_country_code_biased_result.yml → google_country_code_biased_result.yml} +0 -0
- data/fixtures/vcr_cassettes/{google3_full.yml → google_full.yml} +0 -0
- data/fixtures/vcr_cassettes/{google3_full_short.yml → google_full_short.yml} +0 -0
- data/fixtures/vcr_cassettes/{google3_language_response_fr.yml → google_language_response_fr.yml} +0 -0
- data/fixtures/vcr_cassettes/{google3_multi.yml → google_multi.yml} +0 -0
- data/fixtures/vcr_cassettes/{google3_reverse_madrid.yml → google_reverse_madrid.yml} +0 -0
- data/fixtures/vcr_cassettes/ripe_geocode.yml +66 -0
- data/fixtures/vcr_cassettes/ripe_geocode_au.yml +66 -0
- data/geokit.gemspec +1 -1
- data/lib/geokit.rb +5 -0
- data/lib/geokit/bounds.rb +96 -0
- data/lib/geokit/core_ext.rb +17 -0
- data/lib/geokit/geo_loc.rb +134 -0
- data/lib/geokit/geocoders.rb +48 -35
- data/lib/geokit/geocoders/base_ip.rb +43 -0
- data/lib/geokit/geocoders/bing.rb +101 -0
- data/lib/geokit/geocoders/ca_geocoder.rb +50 -0
- data/lib/geokit/{services → geocoders}/fcc.rb +17 -20
- data/lib/geokit/geocoders/free_geo_ip.rb +34 -0
- data/lib/geokit/geocoders/geo_plugin.rb +33 -0
- data/lib/geokit/geocoders/geonames.rb +53 -0
- data/lib/geokit/{services/google3.rb → geocoders/google.rb} +59 -57
- data/lib/geokit/geocoders/ip.rb +69 -0
- data/lib/geokit/geocoders/mapquest.rb +72 -0
- data/lib/geokit/geocoders/maxmind.rb +29 -0
- data/lib/geokit/geocoders/openstreetmap.rb +119 -0
- data/lib/geokit/geocoders/ripe.rb +41 -0
- data/lib/geokit/{services → geocoders}/us_geocoder.rb +15 -20
- data/lib/geokit/{services → geocoders}/yahoo.rb +52 -55
- data/lib/geokit/geocoders/yandex.rb +61 -0
- data/lib/geokit/inflectors.rb +1 -2
- data/lib/geokit/lat_lng.rb +129 -0
- data/lib/geokit/mappable.rb +41 -424
- data/lib/geokit/multi_geocoder.rb +6 -2
- data/lib/geokit/polygon.rb +46 -0
- data/lib/geokit/version.rb +1 -1
- data/test/helper.rb +2 -12
- data/test/test_base_geocoder.rb +0 -10
- data/test/test_bing_geocoder.rb +60 -0
- data/test/test_fcc_geocoder.rb +23 -0
- data/test/test_free_geo_ip_geocoder.rb +23 -0
- data/test/test_geo_plugin_geocoder.rb +23 -0
- data/test/test_geonames_geocoder.rb +23 -0
- data/test/test_google_geocoder.rb +208 -235
- data/test/test_maxmind_geocoder.rb +35 -4
- data/test/test_multi_geocoder.rb +3 -1
- data/test/test_ripe_geocoder.rb +35 -0
- data/test/test_yahoo_geocoder.rb +0 -12
- metadata +78 -52
- data/LICENSE +0 -25
- data/Manifest.txt +0 -21
- data/data/GeoLiteCity.dat +0 -0
- data/lib/geokit/services/ca_geocoder.rb +0 -55
- data/lib/geokit/services/geo_plugin.rb +0 -31
- data/lib/geokit/services/geonames.rb +0 -53
- data/lib/geokit/services/google.rb +0 -158
- data/lib/geokit/services/ip.rb +0 -103
- data/lib/geokit/services/maxmind.rb +0 -39
- data/lib/geokit/services/openstreetmap.rb +0 -119
- data/lib/geokit/services/ripe.rb +0 -32
- data/lib/geokit/services/yandex.rb +0 -51
- data/test/test_google_geocoder3.rb +0 -238
- data/test/test_google_reverse_geocoder.rb +0 -49
@@ -0,0 +1,69 @@
|
|
1
|
+
module Geokit
|
2
|
+
module Geocoders
|
3
|
+
# Provides geocoding based upon an IP address. The underlying web service is a hostip.info
|
4
|
+
# which sources their data through a combination of publicly available information as well
|
5
|
+
# as community contributions.
|
6
|
+
class IpGeocoder < BaseIpGeocoder
|
7
|
+
private
|
8
|
+
|
9
|
+
# Given an IP address, returns a GeoLoc instance which contains latitude,
|
10
|
+
# longitude, city, and country code. Sets the success attribute to false if the ip
|
11
|
+
# parameter does not match an ip address.
|
12
|
+
def self.do_geocode(ip)
|
13
|
+
return GeoLoc.new unless valid_ip?(ip)
|
14
|
+
url = "http://api.hostip.info/get_html.php?ip=#{ip}&position=true"
|
15
|
+
res = call_geocoder_service(url)
|
16
|
+
ensure_utf8_encoding(res)
|
17
|
+
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
18
|
+
body = res.body
|
19
|
+
body = body.encode('UTF-8') if body.respond_to? :encode
|
20
|
+
parse :yaml, body
|
21
|
+
end
|
22
|
+
|
23
|
+
# Converts the body to YAML since its in the form of:
|
24
|
+
#
|
25
|
+
# Country: UNITED STATES (US)
|
26
|
+
# City: Sugar Grove, IL
|
27
|
+
# Latitude: 41.7696
|
28
|
+
# Longitude: -88.4588
|
29
|
+
#
|
30
|
+
# then instantiates a GeoLoc instance to populate with location data.
|
31
|
+
def self.parse_yaml(yaml) # :nodoc:
|
32
|
+
loc = GeoLoc.new
|
33
|
+
loc.provider = 'hostip'
|
34
|
+
loc.city, loc.state = yaml['City'].split(', ')
|
35
|
+
loc.country, loc.country_code = yaml['Country'].split(' (')
|
36
|
+
loc.lat = yaml['Latitude']
|
37
|
+
loc.lng = yaml['Longitude']
|
38
|
+
loc.country_code.chop!
|
39
|
+
loc.success = !(loc.city =~ /\(.+\)/)
|
40
|
+
loc
|
41
|
+
end
|
42
|
+
|
43
|
+
# Forces UTF-8 encoding on the body
|
44
|
+
# Rails expects string input to be UTF-8
|
45
|
+
# hostip.info specifies the charset encoding in the headers
|
46
|
+
# thus extract encoding from headers and tell Rails about it by forcing it
|
47
|
+
def self.ensure_utf8_encoding(res)
|
48
|
+
if (enc_string = extract_charset(res))
|
49
|
+
if defined?(Encoding) && Encoding.aliases.values.include?(enc_string.upcase)
|
50
|
+
res.body.force_encoding(enc_string.upcase) if res.body.respond_to?(:force_encoding)
|
51
|
+
res.body.encode("UTF-8")
|
52
|
+
else
|
53
|
+
require 'iconv'
|
54
|
+
res.body.replace Iconv.conv("UTF8", "iso88591", res.body)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Extracts charset out of the response headers
|
60
|
+
def self.extract_charset(res)
|
61
|
+
if (content_type = res['content-type'])
|
62
|
+
capture = content_type.match(/charset=(.+)/)
|
63
|
+
capture && capture[1]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Geokit
|
2
|
+
module Geocoders
|
3
|
+
# MapQuest geocoder implementation. Requires the Geokit::Geocoders::mapquest variable to
|
4
|
+
# contain a MapQuest API key. Conforms to the interface set by the Geocoder class.
|
5
|
+
class MapQuestGeocoder < Geocoder
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
# Template method which does the reverse-geocode lookup.
|
10
|
+
def self.do_reverse_geocode(latlng)
|
11
|
+
latlng=LatLng.normalize(latlng)
|
12
|
+
url = "http://www.mapquestapi.com/geocoding/v1/reverse?key=#{Geokit::Geocoders::mapquest}&location=#{latlng.lat},#{latlng.lng}"
|
13
|
+
res = call_geocoder_service(url)
|
14
|
+
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
15
|
+
json = res.body
|
16
|
+
logger.debug "MapQuest reverse-geocoding. LL: #{latlng}. Result: #{json}"
|
17
|
+
parse :json, json, latlng
|
18
|
+
end
|
19
|
+
|
20
|
+
# Template method which does the geocode lookup.
|
21
|
+
def self.do_geocode(address)
|
22
|
+
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
23
|
+
url = "http://www.mapquestapi.com/geocoding/v1/address?key=#{Geokit::Geocoders::mapquest}&location=#{Geokit::Inflector::url_escape(address_str)}"
|
24
|
+
res = call_geocoder_service(url)
|
25
|
+
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
26
|
+
json = res.body
|
27
|
+
logger.debug "Mapquest geocoding. Address: #{address}. Result: #{json}"
|
28
|
+
parse :json, json
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.parse_json(results)
|
32
|
+
return GeoLoc.new unless results['info']['statuscode'] == 0
|
33
|
+
loc = nil
|
34
|
+
results['results'].each do |result|
|
35
|
+
result['locations'].each do |location|
|
36
|
+
extracted_geoloc = extract_geoloc(location)
|
37
|
+
if loc.nil?
|
38
|
+
loc = extracted_geoloc
|
39
|
+
else
|
40
|
+
loc.all.push(extracted_geoloc)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
loc
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.extract_geoloc(result_json)
|
48
|
+
loc = GeoLoc.new
|
49
|
+
loc.lat = result_json['latLng']['lat']
|
50
|
+
loc.lng = result_json['latLng']['lng']
|
51
|
+
loc.provider = 'mapquest'
|
52
|
+
set_address_components(result_json, loc)
|
53
|
+
set_precision(result_json, loc)
|
54
|
+
loc.success = true
|
55
|
+
loc
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.set_address_components(result_json, loc)
|
59
|
+
loc.country_code = result_json['adminArea1']
|
60
|
+
loc.street_address = result_json['street'].to_s.empty? ? nil : result_json['street']
|
61
|
+
loc.city = result_json['adminArea5']
|
62
|
+
loc.state = result_json['adminArea3']
|
63
|
+
loc.zip = result_json['postalCode']
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.set_precision(result_json, loc)
|
67
|
+
loc.precision = result_json['geocodeQuality']
|
68
|
+
loc.accuracy = %w{unknown country state state city zip zip+4 street address building}.index(loc.precision)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Geokit
|
2
|
+
module Geocoders
|
3
|
+
|
4
|
+
@@geoip_data_path = 'DEFINE_THE_PATH_TO_GeoLiteCity.dat'
|
5
|
+
__define_accessors
|
6
|
+
|
7
|
+
# Provides geocoding based upon an IP address. The underlying web service is MaxMind
|
8
|
+
class MaxmindGeocoder < Geocoder
|
9
|
+
private
|
10
|
+
|
11
|
+
def self.do_geocode(ip)
|
12
|
+
res = GeoIP.new(Geokit::Geocoders::geoip_data_path).city(ip)
|
13
|
+
|
14
|
+
loc = GeoLoc.new(
|
15
|
+
:provider => 'maxmind_city',
|
16
|
+
:lat => res.latitude,
|
17
|
+
:lng => res.longitude,
|
18
|
+
:city => res.city_name,
|
19
|
+
:state => res.region_name,
|
20
|
+
:zip => res.postal_code,
|
21
|
+
:country_code => res.country_code2
|
22
|
+
)
|
23
|
+
|
24
|
+
loc.success = ( res.longitude.kind_of?(Numeric) && res.latitude.kind_of?(Numeric) )
|
25
|
+
loc
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Geokit
|
2
|
+
module Geocoders
|
3
|
+
# Open Street Map geocoder implementation.
|
4
|
+
class OSMGeocoder < Geocoder
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
# Template method which does the geocode lookup.
|
9
|
+
def self.do_geocode(address, options = {})
|
10
|
+
options_str = generate_bool_param_for_option(:polygon, options)
|
11
|
+
options_str << generate_param_for_option(:json_callback, options)
|
12
|
+
options_str << generate_param_for_option(:countrycodes, options)
|
13
|
+
options_str << generate_param_for_option(:viewbox, options)
|
14
|
+
|
15
|
+
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
16
|
+
|
17
|
+
url = "http://nominatim.openstreetmap.org/search?format=json#{options_str}&addressdetails=1&q=#{Geokit::Inflector::url_escape(address_str)}"
|
18
|
+
res = call_geocoder_service(url)
|
19
|
+
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
20
|
+
json = res.body
|
21
|
+
logger.debug "OSM geocoding. Address: #{address}. Result: #{json}"
|
22
|
+
parse :json, json
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.do_reverse_geocode(latlng, options = {})
|
26
|
+
latlng = LatLng.normalize(latlng)
|
27
|
+
options_str = generate_param_for(:lat, latlng.lat)
|
28
|
+
options_str << generate_param_for(:lon, latlng.lng)
|
29
|
+
options_str << generate_param_for_option(:zoom, options)
|
30
|
+
options_str << generate_param_for_option(:osm_type, options)
|
31
|
+
options_str << generate_param_for_option(:osm_id, options)
|
32
|
+
options_str << generate_param_for_option(:json_callback, options)
|
33
|
+
url = "http://nominatim.openstreetmap.org/reverse?format=json&addressdetails=1#{options_str}"
|
34
|
+
res = call_geocoder_service(url)
|
35
|
+
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
36
|
+
json = res.body
|
37
|
+
logger.debug "OSM reverse geocoding: Lat: #{latlng.lat}, Lng: #{latlng.lng}. Result: #{json}"
|
38
|
+
parse :json, json
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.generate_param_for(param, value)
|
42
|
+
"&#{param}=#{Geokit::Inflector::url_escape(value.to_s)}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.generate_param_for_option(param, options)
|
46
|
+
options[param] ? "&#{param}=#{Geokit::Inflector::url_escape(options[param])}" : ''
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.generate_bool_param_for_option(param, options)
|
50
|
+
options[param] ? "&#{param}=1" : "&#{param}=0"
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.parse_json(results)
|
54
|
+
if results.is_a?(Hash)
|
55
|
+
return GeoLoc.new if results['error']
|
56
|
+
results = [results]
|
57
|
+
end
|
58
|
+
return GeoLoc.new if results.empty?
|
59
|
+
|
60
|
+
loc = nil
|
61
|
+
results.each do |result|
|
62
|
+
extract_geoloc = extract_geoloc(result)
|
63
|
+
if loc.nil?
|
64
|
+
loc = extract_geoloc
|
65
|
+
else
|
66
|
+
loc.all.push(extract_geoloc)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
loc
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.extract_geoloc(result_json)
|
73
|
+
loc = GeoLoc.new
|
74
|
+
|
75
|
+
# basic
|
76
|
+
loc.lat = result_json['lat']
|
77
|
+
loc.lng = result_json['lon']
|
78
|
+
|
79
|
+
loc.provider = 'osm'
|
80
|
+
|
81
|
+
set_address_components(result_json['address'], loc)
|
82
|
+
set_precision(result_json, loc)
|
83
|
+
set_bounds(result_json['boundingbox'], loc)
|
84
|
+
loc.success = true
|
85
|
+
|
86
|
+
loc
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.set_address_components(address_data, loc)
|
90
|
+
return unless address_data
|
91
|
+
loc.country = address_data['country']
|
92
|
+
loc.country_code = address_data['country_code'].upcase if address_data['country_code']
|
93
|
+
loc.state = address_data['state']
|
94
|
+
loc.city = address_data['city']
|
95
|
+
loc.city = address_data['county'] if loc.city.nil? && address_data['county']
|
96
|
+
loc.zip = address_data['postcode']
|
97
|
+
loc.district = address_data['city_district']
|
98
|
+
loc.district = address_data['state_district'] if loc.district.nil? && address_data['state_district']
|
99
|
+
loc.street_address = "#{address_data['road']} #{address_data['house_number']}".strip if address_data['road']
|
100
|
+
loc.street_name = address_data['road']
|
101
|
+
loc.street_number = address_data['house_number']
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.set_precision(result_json, loc)
|
105
|
+
# Todo accuracy does not work as Yahoo and Google maps on OSM
|
106
|
+
#loc.accuracy = %w{unknown amenity building highway historic landuse leisure natural place railway shop tourism waterway man_made}.index(loc.precision)
|
107
|
+
loc.precision = result_json['class']
|
108
|
+
loc.accuracy = result_json['type']
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.set_bounds(result_json, loc)
|
112
|
+
return unless result_json
|
113
|
+
loc.suggested_bounds = Bounds.normalize(
|
114
|
+
[result_json[0], result_json[1]],
|
115
|
+
[result_json[2], result_json[3]])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Geokit
|
2
|
+
module Geocoders
|
3
|
+
# Provides geocoding based upon an IP address. The underlying web service is geoplugin.net
|
4
|
+
class RipeGeocoder < BaseIpGeocoder
|
5
|
+
private
|
6
|
+
|
7
|
+
def self.do_geocode(ip)
|
8
|
+
return GeoLoc.new unless valid_ip?(ip)
|
9
|
+
url = "http://stat.ripe.net/data/geoloc/data.json?resource=#{ip}"
|
10
|
+
res = call_geocoder_service(url)
|
11
|
+
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
12
|
+
parse :json, res.body
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.parse_json(json)
|
16
|
+
loc = GeoLoc.new
|
17
|
+
data = json['data']['locations'][0]
|
18
|
+
|
19
|
+
loc.provider='RIPE'
|
20
|
+
loc.lat = data['latitude']
|
21
|
+
loc.lng = data['longitude']
|
22
|
+
set_address_components(data, loc)
|
23
|
+
loc.success = (data['status_code'] == 200)
|
24
|
+
loc
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.set_address_components(data, loc)
|
28
|
+
match = data['country'].match /([A-Z]+)(\(([A-Z]+)\))?/
|
29
|
+
if match[3]
|
30
|
+
loc.state = match[1]
|
31
|
+
loc.country_code = match[3]
|
32
|
+
else
|
33
|
+
loc.country_code = match[1]
|
34
|
+
end
|
35
|
+
|
36
|
+
loc.city = data['city']
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -6,7 +6,7 @@ module Geokit
|
|
6
6
|
class UsGeocoder < Geocoder
|
7
7
|
|
8
8
|
private
|
9
|
-
def self.do_geocode(address
|
9
|
+
def self.do_geocode(address)
|
10
10
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
11
11
|
|
12
12
|
query = (address_str =~ /^\d{5}(?:-\d{4})?$/ ? "zip" : "address") + "=#{Geokit::Inflector::url_escape(address_str)}"
|
@@ -17,34 +17,29 @@ module Geokit
|
|
17
17
|
end
|
18
18
|
|
19
19
|
url = "#{url}?#{query}"
|
20
|
-
res =
|
20
|
+
res = call_geocoder_service(url)
|
21
21
|
|
22
22
|
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
23
23
|
data = res.body
|
24
24
|
logger.debug "Geocoder.us geocoding. Address: #{address}. Result: #{data}"
|
25
|
+
parse_csv data
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.parse_csv(data)
|
25
29
|
array = data.chomp.split(',')
|
26
30
|
|
31
|
+
loc = GeoLoc.new
|
27
32
|
if array.length == 5
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
res.success=true
|
32
|
-
return res
|
33
|
+
loc.lat,loc.lng,loc.city,loc.state,loc.zip=array
|
34
|
+
loc.country_code='US'
|
35
|
+
loc.success=true
|
33
36
|
elsif array.length == 6
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
res.success=true
|
38
|
-
return res
|
39
|
-
else
|
40
|
-
logger.info "geocoder.us was unable to geocode address: "+address
|
41
|
-
return GeoLoc.new
|
37
|
+
loc.lat,loc.lng,loc.street_address,loc.city,loc.state,loc.zip=array
|
38
|
+
loc.country_code='US'
|
39
|
+
loc.success=true
|
42
40
|
end
|
43
|
-
|
44
|
-
logger.error "Caught an error during geocoder.us geocoding call: "+$!
|
45
|
-
return GeoLoc.new
|
46
|
-
|
41
|
+
loc
|
47
42
|
end
|
48
43
|
end
|
49
|
-
|
44
|
+
end
|
50
45
|
end
|
@@ -5,79 +5,77 @@ module Geokit
|
|
5
5
|
class YahooGeocoder < Geocoder
|
6
6
|
|
7
7
|
private
|
8
|
-
def self.submit_url(
|
8
|
+
def self.submit_url(address)
|
9
|
+
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
10
|
+
query_string = "?q=#{Geokit::Inflector::url_escape(address_str)}&flags=J"
|
11
|
+
|
9
12
|
o = OauthUtil.new
|
10
13
|
o.consumer_key = Geocoders::yahoo_consumer_key
|
11
14
|
o.consumer_secret = Geocoders::yahoo_consumer_secret
|
12
15
|
base = "http://yboss.yahooapis.com/geo/placefinder"
|
13
16
|
parsed_url = URI.parse("#{base}#{query_string}")
|
14
|
-
"
|
17
|
+
"#{base}?#{o.sign(parsed_url).query_string}"
|
15
18
|
end
|
16
19
|
|
17
20
|
# Template method which does the geocode lookup.
|
18
|
-
def self.do_geocode(address
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
res = self.call_geocoder_service(submit_url)
|
21
|
+
def self.do_geocode(address)
|
22
|
+
url = submit_url(address)
|
23
|
+
res = call_geocoder_service(url)
|
23
24
|
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
24
25
|
json = res.body
|
25
26
|
logger.debug "Yahoo geocoding. Address: #{address}. Result: #{json}"
|
26
|
-
|
27
|
+
parse :json, json
|
27
28
|
end
|
28
29
|
|
29
|
-
def self.
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
geoloc.all.push(extracted_geoloc)
|
40
|
-
end
|
30
|
+
def self.parse_json(results)
|
31
|
+
boss_results = results && results['bossresponse'] && results['bossresponse']['placefinder'] && results['bossresponse']['placefinder']['results']
|
32
|
+
return GeoLoc.new unless boss_results && boss_results.first
|
33
|
+
loc = nil
|
34
|
+
boss_results.each do |result|
|
35
|
+
extracted_geoloc = extract_geoloc(result)
|
36
|
+
if loc.nil?
|
37
|
+
loc = extracted_geoloc
|
38
|
+
else
|
39
|
+
loc.all.push(extracted_geoloc)
|
41
40
|
end
|
42
|
-
return geoloc
|
43
|
-
else
|
44
|
-
logger.info "Yahoo was unable to geocode address: " + address
|
45
|
-
return GeoLoc.new
|
46
41
|
end
|
42
|
+
loc
|
47
43
|
end
|
48
44
|
|
49
45
|
def self.extract_geoloc(result_json)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
geoloc.street_address = result_json['line1'].to_s.empty? ? nil : result_json['line1']
|
60
|
-
geoloc.city = result_json['city']
|
61
|
-
geoloc.state = geoloc.is_us? ? result_json['statecode'] : result_json['state']
|
62
|
-
geoloc.zip = result_json['postal']
|
46
|
+
loc = GeoLoc.new
|
47
|
+
loc.lat = result_json['latitude']
|
48
|
+
loc.lng = result_json['longitude']
|
49
|
+
loc.provider = 'yahoo'
|
50
|
+
set_address_components(result_json, loc)
|
51
|
+
set_precision(result_json, loc)
|
52
|
+
loc.success = true
|
53
|
+
loc
|
54
|
+
end
|
63
55
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
when 70..72 then 'street'
|
72
|
-
when 80..87 then 'address'
|
73
|
-
when 62,63,90,99 then 'building'
|
74
|
-
else 'unknown'
|
75
|
-
end
|
56
|
+
def self.set_address_components(result_json, loc)
|
57
|
+
loc.country_code = result_json['countrycode']
|
58
|
+
loc.street_address = result_json['line1'].to_s.empty? ? nil : result_json['line1']
|
59
|
+
loc.city = result_json['city']
|
60
|
+
loc.state = loc.is_us? ? result_json['statecode'] : result_json['state']
|
61
|
+
loc.zip = result_json['postal']
|
62
|
+
end
|
76
63
|
|
77
|
-
|
78
|
-
|
64
|
+
def self.set_precision(result_json, loc)
|
65
|
+
loc.precision = case result_json['quality'].to_i
|
66
|
+
when 9,10 then 'country'
|
67
|
+
when 19..30 then 'state'
|
68
|
+
when 39,40 then 'city'
|
69
|
+
when 49,50 then 'neighborhood'
|
70
|
+
when 59,60,64 then 'zip'
|
71
|
+
when 74,75 then 'zip+4'
|
72
|
+
when 70..72 then 'street'
|
73
|
+
when 80..87 then 'address'
|
74
|
+
when 62,63,90,99 then 'building'
|
75
|
+
else 'unknown'
|
76
|
+
end
|
79
77
|
|
80
|
-
|
78
|
+
loc.accuracy = %w{unknown country state state city zip zip+4 street address building}.index(loc.precision)
|
81
79
|
end
|
82
80
|
end
|
83
81
|
end
|
@@ -118,9 +116,8 @@ class OauthUtil
|
|
118
116
|
end
|
119
117
|
|
120
118
|
def percent_encode( string )
|
121
|
-
|
122
119
|
# ref http://snippets.dzone.com/posts/show/1260
|
123
|
-
|
120
|
+
URI.escape( string, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]") ).gsub('*', '%2A')
|
124
121
|
end
|
125
122
|
|
126
123
|
# @ref http://oauth.net/core/1.0/#rfc.section.9.2
|
@@ -190,6 +187,6 @@ class OauthUtil
|
|
190
187
|
# add signature
|
191
188
|
@params[ 'oauth_signature' ] = signature
|
192
189
|
|
193
|
-
|
190
|
+
self
|
194
191
|
end
|
195
192
|
end
|