graticule 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +31 -10
- data/lib/graticule.rb +1 -0
- data/lib/graticule/cli.rb +4 -2
- data/lib/graticule/distance/vincenty.rb +12 -13
- data/lib/graticule/geocoder/base.rb +9 -1
- data/lib/graticule/geocoder/mapquest.rb +1 -1
- data/lib/graticule/geocoder/yandex.rb +131 -0
- data/lib/graticule/version.rb +1 -1
- metadata +17 -16
data/README.textile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
h1. Graticule
|
2
2
|
|
3
|
-
Graticule is a geocoding API for looking up address coordinates. It supports many popular APIs, including Yahoo, Google, Geocoder.ca and Geocoder.us.
|
3
|
+
Graticule is a geocoding API for looking up address coordinates. It supports many popular APIs, including Yahoo, Google, Yandex, Geocoder.ca and Geocoder.us.
|
4
4
|
|
5
5
|
h2. Usage
|
6
6
|
|
@@ -28,16 +28,37 @@ h2. Mailing List
|
|
28
28
|
|
29
29
|
Join us on the "mailing list":http://groups.google.com/group/graticule.
|
30
30
|
|
31
|
-
h2.
|
31
|
+
h2. Contributing
|
32
32
|
|
33
|
-
|
33
|
+
In the spirit of "free software":http://www.fsf.org/licensing/essays/free-sw.html, **everyone** is encouraged to help improve this project.
|
34
34
|
|
35
|
-
|
36
|
-
# If you don't see anything, create an issue with information on how to reproduce it.
|
35
|
+
Here are some ways *you* can contribute:
|
37
36
|
|
38
|
-
|
37
|
+
* using alpha, beta, and prerelease versions
|
38
|
+
* reporting bugs
|
39
|
+
* suggesting new features
|
40
|
+
* writing or editing documentation
|
41
|
+
* writing specifications
|
42
|
+
* writing code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace)
|
43
|
+
* refactoring code
|
44
|
+
* closing "issues":https://github.com/collectiveidea/graticule/issues
|
45
|
+
* reviewing patches
|
39
46
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
47
|
+
h2. Submitting an Issue
|
48
|
+
|
49
|
+
We use the "GitHub issue tracker":https://github.com/collectiveidea/graticule/issues to track bugs
|
50
|
+
and features. Before submitting a bug report or feature request, check to make sure it hasn't already
|
51
|
+
been submitted. You can indicate support for an existing issuse by voting it up. When submitting a
|
52
|
+
bug report, please include a "Gist":https://gist.github.com/ that includes a stack trace and any
|
53
|
+
details that may be necessary to reproduce the bug, including your gem version, Ruby version, and
|
54
|
+
operating system. Ideally, a bug report should include a pull request with failing specs.
|
55
|
+
|
56
|
+
h2. Submitting a Pull Request
|
57
|
+
|
58
|
+
1. Fork the project.
|
59
|
+
2. Create a topic branch.
|
60
|
+
3. Implement your feature or bug fix.
|
61
|
+
4. Add specs for your feature or bug fix.
|
62
|
+
5. Run @bundle exec rake@. If your changes are not 100% covered and passing, go back to step 4.
|
63
|
+
6. Commit and push your changes.
|
64
|
+
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.)
|
data/lib/graticule.rb
CHANGED
@@ -12,6 +12,7 @@ require 'graticule/geocoder'
|
|
12
12
|
require 'graticule/geocoder/base'
|
13
13
|
require 'graticule/geocoder/bogus'
|
14
14
|
require 'graticule/geocoder/google'
|
15
|
+
require 'graticule/geocoder/yandex'
|
15
16
|
require 'graticule/geocoder/host_ip'
|
16
17
|
require 'graticule/geocoder/multi'
|
17
18
|
require 'graticule/geocoder/yahoo'
|
data/lib/graticule/cli.rb
CHANGED
@@ -29,11 +29,13 @@ module Graticule
|
|
29
29
|
opts.separator ""
|
30
30
|
opts.separator "Options: "
|
31
31
|
|
32
|
-
opts.on("-s service", %w(yahoo google geocoder_us metacarta), "--service service", "Geocoding service") do |service|
|
32
|
+
opts.on("-s service", %w(yahoo google yandex geocoder_us metacarta), "--service service", "Geocoding service") do |service|
|
33
33
|
options[:service] = service
|
34
34
|
end
|
35
35
|
|
36
|
-
opts.on("-a apikey", "--apikey apikey", "API key for the selected service")
|
36
|
+
opts.on("-a apikey", "--apikey apikey", "API key for the selected service") do |apikey|
|
37
|
+
options[:api_key] = apikey
|
38
|
+
end
|
37
39
|
|
38
40
|
opts.on_tail("-h", "--help", "Help") do
|
39
41
|
puts opts
|
@@ -23,7 +23,7 @@ module Graticule
|
|
23
23
|
from_latitude = from.latitude.to_radians
|
24
24
|
to_longitude = to.longitude.to_radians
|
25
25
|
to_latitude = to.latitude.to_radians
|
26
|
-
|
26
|
+
|
27
27
|
earth_major_axis_radius = EARTH_MAJOR_AXIS_RADIUS[units.to_sym]
|
28
28
|
earth_minor_axis_radius = EARTH_MINOR_AXIS_RADIUS[units.to_sym]
|
29
29
|
|
@@ -43,7 +43,7 @@ module Graticule
|
|
43
43
|
while (lambda-lambda_p).abs > 1e-12 && (iteration_limit -= 1) > 0
|
44
44
|
sin_lambda = sin(lambda)
|
45
45
|
cos_lambda = cos(lambda)
|
46
|
-
sin_sigma = sqrt((cos_u2*sin_lambda) * (cos_u2*sin_lambda) +
|
46
|
+
sin_sigma = sqrt((cos_u2*sin_lambda) * (cos_u2*sin_lambda) +
|
47
47
|
(cos_u1*sin_u2-sin_u1*cos_u2*cos_lambda) * (cos_u1*sin_u2-sin_u1*cos_u2*cos_lambda))
|
48
48
|
return 0 if sin_sigma == 0 # co-incident points
|
49
49
|
cos_sigma = sin_u1*sin_u2 + cos_u1*cos_u2*cos_lambda
|
@@ -58,20 +58,19 @@ module Graticule
|
|
58
58
|
lambda_p = lambda
|
59
59
|
lambda = l + (1-c) * f * sin_alpha *
|
60
60
|
(sigma + c*sin_sigma*(cos2SigmaM+c*cos_sigma*(-1+2*cos2SigmaM*cos2SigmaM)))
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
end
|
62
|
+
# formula failed to converge (happens on antipodal points)
|
63
|
+
# We'll call Haversine formula instead.
|
64
|
+
return Haversine.distance(from, to, units) if iteration_limit == 0
|
65
|
+
|
66
|
+
uSq = cosSqAlpha * (earth_major_axis_radius**2 - earth_minor_axis_radius**2) / (earth_minor_axis_radius**2)
|
67
|
+
a = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)))
|
68
|
+
b = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)))
|
69
|
+
delta_sigma = b*sin_sigma*(cos2SigmaM+b/4*(cos_sigma*(-1+2*cos2SigmaM*cos2SigmaM)-
|
70
|
+
b/6*cos2SigmaM*(-3+4*sin_sigma*sin_sigma)*(-3+4*cos2SigmaM*cos2SigmaM)))
|
65
71
|
|
66
|
-
uSq = cosSqAlpha * (earth_major_axis_radius**2 - earth_minor_axis_radius**2) / (earth_minor_axis_radius**2)
|
67
|
-
a = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)))
|
68
|
-
b = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)))
|
69
|
-
delta_sigma = b*sin_sigma*(cos2SigmaM+b/4*(cos_sigma*(-1+2*cos2SigmaM*cos2SigmaM)-
|
70
|
-
b/6*cos2SigmaM*(-3+4*sin_sigma*sin_sigma)*(-3+4*cos2SigmaM*cos2SigmaM)))
|
71
|
-
|
72
72
|
earth_minor_axis_radius * a * (sigma-delta_sigma)
|
73
73
|
end
|
74
|
-
|
75
74
|
end
|
76
75
|
end
|
77
76
|
end
|
@@ -89,7 +89,7 @@ module Graticule #:nodoc:
|
|
89
89
|
# you need to add extra params like an application id or output type.
|
90
90
|
def make_url(params)
|
91
91
|
escaped_params = params.sort_by { |k,v| k.to_s }.map do |k,v|
|
92
|
-
"#{
|
92
|
+
"#{escape k.to_s}=#{escape v.to_s}"
|
93
93
|
end
|
94
94
|
|
95
95
|
url = @url.dup
|
@@ -108,6 +108,14 @@ module Graticule #:nodoc:
|
|
108
108
|
raise NotImplementedError
|
109
109
|
end
|
110
110
|
|
111
|
+
def escape(string)
|
112
|
+
if URI.const_defined?(:Parser)
|
113
|
+
parser = URI::Parser.new
|
114
|
+
parser.escape string
|
115
|
+
else
|
116
|
+
URI.escape string
|
117
|
+
end
|
118
|
+
end
|
111
119
|
end
|
112
120
|
end
|
113
121
|
end
|
@@ -46,7 +46,7 @@ module Graticule #:nodoc:
|
|
46
46
|
query = "e=5&<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><Geocode Version=\"1\"> \
|
47
47
|
#{address_string(params[:q])}#{authentication_string}</Geocode>"
|
48
48
|
url = @url.dup
|
49
|
-
url.query =
|
49
|
+
url.query = escape(query)
|
50
50
|
url
|
51
51
|
end
|
52
52
|
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module Graticule #:nodoc:
|
3
|
+
module Geocoder #:nodoc:
|
4
|
+
|
5
|
+
# First you need a Yandex Maps API key. You can register for one here:
|
6
|
+
# http://api.yandex.ru/maps/form.xml
|
7
|
+
#
|
8
|
+
# gg = Graticule.service(:yandex).new(MAPS_API_KEY)
|
9
|
+
# location = gg.locate 'Россия, Москва, ул. Моховая, д.18'
|
10
|
+
# p location.coordinates
|
11
|
+
# #=> [37.612281, 55.753342]
|
12
|
+
#
|
13
|
+
class Yandex < Base
|
14
|
+
# http://api.yandex.ru/maps/geocoder/doc/desc/concepts/input_params.xml
|
15
|
+
# http://api.yandex.ru/maps/geocoder/doc/desc/concepts/response_structure.xml
|
16
|
+
|
17
|
+
PRECISION = {
|
18
|
+
:country => Precision::Country, # Country level accuracy.
|
19
|
+
:province => Precision::Region, # Region (state, province, prefecture, etc.) level accuracy.
|
20
|
+
:area => Precision::Region, # Sub-region (county, municipality, etc.) level accuracy.
|
21
|
+
:locality => Precision::Locality, # Town (city, village) level accuracy.
|
22
|
+
:metro => Precision::Street, # Street level accuracy.
|
23
|
+
:street => Precision::Street, # Intersection level accuracy.
|
24
|
+
:house => Precision::Address, # Address level accuracy.
|
25
|
+
}.stringify_keys
|
26
|
+
|
27
|
+
def initialize(key)
|
28
|
+
@key = key
|
29
|
+
@url = URI.parse 'http://geocode-maps.yandex.ru/1.x/'
|
30
|
+
end
|
31
|
+
|
32
|
+
# Locates +address+ returning a Location
|
33
|
+
def locate(address)
|
34
|
+
get :geocode => address.is_a?(String) ? address : location_from_params(address).to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
class GeocoderMetaData
|
40
|
+
include HappyMapper
|
41
|
+
|
42
|
+
tag 'GeocoderMetaData'
|
43
|
+
namespace 'http://maps.yandex.ru/geocoder/1.x'
|
44
|
+
|
45
|
+
element :kind, String, :tag => 'kind'
|
46
|
+
end
|
47
|
+
|
48
|
+
class FeatureMember
|
49
|
+
include HappyMapper
|
50
|
+
|
51
|
+
tag 'featureMember'
|
52
|
+
namespace 'http://www.opengis.net/gml'
|
53
|
+
|
54
|
+
has_one :geocoder_meta_data, GeocoderMetaData
|
55
|
+
|
56
|
+
attr_reader :longitude, :latitude
|
57
|
+
|
58
|
+
element :coordinates, String, :tag => 'pos', :deep => true
|
59
|
+
|
60
|
+
with_options :deep => true, :namespace => 'urn:oasis:names:tc:ciq:xsdschema:xAL:2.0' do |map|
|
61
|
+
map.element :street, String, :tag => 'ThoroughfareName'
|
62
|
+
map.element :locality, String, :tag => 'LocalityName'
|
63
|
+
map.element :region, String, :tag => 'AdministrativeAreaName'
|
64
|
+
map.element :postal_code, String, :tag => 'PostalCodeNumber'
|
65
|
+
map.element :country, String, :tag => 'CountryNameCode'
|
66
|
+
end
|
67
|
+
|
68
|
+
def coordinates=(coordinates)
|
69
|
+
@longitude, @latitude = coordinates.split(' ').map { |v| v.to_f }
|
70
|
+
end
|
71
|
+
|
72
|
+
def precision
|
73
|
+
PRECISION[geocoder_meta_data.kind] || :unknown
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class Error
|
78
|
+
include HappyMapper
|
79
|
+
|
80
|
+
tag 'error'
|
81
|
+
element :status, Integer, :tag => 'status'
|
82
|
+
element :message, String, :tag => 'message'
|
83
|
+
end
|
84
|
+
|
85
|
+
class Response
|
86
|
+
include HappyMapper
|
87
|
+
|
88
|
+
tag 'GeoObjectCollection'
|
89
|
+
has_many :feature_members, FeatureMember
|
90
|
+
|
91
|
+
def status
|
92
|
+
200
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def prepare_response(xml)
|
97
|
+
Response.parse(xml, :single => true) || Error.parse(xml, :single => true)
|
98
|
+
end
|
99
|
+
|
100
|
+
def parse_response(response) #:nodoc:
|
101
|
+
result = response.feature_members.first
|
102
|
+
Location.new(
|
103
|
+
:latitude => result.latitude,
|
104
|
+
:longitude => result.longitude,
|
105
|
+
:street => result.street,
|
106
|
+
:locality => result.locality,
|
107
|
+
:country => result.country,
|
108
|
+
:precision => result.precision
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Extracts and raises an error from +xml+, if any.
|
113
|
+
def check_error(response) #:nodoc:
|
114
|
+
case response.status
|
115
|
+
when 200 then # ignore, ok
|
116
|
+
when 401 then
|
117
|
+
raise CredentialsError, response.message
|
118
|
+
else
|
119
|
+
raise Error, response.message
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Creates a URL from the Hash +params+.
|
124
|
+
# sets the output type to 'xml'.
|
125
|
+
def make_url(params) #:nodoc:
|
126
|
+
super params.merge(:key => @key)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
data/lib/graticule/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graticule
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-09-14 00:00:00.000000000Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
17
|
-
requirement: &
|
17
|
+
requirement: &70276838707980 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70276838707980
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: i18n
|
28
|
-
requirement: &
|
28
|
+
requirement: &70276838705640 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70276838705640
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: happymapper
|
39
|
-
requirement: &
|
39
|
+
requirement: &70276838703420 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,10 +44,10 @@ dependencies:
|
|
44
44
|
version: 0.3.0
|
45
45
|
type: :runtime
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *70276838703420
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: json
|
50
|
-
requirement: &
|
50
|
+
requirement: &70276838701800 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ! '>='
|
@@ -55,10 +55,10 @@ dependencies:
|
|
55
55
|
version: '0'
|
56
56
|
type: :runtime
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *70276838701800
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: mocha
|
61
|
-
requirement: &
|
61
|
+
requirement: &70276838699580 !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
64
64
|
- - ! '>='
|
@@ -66,10 +66,10 @@ dependencies:
|
|
66
66
|
version: '0'
|
67
67
|
type: :development
|
68
68
|
prerelease: false
|
69
|
-
version_requirements: *
|
69
|
+
version_requirements: *70276838699580
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: rcov
|
72
|
-
requirement: &
|
72
|
+
requirement: &70276838697520 !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
75
|
- - ! '>='
|
@@ -77,7 +77,7 @@ dependencies:
|
|
77
77
|
version: '0'
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
|
-
version_requirements: *
|
80
|
+
version_requirements: *70276838697520
|
81
81
|
description: Graticule is a geocoding API that provides a common interface to all
|
82
82
|
the popular services, including Google, Yahoo, Geocoder.us, and MetaCarta.
|
83
83
|
email: brandon@opensoul.org
|
@@ -108,6 +108,7 @@ files:
|
|
108
108
|
- lib/graticule/geocoder/multimap.rb
|
109
109
|
- lib/graticule/geocoder/simple_geo.rb
|
110
110
|
- lib/graticule/geocoder/yahoo.rb
|
111
|
+
- lib/graticule/geocoder/yandex.rb
|
111
112
|
- lib/graticule/geocoder.rb
|
112
113
|
- lib/graticule/location.rb
|
113
114
|
- lib/graticule/precision.rb
|
@@ -134,7 +135,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
134
135
|
version: '0'
|
135
136
|
segments:
|
136
137
|
- 0
|
137
|
-
hash:
|
138
|
+
hash: 473484385783211524
|
138
139
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
140
|
none: false
|
140
141
|
requirements:
|
@@ -143,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
144
|
version: '0'
|
144
145
|
segments:
|
145
146
|
- 0
|
146
|
-
hash:
|
147
|
+
hash: 473484385783211524
|
147
148
|
requirements: []
|
148
149
|
rubyforge_project: graticule
|
149
150
|
rubygems_version: 1.8.6
|