geokit 1.4.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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