graticule 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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. How to contribute
31
+ h2. Contributing
32
32
 
33
- If you find what you might think is a bug:
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
- # Check the "GitHub issue tracker":http://github.com/collectiveidea/graticule/issues/ to see if anyone else has had the same issue.
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
- If you want to contribute an enhancement or a fix:
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
- # Fork "the project":http://github.com/collectiveidea/graticule/ on GitHub.
41
- # Make your changes with tests.
42
- # Commit the changes without making changes to the Rakefile, VERSION, or any other files that aren't related to your enhancement or fix
43
- # Send a pull request.
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.)
@@ -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'
@@ -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
- 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
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
- "#{URI.escape k.to_s}=#{URI.escape v.to_s}"
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 = URI.escape(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
+
@@ -1,4 +1,4 @@
1
1
  # encoding: UTF-8
2
2
  module Graticule
3
- VERSION = '2.1.0' unless defined?(::Graticule::VERSION)
3
+ VERSION = '2.2.0' unless defined?(::Graticule::VERSION)
4
4
  end
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.1.0
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-08-05 00:00:00.000000000Z
13
+ date: 2011-09-14 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
17
- requirement: &70239866982580 !ruby/object:Gem::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: *70239866982580
25
+ version_requirements: *70276838707980
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: i18n
28
- requirement: &70239866981940 !ruby/object:Gem::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: *70239866981940
36
+ version_requirements: *70276838705640
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: happymapper
39
- requirement: &70239866981260 !ruby/object:Gem::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: *70239866981260
47
+ version_requirements: *70276838703420
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: json
50
- requirement: &70239866980440 !ruby/object:Gem::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: *70239866980440
58
+ version_requirements: *70276838701800
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: mocha
61
- requirement: &70239866979600 !ruby/object:Gem::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: *70239866979600
69
+ version_requirements: *70276838699580
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: rcov
72
- requirement: &70239866978740 !ruby/object:Gem::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: *70239866978740
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: -334130965420677482
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: -334130965420677482
147
+ hash: 473484385783211524
147
148
  requirements: []
148
149
  rubyforge_project: graticule
149
150
  rubygems_version: 1.8.6