geokit 1.7.1 → 1.8.0
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.
- 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
|