geokit 1.6.5 → 1.6.6
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 +15 -0
- data/.gitignore +9 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +92 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/Manifest.txt +21 -0
- data/README.markdown +179 -176
- data/Rakefile +8 -1
- data/ext/mkrf_conf.rb +15 -0
- data/geokit.gemspec +33 -0
- data/lib/geokit/geocoders.rb +7 -774
- data/lib/geokit/inflectors.rb +38 -0
- data/lib/geokit/mappable.rb +61 -3
- data/lib/geokit/multi_geocoder.rb +61 -0
- data/lib/geokit/services/ca_geocoder.rb +55 -0
- data/lib/geokit/services/fcc.rb +57 -0
- data/lib/geokit/services/geo_plugin.rb +31 -0
- data/lib/geokit/services/geonames.rb +53 -0
- data/lib/geokit/services/google.rb +158 -0
- data/lib/geokit/services/google3.rb +202 -0
- data/lib/geokit/services/ip.rb +103 -0
- data/lib/geokit/services/openstreetmap.rb +119 -0
- data/lib/geokit/services/us_geocoder.rb +50 -0
- data/lib/geokit/services/yahoo.rb +75 -0
- data/lib/geokit/version.rb +3 -0
- data/test/helper.rb +92 -0
- data/test/test_base_geocoder.rb +1 -15
- data/test/test_bounds.rb +1 -2
- data/test/test_ca_geocoder.rb +1 -1
- data/test/test_geoloc.rb +35 -5
- data/test/test_geoplugin_geocoder.rb +1 -2
- data/test/test_google_geocoder.rb +39 -2
- data/test/test_google_geocoder3.rb +55 -3
- data/test/test_google_reverse_geocoder.rb +1 -1
- data/test/test_inflector.rb +5 -3
- data/test/test_ipgeocoder.rb +25 -1
- data/test/test_latlng.rb +1 -3
- data/test/test_multi_geocoder.rb +1 -1
- data/test/test_multi_ip_geocoder.rb +1 -1
- data/test/test_openstreetmap_geocoder.rb +161 -0
- data/test/test_polygon_contains.rb +101 -0
- data/test/test_us_geocoder.rb +1 -1
- data/test/test_yahoo_geocoder.rb +18 -1
- metadata +164 -83
@@ -0,0 +1,202 @@
|
|
1
|
+
module Geokit
|
2
|
+
module Geocoders
|
3
|
+
class GoogleGeocoder3 < Geocoder
|
4
|
+
|
5
|
+
private
|
6
|
+
# Template method which does the reverse-geocode lookup.
|
7
|
+
def self.do_reverse_geocode(latlng)
|
8
|
+
latlng=LatLng.normalize(latlng)
|
9
|
+
submit_url = submit_url("/maps/api/geocode/json?sensor=false&latlng=#{Geokit::Inflector::url_escape(latlng.ll)}")
|
10
|
+
res = self.call_geocoder_service(submit_url)
|
11
|
+
return GeoLoc.new unless (res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPOK))
|
12
|
+
json = res.body
|
13
|
+
logger.debug "Google reverse-geocoding. LL: #{latlng}. Result: #{CGI.escape(json)}"
|
14
|
+
return self.json2GeoLoc(json)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Template method which does the geocode lookup.
|
18
|
+
#
|
19
|
+
# Supports viewport/country code biasing
|
20
|
+
#
|
21
|
+
# ==== OPTIONS
|
22
|
+
# * :bias - This option makes the Google Geocoder return results biased to a particular
|
23
|
+
# country or viewport. Country code biasing is achieved by passing the ccTLD
|
24
|
+
# ('uk' for .co.uk, for example) as a :bias value. For a list of ccTLD's,
|
25
|
+
# look here: http://en.wikipedia.org/wiki/CcTLD. By default, the geocoder
|
26
|
+
# will be biased to results within the US (ccTLD .com).
|
27
|
+
#
|
28
|
+
# If you'd like the Google Geocoder to prefer results within a given viewport,
|
29
|
+
# you can pass a Geokit::Bounds object as the :bias value.
|
30
|
+
#
|
31
|
+
# ==== EXAMPLES
|
32
|
+
# # By default, the geocoder will return Syracuse, NY
|
33
|
+
# Geokit::Geocoders::GoogleGeocoder.geocode('Syracuse').country_code # => 'US'
|
34
|
+
# # With country code biasing, it returns Syracuse in Sicily, Italy
|
35
|
+
# Geokit::Geocoders::GoogleGeocoder.geocode('Syracuse', :bias => :it).country_code # => 'IT'
|
36
|
+
#
|
37
|
+
# # By default, the geocoder will return Winnetka, IL
|
38
|
+
# Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka').state # => 'IL'
|
39
|
+
# # When biased to an bounding box around California, it will now return the Winnetka neighbourhood, CA
|
40
|
+
# bounds = Geokit::Bounds.normalize([34.074081, -118.694401], [34.321129, -118.399487])
|
41
|
+
# Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka', :bias => bounds).state # => 'CA'
|
42
|
+
def self.do_geocode(address, options = {})
|
43
|
+
bias_str = options[:bias] ? construct_bias_string_from_options(options[:bias]) : ''
|
44
|
+
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
45
|
+
submit_url = submit_url("/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector::url_escape(address_str)}#{bias_str}")
|
46
|
+
|
47
|
+
res = self.call_geocoder_service(submit_url)
|
48
|
+
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
49
|
+
|
50
|
+
json = res.body
|
51
|
+
logger.debug "Google geocoding. Address: #{address}. Result: #{CGI.escape(json)}"
|
52
|
+
|
53
|
+
return self.json2GeoLoc(json, address)
|
54
|
+
end
|
55
|
+
|
56
|
+
# This code comes from Googles Examples
|
57
|
+
# http://gmaps-samples.googlecode.com/svn/trunk/urlsigning/urlsigner.rb
|
58
|
+
def self.sign_gmap_bus_api_url(urlToSign, google_cryptographic_key)
|
59
|
+
require 'base64'
|
60
|
+
require 'openssl'
|
61
|
+
# Decode the private key
|
62
|
+
rawKey = Base64.decode64(google_cryptographic_key.tr('-_','+/'))
|
63
|
+
# create a signature using the private key and the URL
|
64
|
+
rawSignature = OpenSSL::HMAC.digest('sha1', rawKey, urlToSign)
|
65
|
+
# encode the signature into base64 for url use form.
|
66
|
+
return Base64.encode64(rawSignature).tr('+/','-_').gsub(/\n/, '')
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def self.submit_url(query_string)
|
71
|
+
if !Geokit::Geocoders::google_client_id.nil? and !Geokit::Geocoders::google_cryptographic_key.nil?
|
72
|
+
urlToSign = query_string + "&client=#{Geokit::Geocoders::google_client_id}" + "#{(!Geokit::Geocoders::google_channel.nil? ? ("&channel="+ Geokit::Geocoders::google_channel) : "")}"
|
73
|
+
signature = sign_gmap_bus_api_url(urlToSign, Geokit::Geocoders::google_cryptographic_key)
|
74
|
+
"http://maps.googleapis.com" + urlToSign + "&signature=#{signature}"
|
75
|
+
else
|
76
|
+
"http://maps.google.com" + query_string
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def self.construct_bias_string_from_options(bias)
|
82
|
+
case bias
|
83
|
+
when String, Symbol
|
84
|
+
# country code biasing
|
85
|
+
"®ion=#{bias.to_s.downcase}"
|
86
|
+
when Bounds
|
87
|
+
# viewport biasing
|
88
|
+
url_escaped_string = Geokit::Inflector::url_escape("#{bias.sw.to_s}|#{bias.ne.to_s}")
|
89
|
+
"&bounds=#{url_escaped_string}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.json2GeoLoc(json, address="")
|
94
|
+
results = MultiJson.load(json)
|
95
|
+
|
96
|
+
case results['status']
|
97
|
+
when 'OVER_QUERY_LIMIT' then raise Geokit::TooManyQueriesError
|
98
|
+
when 'ZERO_RESULTS' then return GeoLoc.new
|
99
|
+
end
|
100
|
+
# this should probably be smarter.
|
101
|
+
if results['status'] != 'OK'
|
102
|
+
raise Geokit::Geocoders::GeocodeError
|
103
|
+
end
|
104
|
+
|
105
|
+
unsorted = results['results'].map do |addr|
|
106
|
+
single_json_to_geoloc(addr)
|
107
|
+
end
|
108
|
+
|
109
|
+
all = unsorted.sort_by(&:accuracy).reverse
|
110
|
+
encoded = all.first
|
111
|
+
encoded.all = all
|
112
|
+
encoded
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
# location_type stores additional data about the specified location.
|
117
|
+
# The following values are currently supported:
|
118
|
+
# "ROOFTOP" indicates that the returned result is a precise geocode
|
119
|
+
# for which we have location information accurate down to street
|
120
|
+
# address precision.
|
121
|
+
# "RANGE_INTERPOLATED" indicates that the returned result reflects an
|
122
|
+
# approximation (usually on a road) interpolated between two precise
|
123
|
+
# points (such as intersections). Interpolated results are generally
|
124
|
+
# returned when rooftop geocodes are unavailable for a street address.
|
125
|
+
# "GEOMETRIC_CENTER" indicates that the returned result is the
|
126
|
+
# geometric center of a result such as a polyline (for example, a
|
127
|
+
# street) or polygon (region).
|
128
|
+
# "APPROXIMATE" indicates that the returned result is approximate
|
129
|
+
|
130
|
+
# these do not map well. Perhaps we should guess better based on size
|
131
|
+
# of bounding box where it exists? Does it really matter?
|
132
|
+
ACCURACY = {
|
133
|
+
"ROOFTOP" => 9,
|
134
|
+
"RANGE_INTERPOLATED" => 8,
|
135
|
+
"GEOMETRIC_CENTER" => 5,
|
136
|
+
"APPROXIMATE" => 4
|
137
|
+
}
|
138
|
+
|
139
|
+
def self.single_json_to_geoloc(addr)
|
140
|
+
res = GeoLoc.new
|
141
|
+
res.provider = 'google3'
|
142
|
+
res.success = true
|
143
|
+
res.full_address = addr['formatted_address']
|
144
|
+
|
145
|
+
addr['address_components'].each do |comp|
|
146
|
+
case
|
147
|
+
when comp['types'].include?("subpremise")
|
148
|
+
res.sub_premise = comp['short_name']
|
149
|
+
when comp['types'].include?("street_number")
|
150
|
+
res.street_number = comp['short_name']
|
151
|
+
when comp['types'].include?("route")
|
152
|
+
res.street_name = comp['long_name']
|
153
|
+
when comp['types'].include?("locality")
|
154
|
+
res.city = comp['long_name']
|
155
|
+
when comp['types'].include?("administrative_area_level_1")
|
156
|
+
res.state = comp['short_name']
|
157
|
+
res.province = comp['short_name']
|
158
|
+
when comp['types'].include?("postal_code")
|
159
|
+
res.zip = comp['long_name']
|
160
|
+
when comp['types'].include?("country")
|
161
|
+
res.country_code = comp['short_name']
|
162
|
+
res.country = comp['long_name']
|
163
|
+
when comp['types'].include?("administrative_area_level_2")
|
164
|
+
res.district = comp['long_name']
|
165
|
+
when comp['types'].include?('neighborhood')
|
166
|
+
res.neighborhood = comp['short_name']
|
167
|
+
end
|
168
|
+
end
|
169
|
+
if res.street_name
|
170
|
+
res.street_address=[res.street_number,res.street_name].join(' ').strip
|
171
|
+
end
|
172
|
+
res.accuracy = ACCURACY[addr['geometry']['location_type']]
|
173
|
+
res.precision=%w{unknown country state state city zip zip+4 street address building}[res.accuracy]
|
174
|
+
# try a few overrides where we can
|
175
|
+
if res.sub_premise
|
176
|
+
res.accuracy = 9
|
177
|
+
res.precision = 'building'
|
178
|
+
end
|
179
|
+
if res.street_name && res.precision=='city'
|
180
|
+
res.precision = 'street'
|
181
|
+
res.accuracy = 7
|
182
|
+
end
|
183
|
+
|
184
|
+
res.lat=addr['geometry']['location']['lat'].to_f
|
185
|
+
res.lng=addr['geometry']['location']['lng'].to_f
|
186
|
+
|
187
|
+
ne=Geokit::LatLng.new(
|
188
|
+
addr['geometry']['viewport']['northeast']['lat'].to_f,
|
189
|
+
addr['geometry']['viewport']['northeast']['lng'].to_f
|
190
|
+
)
|
191
|
+
sw=Geokit::LatLng.new(
|
192
|
+
addr['geometry']['viewport']['southwest']['lat'].to_f,
|
193
|
+
addr['geometry']['viewport']['southwest']['lng'].to_f
|
194
|
+
)
|
195
|
+
res.suggested_bounds = Geokit::Bounds.new(sw,ne)
|
196
|
+
|
197
|
+
res
|
198
|
+
end
|
199
|
+
end
|
200
|
+
Google3Geocoder = GoogleGeocoder3
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,103 @@
|
|
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 < Geocoder
|
7
|
+
|
8
|
+
# A number of non-routable IP ranges.
|
9
|
+
#
|
10
|
+
# --
|
11
|
+
# Sources for these:
|
12
|
+
# RFC 3330: Special-Use IPv4 Addresses
|
13
|
+
# The bogon list: http://www.cymru.com/Documents/bogon-list.html
|
14
|
+
|
15
|
+
NON_ROUTABLE_IP_RANGES = [
|
16
|
+
IPAddr.new('0.0.0.0/8'), # "This" Network
|
17
|
+
IPAddr.new('10.0.0.0/8'), # Private-Use Networks
|
18
|
+
IPAddr.new('14.0.0.0/8'), # Public-Data Networks
|
19
|
+
IPAddr.new('127.0.0.0/8'), # Loopback
|
20
|
+
IPAddr.new('169.254.0.0/16'), # Link local
|
21
|
+
IPAddr.new('172.16.0.0/12'), # Private-Use Networks
|
22
|
+
IPAddr.new('192.0.2.0/24'), # Test-Net
|
23
|
+
IPAddr.new('192.168.0.0/16'), # Private-Use Networks
|
24
|
+
IPAddr.new('198.18.0.0/15'), # Network Interconnect Device Benchmark Testing
|
25
|
+
IPAddr.new('224.0.0.0/4'), # Multicast
|
26
|
+
IPAddr.new('240.0.0.0/4') # Reserved for future use
|
27
|
+
].freeze
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Given an IP address, returns a GeoLoc instance which contains latitude,
|
32
|
+
# longitude, city, and country code. Sets the success attribute to false if the ip
|
33
|
+
# parameter does not match an ip address.
|
34
|
+
def self.do_geocode(ip, options = {})
|
35
|
+
return GeoLoc.new unless /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/.match(ip)
|
36
|
+
return GeoLoc.new if self.private_ip_address?(ip)
|
37
|
+
url = "http://api.hostip.info/get_html.php?ip=#{ip}&position=true"
|
38
|
+
response = self.call_geocoder_service(url)
|
39
|
+
ensure_utf8_encoding(response)
|
40
|
+
response.is_a?(Net::HTTPSuccess) ? parse_body(response.body) : GeoLoc.new
|
41
|
+
rescue
|
42
|
+
logger.error "Caught an error during HostIp geocoding call: " + $!.to_s
|
43
|
+
return GeoLoc.new
|
44
|
+
end
|
45
|
+
|
46
|
+
# Converts the body to YAML since its in the form of:
|
47
|
+
#
|
48
|
+
# Country: UNITED STATES (US)
|
49
|
+
# City: Sugar Grove, IL
|
50
|
+
# Latitude: 41.7696
|
51
|
+
# Longitude: -88.4588
|
52
|
+
#
|
53
|
+
# then instantiates a GeoLoc instance to populate with location data.
|
54
|
+
def self.parse_body(body) # :nodoc:
|
55
|
+
body = body.encode('UTF-8') if body.respond_to? :encode
|
56
|
+
yaml = YAML.load(body)
|
57
|
+
res = GeoLoc.new
|
58
|
+
res.provider = 'hostip'
|
59
|
+
res.city, res.state = yaml['City'].split(', ')
|
60
|
+
res.country, res.country_code = yaml['Country'].split(' (')
|
61
|
+
res.lat = yaml['Latitude']
|
62
|
+
res.lng = yaml['Longitude']
|
63
|
+
res.country_code.chop!
|
64
|
+
res.success = !(res.city =~ /\(.+\)/)
|
65
|
+
res
|
66
|
+
end
|
67
|
+
|
68
|
+
# Forces UTF-8 encoding on the body
|
69
|
+
# Rails expects string input to be UTF-8
|
70
|
+
# hostip.info specifies the charset encoding in the headers
|
71
|
+
# thus extract encoding from headers and tell Rails about it by forcing it
|
72
|
+
def self.ensure_utf8_encoding(response)
|
73
|
+
if (enc_string = extract_charset(response))
|
74
|
+
if defined?(Encoding) && Encoding.aliases.values.include?(enc_string.upcase)
|
75
|
+
response.body.force_encoding(enc_string.upcase) if response.body.respond_to?(:force_encoding)
|
76
|
+
response.body.encode("UTF-8")
|
77
|
+
else
|
78
|
+
require 'iconv'
|
79
|
+
response.body.replace Iconv.conv("UTF8", "iso88591", response.body)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Extracts charset out of the response headers
|
85
|
+
def self.extract_charset(response)
|
86
|
+
if (content_type = response['content-type'])
|
87
|
+
capture = content_type.match(/charset=(.+)/)
|
88
|
+
capture && capture[1]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Checks whether the IP address belongs to a private address range.
|
93
|
+
#
|
94
|
+
# This function is used to reduce the number of useless queries made to
|
95
|
+
# the geocoding service. Such queries can occur frequently during
|
96
|
+
# integration tests.
|
97
|
+
def self.private_ip_address?(ip)
|
98
|
+
return NON_ROUTABLE_IP_RANGES.any? { |range| range.include?(ip) }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -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://where.yahooapis.com/geocode?flags=J&appid=#{Geokit::Geocoders::yahoo}&q=#{Geokit::Inflector::url_escape(address_str)}"
|
18
|
+
url="http://nominatim.openstreetmap.org/search?format=json#{options_str}&addressdetails=1&q=#{Geokit::Inflector::url_escape(address_str)}"
|
19
|
+
res = self.call_geocoder_service(url)
|
20
|
+
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
21
|
+
json = res.body
|
22
|
+
logger.debug "OSM geocoding. Address: #{address}. Result: #{json}"
|
23
|
+
return self.json2GeoLoc(json, address)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.do_reverse_geocode(latlng, options = {})
|
27
|
+
latlng = LatLng.normalize(latlng)
|
28
|
+
options_str = generate_param_for(:lat, latlng.lat)
|
29
|
+
options_str << generate_param_for(:lon, latlng.lng)
|
30
|
+
options_str << generate_param_for_option(:zoom, options)
|
31
|
+
options_str << generate_param_for_option(:osm_type, options)
|
32
|
+
options_str << generate_param_for_option(:osm_id, options)
|
33
|
+
options_str << generate_param_for_option(:json_callback, options)
|
34
|
+
url = "http://nominatim.openstreetmap.org/reverse?format=json&addressdetails=1#{options_str}"
|
35
|
+
res = self.call_geocoder_service(url)
|
36
|
+
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
37
|
+
json = res.body
|
38
|
+
logger.debug "OSM reverse geocoding: Lat: #{latlng.lat}, Lng: #{latlng.lng}. Result: #{json}"
|
39
|
+
return self.json2GeoLoc(json, latlng)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.generate_param_for(param, value)
|
43
|
+
"&#{param}=#{Geokit::Inflector::url_escape(value.to_s)}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.generate_param_for_option(param, options)
|
47
|
+
options[param] ? "&#{param}=#{Geokit::Inflector::url_escape(options[param])}" : ''
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.generate_bool_param_for_option(param, options)
|
51
|
+
options[param] ? "&#{param}=1" : "&#{param}=0"
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.json2GeoLoc(json, obj)
|
55
|
+
results = MultiJson.load(json)
|
56
|
+
if results.is_a?(Hash)
|
57
|
+
return GeoLoc.new if results['error']
|
58
|
+
results = [results]
|
59
|
+
end
|
60
|
+
unless results.empty?
|
61
|
+
geoloc = nil
|
62
|
+
results.each do |result|
|
63
|
+
extract_geoloc = extract_geoloc(result)
|
64
|
+
if geoloc.nil?
|
65
|
+
geoloc = extract_geoloc
|
66
|
+
else
|
67
|
+
geoloc.all.push(extract_geoloc)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
return geoloc
|
71
|
+
else
|
72
|
+
logger.info "OSM was unable to geocode #{obj}"
|
73
|
+
return GeoLoc.new
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.extract_geoloc(result_json)
|
78
|
+
geoloc = GeoLoc.new
|
79
|
+
|
80
|
+
# basic
|
81
|
+
geoloc.lat = result_json['lat']
|
82
|
+
geoloc.lng = result_json['lon']
|
83
|
+
|
84
|
+
geoloc.provider = 'osm'
|
85
|
+
geoloc.precision = result_json['class']
|
86
|
+
geoloc.accuracy = result_json['type']
|
87
|
+
|
88
|
+
# Todo accuracy does not work as Yahoo and Google maps on OSM
|
89
|
+
#geoloc.accuracy = %w{unknown amenity building highway historic landuse leisure natural place railway shop tourism waterway man_made}.index(geoloc.precision)
|
90
|
+
#geoloc.full_address = result_json['display_name']
|
91
|
+
if result_json['address']
|
92
|
+
address_data = result_json['address']
|
93
|
+
|
94
|
+
geoloc.country = address_data['country']
|
95
|
+
geoloc.country_code = address_data['country_code'].upcase if address_data['country_code']
|
96
|
+
geoloc.state = address_data['state']
|
97
|
+
geoloc.city = address_data['city']
|
98
|
+
geoloc.city = address_data['county'] if geoloc.city.nil? && address_data['county']
|
99
|
+
geoloc.zip = address_data['postcode']
|
100
|
+
geoloc.district = address_data['city_district']
|
101
|
+
geoloc.district = address_data['state_district'] if geoloc.district.nil? && address_data['state_district']
|
102
|
+
geoloc.street_address = "#{address_data['road']} #{address_data['house_number']}".strip if address_data['road']
|
103
|
+
geoloc.street_name = address_data['road']
|
104
|
+
geoloc.street_number = address_data['house_number']
|
105
|
+
end
|
106
|
+
|
107
|
+
if result_json['boundingbox']
|
108
|
+
geoloc.suggested_bounds = Bounds.normalize(
|
109
|
+
[result_json['boundingbox'][0], result_json['boundingbox'][1]],
|
110
|
+
[result_json['boundingbox'][2], result_json['boundingbox'][3]])
|
111
|
+
end
|
112
|
+
|
113
|
+
geoloc.success = true
|
114
|
+
|
115
|
+
return geoloc
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Geocoder Us geocoder implementation. Requires the Geokit::Geocoders::GEOCODER_US variable to
|
2
|
+
# contain true or false based upon whether authentication is to occur. Conforms to the
|
3
|
+
# interface set by the Geocoder class.
|
4
|
+
module Geokit
|
5
|
+
module Geocoders
|
6
|
+
class UsGeocoder < Geocoder
|
7
|
+
|
8
|
+
private
|
9
|
+
def self.do_geocode(address, options = {})
|
10
|
+
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
11
|
+
|
12
|
+
query = (address_str =~ /^\d{5}(?:-\d{4})?$/ ? "zip" : "address") + "=#{Geokit::Inflector::url_escape(address_str)}"
|
13
|
+
url = if GeoKit::Geocoders::geocoder_us
|
14
|
+
"http://#{GeoKit::Geocoders::geocoder_us}@geocoder.us/member/service/csv/geocode"
|
15
|
+
else
|
16
|
+
"http://geocoder.us/service/csv/geocode"
|
17
|
+
end
|
18
|
+
|
19
|
+
url = "#{url}?#{query}"
|
20
|
+
res = self.call_geocoder_service(url)
|
21
|
+
|
22
|
+
return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
|
23
|
+
data = res.body
|
24
|
+
logger.debug "Geocoder.us geocoding. Address: #{address}. Result: #{data}"
|
25
|
+
array = data.chomp.split(',')
|
26
|
+
|
27
|
+
if array.length == 5
|
28
|
+
res=GeoLoc.new
|
29
|
+
res.lat,res.lng,res.city,res.state,res.zip=array
|
30
|
+
res.country_code='US'
|
31
|
+
res.success=true
|
32
|
+
return res
|
33
|
+
elsif array.length == 6
|
34
|
+
res=GeoLoc.new
|
35
|
+
res.lat,res.lng,res.street_address,res.city,res.state,res.zip=array
|
36
|
+
res.country_code='US'
|
37
|
+
res.success=true
|
38
|
+
return res
|
39
|
+
else
|
40
|
+
logger.info "geocoder.us was unable to geocode address: "+address
|
41
|
+
return GeoLoc.new
|
42
|
+
end
|
43
|
+
rescue
|
44
|
+
logger.error "Caught an error during geocoder.us geocoding call: "+$!
|
45
|
+
return GeoLoc.new
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|