reverse_geocoder 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2009-12-06
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -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
@@ -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.
@@ -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
@@ -0,0 +1,3 @@
1
+ module ReverseGeocoder #:nodoc:
2
+ VERSION = '0.0.1'
3
+ end
@@ -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"
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1 @@
1
+ --colour
@@ -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
@@ -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
+