darrell-geokit 1.4.1.1 → 1.5.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -54,7 +54,7 @@ If you're using this gem by itself, here are the configuration options:
54
54
  # This is the timeout value in seconds to be used for calls to the geocoder web
55
55
  # services. For no timeout at all, comment out the setting. The timeout unit
56
56
  # is in seconds.
57
- Geokit::Geocoders::timeout = 3
57
+ Geokit::Geocoders::request_timeout = 3
58
58
 
59
59
  # These settings are used if web service calls must be routed through a proxy.
60
60
  # These setting can be nil if not needed, otherwise, addr and port must be
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
@@ -1,10 +1,15 @@
1
1
  require 'net/http'
2
+ require 'ipaddr'
2
3
  require 'rexml/document'
3
4
  require 'yaml'
4
5
  require 'timeout'
5
6
  require 'logger'
7
+ require 'json'
6
8
 
7
9
  module Geokit
10
+
11
+ class TooManyQueriesError < StandardError; end
12
+
8
13
  module Inflector
9
14
 
10
15
  extend self
@@ -67,7 +72,7 @@ module Geokit
67
72
  @@proxy_port = nil
68
73
  @@proxy_user = nil
69
74
  @@proxy_pass = nil
70
- @@timeout = nil
75
+ @@request_timeout = nil
71
76
  @@yahoo = 'REPLACE_WITH_YOUR_YAHOO_KEY'
72
77
  @@google = 'REPLACE_WITH_YOUR_GOOGLE_KEY'
73
78
  @@geocoder_us = false
@@ -133,7 +138,7 @@ module Geokit
133
138
 
134
139
  # Call the geocoder service using the timeout if configured.
135
140
  def self.call_geocoder_service(url)
136
- timeout(Geokit::Geocoders::timeout) { return self.do_get(url) } if Geokit::Geocoders::timeout
141
+ Timeout::timeout(Geokit::Geocoders::request_timeout) { return self.do_get(url) } if Geokit::Geocoders::request_timeout
137
142
  return self.do_get(url)
138
143
  rescue TimeoutError
139
144
  return nil
@@ -162,8 +167,7 @@ module Geokit
162
167
  res = Net::HTTP::Proxy(GeoKit::Geocoders::proxy_addr,
163
168
  GeoKit::Geocoders::proxy_port,
164
169
  GeoKit::Geocoders::proxy_user,
165
- GeoKit::Geocoders::proxy_pass).start(uri.host, uri.port) { |http| http.request(req) }
166
-
170
+ GeoKit::Geocoders::proxy_pass).start(uri.host, uri.port) { |http| http.get(uri.path + "?" + uri.query) }
167
171
  return res
168
172
  end
169
173
 
@@ -463,14 +467,19 @@ module Geokit
463
467
  end
464
468
  end
465
469
  return geoloc
466
- else
470
+ elsif doc.elements['//kml/Response/Status/code'].text == '620'
471
+ raise Geokit::TooManyQueriesError
472
+ else
467
473
  logger.info "Google was unable to geocode address: "+address
468
474
  return GeoLoc.new
469
475
  end
470
476
 
471
- rescue
472
- logger.error "Caught an error during Google geocoding call: "+$!
473
- return GeoLoc.new
477
+ rescue Geokit::TooManyQueriesError
478
+ # re-raise because of other rescue
479
+ 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."
480
+ rescue
481
+ logger.error "Caught an error during Google geocoding call: "+$!
482
+ return GeoLoc.new
474
483
  end
475
484
 
476
485
  # extracts a single geoloc from a //placemark element in the google results xml
@@ -487,9 +496,12 @@ module Geokit
487
496
  #extended -- false if not not available
488
497
  res.city = doc.elements['.//LocalityName'].text if doc.elements['.//LocalityName']
489
498
  res.state = doc.elements['.//AdministrativeAreaName'].text if doc.elements['.//AdministrativeAreaName']
499
+ res.province = doc.elements['.//SubAdministrativeAreaName'].text if doc.elements['.//SubAdministrativeAreaName']
490
500
  res.full_address = doc.elements['.//address'].text if doc.elements['.//address'] # google provides it
491
501
  res.zip = doc.elements['.//PostalCodeNumber'].text if doc.elements['.//PostalCodeNumber']
492
502
  res.street_address = doc.elements['.//ThoroughfareName'].text if doc.elements['.//ThoroughfareName']
503
+ res.country = doc.elements['.//CountryName'].text if doc.elements['.//CountryName']
504
+ res.district = doc.elements['.//DependentLocalityName'].text if doc.elements['.//DependentLocalityName']
493
505
  # Translate accuracy into Yahoo-style token address, street, zip, zip+4, city, state, country
494
506
  # For Google, 1=low accuracy, 8=high accuracy
495
507
  address_details=doc.elements['.//*[local-name() = "AddressDetails"]']
@@ -504,12 +516,163 @@ module Geokit
504
516
  end
505
517
 
506
518
  res.success=true
507
-
508
- return res
519
+
520
+ return res
509
521
  end
510
522
  end
511
523
 
524
+ class GoogleGeocoder3 < Geocoder
525
+
526
+ private
527
+ # Template method which does the reverse-geocode lookup.
528
+ def self.do_reverse_geocode(latlng)
529
+ latlng=LatLng.normalize(latlng)
530
+ res = self.call_geocoder_service("http://maps.google.com/maps/api/geocode/json?sensor=false&latlng=#{Geokit::Inflector::url_escape(latlng.ll)}")
531
+ return GeoLoc.new unless (res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPOK))
532
+ json = res.body
533
+ logger.debug "Google reverse-geocoding. LL: #{latlng}. Result: #{json}"
534
+ return self.json2GeoLoc(json)
535
+ end
512
536
 
537
+ # Template method which does the geocode lookup.
538
+ #
539
+ # Supports viewport/country code biasing
540
+ #
541
+ # ==== OPTIONS
542
+ # * :bias - This option makes the Google Geocoder return results biased to a particular
543
+ # country or viewport. Country code biasing is achieved by passing the ccTLD
544
+ # ('uk' for .co.uk, for example) as a :bias value. For a list of ccTLD's,
545
+ # look here: http://en.wikipedia.org/wiki/CcTLD. By default, the geocoder
546
+ # will be biased to results within the US (ccTLD .com).
547
+ #
548
+ # If you'd like the Google Geocoder to prefer results within a given viewport,
549
+ # you can pass a Geokit::Bounds object as the :bias value.
550
+ #
551
+ # ==== EXAMPLES
552
+ # # By default, the geocoder will return Syracuse, NY
553
+ # Geokit::Geocoders::GoogleGeocoder.geocode('Syracuse').country_code # => 'US'
554
+ # # With country code biasing, it returns Syracuse in Sicily, Italy
555
+ # Geokit::Geocoders::GoogleGeocoder.geocode('Syracuse', :bias => :it).country_code # => 'IT'
556
+ #
557
+ # # By default, the geocoder will return Winnetka, IL
558
+ # Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka').state # => 'IL'
559
+ # # When biased to an bounding box around California, it will now return the Winnetka neighbourhood, CA
560
+ # bounds = Geokit::Bounds.normalize([34.074081, -118.694401], [34.321129, -118.399487])
561
+ # Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka', :bias => bounds).state # => 'CA'
562
+ def self.do_geocode(address, options = {})
563
+ bias_str = options[:bias] ? construct_bias_string_from_options(options[:bias]) : ''
564
+ address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
565
+ res = self.call_geocoder_service("http://maps.google.com/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector::url_escape(address_str)}#{bias_str}")
566
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
567
+ json = res.body
568
+ logger.debug "Google geocoding. Address: #{address}. Result: #{json}"
569
+ return self.json2GeoLoc(json, address)
570
+ end
571
+
572
+ def self.construct_bias_string_from_options(bias)
573
+ if bias.is_a?(String) or bias.is_a?(Symbol)
574
+ # country code biasing
575
+ "&region=#{bias.to_s.downcase}"
576
+ elsif bias.is_a?(Bounds)
577
+ # viewport biasing
578
+ "&bounds=#{bias.nw.to_s}|#{bias.se.to_s}"
579
+ end
580
+ end
581
+
582
+ def self.json2GeoLoc(json, address="")
583
+ ret=nil
584
+ results=JSON.parse(json, :symbolize_names => true)
585
+
586
+ if results[:status] == 'OVER_QUERY_LIMIT'
587
+ raise Geokit::TooManyQueriesError
588
+ end
589
+ if results[:status] == 'ZERO_RESULTS'
590
+ return GeoLoc.new
591
+ end
592
+ # this should probably be smarter.
593
+ if !results[:status] == 'OK'
594
+ raise Geokit::Geocoders::GeocodeError
595
+ end
596
+ # location_type stores additional data about the specified location.
597
+ # The following values are currently supported:
598
+ # "ROOFTOP" indicates that the returned result is a precise geocode
599
+ # for which we have location information accurate down to street
600
+ # address precision.
601
+ # "RANGE_INTERPOLATED" indicates that the returned result reflects an
602
+ # approximation (usually on a road) interpolated between two precise
603
+ # points (such as intersections). Interpolated results are generally
604
+ # returned when rooftop geocodes are unavailable for a street address.
605
+ # "GEOMETRIC_CENTER" indicates that the returned result is the
606
+ # geometric center of a result such as a polyline (for example, a
607
+ # street) or polygon (region).
608
+ # "APPROXIMATE" indicates that the returned result is approximate
609
+
610
+ # these do not map well. Perhaps we should guess better based on size
611
+ # of bounding box where it exists? Does it really matter?
612
+ accuracy = {
613
+ "ROOFTOP" => 9,
614
+ "RANGE_INTERPOLATED" => 8,
615
+ "GEOMETRIC_CENTER" => 5,
616
+ "APPROXIMATE" => 4
617
+ }
618
+ results[:results].sort_by{|a|accuracy[a[:geometry][:location_type]]}.reverse.each do |addr|
619
+ res=GeoLoc.new
620
+ res.provider = 'google3'
621
+ res.success = true
622
+ res.full_address = addr[:formatted_address]
623
+ addr[:address_components].each do |comp|
624
+ case
625
+ when comp[:types].include?("street_number")
626
+ res.street_number = comp[:short_name]
627
+ when comp[:types].include?("route")
628
+ res.street_name = comp[:long_name]
629
+ when comp[:types].include?("locality")
630
+ res.city = comp[:long_name]
631
+ when comp[:types].include?("administrative_area_level_1")
632
+ res.state = comp[:short_name]
633
+ res.province = comp[:short_name]
634
+ when comp[:types].include?("postal_code")
635
+ res.zip = comp[:long_name]
636
+ when comp[:types].include?("country")
637
+ res.country_code = comp[:short_name]
638
+ res.country = comp[:long_name]
639
+ when comp[:types].include?("administrative_area_level_2")
640
+ res.district = comp[:long_name]
641
+ end
642
+ end
643
+ if res.street_name
644
+ res.street_address=[res.street_number,res.street_name].join(' ').strip
645
+ end
646
+ res.accuracy = accuracy[addr[:geometry][:location_type]]
647
+ res.precision=%w{unknown country state state city zip zip+4 street address building}[res.accuracy]
648
+ # try a few overrides where we can
649
+ if res.street_name && res.precision=='city'
650
+ res.precision = 'street'
651
+ res.accuracy = 7
652
+ end
653
+
654
+ res.lat=addr[:geometry][:location][:lat].to_f
655
+ res.lng=addr[:geometry][:location][:lng].to_f
656
+
657
+ ne=Geokit::LatLng.new(
658
+ addr[:geometry][:viewport][:northeast][:lat].to_f,
659
+ addr[:geometry][:viewport][:northeast][:lng].to_f
660
+ )
661
+ sw=Geokit::LatLng.new(
662
+ addr[:geometry][:viewport][:southwest][:lat].to_f,
663
+ addr[:geometry][:viewport][:southwest][:lng].to_f
664
+ )
665
+ res.suggested_bounds = Geokit::Bounds.new(sw,ne)
666
+
667
+ if ret
668
+ ret.all.push(res)
669
+ else
670
+ ret=res
671
+ end
672
+ end
673
+ return ret
674
+ end
675
+ end
513
676
  # -------------------------------------------------------------------------------------------
514
677
  # IP Geocoders
515
678
  # -------------------------------------------------------------------------------------------
@@ -546,14 +709,35 @@ module Geokit
546
709
  # as community contributions.
547
710
  class IpGeocoder < Geocoder
548
711
 
712
+ # A number of non-routable IP ranges.
713
+ #
714
+ # --
715
+ # Sources for these:
716
+ # RFC 3330: Special-Use IPv4 Addresses
717
+ # The bogon list: http://www.cymru.com/Documents/bogon-list.html
718
+
719
+ NON_ROUTABLE_IP_RANGES = [
720
+ IPAddr.new('0.0.0.0/8'), # "This" Network
721
+ IPAddr.new('10.0.0.0/8'), # Private-Use Networks
722
+ IPAddr.new('14.0.0.0/8'), # Public-Data Networks
723
+ IPAddr.new('127.0.0.0/8'), # Loopback
724
+ IPAddr.new('169.254.0.0/16'), # Link local
725
+ IPAddr.new('172.16.0.0/12'), # Private-Use Networks
726
+ IPAddr.new('192.0.2.0/24'), # Test-Net
727
+ IPAddr.new('192.168.0.0/16'), # Private-Use Networks
728
+ IPAddr.new('198.18.0.0/15'), # Network Interconnect Device Benchmark Testing
729
+ IPAddr.new('224.0.0.0/4'), # Multicast
730
+ IPAddr.new('240.0.0.0/4') # Reserved for future use
731
+ ].freeze
732
+
549
733
  private
550
734
 
551
735
  # Given an IP address, returns a GeoLoc instance which contains latitude,
552
736
  # longitude, city, and country code. Sets the success attribute to false if the ip
553
737
  # parameter does not match an ip address.
554
738
  def self.do_geocode(ip, options = {})
555
- return GeoLoc.new if '0.0.0.0' == ip
556
739
  return GeoLoc.new unless /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/.match(ip)
740
+ return GeoLoc.new if self.private_ip_address?(ip)
557
741
  url = "http://api.hostip.info/get_html.php?ip=#{ip}&position=true"
558
742
  response = self.call_geocoder_service(url)
559
743
  response.is_a?(Net::HTTPSuccess) ? parse_body(response.body) : GeoLoc.new
@@ -582,6 +766,15 @@ module Geokit
582
766
  res.success = !(res.city =~ /\(.+\)/)
583
767
  res
584
768
  end
769
+
770
+ # Checks whether the IP address belongs to a private address range.
771
+ #
772
+ # This function is used to reduce the number of useless queries made to
773
+ # the geocoding service. Such queries can occur frequently during
774
+ # integration tests.
775
+ def self.private_ip_address?(ip)
776
+ return NON_ROUTABLE_IP_RANGES.any? { |range| range.include?(ip) }
777
+ end
585
778
  end
586
779
 
587
780
  # -------------------------------------------------------------------------------------------
@@ -336,19 +336,18 @@ 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
+ # Street number and street name are extracted from the street address attribute if they don't exist
346
+ attr_accessor :street_number,:street_name,:street_address, :city, :state, :zip, :country_code, :country, :full_address, :all, :district, :province
346
347
  # Attributes set upon return from geocoding. Success will be true for successful
347
348
  # geocode lookups. The provider will be set to the name of the providing geocoder.
348
349
  # Finally, precision is an indicator of the accuracy of the geocoding.
349
350
  attr_accessor :success, :provider, :precision, :suggested_bounds
350
- # Street number and street name are extracted from the street address attribute.
351
- attr_reader :street_number, :street_name
352
351
  # accuracy is set for Yahoo and Google geocoders, it is a numeric value of the
353
352
  # precision. see http://code.google.com/apis/maps/documentation/geocoding/#GeocodingAccuracy
354
353
  attr_accessor :accuracy
@@ -358,10 +357,13 @@ module Geokit
358
357
  @all = [self]
359
358
 
360
359
  @street_address=h[:street_address]
360
+ @street_number=nil
361
+ @street_name=nil
361
362
  @city=h[:city]
362
363
  @state=h[:state]
363
364
  @zip=h[:zip]
364
- @country_code=h[:country_code]
365
+ @country_code=h[:country_code]
366
+ @province = h[:province]
365
367
  @success=false
366
368
  @precision='unknown'
367
369
  @full_address=nil
@@ -384,21 +386,22 @@ module Geokit
384
386
  @full_address ? @full_address : to_geocodeable_s
385
387
  end
386
388
 
387
- # Extracts the street number from the street address if the street address
388
- # has a value.
389
+ # Extracts the street number from the street address where possible.
389
390
  def street_number
390
- street_address[/(\d*)/] if street_address
391
+ @street_number ||= street_address[/(\d*)/] if street_address
392
+ @street_number
391
393
  end
392
394
 
393
- # Returns the street name portion of the street address.
395
+ # Returns the street name portion of the street address where possible
394
396
  def street_name
395
- street_address[street_number.length, street_address.length].strip if street_address
397
+ @street_name||=street_address[street_number.length, street_address.length].strip if street_address
398
+ @street_name
396
399
  end
397
400
 
398
401
  # gives you all the important fields as key-value pairs
399
402
  def hash
400
403
  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) }
404
+ [: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
405
  res
403
406
  end
404
407
  alias to_hash hash
@@ -410,14 +413,17 @@ module Geokit
410
413
 
411
414
  # Sets the street address after capitalizing each word within the street address.
412
415
  def street_address=(address)
413
- #@street_address = Geokit::Inflector::titleize(address) if address
414
- @street_address = address if address
416
+ if address and not ['google','google3'].include?(self.provider)
417
+ @street_address = Geokit::Inflector::titleize(address)
418
+ else
419
+ @street_address = address
420
+ end
415
421
  end
416
422
 
417
423
  # Returns a comma-delimited string consisting of the street address, city, state,
418
424
  # zip, and country code. Only includes those attributes that are non-blank.
419
425
  def to_geocodeable_s
420
- a=[street_address, city, state, zip, country_code].compact
426
+ a=[street_address, district, city, province, state, zip, country_code].compact
421
427
  a.delete_if { |e| !e || e == '' }
422
428
  a.join(', ')
423
429
  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: \nstreet_name: \nstreet_number: \nsuccess: false\nzip: \"94105\"\n",
69
69
  @loc.to_yaml)
70
70
  end
71
71
 
@@ -33,8 +33,8 @@ class IpGeocoderTest < BaseGeocoderTest #:nodoc: all
33
33
  GeoKit::Geocoders::GeoPluginGeocoder.expects(:call_geocoder_service).with(url).returns(success)
34
34
  location = GeoKit::Geocoders::GeoPluginGeocoder.geocode('200.150.38.66')
35
35
  assert_not_nil location
36
- assert_equal -19.916700, location.lat
37
- assert_equal -43.933300, location.lng
36
+ assert_equal(-19.916700, location.lat)
37
+ assert_equal(-43.933300, location.lng)
38
38
  assert_equal "Belo Horizonte", location.city
39
39
  assert_equal "Minas Gerais", location.state
40
40
  assert_equal "BR", location.country_code
@@ -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
@@ -0,0 +1,536 @@
1
+ require File.join(File.dirname(__FILE__), 'test_base_geocoder')
2
+
3
+ Geokit::Geocoders::google = 'Google'
4
+
5
+ class GoogleGeocoder3Test < BaseGeocoderTest #:nodoc: all
6
+
7
+ GOOGLE3_FULL=%q/
8
+ {
9
+ "status": "OK",
10
+ "results": [ {
11
+ "types": [ "street_address" ],
12
+ "formatted_address": "100 Spear St, San Francisco, CA 94105, USA",
13
+ "address_components": [ {
14
+ "long_name": "100",
15
+ "short_name": "100",
16
+ "types": [ "street_number" ]
17
+ }, {
18
+ "long_name": "Spear St",
19
+ "short_name": "Spear St",
20
+ "types": [ "route" ]
21
+ }, {
22
+ "long_name": "San Francisco",
23
+ "short_name": "San Francisco",
24
+ "types": [ "locality", "political" ]
25
+ }, {
26
+ "long_name": "San Francisco",
27
+ "short_name": "San Francisco",
28
+ "types": [ "administrative_area_level_3", "political" ]
29
+ }, {
30
+ "long_name": "San Francisco",
31
+ "short_name": "San Francisco",
32
+ "types": [ "administrative_area_level_2", "political" ]
33
+ }, {
34
+ "long_name": "California",
35
+ "short_name": "CA",
36
+ "types": [ "administrative_area_level_1", "political" ]
37
+ }, {
38
+ "long_name": "United States",
39
+ "short_name": "US",
40
+ "types": [ "country", "political" ]
41
+ }, {
42
+ "long_name": "94105",
43
+ "short_name": "94105",
44
+ "types": [ "postal_code" ]
45
+ } ],
46
+ "geometry": {
47
+ "location": {
48
+ "lat": 37.7921509,
49
+ "lng": -122.3940000
50
+ },
51
+ "location_type": "ROOFTOP",
52
+ "viewport": {
53
+ "southwest": {
54
+ "lat": 37.7890033,
55
+ "lng": -122.3971476
56
+ },
57
+ "northeast": {
58
+ "lat": 37.7952985,
59
+ "lng": -122.3908524
60
+ }
61
+ }
62
+ }
63
+ } ]
64
+ }
65
+ /.strip
66
+
67
+ GOOGLE3_CITY=%q/
68
+ {
69
+ "status": "OK",
70
+ "results": [ {
71
+ "types": [ "locality", "political" ],
72
+ "formatted_address": "San Francisco, CA, USA",
73
+ "address_components": [ {
74
+ "long_name": "San Francisco",
75
+ "short_name": "San Francisco",
76
+ "types": [ "locality", "political" ]
77
+ }, {
78
+ "long_name": "San Francisco",
79
+ "short_name": "San Francisco",
80
+ "types": [ "administrative_area_level_2", "political" ]
81
+ }, {
82
+ "long_name": "California",
83
+ "short_name": "CA",
84
+ "types": [ "administrative_area_level_1", "political" ]
85
+ }, {
86
+ "long_name": "United States",
87
+ "short_name": "US",
88
+ "types": [ "country", "political" ]
89
+ } ],
90
+ "geometry": {
91
+ "location": {
92
+ "lat": 37.7749295,
93
+ "lng": -122.4194155
94
+ },
95
+ "location_type": "APPROXIMATE",
96
+ "viewport": {
97
+ "southwest": {
98
+ "lat": 37.7043396,
99
+ "lng": -122.5474749
100
+ },
101
+ "northeast": {
102
+ "lat": 37.8454521,
103
+ "lng": -122.2913561
104
+ }
105
+ },
106
+ "bounds": {
107
+ "southwest": {
108
+ "lat": 37.7034000,
109
+ "lng": -122.5270000
110
+ },
111
+ "northeast": {
112
+ "lat": 37.8120000,
113
+ "lng": -122.3482000
114
+ }
115
+ }
116
+ }
117
+ } ]
118
+ }
119
+ /.strip
120
+ GOOGLE3_MULTI=%q/
121
+ {
122
+ "status": "OK",
123
+ "results": [ {
124
+ "types": [ "street_address" ],
125
+ "formatted_address": "Via Sandro Pertini, 8, 20010 Mesero MI, Italy",
126
+ "address_components": [ {
127
+ "long_name": "8",
128
+ "short_name": "8",
129
+ "types": [ "street_number" ]
130
+ }, {
131
+ "long_name": "Via Sandro Pertini",
132
+ "short_name": "Via Sandro Pertini",
133
+ "types": [ "route" ]
134
+ }, {
135
+ "long_name": "Mesero",
136
+ "short_name": "Mesero",
137
+ "types": [ "locality", "political" ]
138
+ }, {
139
+ "long_name": "Milan",
140
+ "short_name": "MI",
141
+ "types": [ "administrative_area_level_2", "political" ]
142
+ }, {
143
+ "long_name": "Lombardy",
144
+ "short_name": "Lombardy",
145
+ "types": [ "administrative_area_level_1", "political" ]
146
+ }, {
147
+ "long_name": "Italy",
148
+ "short_name": "IT",
149
+ "types": [ "country", "political" ]
150
+ }, {
151
+ "long_name": "20010",
152
+ "short_name": "20010",
153
+ "types": [ "postal_code" ]
154
+ } ],
155
+ "geometry": {
156
+ "location": {
157
+ "lat": 45.4966218,
158
+ "lng": 8.8526940
159
+ },
160
+ "location_type": "RANGE_INTERPOLATED",
161
+ "viewport": {
162
+ "southwest": {
163
+ "lat": 45.4934754,
164
+ "lng": 8.8495559
165
+ },
166
+ "northeast": {
167
+ "lat": 45.4997707,
168
+ "lng": 8.8558512
169
+ }
170
+ },
171
+ "bounds": {
172
+ "southwest": {
173
+ "lat": 45.4966218,
174
+ "lng": 8.8526940
175
+ },
176
+ "northeast": {
177
+ "lat": 45.4966243,
178
+ "lng": 8.8527131
179
+ }
180
+ }
181
+ },
182
+ "partial_match": true
183
+ },
184
+ {
185
+ "types": [ "route" ],
186
+ "formatted_address": "Via Sandro Pertini, 20010 Ossona MI, Italy",
187
+ "address_components": [ {
188
+ "long_name": "Via Sandro Pertini",
189
+ "short_name": "Via Sandro Pertini",
190
+ "types": [ "route" ]
191
+ }, {
192
+ "long_name": "Ossona",
193
+ "short_name": "Ossona",
194
+ "types": [ "locality", "political" ]
195
+ }, {
196
+ "long_name": "Milan",
197
+ "short_name": "MI",
198
+ "types": [ "administrative_area_level_2", "political" ]
199
+ }, {
200
+ "long_name": "Lombardy",
201
+ "short_name": "Lombardy",
202
+ "types": [ "administrative_area_level_1", "political" ]
203
+ }, {
204
+ "long_name": "Italy",
205
+ "short_name": "IT",
206
+ "types": [ "country", "political" ]
207
+ }, {
208
+ "long_name": "20010",
209
+ "short_name": "20010",
210
+ "types": [ "postal_code" ]
211
+ } ],
212
+ "geometry": {
213
+ "location": {
214
+ "lat": 45.5074444,
215
+ "lng": 8.9023200
216
+ },
217
+ "location_type": "GEOMETRIC_CENTER",
218
+ "viewport": {
219
+ "southwest": {
220
+ "lat": 45.5043320,
221
+ "lng": 8.8990670
222
+ },
223
+ "northeast": {
224
+ "lat": 45.5106273,
225
+ "lng": 8.9053622
226
+ }
227
+ },
228
+ "bounds": {
229
+ "southwest": {
230
+ "lat": 45.5064427,
231
+ "lng": 8.9020024
232
+ },
233
+ "northeast": {
234
+ "lat": 45.5085166,
235
+ "lng": 8.9024268
236
+ }
237
+ }
238
+ }
239
+ }
240
+ ]
241
+ }
242
+ /.strip
243
+ GOOGLE3_REVERSE_MADRID=%q/
244
+ {
245
+ "status": "OK",
246
+ "results": [ {
247
+ "types": [ ],
248
+ "formatted_address": "Calle de las Carretas, 28013 Madrid, Spain",
249
+ "address_components": [ {
250
+ "long_name": "Calle de las Carretas",
251
+ "short_name": "Calle de las Carretas",
252
+ "types": [ "route" ]
253
+ }, {
254
+ "long_name": "Madrid",
255
+ "short_name": "Madrid",
256
+ "types": [ "locality", "political" ]
257
+ }, {
258
+ "long_name": "Madrid",
259
+ "short_name": "M",
260
+ "types": [ "administrative_area_level_2", "political" ]
261
+ }, {
262
+ "long_name": "Madrid",
263
+ "short_name": "Madrid",
264
+ "types": [ "administrative_area_level_1", "political" ]
265
+ }, {
266
+ "long_name": "Spain",
267
+ "short_name": "ES",
268
+ "types": [ "country", "political" ]
269
+ }, {
270
+ "long_name": "28013",
271
+ "short_name": "28013",
272
+ "types": [ "postal_code" ]
273
+ } ],
274
+ "geometry": {
275
+ "location": {
276
+ "lat": 40.4166824,
277
+ "lng": -3.7033411
278
+ },
279
+ "location_type": "APPROXIMATE",
280
+ "viewport": {
281
+ "southwest": {
282
+ "lat": 40.4135351,
283
+ "lng": -3.7064880
284
+ },
285
+ "northeast": {
286
+ "lat": 40.4198303,
287
+ "lng": -3.7001927
288
+ }
289
+ },
290
+ "bounds": {
291
+ "southwest": {
292
+ "lat": 40.4166419,
293
+ "lng": -3.7033685
294
+ },
295
+ "northeast": {
296
+ "lat": 40.4167235,
297
+ "lng": -3.7033122
298
+ }
299
+ }
300
+ }
301
+ } ]
302
+ }
303
+ /
304
+ GOOGLE3_COUNTRY_CODE_BIASED_RESULT=%q/
305
+ {
306
+ "status": "OK",
307
+ "results": [ {
308
+ "types": [ "administrative_area_level_2", "political" ],
309
+ "formatted_address": "Syracuse, Italy",
310
+ "address_components": [ {
311
+ "long_name": "Syracuse",
312
+ "short_name": "SR",
313
+ "types": [ "administrative_area_level_2", "political" ]
314
+ }, {
315
+ "long_name": "Sicily",
316
+ "short_name": "Sicily",
317
+ "types": [ "administrative_area_level_1", "political" ]
318
+ }, {
319
+ "long_name": "Italy",
320
+ "short_name": "IT",
321
+ "types": [ "country", "political" ]
322
+ } ],
323
+ "geometry": {
324
+ "location": {
325
+ "lat": 37.0630218,
326
+ "lng": 14.9856176
327
+ },
328
+ "location_type": "APPROXIMATE",
329
+ "viewport": {
330
+ "southwest": {
331
+ "lat": 36.7775664,
332
+ "lng": 14.4733800
333
+ },
334
+ "northeast": {
335
+ "lat": 37.3474070,
336
+ "lng": 15.4978552
337
+ }
338
+ },
339
+ "bounds": {
340
+ "southwest": {
341
+ "lat": 36.6441736,
342
+ "lng": 14.7724913
343
+ },
344
+ "northeast": {
345
+ "lat": 37.4125978,
346
+ "lng": 15.3367367
347
+ }
348
+ }
349
+ }
350
+ } ]
351
+ }
352
+ /
353
+ GOOGLE3_TOO_MANY=%q/
354
+ {
355
+ "status": "OVER_QUERY_LIMIT"
356
+ }
357
+ /
358
+ def setup
359
+ super
360
+ @google_full_hash = {:street_address=>"100 Spear St", :city=>"San Francisco", :state=>"CA", :zip=>"94105", :country_code=>"US"}
361
+ @google_city_hash = {:city=>"San Francisco", :state=>"CA"}
362
+
363
+ @google_full_loc = Geokit::GeoLoc.new(@google_full_hash)
364
+ @google_city_loc = Geokit::GeoLoc.new(@google_city_hash)
365
+ end
366
+
367
+ def test_google3_full_address
368
+ response = MockSuccess.new
369
+ response.expects(:body).returns(GOOGLE3_FULL)
370
+ url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector::url_escape(@address)}"
371
+ Geokit::Geocoders::GoogleGeocoder3.expects(:call_geocoder_service).with(url).returns(response)
372
+ res=Geokit::Geocoders::GoogleGeocoder3.geocode(@address)
373
+ assert_equal "CA", res.state
374
+ assert_equal "San Francisco", res.city
375
+ assert_equal "37.7921509,-122.394", res.ll # slightly dif from yahoo
376
+ assert res.is_us?
377
+ assert_equal "100 Spear St, San Francisco, CA 94105, USA", res.full_address #slightly different from yahoo
378
+ assert_equal "google3", res.provider
379
+ end
380
+
381
+ def test_google3_full_address_with_geo_loc
382
+ response = MockSuccess.new
383
+ response.expects(:body).returns(GOOGLE3_FULL)
384
+ url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector::url_escape(@full_address_short_zip)}"
385
+ Geokit::Geocoders::GoogleGeocoder3.expects(:call_geocoder_service).with(url).returns(response)
386
+ res=Geokit::Geocoders::GoogleGeocoder3.geocode(@google_full_loc)
387
+ assert_equal "CA", res.state
388
+ assert_equal "San Francisco", res.city
389
+ assert_equal "37.7921509,-122.394", res.ll # slightly dif from yahoo
390
+ assert res.is_us?
391
+ assert_equal "100 Spear St, San Francisco, CA 94105, USA", res.full_address #slightly different from yahoo
392
+ assert_equal "google3", res.provider
393
+ end
394
+
395
+ def test_google3_full_address_accuracy
396
+ response = MockSuccess.new
397
+ response.expects(:body).returns(GOOGLE3_FULL)
398
+ url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector::url_escape(@full_address_short_zip)}"
399
+ Geokit::Geocoders::GoogleGeocoder3.expects(:call_geocoder_service).with(url).returns(response)
400
+ res=Geokit::Geocoders::GoogleGeocoder3.geocode(@google_full_loc)
401
+ assert_equal 9, res.accuracy
402
+ end
403
+
404
+ def test_google3_city
405
+ response = MockSuccess.new
406
+ response.expects(:body).returns(GOOGLE3_CITY)
407
+ url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector::url_escape(@address)}"
408
+ Geokit::Geocoders::GoogleGeocoder3.expects(:call_geocoder_service).with(url).returns(response)
409
+ res=Geokit::Geocoders::GoogleGeocoder3.do_geocode(@address)
410
+ assert_nil res.street_address
411
+ assert_equal "CA", res.state
412
+ assert_equal "San Francisco", res.city
413
+ assert_equal "37.7749295,-122.4194155", res.ll
414
+ assert res.is_us?
415
+ assert_equal "San Francisco, CA, USA", res.full_address
416
+ assert_equal "google3", res.provider
417
+ end
418
+
419
+ def test_google3_city_accuracy
420
+ response = MockSuccess.new
421
+ response.expects(:body).returns(GOOGLE3_CITY)
422
+ url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector::url_escape(@address)}"
423
+ Geokit::Geocoders::GoogleGeocoder3.expects(:call_geocoder_service).with(url).returns(response)
424
+ res=Geokit::Geocoders::GoogleGeocoder3.geocode(@address)
425
+ assert_equal 4, res.accuracy
426
+ end
427
+
428
+ def test_google3_city_with_geo_loc
429
+ response = MockSuccess.new
430
+ response.expects(:body).returns(GOOGLE3_CITY)
431
+ url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector::url_escape(@address)}"
432
+ Geokit::Geocoders::GoogleGeocoder3.expects(:call_geocoder_service).with(url).returns(response)
433
+ res=Geokit::Geocoders::GoogleGeocoder3.geocode(@google_city_loc)
434
+ assert_equal "CA", res.state
435
+ assert_equal "San Francisco", res.city
436
+ assert_equal "37.7749295,-122.4194155", res.ll
437
+ assert res.is_us?
438
+ assert_equal "San Francisco, CA, USA", res.full_address
439
+ assert_nil res.street_address
440
+ assert_equal "google3", res.provider
441
+ end
442
+
443
+ def test_google3_suggested_bounds
444
+ response = MockSuccess.new
445
+ response.expects(:body).returns(GOOGLE3_FULL)
446
+ url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector::url_escape(@full_address_short_zip)}"
447
+ Geokit::Geocoders::GoogleGeocoder3.expects(:call_geocoder_service).with(url).returns(response)
448
+ res = Geokit::Geocoders::GoogleGeocoder3.geocode(@google_full_loc)
449
+
450
+ assert_instance_of Geokit::Bounds, res.suggested_bounds
451
+ assert_equal Geokit::Bounds.new(Geokit::LatLng.new(37.7890033, -122.3971476), Geokit::LatLng.new(37.7952985, -122.3908524)), res.suggested_bounds
452
+ end
453
+
454
+ def test_service_unavailable
455
+ response = MockFailure.new
456
+ url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector::url_escape(@address)}"
457
+ Geokit::Geocoders::GoogleGeocoder3.expects(:call_geocoder_service).with(url).returns(response)
458
+ assert !Geokit::Geocoders::GoogleGeocoder3.geocode(@google_city_loc).success
459
+ end
460
+
461
+ def test_multiple_results
462
+ #Geokit::Geocoders::GoogleGeocoder3.do_geocode('via Sandro Pertini 8, Ossona, MI')
463
+ response = MockSuccess.new
464
+ response.expects(:body).returns(GOOGLE3_MULTI)
465
+ url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector.url_escape('via Sandro Pertini 8, Ossona, MI')}"
466
+ Geokit::Geocoders::GoogleGeocoder3.expects(:call_geocoder_service).with(url).returns(response)
467
+ res=Geokit::Geocoders::GoogleGeocoder3.geocode('via Sandro Pertini 8, Ossona, MI')
468
+ assert_equal "Lombardy", res.state
469
+ assert_equal "Mesero", res.city
470
+ assert_equal "45.4966218,8.852694", res.ll
471
+ assert !res.is_us?
472
+ assert_equal "Via Sandro Pertini, 8, 20010 Mesero MI, Italy", res.full_address
473
+ assert_equal "8 Via Sandro Pertini", res.street_address
474
+ assert_equal "google3", res.provider
475
+
476
+ assert_equal 2, res.all.size
477
+ res = res.all[1]
478
+ assert_equal "Lombardy", res.state
479
+ assert_equal "Ossona", res.city
480
+ assert_equal "45.5074444,8.90232", res.ll
481
+ assert !res.is_us?
482
+ assert_equal "Via Sandro Pertini, 20010 Ossona MI, Italy", res.full_address
483
+ assert_equal "Via Sandro Pertini", res.street_address
484
+ assert_equal "google3", res.provider
485
+ end
486
+ #
487
+ def test_reverse_geocode
488
+ #Geokit::Geocoders::GoogleGeocoder3.do_reverse_geocode("40.4167413, -3.7032498")
489
+ madrid = Geokit::GeoLoc.new
490
+ madrid.lat, madrid.lng = "40.4167413", "-3.7032498"
491
+ response = MockSuccess.new
492
+ response.expects(:body).returns(GOOGLE3_REVERSE_MADRID)
493
+ url = "http://maps.google.com/maps/api/geocode/json?sensor=false&latlng=#{Geokit::Inflector::url_escape(madrid.ll)}"
494
+ Geokit::Geocoders::GoogleGeocoder3.expects(:call_geocoder_service).with(url).
495
+ returns(response)
496
+ res=Geokit::Geocoders::GoogleGeocoder3.do_reverse_geocode(madrid.ll)
497
+
498
+ assert_equal madrid.lat.to_s.slice(1..5), res.lat.to_s.slice(1..5)
499
+ assert_equal madrid.lng.to_s.slice(1..5), res.lng.to_s.slice(1..5)
500
+ assert_equal "ES", res.country_code
501
+ assert_equal "google3", res.provider
502
+
503
+ assert_equal "Madrid", res.city
504
+ assert_equal "Madrid", res.state
505
+
506
+ assert_equal "Spain", res.country
507
+ assert_equal "street", res.precision
508
+ assert_equal true, res.success
509
+
510
+ assert_equal "Calle de las Carretas, 28013 Madrid, Spain", res.full_address
511
+ assert_equal "28013", res.zip
512
+ assert_equal "Calle de las Carretas", res.street_address
513
+ end
514
+
515
+ def test_country_code_biasing
516
+ response = MockSuccess.new
517
+ response.expects(:body).returns(GOOGLE3_COUNTRY_CODE_BIASED_RESULT)
518
+
519
+ url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=Syracuse&region=it"
520
+ Geokit::Geocoders::GoogleGeocoder3.expects(:call_geocoder_service).with(url).returns(response)
521
+ biased_result = Geokit::Geocoders::GoogleGeocoder3.geocode('Syracuse', :bias => 'it')
522
+
523
+ assert_equal 'IT', biased_result.country_code
524
+ assert_equal 'Sicily', biased_result.state
525
+ end
526
+
527
+ def test_too_many_queries
528
+ response = MockSuccess.new
529
+ response.expects(:body).returns(GOOGLE3_TOO_MANY)
530
+ url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector.url_escape(@address)}"
531
+ Geokit::Geocoders::GoogleGeocoder3.expects(:call_geocoder_service).with(url).returns(response)
532
+ assert_raise Geokit::TooManyQueriesError do
533
+ res=Geokit::Geocoders::GoogleGeocoder3.geocode(@address)
534
+ end
535
+ end
536
+ end
@@ -4,8 +4,8 @@ require File.join(File.dirname(__FILE__), 'test_base_geocoder')
4
4
  class IpGeocoderTest < BaseGeocoderTest #:nodoc: all
5
5
 
6
6
  IP_FAILURE=<<-EOF
7
- Country: (Private Address) (XX)
8
- City: (Private Address)
7
+ Country: SWITZERLAND (CH)
8
+ City: (Unknown City)
9
9
  Latitude:
10
10
  Longitude:
11
11
  EOF
@@ -24,10 +24,22 @@ class IpGeocoderTest < BaseGeocoderTest #:nodoc: all
24
24
  Longitude: 12.9167
25
25
  EOF
26
26
 
27
- def setup
28
- super
29
- @success.provider = "hostip"
30
- end
27
+ PRIVATE_IPS_TO_TEST = [
28
+ '10.10.10.10',
29
+ '172.16.1.3',
30
+ '172.22.3.42',
31
+ '172.30.254.164',
32
+ '192.168.1.1',
33
+ '0.0.0.0',
34
+ '127.0.0.1',
35
+ '240.3.4.5',
36
+ '225.1.6.55'
37
+ ].freeze
38
+
39
+ def setup
40
+ super
41
+ @success.provider = "hostip"
42
+ end
31
43
 
32
44
  def test_successful_lookup
33
45
  success = MockSuccess.new
@@ -64,14 +76,24 @@ class IpGeocoderTest < BaseGeocoderTest #:nodoc: all
64
76
  def test_failed_lookup
65
77
  failure = MockSuccess.new
66
78
  failure.expects(:body).returns(IP_FAILURE)
67
- url = 'http://api.hostip.info/get_html.php?ip=10.10.10.10&position=true'
79
+ url = 'http://api.hostip.info/get_html.php?ip=128.178.0.0&position=true'
68
80
  GeoKit::Geocoders::IpGeocoder.expects(:call_geocoder_service).with(url).returns(failure)
69
- location = GeoKit::Geocoders::IpGeocoder.geocode("10.10.10.10")
81
+ location = GeoKit::Geocoders::IpGeocoder.geocode("128.178.0.0")
70
82
  assert_not_nil location
71
83
  assert !location.success?
72
84
  end
73
85
 
86
+ def test_private_ips
87
+ GeoKit::Geocoders::IpGeocoder.expects(:call_geocoder_service).never
88
+ PRIVATE_IPS_TO_TEST.each do |ip|
89
+ location = GeoKit::Geocoders::IpGeocoder.geocode(ip)
90
+ assert_not_nil location
91
+ assert !location.success?
92
+ end
93
+ end
94
+
74
95
  def test_invalid_ip
96
+ GeoKit::Geocoders::IpGeocoder.expects(:call_geocoder_service).never
75
97
  location = GeoKit::Geocoders::IpGeocoder.geocode("blah")
76
98
  assert_not_nil location
77
99
  assert !location.success?
@@ -79,9 +101,9 @@ class IpGeocoderTest < BaseGeocoderTest #:nodoc: all
79
101
 
80
102
  def test_service_unavailable
81
103
  failure = MockFailure.new
82
- url = 'http://api.hostip.info/get_html.php?ip=10.10.10.10&position=true'
104
+ url = 'http://api.hostip.info/get_html.php?ip=12.215.42.19&position=true'
83
105
  GeoKit::Geocoders::IpGeocoder.expects(:call_geocoder_service).with(url).returns(failure)
84
- location = GeoKit::Geocoders::IpGeocoder.geocode("10.10.10.10")
106
+ location = GeoKit::Geocoders::IpGeocoder.geocode("12.215.42.19")
85
107
  assert_not_nil location
86
108
  assert !location.success?
87
109
  end
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: darrell-geokit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1.1
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 5
8
+ - 0
9
+ - 1
10
+ version: 1.5.0.1
5
11
  platform: ruby
6
12
  authors:
7
13
  - Andre Lewis and Bill Eisenhauer
@@ -9,10 +15,21 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2009-06-15 00:00:00 -07:00
18
+ date: 2009-08-02 00:00:00 -07:00
13
19
  default_executable:
14
- dependencies: []
15
-
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: json_pure
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
16
33
  description: Geokit Gem
17
34
  email:
18
35
  - andre@earthcode.com / bill_eisenhauer@yahoo.com
@@ -34,6 +51,7 @@ files:
34
51
  - test/test_bounds.rb
35
52
  - test/test_ca_geocoder.rb
36
53
  - test/test_geoloc.rb
54
+ - test/test_google_geocoder3.rb
37
55
  - test/test_google_geocoder.rb
38
56
  - test/test_latlng.rb
39
57
  - test/test_multi_geocoder.rb
@@ -41,7 +59,8 @@ files:
41
59
  - test/test_yahoo_geocoder.rb
42
60
  has_rdoc: true
43
61
  homepage: http://geokit.rubyforge.org
44
- licenses:
62
+ licenses: []
63
+
45
64
  post_install_message:
46
65
  rdoc_options:
47
66
  - --main
@@ -52,18 +71,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
52
71
  requirements:
53
72
  - - ">="
54
73
  - !ruby/object:Gem::Version
74
+ segments:
75
+ - 0
55
76
  version: "0"
56
- version:
57
77
  required_rubygems_version: !ruby/object:Gem::Requirement
58
78
  requirements:
59
79
  - - ">="
60
80
  - !ruby/object:Gem::Version
81
+ segments:
82
+ - 0
61
83
  version: "0"
62
- version:
63
84
  requirements: []
64
85
 
65
86
  rubyforge_project: geokit
66
- rubygems_version: 1.3.5
87
+ rubygems_version: 1.3.6
67
88
  signing_key:
68
89
  specification_version: 2
69
90
  summary: none
@@ -73,6 +94,7 @@ test_files:
73
94
  - test/test_ca_geocoder.rb
74
95
  - test/test_geoloc.rb
75
96
  - test/test_geoplugin_geocoder.rb
97
+ - test/test_google_geocoder3.rb
76
98
  - test/test_google_geocoder.rb
77
99
  - test/test_google_reverse_geocoder.rb
78
100
  - test/test_inflector.rb