geokit 1.3.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|