graticule 2.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/graticule.rb +1 -0
- data/lib/graticule/cli.rb +12 -11
- data/lib/graticule/core_ext.rb +3 -2
- data/lib/graticule/distance.rb +3 -2
- data/lib/graticule/distance/haversine.rb +6 -5
- data/lib/graticule/distance/spherical.rb +4 -3
- data/lib/graticule/distance/vincenty.rb +2 -1
- data/lib/graticule/geocoder.rb +3 -2
- data/lib/graticule/geocoder/base.rb +10 -9
- data/lib/graticule/geocoder/bogus.rb +4 -3
- data/lib/graticule/geocoder/freethepostcode.rb +5 -4
- data/lib/graticule/geocoder/geocoder_ca.rb +8 -7
- data/lib/graticule/geocoder/geocoder_us.rb +3 -2
- data/lib/graticule/geocoder/geonames.rb +9 -8
- data/lib/graticule/geocoder/google.rb +12 -11
- data/lib/graticule/geocoder/host_ip.rb +4 -3
- data/lib/graticule/geocoder/local_search_maps.rb +8 -7
- data/lib/graticule/geocoder/mapquest.rb +7 -6
- data/lib/graticule/geocoder/multi.rb +12 -11
- data/lib/graticule/geocoder/multimap.rb +20 -19
- data/lib/graticule/geocoder/simple_geo.rb +1 -0
- data/lib/graticule/geocoder/yahoo.rb +10 -9
- data/lib/graticule/location.rb +2 -1
- data/lib/graticule/precision.rb +8 -7
- data/lib/graticule/version.rb +2 -1
- metadata +76 -107
data/lib/graticule.rb
CHANGED
data/lib/graticule/cli.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require 'graticule'
|
2
3
|
require 'optparse'
|
3
4
|
|
4
5
|
module Graticule
|
5
|
-
|
6
|
+
|
6
7
|
# A command line interface for geocoding. From the command line, run:
|
7
8
|
#
|
8
|
-
# geocode 49423
|
9
|
+
# geocode 49423
|
9
10
|
#
|
10
11
|
# Outputs:
|
11
12
|
#
|
@@ -13,16 +14,16 @@ module Graticule
|
|
13
14
|
# # latitude: 42.7654, longitude: -86.1085
|
14
15
|
#
|
15
16
|
# == Usage: geocode [options] location
|
16
|
-
#
|
17
|
-
# Options:
|
17
|
+
#
|
18
|
+
# Options:
|
18
19
|
# -s, --service service Geocoding service
|
19
20
|
# -a, --apikey apikey API key for the selected service
|
20
21
|
# -h, --help Help
|
21
22
|
class Cli
|
22
|
-
|
23
|
+
|
23
24
|
def self.start(args, out = STDOUT)
|
24
25
|
options = { :service => :yahoo, :api_key => 'YahooDemo' }
|
25
|
-
|
26
|
+
|
26
27
|
OptionParser.new do |opts|
|
27
28
|
opts.banner = "Usage: geocode [options] location"
|
28
29
|
opts.separator ""
|
@@ -31,9 +32,9 @@ module Graticule
|
|
31
32
|
opts.on("-s service", %w(yahoo google geocoder_us metacarta), "--service service", "Geocoding service") do |service|
|
32
33
|
options[:service] = service
|
33
34
|
end
|
34
|
-
|
35
|
+
|
35
36
|
opts.on("-a apikey", "--apikey apikey", "API key for the selected service")
|
36
|
-
|
37
|
+
|
37
38
|
opts.on_tail("-h", "--help", "Help") do
|
38
39
|
puts opts
|
39
40
|
exit
|
@@ -41,7 +42,7 @@ module Graticule
|
|
41
42
|
end.parse! args
|
42
43
|
|
43
44
|
options[:location] = args.join(" ")
|
44
|
-
|
45
|
+
|
45
46
|
result = Graticule.service(options[:service]).new(*options[:api_key].split(',')).locate(options[:location])
|
46
47
|
location = (result.is_a?(Array) ? result.first : result)
|
47
48
|
if location
|
@@ -57,8 +58,8 @@ module Graticule
|
|
57
58
|
Graticule::Error => error
|
58
59
|
$stderr.puts error.message
|
59
60
|
end
|
60
|
-
|
61
|
-
|
61
|
+
|
62
|
+
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
data/lib/graticule/core_ext.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule
|
2
3
|
module RadiansAndDegrees
|
3
4
|
# Convert from degrees to radians
|
4
5
|
def to_radians
|
5
6
|
( self / 360.0 ) * Math::PI * 2
|
6
7
|
end
|
7
|
-
|
8
|
+
|
8
9
|
# Convert from radians to degrees
|
9
10
|
def to_degrees
|
10
11
|
( self * 360.0 ) / Math::PI / 2
|
@@ -12,4 +13,4 @@ module Graticule
|
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
15
|
-
Numeric.send :include, Graticule::RadiansAndDegrees
|
16
|
+
Numeric.send :include, Graticule::RadiansAndDegrees
|
data/lib/graticule/distance.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule
|
2
3
|
module Distance
|
3
|
-
|
4
|
+
|
4
5
|
EARTH_RADIUS = { :kilometers => 6378.135, :miles => 3963.1676 }
|
5
6
|
# WGS-84 numbers
|
6
7
|
EARTH_MAJOR_AXIS_RADIUS = { :kilometers => 6378.137, :miles => 3963.19059 }
|
@@ -9,7 +10,7 @@ module Graticule
|
|
9
10
|
class DistanceFormula
|
10
11
|
include Math
|
11
12
|
extend Math
|
12
|
-
|
13
|
+
|
13
14
|
def initialize
|
14
15
|
raise NotImplementedError
|
15
16
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule
|
2
3
|
module Distance
|
3
4
|
#
|
@@ -7,7 +8,7 @@ module Graticule
|
|
7
8
|
# for distance formulas.
|
8
9
|
#
|
9
10
|
class Haversine < DistanceFormula
|
10
|
-
|
11
|
+
|
11
12
|
# Calculate the distance between two Locations using the Haversine formula
|
12
13
|
#
|
13
14
|
# Graticule::Distance::Haversine.distance(
|
@@ -25,9 +26,9 @@ module Graticule
|
|
25
26
|
latitude_delta = to_latitude - from_latitude
|
26
27
|
longitude_delta = to_longitude - from_longitude
|
27
28
|
|
28
|
-
a = sin(latitude_delta/2)**2 +
|
29
|
-
cos(from_latitude) *
|
30
|
-
cos(to_latitude) *
|
29
|
+
a = sin(latitude_delta/2)**2 +
|
30
|
+
cos(from_latitude) *
|
31
|
+
cos(to_latitude) *
|
31
32
|
sin(longitude_delta/2)**2
|
32
33
|
|
33
34
|
c = 2 * atan2(sqrt(a), sqrt(1-a))
|
@@ -37,4 +38,4 @@ module Graticule
|
|
37
38
|
|
38
39
|
end
|
39
40
|
end
|
40
|
-
end
|
41
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule
|
2
3
|
module Distance
|
3
4
|
|
@@ -25,7 +26,7 @@ module Graticule
|
|
25
26
|
Math.sin(from_latitude) *
|
26
27
|
Math.sin(to_latitude) +
|
27
28
|
|
28
|
-
Math.cos(from_latitude) *
|
29
|
+
Math.cos(from_latitude) *
|
29
30
|
Math.cos(to_latitude) *
|
30
31
|
Math.cos(to_longitude - from_longitude)
|
31
32
|
) * EARTH_RADIUS[units.to_sym]
|
@@ -46,7 +47,7 @@ module Graticule
|
|
46
47
|
) * #{Graticule::Distance::EARTH_RADIUS[options[:units].to_sym]})
|
47
48
|
}.gsub("\n", '').squeeze(" ")
|
48
49
|
end
|
49
|
-
|
50
|
+
|
50
51
|
end
|
51
52
|
end
|
52
|
-
end
|
53
|
+
end
|
data/lib/graticule/geocoder.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require 'happymapper'
|
2
3
|
|
3
4
|
module Graticule
|
4
|
-
|
5
|
+
|
5
6
|
# Get a geocoder for the given service
|
6
7
|
#
|
7
8
|
# geocoder = Graticule.service(:google).new "api_key"
|
@@ -11,7 +12,7 @@ module Graticule
|
|
11
12
|
def self.service(name)
|
12
13
|
Geocoder.const_get name.to_s.camelize
|
13
14
|
end
|
14
|
-
|
15
|
+
|
15
16
|
# Base error class
|
16
17
|
class Error < RuntimeError; end
|
17
18
|
class CredentialsError < Error; end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require 'open-uri'
|
2
3
|
|
3
4
|
module Graticule #:nodoc:
|
@@ -22,7 +23,7 @@ module Graticule #:nodoc:
|
|
22
23
|
# perform URL customization, override +make_url+.
|
23
24
|
#
|
24
25
|
# class FakeGeocoder < Base
|
25
|
-
#
|
26
|
+
#
|
26
27
|
# def initialize(appid)
|
27
28
|
# @appid = appid
|
28
29
|
# @url = URI.parse 'http://example.com/test'
|
@@ -31,22 +32,22 @@ module Graticule #:nodoc:
|
|
31
32
|
# def locate(query)
|
32
33
|
# get :q => query
|
33
34
|
# end
|
34
|
-
#
|
35
|
+
#
|
35
36
|
# private
|
36
|
-
#
|
37
|
+
#
|
37
38
|
# def check_error(xml)
|
38
39
|
# raise Error, xml.elements['error'].text if xml.elements['error']
|
39
40
|
# end
|
40
|
-
#
|
41
|
+
#
|
41
42
|
# def make_url(params)
|
42
43
|
# params[:appid] = @appid
|
43
44
|
# super params
|
44
45
|
# end
|
45
|
-
#
|
46
|
+
#
|
46
47
|
# def parse_response(response)
|
47
48
|
# # return Location
|
48
49
|
# end
|
49
|
-
#
|
50
|
+
#
|
50
51
|
# end
|
51
52
|
#
|
52
53
|
class Base
|
@@ -55,9 +56,9 @@ module Graticule #:nodoc:
|
|
55
56
|
def initialize
|
56
57
|
raise NotImplementedError
|
57
58
|
end
|
58
|
-
|
59
|
+
|
59
60
|
private
|
60
|
-
|
61
|
+
|
61
62
|
def location_from_params(params)
|
62
63
|
case params
|
63
64
|
when Location then params
|
@@ -95,7 +96,7 @@ module Graticule #:nodoc:
|
|
95
96
|
url.query = escaped_params.join '&'
|
96
97
|
return url
|
97
98
|
end
|
98
|
-
|
99
|
+
|
99
100
|
# Override to convert the response to something besides a String, which
|
100
101
|
# will get passed to +check_error+ and +parse_response+.
|
101
102
|
def prepare_response(response)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule #:nodoc:
|
2
3
|
module Geocoder #:nodoc:
|
3
4
|
# Bogus geocoder that can be used for test purposes
|
@@ -5,10 +6,10 @@ module Graticule #:nodoc:
|
|
5
6
|
# A queue of canned responses
|
6
7
|
class_attribute :responses
|
7
8
|
self.responses = []
|
8
|
-
|
9
|
+
|
9
10
|
# A default location to use if the responses queue is empty
|
10
|
-
|
11
|
-
|
11
|
+
class_attribute :default
|
12
|
+
|
12
13
|
def locate(address)
|
13
14
|
responses.shift || default || Location.new(:street => address)
|
14
15
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule #:nodoc:
|
2
3
|
module Geocoder #:nodoc:
|
3
|
-
|
4
|
+
|
4
5
|
# freethepostcode.org (http://www.freethepostcode.org/) is a
|
5
6
|
# free service to convert UK postcodes into geolocation data.
|
6
7
|
#
|
@@ -17,13 +18,13 @@ module Graticule #:nodoc:
|
|
17
18
|
def locate(postcode)
|
18
19
|
get :postcode => postcode
|
19
20
|
end
|
20
|
-
|
21
|
+
|
21
22
|
private
|
22
23
|
|
23
24
|
def prepare_response(response)
|
24
25
|
response.split("\n")[1]
|
25
26
|
end
|
26
|
-
|
27
|
+
|
27
28
|
def parse_response(response)
|
28
29
|
data = response.split
|
29
30
|
Location.new(:latitude => data[0].to_f, :longitude => data[1].to_f, :precision => :unknown)
|
@@ -32,7 +33,7 @@ module Graticule #:nodoc:
|
|
32
33
|
def check_error(response)
|
33
34
|
raise AddressError, 'unknown address' if response.blank?
|
34
35
|
end
|
35
|
-
|
36
|
+
|
36
37
|
end
|
37
38
|
end
|
38
39
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule #:nodoc:
|
2
3
|
module Geocoder #:nodoc:
|
3
4
|
|
@@ -12,9 +13,9 @@ module Graticule #:nodoc:
|
|
12
13
|
def locate(address)
|
13
14
|
get :locate => address.is_a?(String) ? address : location_from_params(address).to_s(:country => false)
|
14
15
|
end
|
15
|
-
|
16
|
+
|
16
17
|
private
|
17
|
-
|
18
|
+
|
18
19
|
class Response
|
19
20
|
include HappyMapper
|
20
21
|
tag 'geodata'
|
@@ -25,25 +26,25 @@ module Graticule #:nodoc:
|
|
25
26
|
element :locality, String, :deep => true, :tag => 'city'
|
26
27
|
element :postal_code, String, :tag => 'postal'
|
27
28
|
element :region, String, :deep => true, :tag => 'prov'
|
28
|
-
|
29
|
+
|
29
30
|
class Error
|
30
31
|
include HappyMapper
|
31
32
|
tag 'error'
|
32
33
|
element :code, Integer
|
33
34
|
element :description, String
|
34
35
|
end
|
35
|
-
|
36
|
+
|
36
37
|
has_one :error, Error
|
37
|
-
|
38
|
+
|
38
39
|
def street
|
39
40
|
[street_number, street_name].join(' ')
|
40
41
|
end
|
41
42
|
end
|
42
|
-
|
43
|
+
|
43
44
|
def prepare_response(xml)
|
44
45
|
Response.parse(xml, :single => true)
|
45
46
|
end
|
46
|
-
|
47
|
+
|
47
48
|
def parse_response(response) #:nodoc:
|
48
49
|
Location.new(
|
49
50
|
:latitude => response.latitude,
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule #:nodoc:
|
2
3
|
module Geocoder #:nodoc:
|
3
4
|
|
@@ -27,13 +28,13 @@ module Graticule #:nodoc:
|
|
27
28
|
def locate(address)
|
28
29
|
get :address => address.is_a?(String) ? address : location_from_params(address).to_s(:country => false)
|
29
30
|
end
|
30
|
-
|
31
|
+
|
31
32
|
private
|
32
33
|
class Point
|
33
34
|
include HappyMapper
|
34
35
|
tag 'Point'
|
35
36
|
namespace 'http://www.w3.org/2003/01/geo/wgs84_pos#'
|
36
|
-
|
37
|
+
|
37
38
|
element :description, String, :namespace => 'http://purl.org/dc/elements/1.1/'
|
38
39
|
element :longitude, Float, :tag => 'long'
|
39
40
|
element :latitude, Float, :tag => 'lat'
|
@@ -1,30 +1,31 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule #:nodoc:
|
2
3
|
module Geocoder #:nodoc:
|
3
4
|
class Geonames < Base
|
4
|
-
|
5
|
+
|
5
6
|
def initialize
|
6
7
|
@url = URI.parse 'http://ws.geonames.org/timezone'
|
7
8
|
end
|
8
|
-
|
9
|
+
|
9
10
|
def time_zone(location)
|
10
|
-
get :formatted => 'true', :style => 'full', :lat => location.latitude, :lng => location.longitude
|
11
|
+
get :formatted => 'true', :style => 'full', :lat => location.latitude, :lng => location.longitude
|
11
12
|
end
|
12
|
-
|
13
|
-
private
|
13
|
+
|
14
|
+
private
|
14
15
|
class Status
|
15
16
|
include HappyMapper
|
16
17
|
tag 'status'
|
17
18
|
attribute :message, String
|
18
19
|
attribute :value, String
|
19
20
|
end
|
20
|
-
|
21
|
+
|
21
22
|
class Response
|
22
23
|
include HappyMapper
|
23
24
|
tag 'geonames'
|
24
25
|
element :timezoneId, String, :deep => true
|
25
26
|
has_one :status, Status
|
26
27
|
end
|
27
|
-
|
28
|
+
|
28
29
|
def prepare_response(xml)
|
29
30
|
Response.parse(xml, :single => true)
|
30
31
|
end
|
@@ -32,7 +33,7 @@ module Graticule #:nodoc:
|
|
32
33
|
def parse_response(response) #:nodoc:
|
33
34
|
response.timezoneId
|
34
35
|
end
|
35
|
-
|
36
|
+
|
36
37
|
# Extracts and raises an error from +xml+, if any.
|
37
38
|
def check_error(response) #:nodoc:
|
38
39
|
if response && response.status
|
@@ -1,9 +1,10 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule #:nodoc:
|
2
3
|
module Geocoder #:nodoc:
|
3
|
-
|
4
|
+
|
4
5
|
# First you need a Google Maps API key. You can register for one here:
|
5
6
|
# http://www.google.com/apis/maps/signup.html
|
6
|
-
#
|
7
|
+
#
|
7
8
|
# gg = Graticule.service(:google).new(MAPS_API_KEY)
|
8
9
|
# location = gg.locate '1600 Amphitheater Pkwy, Mountain View, CA'
|
9
10
|
# p location.coordinates
|
@@ -11,7 +12,7 @@ module Graticule #:nodoc:
|
|
11
12
|
#
|
12
13
|
class Google < Base
|
13
14
|
# http://www.google.com/apis/maps/documentation/#Geocoding_HTTP_Request
|
14
|
-
|
15
|
+
|
15
16
|
# http://www.google.com/apis/maps/documentation/reference.html#GGeoAddressAccuracy
|
16
17
|
PRECISION = {
|
17
18
|
0 => Precision::Unknown, # Unknown location.
|
@@ -44,15 +45,15 @@ module Graticule #:nodoc:
|
|
44
45
|
|
45
46
|
attribute :accuracy, Integer, :tag => 'Accuracy'
|
46
47
|
end
|
47
|
-
|
48
|
+
|
48
49
|
class Placemark
|
49
50
|
include HappyMapper
|
50
51
|
tag 'Placemark'
|
51
52
|
element :coordinates, String, :deep => true
|
52
53
|
has_one :address, Address
|
53
|
-
|
54
|
+
|
54
55
|
attr_reader :longitude, :latitude
|
55
|
-
|
56
|
+
|
56
57
|
with_options :deep => true, :namespace => 'urn:oasis:names:tc:ciq:xsdschema:xAL:2.0' do |map|
|
57
58
|
map.element :street, String, :tag => 'ThoroughfareName'
|
58
59
|
map.element :locality, String, :tag => 'LocalityName'
|
@@ -60,27 +61,27 @@ module Graticule #:nodoc:
|
|
60
61
|
map.element :postal_code, String, :tag => 'PostalCodeNumber'
|
61
62
|
map.element :country, String, :tag => 'CountryNameCode'
|
62
63
|
end
|
63
|
-
|
64
|
+
|
64
65
|
def coordinates=(coordinates)
|
65
66
|
@longitude, @latitude, _ = coordinates.split(',').map { |v| v.to_f }
|
66
67
|
end
|
67
|
-
|
68
|
+
|
68
69
|
def accuracy
|
69
70
|
address.accuracy if address
|
70
71
|
end
|
71
|
-
|
72
|
+
|
72
73
|
def precision
|
73
74
|
PRECISION[accuracy] || :unknown
|
74
75
|
end
|
75
76
|
end
|
76
|
-
|
77
|
+
|
77
78
|
class Response
|
78
79
|
include HappyMapper
|
79
80
|
tag 'Response'
|
80
81
|
element :code, Integer, :tag => 'code', :deep => true
|
81
82
|
has_many :placemarks, Placemark
|
82
83
|
end
|
83
|
-
|
84
|
+
|
84
85
|
def prepare_response(xml)
|
85
86
|
Response.parse(xml, :single => true)
|
86
87
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require 'yaml'
|
2
3
|
|
3
4
|
module Graticule #:nodoc:
|
@@ -13,14 +14,14 @@ module Graticule #:nodoc:
|
|
13
14
|
def locate(address)
|
14
15
|
get :ip => address, :position => true
|
15
16
|
end
|
16
|
-
|
17
|
+
|
17
18
|
private
|
18
|
-
|
19
|
+
|
19
20
|
def prepare_response(response)
|
20
21
|
# add new line so YAML.load doesn't puke
|
21
22
|
YAML.load(response + "\n")
|
22
23
|
end
|
23
|
-
|
24
|
+
|
24
25
|
def parse_response(response) #:nodoc:
|
25
26
|
Location.new.tap do |location|
|
26
27
|
location.latitude = response['Latitude']
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule #:nodoc:
|
2
3
|
module Geocoder #:nodoc:
|
3
4
|
|
@@ -5,20 +6,20 @@ module Graticule #:nodoc:
|
|
5
6
|
#
|
6
7
|
# See http://emad.fano.us/blog/?p=277
|
7
8
|
class LocalSearchMaps < Base
|
8
|
-
|
9
|
+
|
9
10
|
def initialize
|
10
11
|
@url = URI.parse 'http://geo.localsearchmaps.com/'
|
11
12
|
end
|
12
|
-
|
13
|
+
|
13
14
|
# This web service will handle some addresses outside the US
|
14
15
|
# if given more structured arguments than just a string address
|
15
16
|
# So allow input as a hash for the different arguments (:city, :country, :zip)
|
16
17
|
def locate(params)
|
17
18
|
get params.is_a?(String) ? {:loc => params} : map_attributes(location_from_params(params))
|
18
19
|
end
|
19
|
-
|
20
|
+
|
20
21
|
private
|
21
|
-
|
22
|
+
|
22
23
|
def map_attributes(location)
|
23
24
|
mapping = {:street => :street, :locality => :city, :region => :state, :postal_code => :zip, :country => :country}
|
24
25
|
mapping.keys.inject({}) do |result,attribute|
|
@@ -26,7 +27,7 @@ module Graticule #:nodoc:
|
|
26
27
|
result
|
27
28
|
end
|
28
29
|
end
|
29
|
-
|
30
|
+
|
30
31
|
def check_error(js)
|
31
32
|
case js
|
32
33
|
when nil
|
@@ -35,12 +36,12 @@ module Graticule #:nodoc:
|
|
35
36
|
raise AddressError, 'Location not found'
|
36
37
|
end
|
37
38
|
end
|
38
|
-
|
39
|
+
|
39
40
|
def parse_response(js)
|
40
41
|
coordinates = js.match(/map.centerAndZoom\(new GPoint\((.+?), (.+?)\)/)
|
41
42
|
Location.new(:longitude => coordinates[1].to_f, :latitude => coordinates[2].to_f)
|
42
43
|
end
|
43
|
-
|
44
|
+
|
44
45
|
end
|
45
46
|
end
|
46
47
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule #:nodoc:
|
2
3
|
module Geocoder #:nodoc:
|
3
4
|
|
@@ -6,10 +7,10 @@ module Graticule #:nodoc:
|
|
6
7
|
# http://developer.mapquest.com/Home/Register?_devAPISignup_WAR_devAPISignup_action=signup&_devAPISignup_WAR_devAPISignup_clientType=Developer
|
7
8
|
#
|
8
9
|
# mq = Graticule.service(:mapquest).new(CLIENT_ID, PASSWORD)
|
9
|
-
# location = gg.locate('44 Allen Rd., Lovell, ME 04051')
|
10
|
+
# location = gg.locate('44 Allen Rd., Lovell, ME 04051')
|
10
11
|
# [42.78942, -86.104424]
|
11
12
|
#
|
12
|
-
class Mapquest < Base
|
13
|
+
class Mapquest < Base
|
13
14
|
# I would link to the documentation here, but there is none that will do anything but confuse you.
|
14
15
|
|
15
16
|
PRECISION = {
|
@@ -48,7 +49,7 @@ module Graticule #:nodoc:
|
|
48
49
|
url.query = URI.escape(query)
|
49
50
|
url
|
50
51
|
end
|
51
|
-
|
52
|
+
|
52
53
|
class Address
|
53
54
|
include HappyMapper
|
54
55
|
tag 'GeoAddress'
|
@@ -60,18 +61,18 @@ module Graticule #:nodoc:
|
|
60
61
|
element :postal_code, String, :tag => 'PostalCode'
|
61
62
|
element :country, String, :tag => 'AdminArea1'
|
62
63
|
element :result_code, String, :tag => 'ResultCode'
|
63
|
-
|
64
|
+
|
64
65
|
def precision
|
65
66
|
PRECISION[result_code.to_s[0,2]] || :unknown
|
66
67
|
end
|
67
68
|
end
|
68
|
-
|
69
|
+
|
69
70
|
class Result
|
70
71
|
include HappyMapper
|
71
72
|
tag 'GeocodeResponse'
|
72
73
|
has_many :addresses, Address, :deep => true
|
73
74
|
end
|
74
|
-
|
75
|
+
|
75
76
|
def prepare_response(xml)
|
76
77
|
Result.parse(xml, :single => true)
|
77
78
|
end
|
@@ -1,9 +1,10 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require 'timeout'
|
2
3
|
|
3
4
|
module Graticule #:nodoc:
|
4
5
|
module Geocoder #:nodoc:
|
5
6
|
class Multi
|
6
|
-
|
7
|
+
|
7
8
|
# The Multi geocoder allows you to use multiple geocoders in succession.
|
8
9
|
#
|
9
10
|
# geocoder = Graticule.service(:multi).new(
|
@@ -30,10 +31,10 @@ module Graticule #:nodoc:
|
|
30
31
|
#
|
31
32
|
def initialize(*geocoders, &acceptable)
|
32
33
|
@options = {:timeout => 10, :async => false}.merge(geocoders.extract_options!)
|
33
|
-
@acceptable = acceptable ||
|
34
|
-
@geocoders = geocoders
|
34
|
+
@acceptable = acceptable || Proc.new { true }
|
35
|
+
@geocoders = geocoders
|
35
36
|
end
|
36
|
-
|
37
|
+
|
37
38
|
def locate(address)
|
38
39
|
@lookup = @options[:async] ? ParallelLookup.new : SerialLookup.new
|
39
40
|
last_error = nil
|
@@ -53,16 +54,16 @@ module Graticule #:nodoc:
|
|
53
54
|
end
|
54
55
|
@lookup.result || raise(last_error || AddressError.new("Couldn't find '#{address}' with any of the services"))
|
55
56
|
end
|
56
|
-
|
57
|
+
|
57
58
|
class SerialLookup #:nodoc:
|
58
59
|
def initialize
|
59
60
|
@blocks = []
|
60
61
|
end
|
61
|
-
|
62
|
+
|
62
63
|
def perform(&block)
|
63
64
|
@blocks << block
|
64
65
|
end
|
65
|
-
|
66
|
+
|
66
67
|
def result
|
67
68
|
result = nil
|
68
69
|
@blocks.detect do |block|
|
@@ -71,19 +72,19 @@ module Graticule #:nodoc:
|
|
71
72
|
result
|
72
73
|
end
|
73
74
|
end
|
74
|
-
|
75
|
+
|
75
76
|
class ParallelLookup #:nodoc:
|
76
77
|
def initialize
|
77
78
|
@threads = []
|
78
79
|
@monitor = Monitor.new
|
79
80
|
end
|
80
|
-
|
81
|
+
|
81
82
|
def perform(&block)
|
82
83
|
@threads << Thread.new do
|
83
84
|
self.result = block.call
|
84
85
|
end
|
85
86
|
end
|
86
|
-
|
87
|
+
|
87
88
|
def result=(result)
|
88
89
|
if result
|
89
90
|
@monitor.synchronize do
|
@@ -92,7 +93,7 @@ module Graticule #:nodoc:
|
|
92
93
|
end
|
93
94
|
end
|
94
95
|
end
|
95
|
-
|
96
|
+
|
96
97
|
def result
|
97
98
|
@threads.each(&:join)
|
98
99
|
@result
|
@@ -1,14 +1,15 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule #:nodoc:
|
2
3
|
module Geocoder #:nodoc:
|
3
4
|
|
4
5
|
# Multimap geocoding API
|
5
|
-
|
6
|
+
|
6
7
|
class Multimap < Base
|
7
|
-
|
8
|
+
|
8
9
|
# This precision information is not complete.
|
9
10
|
# More details should be implemented from:
|
10
11
|
# http://www.multimap.com/share/documentation/clientzone/gqcodes.htm
|
11
|
-
|
12
|
+
|
12
13
|
PRECISION = {
|
13
14
|
6 => Precision::Country,
|
14
15
|
5 => Precision::Region,
|
@@ -17,39 +18,39 @@ module Graticule #:nodoc:
|
|
17
18
|
2 => Precision::Street,
|
18
19
|
1 => Precision::Address
|
19
20
|
}
|
20
|
-
|
21
|
+
|
21
22
|
# Web services initializer.
|
22
23
|
#
|
23
24
|
# The +api_key+ is the Open API key that uniquely identifies your
|
24
25
|
# application.
|
25
26
|
#
|
26
27
|
# See http://www.multimap.com/openapi/
|
27
|
-
|
28
|
+
|
28
29
|
def initialize(api_key)
|
29
30
|
@api_key = api_key
|
30
31
|
@url = URI.parse "http://clients.multimap.com/API/geocode/1.2/#{@api_key}"
|
31
32
|
end
|
32
|
-
|
33
|
+
|
33
34
|
# Returns a location for an address in the form of a String, Hash or Location.
|
34
|
-
|
35
|
+
|
35
36
|
def locate(address)
|
36
37
|
location = address.is_a?(String) ? address : location_from_params(address)
|
37
38
|
case location
|
38
39
|
when String
|
39
40
|
get :qs => location
|
40
41
|
when Location
|
41
|
-
get "street" => location.street,
|
42
|
-
"region" => location.region,
|
43
|
-
"city" => location.locality,
|
44
|
-
"postalCode" => location.postal_code,
|
42
|
+
get "street" => location.street,
|
43
|
+
"region" => location.region,
|
44
|
+
"city" => location.locality,
|
45
|
+
"postalCode" => location.postal_code,
|
45
46
|
"countryCode" => location.country
|
46
47
|
end
|
47
48
|
end
|
48
|
-
|
49
|
+
|
49
50
|
class Address
|
50
51
|
include HappyMapper
|
51
52
|
tag 'Location'
|
52
|
-
|
53
|
+
|
53
54
|
attribute :quality, Integer, :tag => 'geocodeQuality'
|
54
55
|
element :street, String, :tag => 'Street', :deep => true
|
55
56
|
element :locality, String, :tag => 'Area', :deep => true
|
@@ -58,23 +59,23 @@ module Graticule #:nodoc:
|
|
58
59
|
element :country, String, :tag => 'CountryCode', :deep => true
|
59
60
|
element :latitude, Float, :tag => 'Lat', :deep => true
|
60
61
|
element :longitude, Float, :tag => 'Lon', :deep => true
|
61
|
-
|
62
|
+
|
62
63
|
def precision
|
63
64
|
PRECISION[quality] || :unknown
|
64
65
|
end
|
65
66
|
end
|
66
|
-
|
67
|
+
|
67
68
|
class Result
|
68
69
|
include HappyMapper
|
69
70
|
tag 'Results'
|
70
71
|
attribute :error, String, :tag => 'errorCode'
|
71
72
|
has_many :addresses, Address
|
72
73
|
end
|
73
|
-
|
74
|
+
|
74
75
|
def prepare_response(xml)
|
75
76
|
Result.parse(xml, :single => true)
|
76
77
|
end
|
77
|
-
|
78
|
+
|
78
79
|
def parse_response(result)
|
79
80
|
addr = result.addresses.first
|
80
81
|
Location.new(
|
@@ -88,11 +89,11 @@ module Graticule #:nodoc:
|
|
88
89
|
:precision => addr.precision
|
89
90
|
)
|
90
91
|
end
|
91
|
-
|
92
|
+
|
92
93
|
def check_error(result)
|
93
94
|
raise Error, result.error unless result.error.blank?
|
94
95
|
end
|
95
|
-
|
96
|
+
|
96
97
|
end
|
97
98
|
end
|
98
99
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule #:nodoc:
|
2
3
|
module Geocoder #:nodoc:
|
3
|
-
|
4
|
+
|
4
5
|
# Yahoo geocoding API.
|
5
6
|
#
|
6
7
|
# http://developer.yahoo.com/maps/rest/V1/geocode.html
|
@@ -42,13 +43,13 @@ module Graticule #:nodoc:
|
|
42
43
|
# yahoo pukes on line breaks
|
43
44
|
get :location => location.gsub("\n", ', ')
|
44
45
|
end
|
45
|
-
|
46
|
+
|
46
47
|
private
|
47
|
-
|
48
|
+
|
48
49
|
class Address
|
49
50
|
include HappyMapper
|
50
51
|
tag 'Result'
|
51
|
-
|
52
|
+
|
52
53
|
attribute :precision, String
|
53
54
|
attribute :warning, String
|
54
55
|
element :latitude, Float, :tag => 'Latitude'
|
@@ -58,24 +59,24 @@ module Graticule #:nodoc:
|
|
58
59
|
element :region, String, :tag => 'State'
|
59
60
|
element :postal_code, String, :tag => 'Zip'
|
60
61
|
element :country, String, :tag => 'Country'
|
61
|
-
|
62
|
+
|
62
63
|
def precision
|
63
64
|
PRECISION[@precision] || :unknown
|
64
65
|
end
|
65
66
|
end
|
66
|
-
|
67
|
+
|
67
68
|
class Result
|
68
69
|
include HappyMapper
|
69
70
|
tag 'ResultSet'
|
70
71
|
has_many :addresses, Address
|
71
72
|
end
|
72
|
-
|
73
|
+
|
73
74
|
class Error
|
74
75
|
include HappyMapper
|
75
76
|
tag 'Error'
|
76
77
|
element :message, String, :tag => 'Message'
|
77
78
|
end
|
78
|
-
|
79
|
+
|
79
80
|
def parse_response(response) # :nodoc:
|
80
81
|
addr = Result.parse(response, :single => true).addresses.first
|
81
82
|
Location.new(
|
@@ -108,6 +109,6 @@ module Graticule #:nodoc:
|
|
108
109
|
end
|
109
110
|
|
110
111
|
end
|
111
|
-
|
112
|
+
|
112
113
|
end
|
113
114
|
end
|
data/lib/graticule/location.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule
|
2
3
|
|
3
4
|
# A geographic location
|
@@ -9,7 +10,7 @@ module Graticule
|
|
9
10
|
|
10
11
|
def initialize(attrs = {})
|
11
12
|
attrs.each do |key,value|
|
12
|
-
self.send("#{key}=", value)
|
13
|
+
self.send("#{key}=", value.respond_to?(:force_encoding) ? value.force_encoding('UTF-8') : value)
|
13
14
|
end
|
14
15
|
self.precision ||= :unknown
|
15
16
|
end
|
data/lib/graticule/precision.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
module Graticule
|
2
|
-
|
3
|
+
|
3
4
|
# Used to compare the precision of different geocoded locations
|
4
5
|
class Precision
|
5
6
|
include Comparable
|
6
7
|
attr_reader :name
|
7
|
-
|
8
|
+
|
8
9
|
NAMES = [
|
9
10
|
:unknown,
|
10
11
|
:country,
|
@@ -15,12 +16,12 @@ module Graticule
|
|
15
16
|
:address,
|
16
17
|
:premise
|
17
18
|
]
|
18
|
-
|
19
|
+
|
19
20
|
def initialize(name)
|
20
21
|
@name = name.to_sym
|
21
22
|
raise ArgumentError, "#{name} is not a valid precision. Use one of #{NAMES.inspect}" unless NAMES.include?(@name)
|
22
23
|
end
|
23
|
-
|
24
|
+
|
24
25
|
Unknown = Precision.new(:unknown)
|
25
26
|
Country = Precision.new(:country)
|
26
27
|
Region = Precision.new(:region)
|
@@ -29,16 +30,16 @@ module Graticule
|
|
29
30
|
Street = Precision.new(:street)
|
30
31
|
Address = Precision.new(:address)
|
31
32
|
Premise = Precision.new(:premise)
|
32
|
-
|
33
|
+
|
33
34
|
def to_s
|
34
35
|
@name.to_s
|
35
36
|
end
|
36
|
-
|
37
|
+
|
37
38
|
def <=>(other)
|
38
39
|
other = Precision.new(other) unless other.is_a?(Precision)
|
39
40
|
NAMES.index(self.name) <=> NAMES.index(other.name)
|
40
41
|
end
|
41
|
-
|
42
|
+
|
42
43
|
def ==(other)
|
43
44
|
(self <=> other) == 0
|
44
45
|
end
|
data/lib/graticule/version.rb
CHANGED
metadata
CHANGED
@@ -1,119 +1,92 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: graticule
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 2
|
8
|
-
- 0
|
9
|
-
- 1
|
10
|
-
version: 2.0.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.1.0
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Brandon Keepers
|
14
9
|
- Daniel Morrison
|
15
10
|
autorequire:
|
16
11
|
bindir: bin
|
17
12
|
cert_chain: []
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
version_requirements: &id001 !ruby/object:Gem::Requirement
|
13
|
+
date: 2011-08-05 00:00:00.000000000Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
requirement: &70239866982580 !ruby/object:Gem::Requirement
|
24
18
|
none: false
|
25
|
-
requirements:
|
26
|
-
- -
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
version: "0"
|
32
|
-
requirement: *id001
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
33
23
|
type: :runtime
|
34
|
-
name: activesupport
|
35
24
|
prerelease: false
|
36
|
-
|
37
|
-
|
25
|
+
version_requirements: *70239866982580
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: i18n
|
28
|
+
requirement: &70239866981940 !ruby/object:Gem::Requirement
|
38
29
|
none: false
|
39
|
-
requirements:
|
40
|
-
- -
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
|
43
|
-
segments:
|
44
|
-
- 0
|
45
|
-
version: "0"
|
46
|
-
requirement: *id002
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
47
34
|
type: :runtime
|
48
|
-
name: i18n
|
49
35
|
prerelease: false
|
50
|
-
|
51
|
-
|
36
|
+
version_requirements: *70239866981940
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: happymapper
|
39
|
+
requirement: &70239866981260 !ruby/object:Gem::Requirement
|
52
40
|
none: false
|
53
|
-
requirements:
|
54
|
-
- -
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
hash: 19
|
57
|
-
segments:
|
58
|
-
- 0
|
59
|
-
- 3
|
60
|
-
- 0
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
61
44
|
version: 0.3.0
|
62
|
-
requirement: *id003
|
63
45
|
type: :runtime
|
64
|
-
name: happymapper
|
65
46
|
prerelease: false
|
66
|
-
|
67
|
-
|
47
|
+
version_requirements: *70239866981260
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: json
|
50
|
+
requirement: &70239866980440 !ruby/object:Gem::Requirement
|
68
51
|
none: false
|
69
|
-
requirements:
|
70
|
-
- -
|
71
|
-
- !ruby/object:Gem::Version
|
72
|
-
|
73
|
-
segments:
|
74
|
-
- 0
|
75
|
-
version: "0"
|
76
|
-
requirement: *id004
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
77
56
|
type: :runtime
|
78
|
-
name: json
|
79
57
|
prerelease: false
|
80
|
-
|
81
|
-
|
58
|
+
version_requirements: *70239866980440
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: mocha
|
61
|
+
requirement: &70239866979600 !ruby/object:Gem::Requirement
|
82
62
|
none: false
|
83
|
-
requirements:
|
84
|
-
- -
|
85
|
-
- !ruby/object:Gem::Version
|
86
|
-
|
87
|
-
segments:
|
88
|
-
- 0
|
89
|
-
version: "0"
|
90
|
-
requirement: *id005
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
91
67
|
type: :development
|
92
|
-
name: mocha
|
93
68
|
prerelease: false
|
94
|
-
|
95
|
-
|
69
|
+
version_requirements: *70239866979600
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rcov
|
72
|
+
requirement: &70239866978740 !ruby/object:Gem::Requirement
|
96
73
|
none: false
|
97
|
-
requirements:
|
98
|
-
- -
|
99
|
-
- !ruby/object:Gem::Version
|
100
|
-
|
101
|
-
segments:
|
102
|
-
- 0
|
103
|
-
version: "0"
|
104
|
-
requirement: *id006
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
105
78
|
type: :development
|
106
|
-
name: rcov
|
107
79
|
prerelease: false
|
108
|
-
|
80
|
+
version_requirements: *70239866978740
|
81
|
+
description: Graticule is a geocoding API that provides a common interface to all
|
82
|
+
the popular services, including Google, Yahoo, Geocoder.us, and MetaCarta.
|
109
83
|
email: brandon@opensoul.org
|
110
|
-
executables:
|
84
|
+
executables:
|
111
85
|
- geocode
|
112
86
|
extensions: []
|
113
|
-
|
114
|
-
extra_rdoc_files:
|
87
|
+
extra_rdoc_files:
|
115
88
|
- README.textile
|
116
|
-
files:
|
89
|
+
files:
|
117
90
|
- bin/geocode
|
118
91
|
- lib/graticule/cli.rb
|
119
92
|
- lib/graticule/core_ext.rb
|
@@ -143,42 +116,38 @@ files:
|
|
143
116
|
- CHANGELOG.txt
|
144
117
|
- LICENSE.txt
|
145
118
|
- README.textile
|
146
|
-
has_rdoc: true
|
147
119
|
homepage: http://github.com/collectiveidea/graticule
|
148
120
|
licenses: []
|
149
|
-
|
150
121
|
post_install_message:
|
151
|
-
rdoc_options:
|
122
|
+
rdoc_options:
|
152
123
|
- --main
|
153
124
|
- README.rdoc
|
154
125
|
- --inline-source
|
155
126
|
- --line-numbers
|
156
|
-
require_paths:
|
127
|
+
require_paths:
|
157
128
|
- lib
|
158
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
159
130
|
none: false
|
160
|
-
requirements:
|
161
|
-
- -
|
162
|
-
- !ruby/object:Gem::Version
|
163
|
-
|
164
|
-
segments:
|
131
|
+
requirements:
|
132
|
+
- - ! '>='
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
segments:
|
165
136
|
- 0
|
166
|
-
|
167
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
hash: -334130965420677482
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
168
139
|
none: false
|
169
|
-
requirements:
|
170
|
-
- -
|
171
|
-
- !ruby/object:Gem::Version
|
172
|
-
|
173
|
-
segments:
|
140
|
+
requirements:
|
141
|
+
- - ! '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
segments:
|
174
145
|
- 0
|
175
|
-
|
146
|
+
hash: -334130965420677482
|
176
147
|
requirements: []
|
177
|
-
|
178
148
|
rubyforge_project: graticule
|
179
|
-
rubygems_version: 1.
|
149
|
+
rubygems_version: 1.8.6
|
180
150
|
signing_key:
|
181
151
|
specification_version: 3
|
182
152
|
summary: API for using all the popular geocoding services.
|
183
153
|
test_files: []
|
184
|
-
|