darrell-geokit 1.4.1.1 → 1.5.0.1

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/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