geokit 1.3.0 → 1.3.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/History.txt +4 -0
- data/README.markdown +43 -1
- data/Rakefile +8 -0
- data/geokit.gemspec +7 -3
- data/lib/geokit.rb +1 -1
- data/lib/geokit/geocoders.rb +41 -35
- data/lib/geokit/mappable.rb +1 -1
- data/test/test_multi_ip_geocoder.rb +38 -0
- metadata +9 -6
data/History.txt
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
=== 1.3.1 / 2009-05-21
|
2
|
+
* Support for External geocoders file (thanks dreamcat4)
|
3
|
+
* Support multiple ip geocoders, including new setting for ip_provider_order (thanks dreamcat4)
|
4
|
+
|
1
5
|
=== 1.3.0 / 2009-04-11
|
2
6
|
* Added capability to define multiple API keys for different domains that may be pointing to the same application (thanks Glenn Powell)
|
3
7
|
* Added numeric accuracy accessor for Yahoo and Google geocoders (thanks Andrew Fecheyr Lippens)
|
data/README.markdown
CHANGED
@@ -96,6 +96,10 @@ If you're using this gem by itself, here are the configuration options:
|
|
96
96
|
# and http://geocoder.ca/?register=1
|
97
97
|
Geokit::Geocoders::geocoder_ca = false
|
98
98
|
|
99
|
+
# require "external_geocoder.rb"
|
100
|
+
# Please see the section "writing your own geocoders" for more information.
|
101
|
+
# Geokit::Geocoders::external_key = 'REPLACE_WITH_YOUR_API_KEY'
|
102
|
+
|
99
103
|
# This is the order in which the geocoders are called in a failover scenario
|
100
104
|
# If you only want to use a single geocoder, put a single symbol in the array.
|
101
105
|
# Valid symbols are :google, :yahoo, :us, and :ca.
|
@@ -103,6 +107,10 @@ If you're using this gem by itself, here are the configuration options:
|
|
103
107
|
# various geocoders. Make sure you read up on relevant Terms of Use for each
|
104
108
|
# geocoder you are going to use.
|
105
109
|
Geokit::Geocoders::provider_order = [:google,:us]
|
110
|
+
|
111
|
+
# The IP provider order. Valid symbols are :ip,:geo_plugin.
|
112
|
+
# As before, make sure you read up on relevant Terms of Use for each.
|
113
|
+
# Geokit::Geocoders::ip_provider_order = [:external,:geo_plugin,:ip]
|
106
114
|
|
107
115
|
If you're using this gem with the [geokit-rails plugin](http://github.com/andre/geokit-rails/tree/master), the plugin
|
108
116
|
creates a template with these settings and places it in `config/initializers/geokit_config.rb`.
|
@@ -123,7 +131,11 @@ creates a template with these settings and places it in `config/initializers/geo
|
|
123
131
|
* Geoplugin.net -- another IP address geocoder
|
124
132
|
|
125
133
|
### The Multigeocoder
|
126
|
-
|
134
|
+
Multi Geocoder - provides failover for the physical location geocoders, and also IP address geocoders. Its configured by setting Geokit::Geocoders::provider_order, and Geokit::Geocoders::ip_provider_order. You should call the Multi-Geocoder with its :geocode method, supplying one address parameter which is either a real street address, or an ip address. For example:
|
135
|
+
|
136
|
+
Geokit::Geocoders::MultiGeocoder.geocode("900 Sycamore Drive")
|
137
|
+
|
138
|
+
Geokit::Geocoders::MultiGeocoder.geocode("12.12.12.12")
|
127
139
|
|
128
140
|
## MULTIPLE RESULTS
|
129
141
|
Some geocoding services will return multple results if the there isn't one clear result.
|
@@ -165,6 +177,36 @@ geocoders.rb contains all the geocoder implemenations. All the gercoders
|
|
165
177
|
inherit from a common base (class Geocoder) and implement the private method
|
166
178
|
do_geocode.
|
167
179
|
|
180
|
+
## WRITING YOUR OWN GEOCODERS
|
181
|
+
|
182
|
+
If you would like to write your own geocoders, you can do so by requiring 'geokit' or 'geokit/geocoders.rb' in a new file and subclassing the base class (which is class "Geocoder").
|
183
|
+
You must then also require such extenal file back in your main geokit configuration.
|
184
|
+
|
185
|
+
require "geokit"
|
186
|
+
|
187
|
+
module Geokit
|
188
|
+
module Geocoders
|
189
|
+
|
190
|
+
# Should be overriden as Geokit::Geocoders::external_key in your configuration file
|
191
|
+
@@external_key = 'REPLACE_WITH_YOUR_API_KEY'
|
192
|
+
__define_accessors
|
193
|
+
|
194
|
+
# Replace name 'External' (below) with the name of your custom geocoder class
|
195
|
+
# and use :external to specify this geocoder in your list of geocoders.
|
196
|
+
class ExternalGeocoder < Geocoder
|
197
|
+
private
|
198
|
+
def self.do_geocode(address)
|
199
|
+
# Main geocoding method
|
200
|
+
end
|
201
|
+
|
202
|
+
def self.parse_http_resp(body) # :nodoc:
|
203
|
+
# Helper method to parse http response. See geokit/geocoders.rb.
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
168
210
|
## GOOGLE GROUP
|
169
211
|
|
170
212
|
Follow the Google Group for updates and discussion on Geokit: http://groups.google.com/group/geokit
|
data/Rakefile
CHANGED
@@ -4,6 +4,14 @@ require 'rubygems'
|
|
4
4
|
require 'hoe'
|
5
5
|
require './lib/geokit.rb'
|
6
6
|
|
7
|
+
# undefined method `empty?' for nil:NilClass
|
8
|
+
# /Library/Ruby/Site/1.8/rubygems/specification.rb:886:in `validate'
|
9
|
+
class NilClass
|
10
|
+
def empty?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
7
15
|
project=Hoe.new('geokit', Geokit::VERSION) do |p|
|
8
16
|
#p.rubyforge_name = 'geokit' # if different than lowercase project name
|
9
17
|
p.developer('Andre Lewis', 'andre@earthcode.com')
|
data/geokit.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{geokit}
|
5
|
-
s.version = "1.3.
|
5
|
+
s.version = "1.3.1"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Andre Lewis and Bill Eisenhauer"]
|
9
|
-
s.date = %q{2009-
|
9
|
+
s.date = %q{2009-04-11}
|
10
10
|
s.description = %q{Geokit Gem}
|
11
11
|
s.email = ["andre@earthcode.com / bill_eisenhauer@yahoo.com"]
|
12
12
|
s.extra_rdoc_files = ["Manifest.txt", "README.markdown"]
|
@@ -18,7 +18,10 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.rubyforge_project = %q{geokit}
|
19
19
|
s.rubygems_version = %q{1.3.1}
|
20
20
|
s.summary = %q{none}
|
21
|
-
s.test_files = ["test/test_base_geocoder.rb", "test/test_bounds.rb", "test/test_ca_geocoder.rb", "test/test_geoloc.rb",
|
21
|
+
s.test_files = ["test/test_base_geocoder.rb", "test/test_bounds.rb", "test/test_ca_geocoder.rb", "test/test_geoloc.rb",
|
22
|
+
"test/test_geoplugin_geocoder.rb", "test/test_google_geocoder.rb", "test/test_google_reverse_geocoder.rb",
|
23
|
+
"test/test_inflector.rb", "test/test_ipgeocoder.rb", "test/test_latlng.rb", "test/test_multi_geocoder.rb",
|
24
|
+
"test/test_multi_ip_geocoder.rb", "test/test_us_geocoder.rb", "test/test_yahoo_geocoder.rb"]
|
22
25
|
|
23
26
|
if s.respond_to? :specification_version then
|
24
27
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
@@ -26,3 +29,4 @@ Gem::Specification.new do |s|
|
|
26
29
|
end
|
27
30
|
end
|
28
31
|
|
32
|
+
|
data/lib/geokit.rb
CHANGED
data/lib/geokit/geocoders.rb
CHANGED
@@ -37,6 +37,10 @@ module Geokit
|
|
37
37
|
'%' + $1.unpack('H2' * $1.size).join('%').upcase
|
38
38
|
end.tr(' ', '+')
|
39
39
|
end
|
40
|
+
|
41
|
+
def camelize(str)
|
42
|
+
str.split('_').map {|w| w.capitalize}.join
|
43
|
+
end
|
40
44
|
end
|
41
45
|
|
42
46
|
# Contains a range of geocoders:
|
@@ -70,38 +74,37 @@ module Geokit
|
|
70
74
|
@@geocoder_ca = false
|
71
75
|
@@geonames = false
|
72
76
|
@@provider_order = [:google,:us]
|
77
|
+
@@ip_provider_order = [:geo_plugin,:ip]
|
73
78
|
@@logger=Logger.new(STDOUT)
|
74
79
|
@@logger.level=Logger::INFO
|
75
80
|
@@domain = nil
|
76
81
|
|
77
|
-
def self.
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
82
|
+
def self.__define_accessors
|
83
|
+
class_variables.each do |v|
|
84
|
+
sym = v.delete("@").to_sym
|
85
|
+
unless self.respond_to? sym
|
86
|
+
module_eval <<-EOS, __FILE__, __LINE__
|
87
|
+
def self.#{sym}
|
88
|
+
value = if defined?(#{sym.to_s.upcase})
|
89
|
+
#{sym.to_s.upcase}
|
90
|
+
else
|
91
|
+
@@#{sym}
|
92
|
+
end
|
93
|
+
if value.is_a?(Hash)
|
94
|
+
value = (self.domain.nil? ? nil : value[self.domain]) || value.values.first
|
95
|
+
end
|
96
|
+
value
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.#{sym}=(obj)
|
100
|
+
@@#{sym} = obj
|
101
|
+
end
|
102
|
+
EOS
|
98
103
|
end
|
99
|
-
|
100
|
-
def self.#{sym}=(obj)
|
101
|
-
@@#{sym} = obj
|
102
|
-
end
|
103
|
-
EOS
|
104
|
+
end
|
104
105
|
end
|
106
|
+
|
107
|
+
__define_accessors
|
105
108
|
|
106
109
|
# Error which is thrown in the event a geocoding error occurs.
|
107
110
|
class GeocodeError < StandardError; end
|
@@ -118,9 +121,8 @@ module Geokit
|
|
118
121
|
# empty one with a failed success code.
|
119
122
|
def self.geocode(address)
|
120
123
|
res = do_geocode(address)
|
121
|
-
return res.
|
124
|
+
return res.nil? ? GeoLoc.new : res
|
122
125
|
end
|
123
|
-
|
124
126
|
# Main method which calls the do_reverse_geocode template method which subclasses
|
125
127
|
# are responsible for implementing. Returns a populated GeoLoc or an
|
126
128
|
# empty one with a failed success code.
|
@@ -478,7 +480,7 @@ module Geokit
|
|
478
480
|
response = self.call_geocoder_service("http://www.geoplugin.net/xml.gp?ip=#{ip}")
|
479
481
|
return response.is_a?(Net::HTTPSuccess) ? parse_xml(response.body) : GeoLoc.new
|
480
482
|
rescue
|
481
|
-
logger.error "Caught an error during
|
483
|
+
logger.error "Caught an error during GeoPluginGeocoder geocoding call: "+$!
|
482
484
|
return GeoLoc.new
|
483
485
|
end
|
484
486
|
|
@@ -491,7 +493,7 @@ module Geokit
|
|
491
493
|
geo.country_code = xml.elements['//geoplugin_countryCode'].text
|
492
494
|
geo.lat = xml.elements['//geoplugin_latitude'].text.to_f
|
493
495
|
geo.lng = xml.elements['//geoplugin_longitude'].text.to_f
|
494
|
-
geo.success = !geo.city.empty?
|
496
|
+
geo.success = !!geo.city && !geo.city.empty?
|
495
497
|
return geo
|
496
498
|
end
|
497
499
|
end
|
@@ -544,7 +546,8 @@ module Geokit
|
|
544
546
|
# -------------------------------------------------------------------------------------------
|
545
547
|
|
546
548
|
# Provides methods to geocode with a variety of geocoding service providers, plus failover
|
547
|
-
# among providers in the order you configure.
|
549
|
+
# among providers in the order you configure. When 2nd parameter is set 'true', perform
|
550
|
+
# ip location lookup with 'address' as the ip address.
|
548
551
|
#
|
549
552
|
# Goal:
|
550
553
|
# - homogenize the results of multiple geocoders
|
@@ -552,18 +555,21 @@ module Geokit
|
|
552
555
|
# Limitations:
|
553
556
|
# - currently only provides the first result. Sometimes geocoders will return multiple results.
|
554
557
|
# - currently discards the "accuracy" component of the geocoding calls
|
555
|
-
class MultiGeocoder < Geocoder
|
556
|
-
private
|
558
|
+
class MultiGeocoder < Geocoder
|
557
559
|
|
560
|
+
private
|
558
561
|
# This method will call one or more geocoders in the order specified in the
|
559
562
|
# configuration until one of the geocoders work.
|
560
563
|
#
|
561
564
|
# The failover approach is crucial for production-grade apps, but is rarely used.
|
562
565
|
# 98% of your geocoding calls will be successful with the first call
|
563
566
|
def self.do_geocode(address)
|
564
|
-
|
567
|
+
geocode_ip = !!/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/.match(address)
|
568
|
+
provider_order = geocode_ip ? Geokit::Geocoders::ip_provider_order : Geokit::Geocoders::provider_order
|
569
|
+
|
570
|
+
provider_order.each do |provider|
|
565
571
|
begin
|
566
|
-
klass = Geokit::Geocoders.const_get "#{provider.to_s
|
572
|
+
klass = Geokit::Geocoders.const_get "#{Geokit::Inflector::camelize(provider.to_s)}Geocoder"
|
567
573
|
res = klass.send :geocode, address
|
568
574
|
return res if res.success?
|
569
575
|
rescue
|
data/lib/geokit/mappable.rb
CHANGED
@@ -403,7 +403,7 @@ module Geokit
|
|
403
403
|
|
404
404
|
# Returns a string representation of the instance.
|
405
405
|
def to_s
|
406
|
-
"Provider: #{provider}\
|
406
|
+
"Provider: #{provider}\nStreet: #{street_address}\nCity: #{city}\nState: #{state}\nZip: #{zip}\nLatitude: #{lat}\nLongitude: #{lng}\nCountry: #{country_code}\nSuccess: #{success}"
|
407
407
|
end
|
408
408
|
end
|
409
409
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_base_geocoder')
|
2
|
+
|
3
|
+
Geokit::Geocoders::ip_provider_order=[:geo_plugin,:ip]
|
4
|
+
|
5
|
+
class MultiIpGeocoderTest < BaseGeocoderTest #:nodoc: all
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@ip_address = '10.10.10.10'
|
9
|
+
@success = Geokit::GeoLoc.new({:city=>"SAN FRANCISCO", :state=>"CA", :country_code=>"US", :lat=>37.7742, :lng=>-122.417068})
|
10
|
+
@success.success = true
|
11
|
+
@failure = Geokit::GeoLoc.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_successful_first
|
15
|
+
Geokit::Geocoders::GeoPluginGeocoder.expects(:geocode).with(@ip_address).returns(@success)
|
16
|
+
assert_equal @success, Geokit::Geocoders::MultiGeocoder.geocode(@ip_address)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_failover
|
20
|
+
Geokit::Geocoders::GeoPluginGeocoder.expects(:geocode).with(@ip_address).returns(@failure)
|
21
|
+
Geokit::Geocoders::IpGeocoder.expects(:geocode).with(@ip_address).returns(@success)
|
22
|
+
assert_equal @success, Geokit::Geocoders::MultiGeocoder.geocode(@ip_address)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_failure
|
26
|
+
Geokit::Geocoders::GeoPluginGeocoder.expects(:geocode).with(@ip_address).returns(@failure)
|
27
|
+
Geokit::Geocoders::IpGeocoder.expects(:geocode).with(@ip_address).returns(@failure)
|
28
|
+
assert_equal @failure, Geokit::Geocoders::MultiGeocoder.geocode(@ip_address)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_invalid_provider
|
32
|
+
temp = Geokit::Geocoders::ip_provider_order
|
33
|
+
Geokit::Geocoders.ip_provider_order = [:bogus]
|
34
|
+
assert_equal @failure, Geokit::Geocoders::MultiGeocoder.geocode(@ip_address)
|
35
|
+
Geokit::Geocoders.ip_provider_order = temp
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geokit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andre Lewis
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-05-22 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,9 +20,9 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 1.
|
23
|
+
version: 1.12.2
|
24
24
|
version:
|
25
|
-
description:
|
25
|
+
description: ""
|
26
26
|
email:
|
27
27
|
- andre@earthcode.com
|
28
28
|
executables: []
|
@@ -57,6 +57,8 @@ files:
|
|
57
57
|
- test/test_yahoo_geocoder.rb
|
58
58
|
has_rdoc: true
|
59
59
|
homepage:
|
60
|
+
licenses: []
|
61
|
+
|
60
62
|
post_install_message:
|
61
63
|
rdoc_options:
|
62
64
|
- --main
|
@@ -78,9 +80,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
80
|
requirements: []
|
79
81
|
|
80
82
|
rubyforge_project: geokit
|
81
|
-
rubygems_version: 1.3.
|
83
|
+
rubygems_version: 1.3.2
|
82
84
|
signing_key:
|
83
|
-
specification_version:
|
85
|
+
specification_version: 3
|
84
86
|
summary: Geokit provides geocoding and distance calculation in an easy-to-use API
|
85
87
|
test_files:
|
86
88
|
- test/test_base_geocoder.rb
|
@@ -94,5 +96,6 @@ test_files:
|
|
94
96
|
- test/test_ipgeocoder.rb
|
95
97
|
- test/test_latlng.rb
|
96
98
|
- test/test_multi_geocoder.rb
|
99
|
+
- test/test_multi_ip_geocoder.rb
|
97
100
|
- test/test_us_geocoder.rb
|
98
101
|
- test/test_yahoo_geocoder.rb
|