reverse_geocoder 0.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/History.txt +4 -0
- data/Manifest.txt +26 -0
- data/README.rdoc +70 -0
- data/Rakefile +26 -0
- data/lib/reverse_geocoder.rb +15 -0
- data/lib/reverse_geocoder/exceptions.rb +20 -0
- data/lib/reverse_geocoder/geocoder.rb +104 -0
- data/lib/reverse_geocoder/geonames.rb +35 -0
- data/lib/reverse_geocoder/geoplugin.rb +29 -0
- data/lib/reverse_geocoder/google.rb +56 -0
- data/lib/reverse_geocoder/ibegin.rb +37 -0
- data/lib/reverse_geocoder/multi.rb +21 -0
- data/lib/reverse_geocoder/numerex.rb +34 -0
- data/lib/reverse_geocoder/version.rb +3 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/geocoder_spec.rb +70 -0
- data/spec/geonames_spec.rb +37 -0
- data/spec/geoplugin_spec.rb +31 -0
- data/spec/google_spec.rb +105 -0
- data/spec/ibegin_spec.rb +33 -0
- data/spec/multi_spec.rb +42 -0
- data/spec/numerex_spec.rb +30 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +19 -0
- data/tasks/rspec.rake +21 -0
- metadata +94 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.rdoc
|
4
|
+
Rakefile
|
5
|
+
lib/reverse_geocoder.rb
|
6
|
+
lib/reverse_geocoder/exceptions.rb
|
7
|
+
lib/reverse_geocoder/geocoder.rb
|
8
|
+
lib/reverse_geocoder/geonames.rb
|
9
|
+
lib/reverse_geocoder/google.rb
|
10
|
+
lib/reverse_geocoder/ibegin.rb
|
11
|
+
lib/reverse_geocoder/multi.rb
|
12
|
+
lib/reverse_geocoder/numerex.rb
|
13
|
+
lib/reverse_geocoder/version.rb
|
14
|
+
script/console
|
15
|
+
script/destroy
|
16
|
+
script/generate
|
17
|
+
spec/geocoder_spec.rb
|
18
|
+
spec/geonames_spec.rb
|
19
|
+
spec/geoplugin_spec.rb
|
20
|
+
spec/google_spec.rb
|
21
|
+
spec/ibegin_spec.rb
|
22
|
+
spec/multi_spec.rb
|
23
|
+
spec/numerex_spec.rb
|
24
|
+
spec/spec.opts
|
25
|
+
spec/spec_helper.rb
|
26
|
+
tasks/rspec.rake
|
data/README.rdoc
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
= reverse_geocoder
|
2
|
+
|
3
|
+
* http://github.com/billeisenhauer/reverse_geocoder
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Ruby gem containing production-quality reverse geocoding client service.
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
Supported reverse geocoders:
|
12
|
+
|
13
|
+
- Google
|
14
|
+
- Geonames
|
15
|
+
- Numerex
|
16
|
+
- GeoPlugin
|
17
|
+
- iBegin
|
18
|
+
|
19
|
+
Failover support provided through the Multi reverse geocoder which is built to
|
20
|
+
respect an order preference.
|
21
|
+
|
22
|
+
Exception handling to support query limits exceeded.
|
23
|
+
|
24
|
+
== INSTALL:
|
25
|
+
|
26
|
+
sudo gem install reverse_geocoder
|
27
|
+
|
28
|
+
== USAGE:
|
29
|
+
|
30
|
+
$ ReverseGeocoder::Google.geocode(32.921943,-96.951299, :sensor => true)
|
31
|
+
=> {:geocoder=>"Google", :country_code=>"US", :country_name=>"USA",
|
32
|
+
:address=>"8500 Wellington Point Dr, Irving, TX 75063, USA"}
|
33
|
+
|
34
|
+
Where the return is a Hash and the :address can be used as needed.
|
35
|
+
|
36
|
+
Some exception handling is performed to enable detection of query limit
|
37
|
+
issues. In those cases, rescue ReverseGeocoder::QueryLimitExceeded.
|
38
|
+
|
39
|
+
Other exceptions are below, but see the RDOC for more info:
|
40
|
+
|
41
|
+
- ReverseGeocoder::AuthenticationError
|
42
|
+
- ReverseGeocoder::UnknownServiceError
|
43
|
+
- ReverseGeocoder::UnavailableAddress
|
44
|
+
- ReverseGeocoder::MalformedRequest
|
45
|
+
|
46
|
+
|
47
|
+
== LICENSE:
|
48
|
+
|
49
|
+
(The MIT License)
|
50
|
+
|
51
|
+
Copyright (c) 2009 Bill Eisenhauer
|
52
|
+
|
53
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
54
|
+
a copy of this software and associated documentation files (the
|
55
|
+
"Software"), to deal in the Software without restriction, including
|
56
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
57
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
58
|
+
permit persons to whom the Software is furnished to do so, subject to
|
59
|
+
the following conditions:
|
60
|
+
|
61
|
+
The above copyright notice and this permission notice shall be
|
62
|
+
included in all copies or substantial portions of the Software.
|
63
|
+
|
64
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
65
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
66
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
67
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
68
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
69
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
70
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'hoe', '>= 2.1.0'
|
3
|
+
require 'hoe'
|
4
|
+
require 'fileutils'
|
5
|
+
require './lib/reverse_geocoder'
|
6
|
+
|
7
|
+
Hoe.plugin :newgem
|
8
|
+
# Hoe.plugin :website
|
9
|
+
# Hoe.plugin :cucumberfeatures
|
10
|
+
|
11
|
+
# Generate all the Rake tasks
|
12
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
13
|
+
$hoe = Hoe.spec 'reverse_geocoder' do
|
14
|
+
self.developer 'FIXME full name', 'FIXME email'
|
15
|
+
self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
16
|
+
self.rubyforge_name = self.name # TODO this is default value
|
17
|
+
# self.extra_deps = [['activesupport','>= 2.0.2']]
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'newgem/tasks'
|
22
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
23
|
+
|
24
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
25
|
+
# remove_task :default
|
26
|
+
# task :default => [:spec, :features]
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
gem 'httparty', '>= 0.4.5'
|
4
|
+
require 'httparty'
|
5
|
+
|
6
|
+
dir = Pathname(__FILE__).dirname.expand_path
|
7
|
+
require "#{dir}/reverse_geocoder/version"
|
8
|
+
require "#{dir}/reverse_geocoder/exceptions"
|
9
|
+
require "#{dir}/reverse_geocoder/geocoder"
|
10
|
+
require "#{dir}/reverse_geocoder/google"
|
11
|
+
require "#{dir}/reverse_geocoder/geonames"
|
12
|
+
require "#{dir}/reverse_geocoder/geoplugin"
|
13
|
+
require "#{dir}/reverse_geocoder/ibegin"
|
14
|
+
require "#{dir}/reverse_geocoder/numerex"
|
15
|
+
require "#{dir}/reverse_geocoder/multi"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ReverseGeocoder
|
2
|
+
|
3
|
+
# Exception raised when API key or service credentials are rejected
|
4
|
+
class AuthenticationError < StandardError; end
|
5
|
+
|
6
|
+
# Exception raised when query limits are exceeded for a service.
|
7
|
+
class QueryLimitExceeded < StandardError; end
|
8
|
+
|
9
|
+
# Exception raised when an unknown service error occurs.
|
10
|
+
class UnknownServiceError < StandardError; end
|
11
|
+
|
12
|
+
# Exception that is raised when an address is unknown or cannot be returned
|
13
|
+
# for other reasons.
|
14
|
+
class UnavailableAddress < StandardError; end
|
15
|
+
|
16
|
+
# Exception that is raised when a service query is not understood by the
|
17
|
+
# service.
|
18
|
+
class MalformedRequest < StandardError; end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module ReverseGeocoder
|
2
|
+
|
3
|
+
# This module is meant to be mixed in to a class to enable it to perform
|
4
|
+
# reverse geocoding. The including class needs to implement three hook
|
5
|
+
# methods:
|
6
|
+
#
|
7
|
+
# request(lat, lng, options)
|
8
|
+
# normalize(response)
|
9
|
+
# validate_geocoder_options(options)
|
10
|
+
#
|
11
|
+
# The request method uses HTTParty syntax to effect the service call and
|
12
|
+
# should return its reponse. Exception handling is not required for the
|
13
|
+
# HTTP response code.
|
14
|
+
#
|
15
|
+
# The normalize method takes the response parameter which behaves like a
|
16
|
+
# Hash and maps it to an expected Hash structure. As an alternative
|
17
|
+
#
|
18
|
+
# The validate_geocoder_options method is used to validate options provided
|
19
|
+
# to a geocoder. The geocoder can throw an exception or snuff out the
|
20
|
+
# options as needed.
|
21
|
+
#
|
22
|
+
module Geocoder
|
23
|
+
|
24
|
+
# Mix class methods into the base.
|
25
|
+
def self.included(base) # :nodoc:
|
26
|
+
base.extend(ClassMethods)
|
27
|
+
base.send(:include, HTTParty)
|
28
|
+
end
|
29
|
+
|
30
|
+
module ClassMethods
|
31
|
+
|
32
|
+
def geocode(lat, lng, options={})
|
33
|
+
validate_coordinates(lat, lng)
|
34
|
+
validate_options(options)
|
35
|
+
process_request(lat, lng, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
### HOOK METHODS ###
|
41
|
+
|
42
|
+
# Perform the request from the web service. This is the only required
|
43
|
+
# hook method.
|
44
|
+
#
|
45
|
+
def request(lat, lng, options)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Normalize the hash response into a common response so responses are
|
49
|
+
# common between geocoders. By default, return the non-normalized
|
50
|
+
# response if implementers don't wish to perform normalization.
|
51
|
+
#
|
52
|
+
def normalize(response)
|
53
|
+
response
|
54
|
+
end
|
55
|
+
|
56
|
+
# Used by geocoders to validate specific options. If options are
|
57
|
+
# found to be invalid, simply raise an ArgumentError.
|
58
|
+
#
|
59
|
+
def validate_geocoder_options(options)
|
60
|
+
end
|
61
|
+
|
62
|
+
### VALIDATION ###
|
63
|
+
|
64
|
+
def validate_coordinates(lat, lng)
|
65
|
+
if lat.to_f < -90 || lat.to_f > 90
|
66
|
+
raise ArgumentError, "Latitude #{lat} is invalid, try -90 to 90"
|
67
|
+
end
|
68
|
+
if lng.to_f < -180 || lng.to_f > 180
|
69
|
+
raise ArgumentError, "Longitude #{lng} is invalid, try -180 to 180"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def validate_options(options)
|
74
|
+
unless options.is_a?(Hash)
|
75
|
+
raise ArgumentError, "Options should be Hash, but is a #{options.class.name}"
|
76
|
+
end
|
77
|
+
validate_geocoder_options(options)
|
78
|
+
end
|
79
|
+
|
80
|
+
### REQUEST HANDLING ###
|
81
|
+
|
82
|
+
def process_request(lat, lng, options={})
|
83
|
+
response = request(lat, lng, options)
|
84
|
+
handle_response(response)
|
85
|
+
end
|
86
|
+
|
87
|
+
def handle_response(response)
|
88
|
+
case response.code.to_i
|
89
|
+
when 200
|
90
|
+
normalize(response)
|
91
|
+
when 401..403
|
92
|
+
raise AuthenticationError
|
93
|
+
when 404
|
94
|
+
raise UnavailableAddress
|
95
|
+
else
|
96
|
+
raise UnknownServiceError
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ReverseGeocoder
|
2
|
+
|
3
|
+
# Geonames Reverse Geocoder
|
4
|
+
#
|
5
|
+
class Geonames
|
6
|
+
include ReverseGeocoder::Geocoder
|
7
|
+
|
8
|
+
base_uri 'http://ws.geonames.org'
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def self.request(lat, lng, options)
|
13
|
+
query = {:lat => "#{lat}", :lng => "#{lng}"}.merge(options)
|
14
|
+
get("/findNearestAddress", :query => query)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.normalize(response)
|
18
|
+
results = {:geocoder => 'Geonames'}
|
19
|
+
results[:address] = assemble_address_from(response)
|
20
|
+
results[:country_code] = results[:country_name] = response['geonames']['address']['countryCode']
|
21
|
+
results
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.assemble_address_from(response)
|
25
|
+
address = response['geonames']['address']
|
26
|
+
str = "#{address['placename']}, "
|
27
|
+
str += "#{address['streetNumber']} #{address['street']}, "
|
28
|
+
str += "#{address['adminName2']}, #{address['adminCode1']}, "
|
29
|
+
str += "#{address['postalcode']}, "
|
30
|
+
str += "#{address['countryCode']}"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ReverseGeocoder
|
2
|
+
|
3
|
+
# GeoPlugin Reverse Geocoder
|
4
|
+
#
|
5
|
+
class GeoPlugin
|
6
|
+
include ReverseGeocoder::Geocoder
|
7
|
+
|
8
|
+
base_uri 'http://www.geoplugin.net/extras'
|
9
|
+
default_params :format => 'xml'
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def self.request(lat, lng, options)
|
14
|
+
query = {:lat => "#{lat}", :long => "#{lng}"}.merge(options)
|
15
|
+
get("/location.gp", :query => query)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.normalize(response)
|
19
|
+
results = {:geocoder => 'GeoPlugin'}
|
20
|
+
address = response['geoPlugin']
|
21
|
+
results[:address] = "#{address['geoplugin_place']}, #{address['geoplugin_regionAbbreviated']}, #{address['geoplugin_countryCode']}"
|
22
|
+
results[:country_code] = address['geoplugin_countryCode']
|
23
|
+
results[:country_name] = address['geoplugin_countryCode']
|
24
|
+
results
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module ReverseGeocoder
|
2
|
+
|
3
|
+
# Google Reverse Geocoder
|
4
|
+
#
|
5
|
+
class Google
|
6
|
+
include ReverseGeocoder::Geocoder
|
7
|
+
|
8
|
+
class << self; attr_accessor :api_key end
|
9
|
+
|
10
|
+
base_uri 'http://maps.google.com/maps'
|
11
|
+
default_params :key => api_key
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def self.request(lat, lng, options)
|
16
|
+
query = {:q => "#{lat},#{lng}"}.merge(options)
|
17
|
+
get("/geo", :query => query)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.normalize(response)
|
21
|
+
handle_status(response['Status']['code'])
|
22
|
+
results = {:geocoder => 'Google'}
|
23
|
+
results[:address] = response['Placemark'][0]['address']
|
24
|
+
results[:country_code] = response['Placemark'][0]['AddressDetails']['Country']['CountryNameCode']
|
25
|
+
results[:country_name] = response['Placemark'][0]['AddressDetails']['Country']['CountryName']
|
26
|
+
results
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.handle_status(status)
|
30
|
+
case status.to_i
|
31
|
+
when 500
|
32
|
+
raise UnknownServiceError
|
33
|
+
when 601
|
34
|
+
raise MalformedRequest
|
35
|
+
when 602, 603
|
36
|
+
raise UnavailableAddress
|
37
|
+
when 610
|
38
|
+
raise AuthenticationError
|
39
|
+
when 620
|
40
|
+
raise QueryLimitExceeded
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
VALID_OPTIONS = [:sensor]
|
45
|
+
|
46
|
+
def self.validate_geocoder_options(options)
|
47
|
+
options.keys.each do |key|
|
48
|
+
unless VALID_OPTIONS.include?(key)
|
49
|
+
raise ArgumentError, "Options cannot include :#{key}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ReverseGeocoder
|
2
|
+
|
3
|
+
# iBegin Reverse Geocoder
|
4
|
+
#
|
5
|
+
class IBegin
|
6
|
+
include ReverseGeocoder::Geocoder
|
7
|
+
|
8
|
+
class << self; attr_accessor :api_key end
|
9
|
+
|
10
|
+
base_uri 'http://geocoder.ibegin.com'
|
11
|
+
default_params :apikey => api_key
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def self.request(lat, lng, options)
|
16
|
+
query = {:latitude => "#{lat}", :longitude => "#{lng}"}.merge(options)
|
17
|
+
get("/geoxml/", :query => query)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.normalize(response)
|
21
|
+
results = {:geocoder => 'iBegin'}
|
22
|
+
address = response['geocode']
|
23
|
+
results[:address] = assemble_address_from(address)
|
24
|
+
results[:country_code] = "US"
|
25
|
+
results[:country_name] = "USA"
|
26
|
+
results
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.assemble_address_from(address)
|
30
|
+
str = "#{address['stnumber']} #{address['stname']}, "
|
31
|
+
str += "#{address['stcity']}, #{address['ststate']}, "
|
32
|
+
str += "#{address['stzip']}, US"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ReverseGeocoder
|
2
|
+
|
3
|
+
@geocoder_order = [Google, Numerex, Geonames, GeoPlugin]
|
4
|
+
class << self; attr_accessor :geocoder_order end
|
5
|
+
|
6
|
+
# Multi Reverse Geocoder
|
7
|
+
#
|
8
|
+
class Multi
|
9
|
+
|
10
|
+
def self.geocode(lat, lng, options={})
|
11
|
+
ReverseGeocoder::geocoder_order.each do |geocoder_class|
|
12
|
+
begin
|
13
|
+
return geocoder_class.send(:geocode, lat, lng, options)
|
14
|
+
rescue
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ReverseGeocoder
|
2
|
+
|
3
|
+
# Numerex Reverse Geocoder
|
4
|
+
#
|
5
|
+
class Numerex
|
6
|
+
include ReverseGeocoder::Geocoder
|
7
|
+
|
8
|
+
base_uri 'http://geo.numerex.com/api/rest/'
|
9
|
+
default_params :function => 'getAddress'
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def self.request(lat, lng, options)
|
14
|
+
query = {:lat => "#{lat}", :lon => "#{lng}"}.merge(options)
|
15
|
+
get("/AddressFinder.php", :query => query)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.normalize(response)
|
19
|
+
results = {:geocoder => 'Numerex'}
|
20
|
+
address = response['address']
|
21
|
+
results[:address] = assemble_address_from(address)
|
22
|
+
results[:country_code] = ''
|
23
|
+
results[:country_name] = ''
|
24
|
+
results
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.assemble_address_from(address)
|
28
|
+
str = address['addr_line1'] + ', '
|
29
|
+
str += address['addr_line2']
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/reverse_geocoder.rb'}"
|
9
|
+
puts "Loading reverse_geocoder gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
class TestGeocoder
|
4
|
+
include ReverseGeocoder::Geocoder
|
5
|
+
end
|
6
|
+
|
7
|
+
describe 'Geocoder Validations' do
|
8
|
+
|
9
|
+
it "should reject invalid latitude" do
|
10
|
+
lambda do
|
11
|
+
TestGeocoder.geocode(180,180)
|
12
|
+
end.should raise_error(ArgumentError, "Latitude 180 is invalid, try -90 to 90")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should reject invalid longitude" do
|
16
|
+
lambda do
|
17
|
+
TestGeocoder.geocode(0,200)
|
18
|
+
end.should raise_error(ArgumentError, "Longitude 200 is invalid, try -180 to 180")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should reject invalid longitude" do
|
22
|
+
lambda do
|
23
|
+
TestGeocoder.geocode(90,180, Array.new)
|
24
|
+
end.should raise_error(ArgumentError, "Options should be Hash, but is a Array")
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'Geocoder Processing' do
|
30
|
+
|
31
|
+
describe 'valid response' do
|
32
|
+
|
33
|
+
it 'should return hash response' do
|
34
|
+
response = stub_httparty_response({:status => '200'}, '200')
|
35
|
+
TestGeocoder.stub!(:request).and_return(response)
|
36
|
+
response = TestGeocoder.geocode(90, 180)
|
37
|
+
response.should eql({:status => '200'})
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'with non-200 responses' do
|
43
|
+
|
44
|
+
it 'should raise an authorization error' do
|
45
|
+
response = stub_httparty_response({}, '401')
|
46
|
+
TestGeocoder.stub!(:request).and_return(response)
|
47
|
+
lambda do
|
48
|
+
TestGeocoder.geocode(90, 180)
|
49
|
+
end.should raise_error(ReverseGeocoder::AuthenticationError)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should raise an unknown server error' do
|
53
|
+
response = stub_httparty_response({}, '404')
|
54
|
+
TestGeocoder.stub!(:request).and_return(response)
|
55
|
+
lambda do
|
56
|
+
TestGeocoder.geocode(90, 180)
|
57
|
+
end.should raise_error(ReverseGeocoder::UnavailableAddress)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should raise an unknown server error' do
|
61
|
+
response = stub_httparty_response({}, '500')
|
62
|
+
TestGeocoder.stub!(:request).and_return(response)
|
63
|
+
lambda do
|
64
|
+
TestGeocoder.geocode(90, 180)
|
65
|
+
end.should raise_error(ReverseGeocoder::UnknownServiceError)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'Geonames Geocoder Processing' do
|
4
|
+
|
5
|
+
describe 'valid response' do
|
6
|
+
|
7
|
+
valid_response_hash = {
|
8
|
+
'geonames' => {
|
9
|
+
'address' => {
|
10
|
+
'street' => 'Kenwood Dr',
|
11
|
+
'streetNumber' => '598',
|
12
|
+
'postalcode' => '94025',
|
13
|
+
'placename' => 'Menlo Park',
|
14
|
+
'adminName2' => 'San Mateo',
|
15
|
+
'adminCode1' => 'CA',
|
16
|
+
'countryCode' => 'US'
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
normalized_hash = {
|
22
|
+
:geocoder => 'Geonames',
|
23
|
+
:address => 'Menlo Park, 598 Kenwood Dr, San Mateo, CA, 94025, US',
|
24
|
+
:country_code => 'US',
|
25
|
+
:country_name => 'US'
|
26
|
+
}
|
27
|
+
|
28
|
+
it 'should return normalized data' do
|
29
|
+
response = stub_httparty_response(valid_response_hash, '200')
|
30
|
+
ReverseGeocoder::Geonames.stub!(:request).and_return(response)
|
31
|
+
response = ReverseGeocoder::Geonames.geocode(90, 180)
|
32
|
+
response.should eql(normalized_hash)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'GeoPlugin Geocoder Processing' do
|
4
|
+
|
5
|
+
describe 'valid response' do
|
6
|
+
|
7
|
+
valid_response_hash = {
|
8
|
+
'geoPlugin' => {
|
9
|
+
'geoplugin_place' => 'Port Gibson',
|
10
|
+
'geoplugin_countryCode' => 'US',
|
11
|
+
'geoplugin_regionAbbreviated' => 'MS'
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
normalized_hash = {
|
16
|
+
:geocoder => 'GeoPlugin',
|
17
|
+
:address => 'Port Gibson, MS, US',
|
18
|
+
:country_code => 'US',
|
19
|
+
:country_name => 'US'
|
20
|
+
}
|
21
|
+
|
22
|
+
it 'should return normalized data' do
|
23
|
+
response = stub_httparty_response(valid_response_hash, '200')
|
24
|
+
ReverseGeocoder::GeoPlugin.stub!(:request).and_return(response)
|
25
|
+
response = ReverseGeocoder::GeoPlugin.geocode(32, 91)
|
26
|
+
response.should eql(normalized_hash)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/spec/google_spec.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'Google Geocoder Validations' do
|
4
|
+
|
5
|
+
it "should reject invalid option" do
|
6
|
+
lambda do
|
7
|
+
ReverseGeocoder::Google.geocode(90, 180, :invalid => 'invalid')
|
8
|
+
end.should raise_error(ArgumentError, "Options cannot include :invalid")
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'Google Geocoder Processing' do
|
14
|
+
|
15
|
+
describe 'valid response' do
|
16
|
+
|
17
|
+
valid_response_hash = {
|
18
|
+
'name' => '1600 Amphitheatre Parkway, Mountain View, CA',
|
19
|
+
'Status' => {
|
20
|
+
'code' => 200,
|
21
|
+
'request' => 'geocode'
|
22
|
+
},
|
23
|
+
'Placemark' => [
|
24
|
+
{
|
25
|
+
'id' => 'p1',
|
26
|
+
'address' => '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA',
|
27
|
+
'AddressDetails' => {
|
28
|
+
'Country' => {
|
29
|
+
'CountryNameCode' => 'US',
|
30
|
+
'CountryName' => 'USA'
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
]
|
35
|
+
}
|
36
|
+
|
37
|
+
normalized_hash = {
|
38
|
+
:geocoder => 'Google',
|
39
|
+
:address => '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA',
|
40
|
+
:country_code => 'US',
|
41
|
+
:country_name => 'USA'
|
42
|
+
}
|
43
|
+
|
44
|
+
it 'should return normalized data' do
|
45
|
+
response = stub_httparty_response(valid_response_hash, '200')
|
46
|
+
ReverseGeocoder::Google.stub!(:request).and_return(response)
|
47
|
+
response = ReverseGeocoder::Google.geocode(90, 180)
|
48
|
+
response.should eql(normalized_hash)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'with non-200 responses' do
|
54
|
+
|
55
|
+
it 'should raise a malformed request error' do
|
56
|
+
response = stub_httparty_response({'Status' => {'code' => '500'}}, 200)
|
57
|
+
ReverseGeocoder::Google.stub!(:request).and_return(response)
|
58
|
+
lambda do
|
59
|
+
ReverseGeocoder::Google.geocode(90, 180)
|
60
|
+
end.should raise_error(ReverseGeocoder::UnknownServiceError)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should raise a malformed request error' do
|
64
|
+
response = stub_httparty_response({'Status' => {'code' => '601'}}, 200)
|
65
|
+
ReverseGeocoder::Google.stub!(:request).and_return(response)
|
66
|
+
lambda do
|
67
|
+
ReverseGeocoder::Google.geocode(90, 180)
|
68
|
+
end.should raise_error(ReverseGeocoder::MalformedRequest)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should raise a malformed request error' do
|
72
|
+
response = stub_httparty_response({'Status' => {'code' => '602'}}, 200)
|
73
|
+
ReverseGeocoder::Google.stub!(:request).and_return(response)
|
74
|
+
lambda do
|
75
|
+
ReverseGeocoder::Google.geocode(90, 180)
|
76
|
+
end.should raise_error(ReverseGeocoder::UnavailableAddress)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should raise a malformed request error' do
|
80
|
+
response = stub_httparty_response({'Status' => {'code' => '603'}}, 200)
|
81
|
+
ReverseGeocoder::Google.stub!(:request).and_return(response)
|
82
|
+
lambda do
|
83
|
+
ReverseGeocoder::Google.geocode(90, 180)
|
84
|
+
end.should raise_error(ReverseGeocoder::UnavailableAddress)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should raise a malformed request error' do
|
88
|
+
response = stub_httparty_response({'Status' => {'code' => '610'}}, 200)
|
89
|
+
ReverseGeocoder::Google.stub!(:request).and_return(response)
|
90
|
+
lambda do
|
91
|
+
ReverseGeocoder::Google.geocode(90, 180)
|
92
|
+
end.should raise_error(ReverseGeocoder::AuthenticationError)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should raise a malformed request error' do
|
96
|
+
response = stub_httparty_response({'Status' => {'code' => '620'}}, 200)
|
97
|
+
ReverseGeocoder::Google.stub!(:request).and_return(response)
|
98
|
+
lambda do
|
99
|
+
ReverseGeocoder::Google.geocode(90, 180)
|
100
|
+
end.should raise_error(ReverseGeocoder::QueryLimitExceeded)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
data/spec/ibegin_spec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'iBegin Geocoder Processing' do
|
4
|
+
|
5
|
+
describe 'valid response' do
|
6
|
+
|
7
|
+
valid_response_hash = {
|
8
|
+
'geocode' => {
|
9
|
+
'stcity' => 'Washington',
|
10
|
+
'ststate' => 'DC',
|
11
|
+
'stzip' => '20502',
|
12
|
+
'stnumber' => '1610',
|
13
|
+
'stname' => 'Pennsylvania Ave'
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
normalized_hash = {
|
18
|
+
:geocoder => 'iBegin',
|
19
|
+
:address => '1610 Pennsylvania Ave, Washington, DC, 20502, US',
|
20
|
+
:country_code => 'US',
|
21
|
+
:country_name => 'USA'
|
22
|
+
}
|
23
|
+
|
24
|
+
it 'should return normalized data' do
|
25
|
+
response = stub_httparty_response(valid_response_hash, '200')
|
26
|
+
ReverseGeocoder::IBegin.stub!(:request).and_return(response)
|
27
|
+
response = ReverseGeocoder::IBegin.geocode(38.898747, -77.037947)
|
28
|
+
response.should eql(normalized_hash)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/spec/multi_spec.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'Multi Geocoder' do
|
4
|
+
|
5
|
+
it "should contain a list of geocoder providers" do
|
6
|
+
geocoder_order = [
|
7
|
+
ReverseGeocoder::Google,
|
8
|
+
ReverseGeocoder::Numerex,
|
9
|
+
ReverseGeocoder::Geonames,
|
10
|
+
ReverseGeocoder::GeoPlugin
|
11
|
+
]
|
12
|
+
ReverseGeocoder.geocoder_order.should eql(geocoder_order)
|
13
|
+
end
|
14
|
+
|
15
|
+
google_normalized_hash = {
|
16
|
+
:geocoder => 'Google',
|
17
|
+
:address => '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA',
|
18
|
+
:country_code => 'US',
|
19
|
+
:country_name => 'USA'
|
20
|
+
}
|
21
|
+
|
22
|
+
numerex_normalized_hash = {
|
23
|
+
:geocoder => 'Numerex',
|
24
|
+
:address => '8513 Wellington Point Dr, Irving, TX 75063',
|
25
|
+
:country_code => '',
|
26
|
+
:country_name => ''
|
27
|
+
}
|
28
|
+
|
29
|
+
it 'should call the first geocoder provider' do
|
30
|
+
ReverseGeocoder::Google.stub!(:geocode).and_return(google_normalized_hash)
|
31
|
+
response = ReverseGeocoder::Multi.geocode(90, 180)
|
32
|
+
response.should eql(google_normalized_hash)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should fail over to the second geocoder provider' do
|
36
|
+
ReverseGeocoder::Google.stub!(:geocode).and_raise(ReverseGeocoder::QueryLimitExceeded)
|
37
|
+
ReverseGeocoder::Numerex.stub!(:geocode).and_return(numerex_normalized_hash)
|
38
|
+
response = ReverseGeocoder::Multi.geocode(90, 180)
|
39
|
+
response.should eql(numerex_normalized_hash)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'Numerex Geocoder Processing' do
|
4
|
+
|
5
|
+
describe 'valid response' do
|
6
|
+
|
7
|
+
valid_response_hash = {
|
8
|
+
'address' => {
|
9
|
+
'addr_line1' => '8513 Wellington Point Dr',
|
10
|
+
'addr_line2' => 'Irving, TX 75063'
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
normalized_hash = {
|
15
|
+
:geocoder => 'Numerex',
|
16
|
+
:address => '8513 Wellington Point Dr, Irving, TX 75063',
|
17
|
+
:country_code => '',
|
18
|
+
:country_name => ''
|
19
|
+
}
|
20
|
+
|
21
|
+
it 'should return normalized data' do
|
22
|
+
response = stub_httparty_response(valid_response_hash, '200')
|
23
|
+
ReverseGeocoder::Numerex.stub!(:request).and_return(response)
|
24
|
+
response = ReverseGeocoder::Numerex.geocode(32.9219569943518, -96.9514012365146)
|
25
|
+
response.should eql(normalized_hash)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
5
|
+
gem 'rspec'
|
6
|
+
require 'spec'
|
7
|
+
end
|
8
|
+
|
9
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
10
|
+
require 'reverse_geocoder'
|
11
|
+
|
12
|
+
|
13
|
+
Spec::Runner.configure do |config|
|
14
|
+
|
15
|
+
def stub_httparty_response(response_object, code)
|
16
|
+
HTTParty::Response.new(response_object, '', code, 'OK')
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
5
|
+
require 'spec'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
rescue LoadError
|
10
|
+
puts <<-EOS
|
11
|
+
To use rspec for testing you must install rspec gem:
|
12
|
+
gem install rspec
|
13
|
+
EOS
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run the specs under spec/models"
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: reverse_geocoder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bill Eisenhauer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-06 00:00:00 -06:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.3.3
|
24
|
+
version:
|
25
|
+
description: ReverseGeocoder Gem
|
26
|
+
email:
|
27
|
+
- bill@billeisenhauer.com
|
28
|
+
executables: []
|
29
|
+
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files:
|
33
|
+
- History.txt
|
34
|
+
- Manifest.txt
|
35
|
+
- README.rdoc
|
36
|
+
files:
|
37
|
+
- History.txt
|
38
|
+
- Manifest.txt
|
39
|
+
- README.rdoc
|
40
|
+
- Rakefile
|
41
|
+
- lib/reverse_geocoder.rb
|
42
|
+
- lib/reverse_geocoder/exceptions.rb
|
43
|
+
- lib/reverse_geocoder/geocoder.rb
|
44
|
+
- lib/reverse_geocoder/geonames.rb
|
45
|
+
- lib/reverse_geocoder/geoplugin.rb
|
46
|
+
- lib/reverse_geocoder/google.rb
|
47
|
+
- lib/reverse_geocoder/ibegin.rb
|
48
|
+
- lib/reverse_geocoder/multi.rb
|
49
|
+
- lib/reverse_geocoder/numerex.rb
|
50
|
+
- lib/reverse_geocoder/version.rb
|
51
|
+
- script/console
|
52
|
+
- script/destroy
|
53
|
+
- script/generate
|
54
|
+
- spec/geocoder_spec.rb
|
55
|
+
- spec/geonames_spec.rb
|
56
|
+
- spec/geoplugin_spec.rb
|
57
|
+
- spec/google_spec.rb
|
58
|
+
- spec/ibegin_spec.rb
|
59
|
+
- spec/multi_spec.rb
|
60
|
+
- spec/numerex_spec.rb
|
61
|
+
- spec/spec.opts
|
62
|
+
- spec/spec_helper.rb
|
63
|
+
- tasks/rspec.rake
|
64
|
+
has_rdoc: true
|
65
|
+
homepage: http://github.com/billeisenhauer/reverse_geocoder
|
66
|
+
licenses: []
|
67
|
+
|
68
|
+
post_install_message: PostInstall.txt
|
69
|
+
rdoc_options:
|
70
|
+
- --main
|
71
|
+
- README.rdoc
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: "0"
|
79
|
+
version:
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: "0"
|
85
|
+
version:
|
86
|
+
requirements: []
|
87
|
+
|
88
|
+
rubyforge_project: reverse_geocoder
|
89
|
+
rubygems_version: 1.3.5
|
90
|
+
signing_key:
|
91
|
+
specification_version: 3
|
92
|
+
summary: Ruby gem containing production-quality reverse geocoding client service.
|
93
|
+
test_files: []
|
94
|
+
|