geokit 1.4.1 → 1.5.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.
data/History.txt CHANGED
@@ -1,3 +1,9 @@
1
+ === 1.5.0 / 2009-09-21
2
+ * fixed jruby compatibility (thanks manalang)
3
+ * added country name to Google reverse geocoder (thanks joahking)
4
+ * added DependentLocalityName as district, and SubAdministrativeAreaName as province (google geocoder only)
5
+ * Google geocoder throws an error if you exceed geocoding rates (thanks drogus)
6
+
1
7
  === 1.4.1 / 2009-06-15
2
8
  * Fixed Ruby 1.9.1 compat and load order (thanks Niels Ganser)
3
9
 
data/geokit.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{geokit}
5
- s.version = "1.4.1"
5
+ s.version = "1.5.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Andre Lewis and Bill Eisenhauer"]
9
- s.date = %q{2009-06-15}
9
+ s.date = %q{2009-08-02}
10
10
  s.description = %q{Geokit Gem}
11
11
  s.email = ["andre@earthcode.com / bill_eisenhauer@yahoo.com"]
12
12
  s.extra_rdoc_files = ["Manifest.txt", "README.markdown"]
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
  s.rdoc_options = ["--main", "README.markdown"]
17
17
  s.require_paths = ["lib"]
18
18
  s.rubyforge_project = %q{geokit}
19
- s.rubygems_version = %q{1.3.1}
19
+ s.rubygems_version = %q{1.3.5}
20
20
  s.summary = %q{none}
21
21
  s.test_files = ["test/test_base_geocoder.rb", "test/test_bounds.rb", "test/test_ca_geocoder.rb", "test/test_geoloc.rb",
22
22
  "test/test_geoplugin_geocoder.rb", "test/test_google_geocoder.rb", "test/test_google_reverse_geocoder.rb",
data/lib/geokit.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Geokit
2
- VERSION = '1.4.1'
2
+ VERSION = '1.5.0'
3
3
  # These defaults are used in Geokit::Mappable.distance_to and in acts_as_mappable
4
4
  @@default_units = :miles
5
5
  @@default_formula = :sphere
@@ -5,6 +5,9 @@ require 'timeout'
5
5
  require 'logger'
6
6
 
7
7
  module Geokit
8
+
9
+ class TooManyQueriesError < StandardError; end
10
+
8
11
  module Inflector
9
12
 
10
13
  extend self
@@ -67,7 +70,7 @@ module Geokit
67
70
  @@proxy_port = nil
68
71
  @@proxy_user = nil
69
72
  @@proxy_pass = nil
70
- @@timeout = nil
73
+ @@request_timeout = nil
71
74
  @@yahoo = 'REPLACE_WITH_YOUR_YAHOO_KEY'
72
75
  @@google = 'REPLACE_WITH_YOUR_GOOGLE_KEY'
73
76
  @@geocoder_us = false
@@ -133,7 +136,7 @@ module Geokit
133
136
 
134
137
  # Call the geocoder service using the timeout if configured.
135
138
  def self.call_geocoder_service(url)
136
- timeout(Geokit::Geocoders::timeout) { return self.do_get(url) } if Geokit::Geocoders::timeout
139
+ Timeout::timeout(Geokit::Geocoders::request_timeout) { return self.do_get(url) } if Geokit::Geocoders::request_timeout
137
140
  return self.do_get(url)
138
141
  rescue TimeoutError
139
142
  return nil
@@ -162,8 +165,7 @@ module Geokit
162
165
  res = Net::HTTP::Proxy(GeoKit::Geocoders::proxy_addr,
163
166
  GeoKit::Geocoders::proxy_port,
164
167
  GeoKit::Geocoders::proxy_user,
165
- GeoKit::Geocoders::proxy_pass).start(uri.host, uri.port) { |http| http.request(req) }
166
-
168
+ GeoKit::Geocoders::proxy_pass).start(uri.host, uri.port) { |http| http.get(uri.path + "?" + uri.query) }
167
169
  return res
168
170
  end
169
171
 
@@ -463,14 +465,19 @@ module Geokit
463
465
  end
464
466
  end
465
467
  return geoloc
466
- else
468
+ elsif doc.elements['//kml/Response/Status/code'].text == '620'
469
+ raise Geokit::TooManyQueriesError
470
+ else
467
471
  logger.info "Google was unable to geocode address: "+address
468
472
  return GeoLoc.new
469
473
  end
470
474
 
471
- rescue
472
- logger.error "Caught an error during Google geocoding call: "+$!
473
- return GeoLoc.new
475
+ rescue Geokit::TooManyQueriesError
476
+ # re-raise because of other rescue
477
+ raise Geokit::TooManyQueriesError, "Google returned a 620 status, too many queries. The given key has gone over the requests limit in the 24 hour period or has submitted too many requests in too short a period of time. If you're sending multiple requests in parallel or in a tight loop, use a timer or pause in your code to make sure you don't send the requests too quickly."
478
+ rescue
479
+ logger.error "Caught an error during Google geocoding call: "+$!
480
+ return GeoLoc.new
474
481
  end
475
482
 
476
483
  # extracts a single geoloc from a //placemark element in the google results xml
@@ -487,9 +494,12 @@ module Geokit
487
494
  #extended -- false if not not available
488
495
  res.city = doc.elements['.//LocalityName'].text if doc.elements['.//LocalityName']
489
496
  res.state = doc.elements['.//AdministrativeAreaName'].text if doc.elements['.//AdministrativeAreaName']
497
+ res.province = doc.elements['.//SubAdministrativeAreaName'].text if doc.elements['.//SubAdministrativeAreaName']
490
498
  res.full_address = doc.elements['.//address'].text if doc.elements['.//address'] # google provides it
491
499
  res.zip = doc.elements['.//PostalCodeNumber'].text if doc.elements['.//PostalCodeNumber']
492
500
  res.street_address = doc.elements['.//ThoroughfareName'].text if doc.elements['.//ThoroughfareName']
501
+ res.country = doc.elements['.//CountryName'].text if doc.elements['.//CountryName']
502
+ res.district = doc.elements['.//DependentLocalityName'].text if doc.elements['.//DependentLocalityName']
493
503
  # Translate accuracy into Yahoo-style token address, street, zip, zip+4, city, state, country
494
504
  # For Google, 1=low accuracy, 8=high accuracy
495
505
  address_details=doc.elements['.//*[local-name() = "AddressDetails"]']
@@ -504,8 +514,8 @@ module Geokit
504
514
  end
505
515
 
506
516
  res.success=true
507
-
508
- return res
517
+
518
+ return res
509
519
  end
510
520
  end
511
521
 
@@ -336,13 +336,13 @@ module Geokit
336
336
  #
337
337
  # puts geo.full_address # just like usual
338
338
  # puts geo.all.size => 3 # there's three results total
339
- # puts geo.all.first # all is just an array or additional geolocs,
339
+ # puts geo.all.first # all is just an array or additional geolocs,
340
340
  # so do what you want with it
341
341
  class GeoLoc < LatLng
342
342
 
343
343
  # Location attributes. Full address is a concatenation of all values. For example:
344
344
  # 100 Spear St, San Francisco, CA, 94101, US
345
- attr_accessor :street_address, :city, :state, :zip, :country_code, :full_address, :all
345
+ attr_accessor :street_address, :city, :state, :zip, :country_code, :country, :full_address, :all, :district, :province
346
346
  # Attributes set upon return from geocoding. Success will be true for successful
347
347
  # geocode lookups. The provider will be set to the name of the providing geocoder.
348
348
  # Finally, precision is an indicator of the accuracy of the geocoding.
@@ -361,7 +361,8 @@ module Geokit
361
361
  @city=h[:city]
362
362
  @state=h[:state]
363
363
  @zip=h[:zip]
364
- @country_code=h[:country_code]
364
+ @country_code=h[:country_code]
365
+ @province = h[:province]
365
366
  @success=false
366
367
  @precision='unknown'
367
368
  @full_address=nil
@@ -398,7 +399,7 @@ module Geokit
398
399
  # gives you all the important fields as key-value pairs
399
400
  def hash
400
401
  res={}
401
- [:success,:lat,:lng,:country_code,:city,:state,:zip,:street_address,:provider,:full_address,:is_us?,:ll,:precision].each { |s| res[s] = self.send(s.to_s) }
402
+ [:success,:lat,:lng,:country_code,:city,:state,:zip,:street_address,:province,:district,:provider,:full_address,:is_us?,:ll,:precision].each { |s| res[s] = self.send(s.to_s) }
402
403
  res
403
404
  end
404
405
  alias to_hash hash
@@ -416,7 +417,7 @@ module Geokit
416
417
  # Returns a comma-delimited string consisting of the street address, city, state,
417
418
  # zip, and country code. Only includes those attributes that are non-blank.
418
419
  def to_geocodeable_s
419
- a=[street_address, city, state, zip, country_code].compact
420
+ a=[street_address, district, city, province, state, zip, country_code].compact
420
421
  a.delete_if { |e| !e || e == '' }
421
422
  a.join(', ')
422
423
  end
@@ -36,7 +36,7 @@ class BaseGeocoderTest < Test::Unit::TestCase #:nodoc: all
36
36
 
37
37
  def test_timeout_call_web_service
38
38
  url = "http://www.anything.com"
39
- Geokit::Geocoders::timeout = 1
39
+ Geokit::Geocoders::request_timeout = 1
40
40
  assert_nil Geokit::Geocoders::TestGeocoder.call_geocoder_service(url)
41
41
  end
42
42
 
data/test/test_geoloc.rb CHANGED
@@ -65,7 +65,7 @@ class GeoLocTest < Test::Unit::TestCase #:nodoc: all
65
65
  @loc.zip = '94105'
66
66
  @loc.country_code = 'US'
67
67
  assert_equal(
68
- "--- !ruby/object:Geokit::GeoLoc \ncity: San Francisco\ncountry_code: US\nfull_address: \nlat: \nlng: \nprecision: unknown\nstate: CA\nstreet_address: \nsuccess: false\nzip: \"94105\"\n",
68
+ "--- !ruby/object:Geokit::GeoLoc \ncity: San Francisco\ncountry_code: US\nfull_address: \nlat: \nlng: \nprecision: unknown\nprovince: \nstate: CA\nstreet_address: \nsuccess: false\nzip: \"94105\"\n",
69
69
  @loc.to_yaml)
70
70
  end
71
71
 
@@ -18,6 +18,8 @@ class GoogleGeocoderTest < BaseGeocoderTest #:nodoc: all
18
18
 
19
19
  GOOGLE_MULTI="<?xml version='1.0' encoding='UTF-8'?>\n<kml xmlns='http://earth.google.com/kml/2.0'><Response>\n <name>via Sandro Pertini 8, Ossona, MI</name>\n <Status>\n <code>200</code>\n <request>geocode</request>\n </Status>\n <Placemark id='p1'>\n <address>Via Sandro Pertini, 8, 20010 Mesero MI, Italy</address>\n <AddressDetails Accuracy='8' xmlns='urn:oasis:names:tc:ciq:xsdschema:xAL:2.0'><Country><CountryNameCode>IT</CountryNameCode><CountryName>Italy</CountryName><AdministrativeArea><AdministrativeAreaName>Lombardy</AdministrativeAreaName><SubAdministrativeArea><SubAdministrativeAreaName>Milan</SubAdministrativeAreaName><Locality><LocalityName>Mesero</LocalityName><Thoroughfare><ThoroughfareName>8 Via Sandro Pertini</ThoroughfareName></Thoroughfare><PostalCode><PostalCodeNumber>20010</PostalCodeNumber></PostalCode></Locality></SubAdministrativeArea></AdministrativeArea></Country></AddressDetails>\n <Point><coordinates>8.8527131,45.4966243,0</coordinates></Point>\n </Placemark>\n <Placemark id='p2'>\n <address>Via Sandro Pertini, 20010 Ossona MI, Italy</address>\n <AddressDetails Accuracy='6' xmlns='urn:oasis:names:tc:ciq:xsdschema:xAL:2.0'><Country><CountryNameCode>IT</CountryNameCode><CountryName>Italy</CountryName><AdministrativeArea><AdministrativeAreaName>Lombardy</AdministrativeAreaName><SubAdministrativeArea><SubAdministrativeAreaName>Milan</SubAdministrativeAreaName><Locality><LocalityName>Ossona</LocalityName><Thoroughfare><ThoroughfareName>Via Sandro Pertini</ThoroughfareName></Thoroughfare><PostalCode><PostalCodeNumber>20010</PostalCodeNumber></PostalCode></Locality></SubAdministrativeArea></AdministrativeArea></Country></AddressDetails>\n <Point><coordinates>8.9023200,45.5074444,0</coordinates></Point>\n </Placemark>\n</Response></kml>\n"
20
20
 
21
+ GOOGLE_REVERSE_MADRID="<?xml version='1.0' encoding='UTF-8' ?><kml xmlns='http://earth.google.com/kml/2.0'><Response><name>40.416741,-3.703250</name><Status><code>200</code><request>geocode</request></Status><Placemark id='p1'><address>Plaza de la Puerta del Sol, 28013, Madrid, Spain</address><AddressDetails Accuracy='6' xmlns='urn:oasis:names:tc:ciq:xsdschema:xAL:2.0'><Country><CountryNameCode>ES</CountryNameCode><CountryName>Spain</CountryName><AdministrativeArea><AdministrativeAreaName>Madrid</AdministrativeAreaName><SubAdministrativeArea><SubAdministrativeAreaName>Madrid</SubAdministrativeAreaName><Locality><LocalityName>Madrid</LocalityName><Thoroughfare><ThoroughfareName>Plaza de la Puerta del Sol</ThoroughfareName></Thoroughfare><PostalCode><PostalCodeNumber>28013</PostalCodeNumber></PostalCode></Locality></SubAdministrativeArea></AdministrativeArea></Country></AddressDetails><ExtendedData><LatLonBox north='40.4199522' south='40.4136570' east='-3.7001138' west='-3.7064091' /></ExtendedData><Point><coordinates>-3.7032537,40.4168023,0</coordinates></Point></Placemark><Placemark id='p2'><address>28013, Madrid, Spain</address><AddressDetails Accuracy='5' xmlns='urn:oasis:names:tc:ciq:xsdschema:xAL:2.0'><Country><CountryNameCode>ES</CountryNameCode><CountryName>Spain</CountryName><AdministrativeArea><AdministrativeAreaName>Madrid</AdministrativeAreaName><SubAdministrativeArea><SubAdministrativeAreaName>Madrid</SubAdministrativeAreaName><Locality><LocalityName>Madrid</LocalityName><PostalCode><PostalCodeNumber>28013</PostalCodeNumber></PostalCode></Locality></SubAdministrativeArea></AdministrativeArea></Country></AddressDetails><ExtendedData><LatLonBox north='40.4244113' south='40.4142840' east='-3.6969862' west='-3.7224820' /></ExtendedData><Point><coordinates>-3.7117806,40.4189645,0</coordinates></Point></Placemark><Placemark id='p3'><address>Madrid, Spain</address><AddressDetails Accuracy='4' xmlns='urn:oasis:names:tc:ciq:xsdschema:xAL:2.0'><Country><CountryNameCode>ES</CountryNameCode><CountryName>Spain</CountryName><AdministrativeArea><AdministrativeAreaName>Madrid</AdministrativeAreaName><SubAdministrativeArea><SubAdministrativeAreaName>Madrid</SubAdministrativeAreaName><Locality><LocalityName>Madrid</LocalityName></Locality></SubAdministrativeArea></AdministrativeArea></Country></AddressDetails><ExtendedData><LatLonBox north='40.6435181' south='40.3120713' east='-3.5180102' west='-3.8890049' /></ExtendedData><Point><coordinates>-3.7032498,40.4167413,0</coordinates></Point></Placemark><Placemark id='p4'><address>Madrid, Spain</address><AddressDetails Accuracy='2' xmlns='urn:oasis:names:tc:ciq:xsdschema:xAL:2.0'><Country><CountryNameCode>ES</CountryNameCode><CountryName>Spain</CountryName><AdministrativeArea><AdministrativeAreaName>Madrid</AdministrativeAreaName></AdministrativeArea></Country></AddressDetails><ExtendedData><LatLonBox north='41.1649106' south='39.8845366' east='-3.0531322' west='-4.5791745' /></ExtendedData><Point><coordinates>-3.5812692,40.4167088,0</coordinates></Point></Placemark><Placemark id='p5'><address>Madrid, Spain</address><AddressDetails Accuracy='3' xmlns='urn:oasis:names:tc:ciq:xsdschema:xAL:2.0'><Country><CountryNameCode>ES</CountryNameCode><CountryName>Spain</CountryName><AdministrativeArea><AdministrativeAreaName>Madrid</AdministrativeAreaName><SubAdministrativeArea><SubAdministrativeAreaName>Madrid</SubAdministrativeAreaName></SubAdministrativeArea></AdministrativeArea></Country></AddressDetails><ExtendedData><LatLonBox north='41.1649106' south='39.8845366' east='-3.0531322' west='-4.5791745' /></ExtendedData><Point><coordinates>-3.5812692,40.4167088,0</coordinates></Point></Placemark><Placemark id='p6'><address>Spain</address><AddressDetails Accuracy='1' xmlns='urn:oasis:names:tc:ciq:xsdschema:xAL:2.0'><Country><CountryNameCode>ES</CountryNameCode><CountryName>Spain</CountryName></Country></AddressDetails><ExtendedData><LatLonBox north='43.7903881' south='27.6377504' east='4.3279851' west='-18.1606948' /></ExtendedData><Point><coordinates>-3.7492200,40.4636670,0</coordinates></Point></Placemark></Response></kml>"
22
+
21
23
  GOOGLE_COUNTRY_CODE_BIASED_RESULT = <<-EOF.strip
22
24
  <?xml version="1.0" encoding="UTF-8" ?><kml xmlns="http://earth.google.com/kml/2.0"><Response><name>Syracuse</name><Status><code>200</code><request>geocode</request></Status><Placemark id="p1"><address>Syracuse, Italy</address><AddressDetails Accuracy="3" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"><Country><CountryNameCode>IT</CountryNameCode><CountryName>Italy</CountryName><AdministrativeArea><AdministrativeAreaName>Sicily</AdministrativeAreaName><SubAdministrativeArea><SubAdministrativeAreaName>Syracuse</SubAdministrativeAreaName></SubAdministrativeArea></AdministrativeArea></Country></AddressDetails><ExtendedData><LatLonBox north="37.4125978" south="36.6441736" east="15.3367367" west="14.7724913" /></ExtendedData><Point><coordinates>14.9856176,37.0630218,0</coordinates></Point></Placemark></Response></kml>
23
25
  EOF
@@ -25,6 +27,10 @@ class GoogleGeocoderTest < BaseGeocoderTest #:nodoc: all
25
27
  GOOGLE_BOUNDS_BIASED_RESULT = <<-EOF.strip
26
28
  <?xml version="1.0" encoding="UTF-8" ?><kml xmlns="http://earth.google.com/kml/2.0"><Response><name>Winnetka</name><Status><code>200</code><request>geocode</request></Status><Placemark id="p1"><address>Winnetka, California, USA</address><AddressDetails Accuracy="4" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"><Country><CountryNameCode>US</CountryNameCode><CountryName>USA</CountryName><AdministrativeArea><AdministrativeAreaName>CA</AdministrativeAreaName><AddressLine>Winnetka</AddressLine></AdministrativeArea></Country></AddressDetails><ExtendedData><LatLonBox north="34.2353090" south="34.1791050" east="-118.5534191" west="-118.5883200" /></ExtendedData><Point><coordinates>-118.5710220,34.2131710,0</coordinates></Point></Placemark></Response></kml>
27
29
  EOF
30
+
31
+ GOOGLE_TOO_MANY=<<-EOF.strip
32
+ <?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://earth.google.com/kml/2.0"><Response><name>100 spear st, san francisco, ca</name><Status><code>620</code><request>geocode</request></Status></Response></kml>
33
+ EOF
28
34
 
29
35
  def setup
30
36
  super
@@ -154,6 +160,34 @@ class GoogleGeocoderTest < BaseGeocoderTest #:nodoc: all
154
160
  assert_equal "Via Sandro Pertini", res.street_address
155
161
  assert_equal "google", res.provider
156
162
  end
163
+
164
+ def test_reverse_geocode
165
+ #Geokit::Geocoders::GoogleGeocoder.do_reverse_geocode("40.4167413, -3.7032498")
166
+ madrid = Geokit::GeoLoc.new
167
+ madrid.lat, madrid.lng = "40.4167413", "-3.7032498"
168
+ response = MockSuccess.new
169
+ response.expects(:body).returns(GOOGLE_REVERSE_MADRID)
170
+ url = "http://maps.google.com/maps/geo?ll=#{Geokit::Inflector::url_escape(madrid.ll)}&output=xml&key=#{Geokit::Geocoders::google}&oe=utf-8"
171
+ Geokit::Geocoders::GoogleGeocoder.expects(:call_geocoder_service).with(url).
172
+ returns(response)
173
+ res=Geokit::Geocoders::GoogleGeocoder.do_reverse_geocode(madrid.ll)
174
+
175
+ assert_equal madrid.lat.to_s.slice(1..5), res.lat.to_s.slice(1..5)
176
+ assert_equal madrid.lng.to_s.slice(1..5), res.lng.to_s.slice(1..5)
177
+ assert_equal "ES", res.country_code
178
+ assert_equal "google", res.provider
179
+
180
+ assert_equal "Madrid", res.city
181
+ assert_equal "Madrid", res.state
182
+
183
+ assert_equal "Spain", res.country
184
+ assert_equal "zip+4", res.precision
185
+ assert_equal true, res.success
186
+
187
+ assert_equal "Plaza de la Puerta del Sol, 28013, Madrid, Spain", res.full_address
188
+ assert_equal "28013", res.zip
189
+ assert_equal "Plaza De La Puerta Del Sol", res.street_address
190
+ end
157
191
 
158
192
  def test_country_code_biasing
159
193
  response = MockSuccess.new
@@ -180,4 +214,14 @@ class GoogleGeocoderTest < BaseGeocoderTest #:nodoc: all
180
214
  assert_equal 'US', biased_result.country_code
181
215
  assert_equal 'CA', biased_result.state
182
216
  end
217
+
218
+ def test_too_many_queries
219
+ response = MockSuccess.new
220
+ response.expects(:body).returns(GOOGLE_TOO_MANY)
221
+ url = "http://maps.google.com/maps/geo?q=#{Geokit::Inflector.url_escape(@address)}&output=xml&key=Google&oe=utf-8"
222
+ Geokit::Geocoders::GoogleGeocoder.expects(:call_geocoder_service).with(url).returns(response)
223
+ assert_raise Geokit::TooManyQueriesError do
224
+ res=Geokit::Geocoders::GoogleGeocoder.geocode(@address)
225
+ end
226
+ end
183
227
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geokit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andre Lewis
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-15 00:00:00 -07:00
12
+ date: 2009-09-21 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency