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 +1 -1
- data/lib/geokit.rb +1 -1
- data/lib/geokit/geocoders.rb +204 -11
- data/lib/geokit/mappable.rb +20 -14
- data/test/test_base_geocoder.rb +1 -1
- data/test/test_geoloc.rb +1 -1
- data/test/test_geoplugin_geocoder.rb +2 -2
- data/test/test_google_geocoder.rb +44 -0
- data/test/test_google_geocoder3.rb +536 -0
- data/test/test_ipgeocoder.rb +32 -10
- metadata +30 -8
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::
|
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
data/lib/geokit/geocoders.rb
CHANGED
@@ -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
|
-
@@
|
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::
|
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.
|
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
|
-
|
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
|
-
|
472
|
-
|
473
|
-
|
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
|
+
"®ion=#{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
|
# -------------------------------------------------------------------------------------------
|
data/lib/geokit/mappable.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
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
|
-
|
414
|
-
|
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
|
data/test/test_base_geocoder.rb
CHANGED
@@ -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::
|
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
|
37
|
-
assert_equal
|
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®ion=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
|
data/test/test_ipgeocoder.rb
CHANGED
@@ -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:
|
8
|
-
City: (
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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=
|
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("
|
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=
|
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("
|
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
|
-
|
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-
|
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.
|
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
|