andre-geokit 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest.txt CHANGED
@@ -1,17 +1,17 @@
1
- .loadpath
2
1
  .project
3
2
  Manifest.txt
4
3
  README.markdown
5
4
  Rakefile
6
5
  geokit.gemspec
7
- lib/geokit/geocoders.rb
8
6
  lib/geokit.rb
7
+ lib/geokit/geocoders.rb
9
8
  lib/geokit/mappable.rb
10
9
  test/test_base_geocoder.rb
11
10
  test/test_bounds.rb
12
11
  test/test_ca_geocoder.rb
13
12
  test/test_geoloc.rb
14
13
  test/test_google_geocoder.rb
14
+ test/test_google_reverse_geocoder.rb
15
15
  test/test_ipgeocoder.rb
16
16
  test/test_latlng.rb
17
17
  test/test_multi_geocoder.rb
data/README.markdown CHANGED
@@ -1,12 +1,13 @@
1
1
  # Geokit gem
2
2
 
3
- [http://geokit.rubyforge.org](http://geokit.rubyforge.org)
3
+ * Geokit Documentation at Rubyforge [http://geokit.rubyforge.org](http://geokit.rubyforge.org).
4
+ * Repository at Github: [http://github.com/andre/geokit-gem/tree/master](http://github.com/andre/geokit-gem/tree/master).
4
5
 
5
6
  ## DESCRIPTION:
6
7
 
7
8
  The Geokit gem provides the following:
8
9
 
9
- * Distance calculations between two points on the earth. Calculate the distance in miles or KM, with all the trigonometry abstracted away by GeoKit.
10
+ * Distance calculations between two points on the earth. Calculate the distance in miles, kilometers, or nautical miles, with all the trigonometry abstracted away by GeoKit.
10
11
  * Geocoding from multiple providers. It currently supports Google, Yahoo, Geocoder.us, and Geocoder.ca geocoders, and it provides a uniform response structure from all of them. It also provides a fail-over mechanism, in case your input fails to geocode in one service.
11
12
  * Rectangular bounds calculations: is a point within a given rectangular bounds?
12
13
  * Heading and midpoint calculations
@@ -36,10 +37,12 @@ Combine this with gem with the [geokit-rails plugin](http://github.com/andre/geo
36
37
 
37
38
  FYI, that `.ll` method means "latitude longitude".
38
39
 
40
+ See the RDOC more more ... there is also operations on rectangular bounds (e.g., determining if a point is within bounds, find the center, etc).
41
+
39
42
  ## INSTALL:
40
43
 
41
44
  * gem sources -a http://gems.github.com
42
- * sudo gem install andre-geokit-gem
45
+ * sudo gem install andre-geokit
43
46
 
44
47
  ## Configuration
45
48
 
data/Rakefile CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'hoe'
5
- require './lib/geokit.rb'
5
+ require './lib/geokit'
6
6
 
7
7
  Hoe.new('Geokit', Geokit::VERSION) do |p|
8
8
  # p.rubyforge_name = 'Geokitx' # if different than lowercase project name
data/lib/geokit.rb CHANGED
@@ -25,6 +25,4 @@ require 'geokit/geocoders'
25
25
  require 'geokit/mappable'
26
26
 
27
27
  # make old-style module name "GeoKit" equivilent to new-style "Geokit"
28
- module GeoKit
29
- include Geokit
30
- end
28
+ GeoKit=Geokit
@@ -95,6 +95,14 @@ module Geokit
95
95
  return res.success ? res : GeoLoc.new
96
96
  end
97
97
 
98
+ # Main method which calls the do_reverse_geocode template method which subclasses
99
+ # are responsible for implementing. Returns a populated GeoLoc or an
100
+ # empty one with a failed success code.
101
+ def self.reverse_geocode(latlng)
102
+ res = do_reverse_geocode(latlng)
103
+ return res.success ? res : GeoLoc.new
104
+ end
105
+
98
106
  # Call the geocoder service using the timeout if configured.
99
107
  def self.call_geocoder_service(url)
100
108
  timeout(Geokit::Geocoders::timeout) { return self.do_get(url) } if Geokit::Geocoders::timeout
@@ -103,6 +111,13 @@ module Geokit
103
111
  return nil
104
112
  end
105
113
 
114
+ # Not all geocoders can do reverse geocoding. So, unless the subclass explicitly overrides this method,
115
+ # a call to reverse_geocode will return an empty GeoLoc. If you happen to be using MultiGeocoder,
116
+ # this will cause it to failover to the next geocoder, which will hopefully be one which supports reverse geocoding.
117
+ def self.do_reverse_geocode(latlng)
118
+ return GeoLoc.new
119
+ end
120
+
106
121
  protected
107
122
 
108
123
  def self.logger()
@@ -193,6 +208,16 @@ module Geokit
193
208
  class GoogleGeocoder < Geocoder
194
209
 
195
210
  private
211
+
212
+ # Template method which does the reverse-geocode lookup.
213
+ def self.do_reverse_geocode(latlng)
214
+ res = self.call_geocoder_service("http://maps.google.com/maps/geo?ll=#{Geokit::Inflector::url_escape(latlng)}&output=xml&key=#{Geokit::Geocoders::google}&oe=utf-8")
215
+ # res = Net::HTTP.get_response(URI.parse("http://maps.google.com/maps/geo?ll=#{Geokit::Inflector::url_escape(address_str)}&output=xml&key=#{Geokit::Geocoders::google}&oe=utf-8"))
216
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
217
+ xml = res.body
218
+ logger.debug "Google reverse-geocoding. LL: #{latlng}. Result: #{xml}"
219
+ return self.xml2GeoLoc(xml)
220
+ end
196
221
 
197
222
  # Template method which does the geocode lookup.
198
223
  def self.do_geocode(address)
@@ -200,8 +225,12 @@ module Geokit
200
225
  res = self.call_geocoder_service("http://maps.google.com/maps/geo?q=#{Geokit::Inflector::url_escape(address_str)}&output=xml&key=#{Geokit::Geocoders::google}&oe=utf-8")
201
226
  # res = Net::HTTP.get_response(URI.parse("http://maps.google.com/maps/geo?q=#{Geokit::Inflector::url_escape(address_str)}&output=xml&key=#{Geokit::Geocoders::google}&oe=utf-8"))
202
227
  return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
203
- xml=res.body
228
+ xml = res.body
204
229
  logger.debug "Google geocoding. Address: #{address}. Result: #{xml}"
230
+ return self.xml2GeoLoc(xml)
231
+ end
232
+
233
+ def self.xml2GeoLoc(xml)
205
234
  doc=REXML::Document.new(xml)
206
235
 
207
236
  if doc.elements['//kml/Response/Status/code'].text == '200'
@@ -211,7 +240,7 @@ module Geokit
211
240
  #basics
212
241
  res.lat=coordinates[1]
213
242
  res.lng=coordinates[0]
214
- res.country_code=doc.elements['//CountryNameCode'].text
243
+ res.country_code=doc.elements['//CountryNameCode'].text if doc.elements['//CountryNameCode']
215
244
  res.provider='google'
216
245
 
217
246
  #extended -- false if not not available
@@ -9,14 +9,17 @@ module Geokit
9
9
  # * Pythagorean Theory (flat Earth) - which assumes the world is flat and loses accuracy over long distances.
10
10
  # * Haversine (sphere) - which is fairly accurate, but at a performance cost.
11
11
  #
12
- # Distance units supported are :miles and :kms.
12
+ # Distance units supported are :miles, :kms, and :nms.
13
13
  module Mappable
14
14
  PI_DIV_RAD = 0.0174
15
15
  KMS_PER_MILE = 1.609
16
+ NMS_PER_MILE = 0.868976242
16
17
  EARTH_RADIUS_IN_MILES = 3963.19
17
18
  EARTH_RADIUS_IN_KMS = EARTH_RADIUS_IN_MILES * KMS_PER_MILE
19
+ EARTH_RADIUS_IN_NMS = EARTH_RADIUS_IN_MILES * NMS_PER_MILE
18
20
  MILES_PER_LATITUDE_DEGREE = 69.1
19
21
  KMS_PER_LATITUDE_DEGREE = MILES_PER_LATITUDE_DEGREE * KMS_PER_MILE
22
+ NMS_PER_LATITUDE_DEGREE = MILES_PER_LATITUDE_DEGREE * NMS_PER_MILE
20
23
  LATITUDE_DEGREES = EARTH_RADIUS_IN_MILES / MILES_PER_LATITUDE_DEGREE
21
24
 
22
25
  # Mix below class methods into the includer.
@@ -27,7 +30,7 @@ module Geokit
27
30
  module ClassMethods #:nodoc:
28
31
  # Returns the distance between two points. The from and to parameters are
29
32
  # required to have lat and lng attributes. Valid options are:
30
- # :units - valid values are :miles or :kms (Geokit::default_units is the default)
33
+ # :units - valid values are :miles, :kms, :nms (Geokit::default_units is the default)
31
34
  # :formula - valid values are :flat or :sphere (Geokit::default_formula is the default)
32
35
  def distance_between(from, to, options={})
33
36
  from=Geokit::LatLng.normalize(from)
@@ -36,11 +39,15 @@ module Geokit
36
39
  units = options[:units] || Geokit::default_units
37
40
  formula = options[:formula] || Geokit::default_formula
38
41
  case formula
39
- when :sphere
40
- units_sphere_multiplier(units) *
41
- Math.acos( Math.sin(deg2rad(from.lat)) * Math.sin(deg2rad(to.lat)) +
42
- Math.cos(deg2rad(from.lat)) * Math.cos(deg2rad(to.lat)) *
43
- Math.cos(deg2rad(to.lng) - deg2rad(from.lng)))
42
+ when :sphere
43
+ begin
44
+ units_sphere_multiplier(units) *
45
+ Math.acos( Math.sin(deg2rad(from.lat)) * Math.sin(deg2rad(to.lat)) +
46
+ Math.cos(deg2rad(from.lat)) * Math.cos(deg2rad(to.lat)) *
47
+ Math.cos(deg2rad(to.lng) - deg2rad(from.lng)))
48
+ rescue Errno::EDOM
49
+ 0.0
50
+ end
44
51
  when :flat
45
52
  Math.sqrt((units_per_latitude_degree(units)*(from.lat-to.lat))**2 +
46
53
  (units_per_longitude_degree(from.lat, units)*(from.lng-to.lng))**2)
@@ -67,7 +74,11 @@ module Geokit
67
74
  # will be used instead of this method.
68
75
  def endpoint(start,heading, distance, options={})
69
76
  units = options[:units] || Geokit::default_units
70
- radius = units == :miles ? EARTH_RADIUS_IN_MILES : EARTH_RADIUS_IN_KMS
77
+ radius = case units
78
+ when :kms: EARTH_RADIUS_IN_KMS
79
+ when :nms: EARTH_RADIUS_IN_NMS
80
+ else EARTH_RADIUS_IN_MILES
81
+ end
71
82
  start=Geokit::LatLng.normalize(start)
72
83
  lat=deg2rad(start.lat)
73
84
  lng=deg2rad(start.lng)
@@ -86,7 +97,7 @@ module Geokit
86
97
  # Returns the midpoint, given two points. Returns a LatLng.
87
98
  # Typically, the instance method will be used instead of this method.
88
99
  # Valid option:
89
- # :units - valid values are :miles or :kms (:miles is the default)
100
+ # :units - valid values are :miles, :kms, or :nms (:miles is the default)
90
101
  def midpoint_between(from,to,options={})
91
102
  from=Geokit::LatLng.normalize(from)
92
103
 
@@ -120,18 +131,30 @@ module Geokit
120
131
 
121
132
  # Returns the multiplier used to obtain the correct distance units.
122
133
  def units_sphere_multiplier(units)
123
- units == :miles ? EARTH_RADIUS_IN_MILES : EARTH_RADIUS_IN_KMS
134
+ case units
135
+ when :kms: EARTH_RADIUS_IN_KMS
136
+ when :nms: EARTH_RADIUS_IN_NMS
137
+ else EARTH_RADIUS_IN_MILES
138
+ end
124
139
  end
125
140
 
126
141
  # Returns the number of units per latitude degree.
127
142
  def units_per_latitude_degree(units)
128
- units == :miles ? MILES_PER_LATITUDE_DEGREE : KMS_PER_LATITUDE_DEGREE
143
+ case units
144
+ when :kms: KMS_PER_LATITUDE_DEGREE
145
+ when :nms: NMS_PER_LATITUDE_DEGREE
146
+ else MILES_PER_LATITUDE_DEGREE
147
+ end
129
148
  end
130
149
 
131
150
  # Returns the number units per longitude degree.
132
151
  def units_per_longitude_degree(lat, units)
133
152
  miles_per_longitude_degree = (LATITUDE_DEGREES * Math.cos(lat * PI_DIV_RAD)).abs
134
- units == :miles ? miles_per_longitude_degree : miles_per_longitude_degree * KMS_PER_MILE
153
+ case units
154
+ when :kms: miles_per_longitude_degree * KMS_PER_MILE
155
+ when :nms: miles_per_longitude_degree * NMS_PER_MILE
156
+ else miles_per_longitude_degree
157
+ end
135
158
  end
136
159
  end
137
160
 
@@ -143,12 +166,12 @@ module Geokit
143
166
  def to_lat_lng
144
167
  return self if instance_of?(Geokit::LatLng) || instance_of?(Geokit::GeoLoc)
145
168
  return LatLng.new(send(self.class.lat_column_name),send(self.class.lng_column_name)) if self.class.respond_to?(:acts_as_mappable)
146
- return nil
169
+ nil
147
170
  end
148
171
 
149
172
  # Returns the distance from another point. The other point parameter is
150
173
  # required to have lat and lng attributes. Valid options are:
151
- # :units - valid values are :miles or :kms (:miles is the default)
174
+ # :units - valid values are :miles, :kms, :or :nms (:miles is the default)
152
175
  # :formula - valid values are :flat or :sphere (:sphere is the default)
153
176
  def distance_to(other, options={})
154
177
  self.class.distance_between(self, other, options)
@@ -169,14 +192,14 @@ module Geokit
169
192
 
170
193
  # Returns the endpoint, given a heading (in degrees) and distance.
171
194
  # Valid option:
172
- # :units - valid values are :miles or :kms (:miles is the default)
195
+ # :units - valid values are :miles, :kms, or :nms (:miles is the default)
173
196
  def endpoint(heading,distance,options={})
174
197
  self.class.endpoint(self,heading,distance,options)
175
198
  end
176
199
 
177
200
  # Returns the midpoint, given another point on the map.
178
201
  # Valid option:
179
- # :units - valid values are :miles or :kms (:miles is the default)
202
+ # :units - valid values are :miles, :kms, or :nms (:miles is the default)
180
203
  def midpoint_to(other, options={})
181
204
  self.class.midpoint_between(self,other,options)
182
205
  end
data/test/test_bounds.rb CHANGED
@@ -55,7 +55,7 @@ class BoundsTest < Test::Unit::TestCase #:nodoc: all
55
55
 
56
56
  def test_center
57
57
  assert_in_delta 32.939828,@bounds.center.lat,0.00005
58
- assert_in_delta -96.9511763,@bounds.center.lng,0.00005
58
+ assert_in_delta(-96.9511763,@bounds.center.lng,0.00005)
59
59
  end
60
60
 
61
61
  def test_center_cross_meridian
data/test/test_latlng.rb CHANGED
@@ -24,6 +24,11 @@ class LatLngTest < Test::Unit::TestCase #:nodoc: all
24
24
  assert_equal 0, @loc_a.distance_to(@loc_a, :units => :kms, :formula => :flat)
25
25
  end
26
26
 
27
+ def test_distance_between_same_with_nms_and_flat
28
+ assert_equal 0, Geokit::LatLng.distance_between(@loc_a, @loc_a, :units => :nms, :formula => :flat)
29
+ assert_equal 0, @loc_a.distance_to(@loc_a, :units => :nms, :formula => :flat)
30
+ end
31
+
27
32
  def test_distance_between_same_with_miles_and_sphere
28
33
  assert_equal 0, Geokit::LatLng.distance_between(@loc_a, @loc_a, :units => :miles, :formula => :sphere)
29
34
  assert_equal 0, @loc_a.distance_to(@loc_a, :units => :miles, :formula => :sphere)
@@ -34,6 +39,11 @@ class LatLngTest < Test::Unit::TestCase #:nodoc: all
34
39
  assert_equal 0, @loc_a.distance_to(@loc_a, :units => :kms, :formula => :sphere)
35
40
  end
36
41
 
42
+ def test_distance_between_same_with_nms_and_sphere
43
+ assert_equal 0, Geokit::LatLng.distance_between(@loc_a, @loc_a, :units => :nms, :formula => :sphere)
44
+ assert_equal 0, @loc_a.distance_to(@loc_a, :units => :nms, :formula => :sphere)
45
+ end
46
+
37
47
  def test_distance_between_diff_using_defaults
38
48
  assert_in_delta 3.97, Geokit::LatLng.distance_between(@loc_a, @loc_e), 0.01
39
49
  assert_in_delta 3.97, @loc_a.distance_to(@loc_e), 0.01
@@ -49,6 +59,11 @@ class LatLngTest < Test::Unit::TestCase #:nodoc: all
49
59
  assert_in_delta 6.39, @loc_a.distance_to(@loc_e, :units => :kms, :formula => :flat), 0.4
50
60
  end
51
61
 
62
+ def test_distance_between_diff_with_nms_and_flat
63
+ assert_in_delta 3.334, Geokit::LatLng.distance_between(@loc_a, @loc_e, :units => :nms, :formula => :flat), 0.4
64
+ assert_in_delta 3.334, @loc_a.distance_to(@loc_e, :units => :nms, :formula => :flat), 0.4
65
+ end
66
+
52
67
  def test_distance_between_diff_with_miles_and_sphere
53
68
  assert_in_delta 3.97, Geokit::LatLng.distance_between(@loc_a, @loc_e, :units => :miles, :formula => :sphere), 0.01
54
69
  assert_in_delta 3.97, @loc_a.distance_to(@loc_e, :units => :miles, :formula => :sphere), 0.01
@@ -59,12 +74,18 @@ class LatLngTest < Test::Unit::TestCase #:nodoc: all
59
74
  assert_in_delta 6.39, @loc_a.distance_to(@loc_e, :units => :kms, :formula => :sphere), 0.01
60
75
  end
61
76
 
77
+ def test_distance_between_diff_with_nms_and_sphere
78
+ assert_in_delta 3.454, Geokit::LatLng.distance_between(@loc_a, @loc_e, :units => :nms, :formula => :sphere), 0.01
79
+ assert_in_delta 3.454, @loc_a.distance_to(@loc_e, :units => :nms, :formula => :sphere), 0.01
80
+ end
81
+
62
82
  def test_manually_mixed_in
63
83
  assert_equal 0, Geokit::LatLng.distance_between(@point, @point)
64
84
  assert_equal 0, @point.distance_to(@point)
65
85
  assert_equal 0, @point.distance_to(@loc_a)
66
86
  assert_in_delta 3.97, @point.distance_to(@loc_e, :units => :miles, :formula => :flat), 0.2
67
87
  assert_in_delta 6.39, @point.distance_to(@loc_e, :units => :kms, :formula => :flat), 0.4
88
+ assert_in_delta 3.334, @point.distance_to(@loc_e, :units => :nms, :formula => :flat), 0.4
68
89
  end
69
90
 
70
91
  def test_heading_between
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: andre-geokit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andre Lewis and Bill Eisenhauer
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-11-30 00:00:00 -08:00
12
+ date: 2009-02-01 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -52,7 +52,7 @@ homepage: http://geokit.rubyforge.org
52
52
  post_install_message:
53
53
  rdoc_options:
54
54
  - --main
55
- - README.txt
55
+ - README.markdown
56
56
  require_paths:
57
57
  - lib
58
58
  required_ruby_version: !ruby/object:Gem::Requirement