darrell-geokit 1.4.1.1 → 1.5.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +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
|