graticule 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +13 -0
- data/.gitignore +10 -0
- data/.travis.yml +14 -0
- data/CHANGELOG.txt +4 -1
- data/Gemfile +7 -0
- data/LICENSE.txt +1 -1
- data/README.md +122 -0
- data/Rakefile +78 -0
- data/graticule.gemspec +26 -0
- data/lib/graticule/cli.rb +3 -1
- data/lib/graticule/geocoder/google.rb +33 -4
- data/lib/graticule/geocoder/mapquest.rb +52 -59
- data/lib/graticule/precision.rb +3 -1
- data/lib/graticule/version.rb +1 -1
- data/site/index.html +114 -0
- data/site/plugin.html +82 -0
- data/site/stylesheets/style.css +69 -0
- data/test/config.yml.default +33 -0
- data/test/fixtures/responses/freethepostcode/not_found.txt +3 -0
- data/test/fixtures/responses/freethepostcode/success.txt +2 -0
- data/test/fixtures/responses/geocoder_ca/success.xml +12 -0
- data/test/fixtures/responses/geocoder_us/success.xml +8 -0
- data/test/fixtures/responses/geocoder_us/unknown.xml +1 -0
- data/test/fixtures/responses/geonames/missing.xml +4 -0
- data/test/fixtures/responses/geonames/success.xml +14 -0
- data/test/fixtures/responses/geonames/unknown.xml +4 -0
- data/test/fixtures/responses/google/address.json +64 -0
- data/test/fixtures/responses/google/badkey.json +4 -0
- data/test/fixtures/responses/google/country.json +43 -0
- data/test/fixtures/responses/google/limit.json +4 -0
- data/test/fixtures/responses/google/locality.json +58 -0
- data/test/fixtures/responses/google/region.json +48 -0
- data/test/fixtures/responses/google/server_error.json +4 -0
- data/test/fixtures/responses/google/street.json +58 -0
- data/test/fixtures/responses/google/success.json +64 -0
- data/test/fixtures/responses/google/success_multiple_results.json +68 -0
- data/test/fixtures/responses/google/zero_results.json +4 -0
- data/test/fixtures/responses/host_ip/private.txt +4 -0
- data/test/fixtures/responses/host_ip/success.txt +4 -0
- data/test/fixtures/responses/host_ip/unknown.txt +4 -0
- data/test/fixtures/responses/local_search_maps/empty.txt +1 -0
- data/test/fixtures/responses/local_search_maps/not_found.txt +1 -0
- data/test/fixtures/responses/local_search_maps/success.txt +1 -0
- data/test/fixtures/responses/mapquest/multi_result.xml +317 -0
- data/test/fixtures/responses/mapquest/success.xml +54 -0
- data/test/fixtures/responses/multimap/missing_params.xml +4 -0
- data/test/fixtures/responses/multimap/no_matches.xml +4 -0
- data/test/fixtures/responses/multimap/success.xml +19 -0
- data/test/fixtures/responses/simple_geo/error.json +4 -0
- data/test/fixtures/responses/simple_geo/success.json +255 -0
- data/test/fixtures/responses/yahoo/success.xml +3 -0
- data/test/fixtures/responses/yahoo/unknown_address.xml +6 -0
- data/test/fixtures/responses/yandex/badkey.xml +5 -0
- data/test/fixtures/responses/yandex/success.xml +204 -0
- data/test/graticule/distance_test.rb +59 -0
- data/test/graticule/geocoder/freethepostcode_test.rb +37 -0
- data/test/graticule/geocoder/geocoder_ca_test.rb +42 -0
- data/test/graticule/geocoder/geocoder_us_test.rb +44 -0
- data/test/graticule/geocoder/geocoders.rb +56 -0
- data/test/graticule/geocoder/geonames_test.rb +56 -0
- data/test/graticule/geocoder/google_signed_test.rb +19 -0
- data/test/graticule/geocoder/google_test.rb +138 -0
- data/test/graticule/geocoder/host_ip_test.rb +41 -0
- data/test/graticule/geocoder/local_search_maps_test.rb +31 -0
- data/test/graticule/geocoder/mapquest_test.rb +56 -0
- data/test/graticule/geocoder/multi_test.rb +52 -0
- data/test/graticule/geocoder/multimap_test.rb +53 -0
- data/test/graticule/geocoder/simple_geo_test.rb +45 -0
- data/test/graticule/geocoder/yahoo_test.rb +50 -0
- data/test/graticule/geocoder/yandex_test.rb +42 -0
- data/test/graticule/geocoder_test.rb +24 -0
- data/test/graticule/location_test.rb +79 -0
- data/test/graticule/precision_test.rb +38 -0
- data/test/mocks/uri.rb +53 -0
- data/test/test_helper.rb +32 -0
- metadata +147 -75
- data/README.textile +0 -64
data/.autotest
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Autotest.add_hook :initialize do |at|
|
2
|
+
at.clear_mappings
|
3
|
+
|
4
|
+
at.add_mapping %r%^lib/(.*)\.rb$% do |_, m|
|
5
|
+
at.files_matching %r%^test/#{m[1]}_test.rb$%
|
6
|
+
end
|
7
|
+
|
8
|
+
at.add_mapping(%r%^test/.*\.rb$%) {|filename, _| filename }
|
9
|
+
|
10
|
+
at.add_mapping %r%^test/fixtures/(.*)s.yml% do |_, _|
|
11
|
+
at.files_matching %r%^test/.*\.rb$%
|
12
|
+
end
|
13
|
+
end
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.txt
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
2.4.0 (--)
|
2
|
+
* Update MapQuest handler to use the current API
|
3
|
+
|
1
4
|
2.3.0 (2013-04-01)
|
2
5
|
* Google v3 API [Adamlb]
|
3
6
|
* Escape XML entities in GeoCoder::MapQuest queries [Simon Coffey]
|
@@ -72,4 +75,4 @@
|
|
72
75
|
* added Haversine, Spherical and Vincenty distance calculations
|
73
76
|
|
74
77
|
0.1 (2006-10-31)
|
75
|
-
* Initial release
|
78
|
+
* Initial release
|
data/Gemfile
ADDED
data/LICENSE.txt
CHANGED
data/README.md
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
Graticule
|
2
|
+
=========
|
3
|
+
|
4
|
+
```
|
5
|
+
grat·i·cule |ˈgratəˌkyoōl|
|
6
|
+
Navigation. a network of parallels and meridians on a map or chart.
|
7
|
+
```
|
8
|
+
|
9
|
+
Graticule is a geocoding API for looking up address coordinates and performing distance calculations. It supports many popular APIs:
|
10
|
+
|
11
|
+
* Yahoo
|
12
|
+
* Google
|
13
|
+
* MapQuest
|
14
|
+
* Geocoder.ca
|
15
|
+
* Geocoder.us
|
16
|
+
* Geonames
|
17
|
+
* SimpleGeo
|
18
|
+
* Postcode Anywhere
|
19
|
+
* MetaCarta
|
20
|
+
* FreeThePostcode
|
21
|
+
* LocalSearchMaps
|
22
|
+
* Yandex
|
23
|
+
|
24
|
+
### Installation
|
25
|
+
|
26
|
+
```
|
27
|
+
gem install graticule
|
28
|
+
```
|
29
|
+
|
30
|
+
### Usage
|
31
|
+
|
32
|
+
There is a companion Rails plugin called [acts_as_geocodable](https://github.com/collectiveidea/acts_as_geocodable) that makes geocoding seem like magic.
|
33
|
+
|
34
|
+
Graticule exposes to main APIs: location search and distance calculations. Graticule also
|
35
|
+
provides a command line utility.
|
36
|
+
|
37
|
+
#### Location Search / Geocoding
|
38
|
+
|
39
|
+
```
|
40
|
+
require 'rubygems'
|
41
|
+
require 'graticule'
|
42
|
+
|
43
|
+
geocoder = Graticule.service(:google).new "api_key"
|
44
|
+
location = geocoder.locate("61 East 9th Street, Holland, MI")
|
45
|
+
```
|
46
|
+
|
47
|
+
For specific service documentation, please visit the [RDOCS -- rdoc.info link].
|
48
|
+
|
49
|
+
#### Distance Calculation
|
50
|
+
|
51
|
+
Graticule includes 3 different distance formulas, Spherical (simplest but least accurate), Vincenty (most accurate and most complicated), and Haversine (somewhere inbetween). The default is Haversine. There are two ways to calculate the distance between two points.
|
52
|
+
|
53
|
+
First is `Location#distance_to`:
|
54
|
+
|
55
|
+
```
|
56
|
+
holland = geocoder.locate("Holland, MI")
|
57
|
+
chicago = geocoder.locate("Chicago, IL")
|
58
|
+
holland.distance_to(chicago, :formula => :haversine) # or :spherical or :vincenty
|
59
|
+
# => 101.997458788177
|
60
|
+
```
|
61
|
+
|
62
|
+
You can also use the formula classes directly:
|
63
|
+
|
64
|
+
```
|
65
|
+
Graticule::Distance::Haversine.distance(holland, chicago)
|
66
|
+
# => 101.997458788177
|
67
|
+
```
|
68
|
+
|
69
|
+
All units are miles by default, but you can switch to kilometers with the `units` option
|
70
|
+
|
71
|
+
```
|
72
|
+
holland.distance_to(chicago, :units => :kilometers)
|
73
|
+
#
|
74
|
+
Graticule::Distance::Haversine.distance(holland, chicago, :kilometers)
|
75
|
+
```
|
76
|
+
|
77
|
+
|
78
|
+
#### Command Line
|
79
|
+
|
80
|
+
Graticule includes a command line interface (CLI). The CLI does not currently support all of the implemented services.
|
81
|
+
|
82
|
+
```
|
83
|
+
$ geocode -s google -a [api_key] Washington, DC
|
84
|
+
Washington, DC US
|
85
|
+
latitude: 38.895222, longitude: -77.036758
|
86
|
+
```
|
87
|
+
|
88
|
+
### Contributing
|
89
|
+
|
90
|
+
In the spirit of [free software](http://www.fsf.org/licensing/essays/free-sw.html), **everyone** is encouraged to help improve this project.
|
91
|
+
|
92
|
+
Here are some ways you can contribute:
|
93
|
+
|
94
|
+
* Reporting bugs
|
95
|
+
* Suggesting new features
|
96
|
+
* Writing or editing documentation
|
97
|
+
* Writing specifications
|
98
|
+
* Writing code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace)
|
99
|
+
* Refactoring code
|
100
|
+
* Reviewing patches
|
101
|
+
|
102
|
+
### Submitting an Issue
|
103
|
+
|
104
|
+
We use the [GitHub issue tracker](https://github.com/collectiveidea/graticule/issues) to track bugs and features. Before submitting a bug report or feature request, check to make sure it hasn't already been submitted. When submitting a bug report, please include a [Gist](https://gist.github.com/) that includes a stack trace and any details that may be necessary to reproduce the bug, including your gem version, Ruby version, and operating system.
|
105
|
+
|
106
|
+
### Submitting a Pull Request
|
107
|
+
|
108
|
+
1. Fork the project.
|
109
|
+
2. Create a topic branch.
|
110
|
+
3. Implement your feature or bug fix.
|
111
|
+
4. Add specs for your feature or bug fix.
|
112
|
+
5. Run `rake`. If your changes are not 100% covered and passing, go back to step 4.
|
113
|
+
6. Commit and push your changes.
|
114
|
+
7. Submit a pull request. Please do not include changes to the gemspec, version, or history file. (If you want to create your own version for some reason, please do so in a separate commit.)
|
115
|
+
|
116
|
+
### Other Links
|
117
|
+
|
118
|
+
[Blog posts about Graticule](http://opensoul.org/tags/geocoding)
|
119
|
+
|
120
|
+
[Geocoder: Alternative Geocoding library](https://github.com/alex.../geocoder)
|
121
|
+
|
122
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'graticule/version'
|
5
|
+
require 'active_support'
|
6
|
+
require 'rake/testtask'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
desc 'Default: run unit tests.'
|
10
|
+
task :default => :test
|
11
|
+
|
12
|
+
task :build do
|
13
|
+
system "gem build graticule.gemspec"
|
14
|
+
end
|
15
|
+
|
16
|
+
task :release => :build do
|
17
|
+
system "gem push graticule-#{Graticule::VERSION}.gem"
|
18
|
+
end
|
19
|
+
|
20
|
+
task :install => :build do
|
21
|
+
system "gem install graticule-#{Graticule::VERSION}.gem"
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Run the unit tests'
|
25
|
+
Rake::TestTask.new(:test) do |t|
|
26
|
+
t.libs << 'lib' << 'test'
|
27
|
+
t.pattern = 'test/**/*_test.rb'
|
28
|
+
t.verbose = true
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'active_support'
|
32
|
+
require 'net/http'
|
33
|
+
require 'uri'
|
34
|
+
RESPONSES_PATH = File.dirname(__FILE__) + '/test/fixtures/responses'
|
35
|
+
|
36
|
+
def cache_responses(service)
|
37
|
+
test_config[service.to_s]['responses'].each do |file,url|
|
38
|
+
File.open("#{RESPONSES_PATH}/#{service}/#{file}", 'w') do |f|
|
39
|
+
f.puts Net::HTTP.get(URI.parse(url))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_config
|
45
|
+
file = File.dirname(__FILE__) + '/test/config.yml'
|
46
|
+
unless File.exists?(file)
|
47
|
+
raise "API keys not found. Add them by:
|
48
|
+
cp #{file}.default #{file}
|
49
|
+
vi #{file}
|
50
|
+
"
|
51
|
+
end
|
52
|
+
@test_config ||= YAML.load(File.read(file)).tap do |config|
|
53
|
+
config.each do |service,values|
|
54
|
+
values['responses'].each {|f,url| update_placeholders!(values, url) }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def update_placeholders!(config, thing)
|
60
|
+
config.each do |option, value|
|
61
|
+
thing.gsub!(":#{option}", value) if value.is_a?(String)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
namespace :test do
|
66
|
+
namespace :cache do
|
67
|
+
desc 'Cache test responses from all the geocoders'
|
68
|
+
task :all => test_config.keys
|
69
|
+
|
70
|
+
test_config.keys.each do |service|
|
71
|
+
desc "Cache test responses for #{service}"
|
72
|
+
task service do
|
73
|
+
cache_responses(service)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
data/graticule.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path("../lib/graticule/version.rb", __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "graticule"
|
7
|
+
spec.version = Graticule::VERSION
|
8
|
+
|
9
|
+
spec.authors = ["Brandon Keepers", "Daniel Morrison", "Collective Idea"]
|
10
|
+
spec.email = ["brandon@opensoul.org", "daniel@collectiveidea.com", "code@collectiveidea.com"]
|
11
|
+
spec.description = "Graticule is a geocoding API that provides a common interface to all the popular services, including Google, Yahoo, Geocoder.us, and MetaCarta."
|
12
|
+
spec.summary = "API for using all the popular geocoding services"
|
13
|
+
spec.homepage = "https://github.com/collectiveidea/graticule"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($\)
|
17
|
+
spec.test_files = spec.files.grep(/^test/)
|
18
|
+
spec.executables = ["geocode"]
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activesupport"
|
22
|
+
spec.add_dependency "i18n"
|
23
|
+
spec.add_dependency "happymapper", ">= 0.3.0"
|
24
|
+
spec.add_dependency "json"
|
25
|
+
end
|
26
|
+
|
data/lib/graticule/cli.rb
CHANGED
@@ -23,13 +23,15 @@ module Graticule
|
|
23
23
|
|
24
24
|
def self.start(args, out = STDOUT)
|
25
25
|
options = { :service => :yahoo, :api_key => 'YahooDemo' }
|
26
|
+
supported_services = %w(yahoo google yandex geocoder_us metacarta)
|
26
27
|
|
27
28
|
OptionParser.new do |opts|
|
28
29
|
opts.banner = "Usage: geocode [options] location"
|
29
30
|
opts.separator ""
|
30
31
|
opts.separator "Options: "
|
31
32
|
|
32
|
-
opts.on("-s service",
|
33
|
+
opts.on("-s service", supported_services, "--service service",
|
34
|
+
"Geocoding service.", "Currently supported services: #{supported_services.join(", ")}") do |service|
|
33
35
|
options[:service] = service
|
34
36
|
end
|
35
37
|
|
@@ -2,16 +2,21 @@
|
|
2
2
|
require 'json'
|
3
3
|
module Graticule #:nodoc:
|
4
4
|
module Geocoder #:nodoc:
|
5
|
-
# gg = Graticule.service(:google).new
|
5
|
+
# gg = Graticule.service(:google).new
|
6
6
|
# location = gg.locate '1600 Amphitheater Pkwy, Mountain View, CA'
|
7
7
|
# p location.coordinates
|
8
8
|
# #=> [37.423111, -122.081783
|
9
9
|
#
|
10
|
+
# If you have a Google business account, initialize with:
|
11
|
+
#
|
12
|
+
# gg = Graticule.service(:google).new(MAPS_API_KEY, MAPS_CLIENT_ID)
|
13
|
+
#
|
10
14
|
class Google < Base
|
11
15
|
# https://developers.google.com/maps/documentation/geocoding/
|
12
16
|
|
13
|
-
def initialize(key)
|
17
|
+
def initialize(key=nil, client_id=nil)
|
14
18
|
@key = key
|
19
|
+
@client_id = client_id
|
15
20
|
@url = URI.parse 'http://maps.googleapis.com/maps/api/geocode/json'
|
16
21
|
end
|
17
22
|
|
@@ -129,9 +134,33 @@ module Graticule #:nodoc:
|
|
129
134
|
end
|
130
135
|
|
131
136
|
# Creates a URL from the Hash +params+..
|
137
|
+
#
|
138
|
+
# If initialized with a key and client id for a Business account, signs
|
139
|
+
# the url as required by v3 of the library:
|
140
|
+
#
|
141
|
+
# https://developers.google.com/maps/documentation/business/webservices#digital_signatures
|
142
|
+
#
|
132
143
|
def make_url(params) #:nodoc:
|
133
|
-
|
144
|
+
if @key && @client_id
|
145
|
+
url = super params.merge(:sensor => false, :client => @client_id)
|
146
|
+
make_signed_url(url)
|
147
|
+
else
|
148
|
+
super params.merge(:sensor => false)
|
149
|
+
end
|
134
150
|
end
|
151
|
+
|
152
|
+
def make_signed_url(original_url) #:nodoc:
|
153
|
+
require "base64"
|
154
|
+
require 'openssl'
|
155
|
+
url_to_sign = "#{original_url.path}?#{original_url.query}"
|
156
|
+
decoded_key = Base64.decode64(@key.tr("-_", "+/"))
|
157
|
+
signature = OpenSSL::HMAC.digest('sha1', decoded_key, url_to_sign)
|
158
|
+
encoded_signature = Base64.encode64(signature).tr("+/", "-_")
|
159
|
+
signed_url = original_url.to_s + "&signature=#{encoded_signature}"
|
160
|
+
#puts signed_url
|
161
|
+
URI.parse signed_url
|
162
|
+
end
|
163
|
+
|
135
164
|
end
|
136
165
|
end
|
137
|
-
end
|
166
|
+
end
|
@@ -1,40 +1,19 @@
|
|
1
|
-
require 'htmlentities'
|
2
|
-
|
3
1
|
# encoding: UTF-8
|
4
2
|
module Graticule #:nodoc:
|
5
3
|
module Geocoder #:nodoc:
|
6
4
|
|
7
|
-
# Mapquest
|
8
|
-
# get by registering at:
|
9
|
-
# http://developer.mapquest.com/Home/Register?_devAPISignup_WAR_devAPISignup_action=signup&_devAPISignup_WAR_devAPISignup_clientType=Developer
|
5
|
+
# Mapquest uses the Licenced Community API which requires an api key. You can sign up an account
|
6
|
+
# and get an api key by registering at: http://developer.mapquest.com/
|
10
7
|
#
|
11
|
-
# mq = Graticule.service(:mapquest).new(
|
8
|
+
# mq = Graticule.service(:mapquest).new(API_KEY)
|
12
9
|
# location = gg.locate('44 Allen Rd., Lovell, ME 04051')
|
13
10
|
# [42.78942, -86.104424]
|
14
11
|
#
|
15
12
|
class Mapquest < Base
|
16
|
-
# I would link to the documentation here, but there is none that will do anything but confuse you.
|
17
|
-
|
18
|
-
PRECISION = {
|
19
|
-
'L1' => Precision::Address,
|
20
|
-
'I1' => Precision::Street,
|
21
|
-
'B1' => Precision::Street,
|
22
|
-
'B2' => Precision::Street,
|
23
|
-
'B3' => Precision::Street,
|
24
|
-
'Z3' => Precision::PostalCode,
|
25
|
-
'Z4' => Precision::PostalCode,
|
26
|
-
'Z2' => Precision::PostalCode,
|
27
|
-
'Z1' => Precision::PostalCode,
|
28
|
-
'A5' => Precision::Locality,
|
29
|
-
'A4' => Precision::Region,
|
30
|
-
'A3' => Precision::Region,
|
31
|
-
'A1' => Precision::Country
|
32
|
-
}
|
33
13
|
|
34
|
-
def initialize(
|
35
|
-
@
|
36
|
-
@
|
37
|
-
@url = URI.parse('http://geocode.dev.mapquest.com/mq/mqserver.dll')
|
14
|
+
def initialize(api_key)
|
15
|
+
@api_key = api_key
|
16
|
+
@url = URI.parse('http://www.mapquestapi.com/geocoding/v1/address')
|
38
17
|
end
|
39
18
|
|
40
19
|
# Locates +address+ returning a Location
|
@@ -45,66 +24,80 @@ module Graticule #:nodoc:
|
|
45
24
|
protected
|
46
25
|
|
47
26
|
def make_url(params) #:nodoc
|
48
|
-
request = Mapquest::Request.new(params[:q], @
|
27
|
+
request = Mapquest::Request.new(params[:q], @api_key)
|
49
28
|
url = @url.dup
|
50
|
-
url.query =
|
29
|
+
url.query = request.query
|
51
30
|
url
|
52
31
|
end
|
53
32
|
|
54
33
|
class Request
|
55
|
-
def initialize(address,
|
34
|
+
def initialize(address, api_key)
|
56
35
|
@address = address
|
57
|
-
@
|
58
|
-
@password = password
|
36
|
+
@api_key = api_key
|
59
37
|
end
|
60
38
|
|
61
39
|
def query
|
62
|
-
"
|
63
|
-
end
|
64
|
-
|
65
|
-
def address_string
|
66
|
-
"<Address><Street>#{escaped_address}</Street></Address><GeocodeOptionsCollection Count=\"0\"/>"
|
67
|
-
end
|
68
|
-
|
69
|
-
def authentication_string
|
70
|
-
"<Authentication Version=\"2\"><Password>#{@password}</Password><ClientId>#{@client_id}</ClientId></Authentication>"
|
71
|
-
end
|
72
|
-
|
73
|
-
def escaped_address
|
74
|
-
HTMLEntities.new.encode(@address, :basic)
|
40
|
+
"key=#{URI.escape(@api_key)}&outFormat=xml&inFormat=kvp&location=#{URI.escape(@address)}"
|
75
41
|
end
|
76
42
|
end
|
77
43
|
|
44
|
+
# See http://www.mapquestapi.com/geocoding/geocodequality.html#granularity
|
45
|
+
PRECISION = {
|
46
|
+
'P1' => Precision::Point,
|
47
|
+
'L1' => Precision::Address,
|
48
|
+
'I1' => Precision::Street,
|
49
|
+
'B1' => Precision::Street,
|
50
|
+
'B2' => Precision::Street,
|
51
|
+
'B3' => Precision::Street,
|
52
|
+
'Z3' => Precision::PostalCode,
|
53
|
+
'Z4' => Precision::PostalCode,
|
54
|
+
'Z2' => Precision::PostalCode,
|
55
|
+
'Z1' => Precision::PostalCode,
|
56
|
+
'A5' => Precision::Locality,
|
57
|
+
'A4' => Precision::Region,
|
58
|
+
'A3' => Precision::Region,
|
59
|
+
'A1' => Precision::Country
|
60
|
+
}
|
61
|
+
|
78
62
|
class Address
|
79
63
|
include HappyMapper
|
80
|
-
tag '
|
81
|
-
element :latitude, Float, :tag => '
|
82
|
-
element :longitude, Float, :tag => '
|
83
|
-
element :street, String, :tag => '
|
84
|
-
element :locality, String, :tag => '
|
85
|
-
element :region, String, :tag => '
|
86
|
-
element :postal_code, String, :tag => '
|
87
|
-
element :country, String, :tag => '
|
88
|
-
element :result_code, String, :tag => '
|
64
|
+
tag 'location'
|
65
|
+
element :latitude, Float, :tag => 'lat', :deep => true
|
66
|
+
element :longitude, Float, :tag => 'lng', :deep => true
|
67
|
+
element :street, String, :tag => 'street'
|
68
|
+
element :locality, String, :tag => 'adminArea5'
|
69
|
+
element :region, String, :tag => 'adminArea3'
|
70
|
+
element :postal_code, String, :tag => 'postalCode'
|
71
|
+
element :country, String, :tag => 'adminArea1'
|
72
|
+
element :result_code, String, :tag => 'geocodeQualityCode'
|
89
73
|
|
90
74
|
def precision
|
91
75
|
PRECISION[result_code.to_s[0,2]] || :unknown
|
92
76
|
end
|
93
77
|
end
|
94
78
|
|
79
|
+
class Locations
|
80
|
+
include HappyMapper
|
81
|
+
has_many :addresses, Address, :tag => "location"
|
82
|
+
end
|
83
|
+
|
95
84
|
class Result
|
96
85
|
include HappyMapper
|
97
|
-
tag
|
98
|
-
|
86
|
+
has_one :locations, Locations, :tag => "locations"
|
87
|
+
end
|
88
|
+
|
89
|
+
class Response
|
90
|
+
include HappyMapper
|
91
|
+
has_one :result, Result, :deep => true
|
99
92
|
end
|
100
93
|
|
101
94
|
def prepare_response(xml)
|
102
|
-
|
95
|
+
Response.parse(xml, :single => true)
|
103
96
|
end
|
104
97
|
|
105
98
|
# Extracts a location from +xml+.
|
106
|
-
def parse_response(
|
107
|
-
addr = result.addresses.first
|
99
|
+
def parse_response(response) #:nodoc:
|
100
|
+
addr = response.result.locations.addresses.first
|
108
101
|
Location.new(
|
109
102
|
:latitude => addr.latitude,
|
110
103
|
:longitude => addr.longitude,
|