dusty-geocoder 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +36 -0
- data/lib/geocoder/client.rb +81 -0
- data/lib/geocoder/response.rb +170 -0
- data/lib/geocoder.rb +46 -0
- metadata +76 -0
data/README.txt
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
== DESCRIPTION:
|
2
|
+
|
3
|
+
Used to interact with Googles Geocoding API
|
4
|
+
|
5
|
+
|
6
|
+
== REQUIREMENTS:
|
7
|
+
|
8
|
+
patron
|
9
|
+
json
|
10
|
+
|
11
|
+
|
12
|
+
== INSTALL:
|
13
|
+
|
14
|
+
$ gem build geocoder.gemspec
|
15
|
+
$ sudo gem install geocoder-x.x.x.gem
|
16
|
+
|
17
|
+
|
18
|
+
== USAGE:
|
19
|
+
|
20
|
+
Geocoder.search('123 My St, MyCity, NY')
|
21
|
+
# returns an array of addresses or throws an exception
|
22
|
+
|
23
|
+
Geocoder.first('123 My St, MyCity, NY')
|
24
|
+
# returns a single address or throws an exception
|
25
|
+
|
26
|
+
See Geocoder::Response.check_status for errors thrown depending on the results
|
27
|
+
Errors in the HTTP call throw a Geocoder::ConnectionError
|
28
|
+
|
29
|
+
Your google API key is required. You may set this in one of two ways.
|
30
|
+
|
31
|
+
1. Set the class variable with Geocoder::Client.key = 'xxxxxx'
|
32
|
+
$ Geocoder.search('My Address') # uses default key
|
33
|
+
|
34
|
+
2. Send the key with your searches
|
35
|
+
$ Geocoder.search('My Address', key) # uses defined key
|
36
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Geocoder
|
2
|
+
|
3
|
+
class Client
|
4
|
+
|
5
|
+
##
|
6
|
+
# Returns the default key
|
7
|
+
def self.key
|
8
|
+
@key ||= raise(MapKeyError, 'Missing Key: Geocoder::Client.key=(key)')
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# Sets the default key
|
13
|
+
def self.key=(key)
|
14
|
+
@key = key
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Initialize the Client
|
19
|
+
#
|
20
|
+
# If the key is passed it will be used in place of the default key
|
21
|
+
def initialize(key=nil)
|
22
|
+
@key ||= self.class.key
|
23
|
+
@http = Patron::Session.new
|
24
|
+
@http.base_url = 'http://maps.google.com/maps/geo'
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Search for addresses that match a string
|
29
|
+
#
|
30
|
+
# @param [String] Address required for search
|
31
|
+
# @return [Array<Address>] An array of addresses
|
32
|
+
# @raise [StandardError] on errors
|
33
|
+
def search(address)
|
34
|
+
begin
|
35
|
+
response = @http.get(hash_to_string(:q => address))
|
36
|
+
rescue StandardError => e
|
37
|
+
raise(ConnectionError, e.message)
|
38
|
+
end
|
39
|
+
unless response.status == 200
|
40
|
+
raise(ConnectionError, "Response Code: #{response.status}")
|
41
|
+
end
|
42
|
+
Response.new(response.body).addresses
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Search for the first address that matches a string
|
47
|
+
#
|
48
|
+
# @see Geocoder::Client#search
|
49
|
+
# @param [String] Address required for search
|
50
|
+
# @return [Address] An address object
|
51
|
+
# @raise [StandardError] on errors
|
52
|
+
def first(address)
|
53
|
+
search.first
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
##
|
59
|
+
# Set the default parameters to use in searches
|
60
|
+
def default_params
|
61
|
+
{:key => @key, :gl => 'US', :output => 'json'}
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Convert a hash into a query parameter
|
66
|
+
#
|
67
|
+
# Example:
|
68
|
+
# {:q => 'my address', :output => 'json'}
|
69
|
+
# ?q=my%20address&output=json
|
70
|
+
#
|
71
|
+
# @return [String]
|
72
|
+
def hash_to_string(hash)
|
73
|
+
output = default_params.update(hash).map do |key, value|
|
74
|
+
"#{key}=#{@http.escape(value.to_s)}"
|
75
|
+
end.join("&")
|
76
|
+
"?#{output}"
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module Geocoder
|
2
|
+
|
3
|
+
class Response
|
4
|
+
attr_reader :json
|
5
|
+
|
6
|
+
##
|
7
|
+
# Initialize the response and check for errors in the Status code
|
8
|
+
#
|
9
|
+
# @see check_status
|
10
|
+
# @raise [StandardError]
|
11
|
+
def initialize(json)
|
12
|
+
@json = json
|
13
|
+
@hash = JSON.parse(json)
|
14
|
+
check_status
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Return an array of addresses that matched the search
|
19
|
+
#
|
20
|
+
# @return [Array<Address>]
|
21
|
+
def addresses
|
22
|
+
@addresses ||= @hash["Placemark"].map {|place| Address.new(place)}
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Check the status returned by Google and throw exception on
|
27
|
+
# results that are not 200
|
28
|
+
#
|
29
|
+
# @raise [StandardError]
|
30
|
+
def check_status
|
31
|
+
case @hash["Status"]["code"]
|
32
|
+
when 200 then
|
33
|
+
true
|
34
|
+
when 500
|
35
|
+
raise ServerError, "Unknown server error"
|
36
|
+
when 601
|
37
|
+
raise MissingAddressError, "Missing address"
|
38
|
+
when 602
|
39
|
+
raise UnknownAddressError, "Unknown address"
|
40
|
+
when 603
|
41
|
+
raise UnavailableAddressError, "Unavailable address"
|
42
|
+
when 610
|
43
|
+
raise InvalidMapKeyError, "Invalid map key"
|
44
|
+
when 620
|
45
|
+
raise TooManyQueriesError, "Too many queries for map key"
|
46
|
+
else
|
47
|
+
raise UnknownError, "Unknown error: #{@hash['code']}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class Address
|
54
|
+
##
|
55
|
+
# Initialize class
|
56
|
+
#
|
57
|
+
# set @hash to the json parsed hash
|
58
|
+
def initialize(hash)
|
59
|
+
@hash = hash
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Address
|
64
|
+
#
|
65
|
+
# @return [String] address
|
66
|
+
def address
|
67
|
+
@address ||= @hash["address"]
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Street
|
72
|
+
#
|
73
|
+
# @return [String] street
|
74
|
+
def street
|
75
|
+
@street ||= @hash["AddressDetails"]["Country"]["AdministrativeArea"]\
|
76
|
+
["Locality"]["Thoroughfare"]["ThoroughfareName"]
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# City
|
81
|
+
#
|
82
|
+
# @return [String] city
|
83
|
+
def city
|
84
|
+
@city ||= @hash["AddressDetails"]["Country"]["AdministrativeArea"]\
|
85
|
+
["Locality"]["LocalityName"]
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# State
|
90
|
+
#
|
91
|
+
# @return [String] state
|
92
|
+
def state
|
93
|
+
@state ||= @hash["AddressDetails"]["Country"]["AdministrativeArea"]\
|
94
|
+
["AdministrativeAreaName"]
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Zipcode
|
99
|
+
#
|
100
|
+
# @return [String] zipcode
|
101
|
+
def zipcode
|
102
|
+
@zipcode ||= @hash["AddressDetails"]["Country"]["AdministrativeArea"]\
|
103
|
+
["Locality"]["PostalCode"]["PostalCodeNumber"]
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Country
|
108
|
+
#
|
109
|
+
# @return [String] country
|
110
|
+
def country
|
111
|
+
@country ||= @hash["AddressDetails"]["Country"]["CountryNameCode"]
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Accuracy
|
116
|
+
#
|
117
|
+
# Convert Googles Accuracy integer into a word describing how accurate
|
118
|
+
# the match is
|
119
|
+
#
|
120
|
+
# @see accuracy_map
|
121
|
+
# @return [String] accuracy
|
122
|
+
def accuracy
|
123
|
+
@accuracy ||= accuracy_map[@hash["AddressDetails"]["Accuracy"].to_i]
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# Coordinates
|
128
|
+
#
|
129
|
+
# An array of coordinates in the form of [lat, lng, elevation]
|
130
|
+
#
|
131
|
+
# @return [<Array>] lat, lng, elevation
|
132
|
+
def coordinates
|
133
|
+
@coordinates ||= [lat, lng, elevation]
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Lat
|
138
|
+
#
|
139
|
+
# @return [Float] latitude
|
140
|
+
def lat
|
141
|
+
@lat ||= @hash["Point"]["coordinates"][0].to_f
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
145
|
+
# Lng
|
146
|
+
#
|
147
|
+
# @return [Float] longitude
|
148
|
+
def lng
|
149
|
+
@lng ||= @hash["Point"]["coordinates"][1].to_f
|
150
|
+
end
|
151
|
+
|
152
|
+
##
|
153
|
+
# Elevation
|
154
|
+
#
|
155
|
+
# @return [Float] elevation
|
156
|
+
def elevation
|
157
|
+
@elevation ||= @hash["Point"]["coordinates"][2].to_f
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
##
|
163
|
+
# Map Googles code to a user friendly word
|
164
|
+
#
|
165
|
+
# @return [String]
|
166
|
+
def accuracy_map
|
167
|
+
%w{ unknown country state county city zip zip+4 street address }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
data/lib/geocoder.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'patron'
|
2
|
+
require 'json'
|
3
|
+
module Geocoder
|
4
|
+
|
5
|
+
class Error < StandardError; end
|
6
|
+
class ServerError < Error; end
|
7
|
+
class AddressError < Error; end
|
8
|
+
class MissingAddressError < AddressError; end
|
9
|
+
class UnknownAddressError < AddressError; end
|
10
|
+
class UnavailableAddressError < AddressError; end
|
11
|
+
class MapKeyError < Error; end
|
12
|
+
class InvalidMapKeyError < MapKeyError; end
|
13
|
+
class TooManyQueriesError < MapKeyError; end
|
14
|
+
class UnknownError < Error; end
|
15
|
+
class ConnectionError < Error; end
|
16
|
+
|
17
|
+
##
|
18
|
+
# The current version of the application
|
19
|
+
def self.version
|
20
|
+
"0.0.2"
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Search for addresses that match a string
|
25
|
+
#
|
26
|
+
# @see Geocoder::Client#search
|
27
|
+
def self.search(address,key=nil)
|
28
|
+
Client.new(key).search(address)
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Search for the first address that matches a string
|
33
|
+
#
|
34
|
+
# @see Geocoder::Client#first
|
35
|
+
def self.first(address,key=nil)
|
36
|
+
search(address,key).first
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
require File.join(
|
42
|
+
File.expand_path(File.dirname(__FILE__)), 'geocoder', 'client'
|
43
|
+
)
|
44
|
+
require File.join(
|
45
|
+
File.expand_path(File.dirname(__FILE__)), 'geocoder', 'response'
|
46
|
+
)
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dusty-geocoder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dusty Doris
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-16 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: patron
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description:
|
36
|
+
email: github@dusty.name
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.txt
|
43
|
+
files:
|
44
|
+
- README.txt
|
45
|
+
- lib/geocoder.rb
|
46
|
+
- lib/geocoder/client.rb
|
47
|
+
- lib/geocoder/response.rb
|
48
|
+
has_rdoc: true
|
49
|
+
homepage: http://code.dusty.name
|
50
|
+
licenses:
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project: none
|
71
|
+
rubygems_version: 1.3.5
|
72
|
+
signing_key:
|
73
|
+
specification_version: 2
|
74
|
+
summary: Interface to Google's Geocoder API
|
75
|
+
test_files: []
|
76
|
+
|