vincenty 1.0.4 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
data/lib/vincenty.rb CHANGED
@@ -1,191 +1,201 @@
1
- require 'core_extensions.rb'
2
- require 'angle.rb'
3
- require 'latitude.rb'
4
- require 'longitude.rb'
5
- require 'track_and_distance.rb'
6
- require 'coordinate.rb'
7
-
8
- #Vincenty's algorithms for finding the bearing and distance between two coordinates and
9
- #for finding the latitude and longitude, given a start coordinate, distance and bearing.
1
+ require_relative 'core_extensions.rb'
2
+ require_relative 'angle.rb'
3
+ require_relative 'latitude.rb'
4
+ require_relative 'longitude.rb'
5
+ require_relative 'track_and_distance.rb'
6
+ require_relative 'coordinate.rb'
7
+
8
+ # Vincenty's algorithms for finding the bearing and distance between two coordinates and
9
+ # for finding the latitude and longitude, given a start coordinate, distance and bearing.
10
10
  #
11
11
  # Coded from formulae from Wikipedia http://en.wikipedia.org/wiki/Vincenty%27s_formulae
12
12
  # Modified to incorporate corrections to formulae as found in script on http://www.movable-type.co.uk/scripts/LatLongVincenty.html
13
13
  # Added my Modification of the distanceAndAngle formulae to correct the compass bearing.
14
14
  class Vincenty < Coordinate
15
- VERSION = '1.0.4'
16
-
15
+ VERSION = '1.0.9'
16
+
17
+ # @return [String] constant VERSION
17
18
  def version
18
19
  VERSION
19
20
  end
20
-
21
- #Great Circle formulae http://en.wikipedia.org/wiki/Great-circle_distance
22
- #Reference calculation for testing, assumes the earth is a sphere, which it isn't.
23
- #This gives us an approximation to verify Vincenty algorithm.
24
- #Takes: argument p2 is target coordinate that we want the bearing to.
25
- #Returns: TrackAndDistance object with the compass bearing and distance in meters to P2
26
- def sphericalDistanceAndAngle( p2 )
27
- a = 6378137 #equatorial radius in meters (+/-2 m)
28
- b = 6356752.31424518 #polar radius in meters
29
- r = (a+b)/2 #average diametre as a rough estimate for our tests.
30
-
31
- sin_lat1 = Math.sin(@latitude.to_r)
32
- sin_lat2 = Math.sin(p2.latitude.to_r)
33
- cos_lat1 = Math.cos(@latitude.to_r)
21
+
22
+ WGS84_ER = 6378137 # Equatorial Radius of earth
23
+ WGS84_IF = 298.257223563 # Inverse Flattening
24
+ GRS80_ER = 6378137 # Equatorial Radius of earth
25
+ GRS80_IF = 298.25722210882711 # Inverse Flattening
26
+
27
+ # Great Circle formulae http://en.wikipedia.org/wiki/Great-circle_distance
28
+ # Reference calculation for testing, assumes the earth is a sphere, which it isn't.
29
+ # This gives us an approximation to verify Vincenty algorithm.
30
+ # @param [Coordinate] p2 is target coordinate that we want the bearing to.
31
+ # @return [TrackAndDistance] with the compass bearing and distance in meters to P2
32
+ def sphericalDistanceAndAngle( p2, equatorial_radius = WGS84_ER, inverse_flattening = WGS84_IF )
33
+ if self.latitude == p2.latitude && self.longitude == p2.longitude
34
+ return TrackAndDistance.new(0, 0, true) # No calculations necessary
35
+ end
36
+
37
+ a = equatorial_radius # equatorial radius in meters (+/-2 m)
38
+ f = inverse_flattening
39
+ b = a - a / f # WGS84 = 6356752.314245179 polar radius in meters
40
+ r = (a + b) / 2 # average diametre as a rough estimate for our tests.
41
+
42
+ sin_lat1 = Math.sin(@latitude.to_rad)
43
+ sin_lat2 = Math.sin(p2.latitude.to_rad)
44
+ cos_lat1 = Math.cos(@latitude.to_rad)
34
45
  atan1_2 = Math.atan(1) * 2
35
- t1 = cos_lat1 * Math.cos(p2.latitude.to_r) * ( Math.cos(@longitude.to_r - p2.longitude.to_r) ) + sin_lat1 * sin_lat2
36
- angular_distance = Math.atan(-t1/Math.sqrt(-t1 * t1 +1)) + atan1_2 #central angle in radians so we can calculate the arc length.
46
+ t1 = cos_lat1 * Math.cos(p2.latitude.to_rad) * Math.cos(@longitude.to_rad - p2.longitude.to_rad) + sin_lat1 * sin_lat2
47
+ angular_distance = Math.atan(-t1 / Math.sqrt(-t1 * t1 + 1)) + atan1_2 # central angle in radians so we can calculate the arc length.
37
48
 
38
49
  t2 = (sin_lat2 - sin_lat1 * Math.cos(angular_distance)) / (cos_lat1 * Math.sin(angular_distance))
39
- if(Math.sin(p2.longitude.to_r - @longitude.to_r) < 0)
40
- bearing = 2 * Math::PI - (Math.atan(-t2 / Math.sqrt(-t2 * t2 + 1)) + atan1_2) #Compass Bearing in radians (clockwise)
41
- else
42
- bearing = Math.atan(-t2 / Math.sqrt(-t2 * t2 + 1)) + atan1_2 #Compass Bearing in radians (clockwise)
43
- end
50
+ bearing = if Math.sin(p2.longitude.to_rad - @longitude.to_rad) < 0
51
+ 2 * Math::PI - (Math.atan(-t2 / Math.sqrt(-t2 * t2 + 1)) + atan1_2) # Compass Bearing in radians (clockwise)
52
+ else
53
+ Math.atan(-t2 / Math.sqrt(-t2 * t2 + 1)) + atan1_2 # Compass Bearing in radians (clockwise)
54
+ end
44
55
 
45
- #Note that the bearing is a compass angle. That is angles are positive clockwise.
56
+ # Note that the bearing is a compass angle. That is angles are positive clockwise.
46
57
  return TrackAndDistance.new(bearing, angular_distance * r, true)
47
58
  end
48
59
 
49
- #Vincenty's algorithm for finding bearing and distance between to coordinates.
50
- #Assumes earth is a WGS-84 Ellipsod.
51
- #Takes: argument p2 is target coordinate that we want the bearing to.
52
- #Returns: TrackAndDistance object with the compass bearing and distance in meters to P2
60
+ # Vincenty's algorithm for finding bearing and distance between to coordinates.
61
+ # Assumes earth is a WGS-84 Ellipsod.
62
+ # @param [Coordinate] p2 is target coordinate that we want the bearing to.
63
+ # @return [TrackAndDistance] with the compass bearing and distance in meters to P2
53
64
  def distanceAndAngle( p2 )
65
+ if self.latitude == p2.latitude && self.longitude == p2.longitude
66
+ return TrackAndDistance.new(0, 0, true) # No calculations necessary. Solv NAN issue
67
+ end
68
+
54
69
  # a, b = major & minor semiaxes of the ellipsoid
55
- a = 6378137 #equatorial radius in meters (+/-2 m)
56
- b = 6356752.31424518 #polar radius in meters
57
- f = (a-b)/a # flattening
58
-
59
- lat1 = @latitude.to_r
60
- lon1 = @longitude.to_r
61
- lat2 = p2.latitude.to_r
62
- lon2 = p2.longitude.to_r
63
- lat1 = lat1.sign * (Math::PI/2-(1e-10)) if (Math::PI/2-lat1.abs).abs < 1.0e-10
64
- lat2 = lat2.sign * (Math::PI/2-(1e-10)) if (Math::PI/2-lat2.abs).abs < 1.0e-10
70
+ a = 6378137 # equatorial radius in meters (+/-2 m)
71
+ b = 6356752.31424518 # polar radius in meters
72
+ f = (a - b) / a # flattening
73
+
74
+ lat1 = @latitude.to_rad
75
+ lon1 = @longitude.to_rad
76
+ lat2 = p2.latitude.to_rad
77
+ lon2 = p2.longitude.to_rad
78
+ lat1 = lat1.sign * (Math::PI / 2 - 1e-10) if (Math::PI / 2 - lat1.abs).abs < 1.0e-10
79
+ lat2 = lat2.sign * (Math::PI / 2 - 1e-10) if (Math::PI / 2 - lat2.abs).abs < 1.0e-10
65
80
 
66
81
  # lat1, lat2 = geodetic latitude
67
-
68
- l = (lon2 - lon1).abs #difference in longitude
69
- l = 2*Math::PI - l if l > Math::PI
70
- u1 = Math.atan( ( 1 - f) * Math.tan( lat1 ) ) #U is 'reduced latitude'
71
- u2 = Math.atan( ( 1 - f) * Math.tan( lat2 ) )
72
- sin_u1 = Math.sin(u1)
73
- cos_u1 = Math.cos(u1)
74
- sin_u2 = Math.sin(u2)
75
- cos_u2 = Math.cos(u2)
76
-
77
- lambda_v = l
78
- lambda_dash = Math::PI * 2
79
- while( (lambda_v - lambda_dash).abs > 1.0e-12 ) #i.e. 0.06 mm error
80
- sin_lambda_v = Math.sin(lambda_v)
81
- cos_lambda_v = Math.cos(lambda_v)
82
- sin_sigma = Math.sqrt( ( cos_u2 * sin_lambda_v ) ** 2 + ( cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v ) ** 2 )
83
- cos_sigma = sin_u1 * sin_u2 + cos_u1 * cos_u2 * cos_lambda_v
84
- sigma = Math.atan2(sin_sigma, cos_sigma)
85
- sin_alpha= cos_u1 * cos_u2 * sin_lambda_v / sin_sigma
86
- cos_2_alpha = 1 - sin_alpha * sin_alpha #trig identity
87
- cos_2_sigma_m = cos_sigma - 2 * sin_u1 * sin_u2/cos_2_alpha
88
- c = f / 16 * cos_2_alpha * (4 + f*(4-3*cos_2_alpha))
89
- lambda_dash = lambda_v
90
- lambda_v = l + (1-c) * f * sin_alpha * (sigma + c * sin_sigma * (cos_2_sigma_m + c * cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) ) ) # use cos_2_sigma_m=0 when over equatorial lines
91
- if lambda_v > Math::PI
92
- lambda_v = Math::PI
93
- break
94
- end
95
- end
96
82
 
97
- u_2 = cos_2_alpha * (a * a - b * b) / (b * b)
98
- a1 = 1 + u_2 / 16384 * (4096 + u_2 * (-768 + u_2 * (320 - 175 * u_2)))
99
- b1 = u_2 / 1024 * (256 + u_2 * (-128 + u_2 * (74 - 47 * u_2)))
100
- delta_sigma = b1 * sin_sigma * (cos_2_sigma_m + b1 / 4 * (cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) - b1 / 6 * cos_2_sigma_m * (-3 + 4 * sin_sigma * sin_sigma) * (-3 + 4 * cos_2_sigma_m * cos_2_sigma_m)))
101
- s = b * a1 * (sigma - delta_sigma)
83
+ l = (lon2 - lon1).abs # difference in longitude
84
+ l = 2 * Math::PI - l if l > Math::PI
85
+ u1 = Math.atan( ( 1 - f) * Math.tan( lat1 ) ) # U is 'reduced latitude'
86
+ u2 = Math.atan( ( 1 - f) * Math.tan( lat2 ) )
87
+ sin_u1 = Math.sin(u1)
88
+ cos_u1 = Math.cos(u1)
89
+ sin_u2 = Math.sin(u2)
90
+ cos_u2 = Math.cos(u2)
91
+
92
+ lambda_v = l
93
+ lambda_dash = Math::PI * 2
94
+ while (lambda_v - lambda_dash).abs > 1.0e-12 # i.e. 0.06 mm error
102
95
  sin_lambda_v = Math.sin(lambda_v)
103
96
  cos_lambda_v = Math.cos(lambda_v)
104
-
105
- #This test isn't in original formulae, and fixes the problem of all angles returned being between 0 - PI (0-180)
106
- #Also converts the result to compass bearing, rather than the mathmatical anticlockwise angles.
107
- if(Math.sin(p2.longitude.to_r - @longitude.to_r) < 0)
108
- alpha_1 = Math::PI*2-Math.atan2( cos_u2 * sin_lambda_v, cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v)
109
- #alpha_2 = Math::PI*2-Math.atan2(cos_u1 * sin_lambda_v, -sin_u1 * cos_u2 + cos_u1 * sin_u2 * cos_lambda_v)
110
- else
111
- alpha_1 = Math.atan2( cos_u2 * sin_lambda_v, cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v)
112
- #alpha_2 = Math.atan2(cos_u1 * sin_lambda_v, -sin_u1 * cos_u2 + cos_u1 * sin_u2 * cos_lambda_v)
97
+ sin_sigma = Math.sqrt( ( cos_u2 * sin_lambda_v )**2 + ( cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v )**2 )
98
+ cos_sigma = sin_u1 * sin_u2 + cos_u1 * cos_u2 * cos_lambda_v
99
+ sigma = Math.atan2(sin_sigma, cos_sigma)
100
+ sin_alpha = cos_u1 * cos_u2 * sin_lambda_v / sin_sigma
101
+ cos_2_alpha = 1 - sin_alpha * sin_alpha # trig identity
102
+ cos_2_sigma_m = cos_sigma - 2 * sin_u1 * sin_u2 / cos_2_alpha
103
+ c = f / 16 * cos_2_alpha * (4 + f * (4 - 3 * cos_2_alpha))
104
+ lambda_dash = lambda_v
105
+ lambda_v = l + (1 - c) * f * sin_alpha * (sigma + c * sin_sigma * (cos_2_sigma_m + c * cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) ) ) # use cos_2_sigma_m=0 when over equatorial lines
106
+ if lambda_v > Math::PI
107
+ lambda_v = Math::PI
108
+ break
113
109
  end
114
-
115
- #Note that the bearing is a compass (i.e. clockwise) angle.
116
- return TrackAndDistance.new(alpha_1, s, true) #What to do with alpha_2?
110
+ end
111
+
112
+ u_2 = cos_2_alpha * (a * a - b * b) / (b * b)
113
+ a1 = 1 + u_2 / 16384 * (4096 + u_2 * (-768 + u_2 * (320 - 175 * u_2)))
114
+ b1 = u_2 / 1024 * (256 + u_2 * (-128 + u_2 * (74 - 47 * u_2)))
115
+ delta_sigma = b1 * sin_sigma * (cos_2_sigma_m + b1 / 4 * (cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) - b1 / 6 * cos_2_sigma_m * (-3 + 4 * sin_sigma * sin_sigma) * (-3 + 4 * cos_2_sigma_m * cos_2_sigma_m)))
116
+ s = b * a1 * (sigma - delta_sigma)
117
+ sin_lambda_v = Math.sin(lambda_v)
118
+ cos_lambda_v = Math.cos(lambda_v)
119
+
120
+ # This test isn't in original formulae, and fixes the problem of all angles returned being between 0 - PI (0-180)
121
+ # Also converts the result to compass bearing, rather than the mathmatical anticlockwise angles.
122
+ alpha_1 = if Math.sin(p2.longitude.to_rad - @longitude.to_rad) < 0
123
+ Math::PI * 2 - Math.atan2( cos_u2 * sin_lambda_v, cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v)
124
+ # alpha_2 = Math::PI*2-Math.atan2(cos_u1 * sin_lambda_v, -sin_u1 * cos_u2 + cos_u1 * sin_u2 * cos_lambda_v)
125
+ else
126
+ Math.atan2( cos_u2 * sin_lambda_v, cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v)
127
+ # alpha_2 = Math.atan2(cos_u1 * sin_lambda_v, -sin_u1 * cos_u2 + cos_u1 * sin_u2 * cos_lambda_v)
128
+ end
129
+
130
+ # Note that the bearing is a compass (i.e. clockwise) angle.
131
+ return TrackAndDistance.new(alpha_1, s, true) # What to do with alpha_2?
117
132
  end
118
133
 
119
- #spherical earth estimate of calculation for finding target coordinate from start coordinate, bearing and distance
120
- #Used to run checks on the Vincenty algorithm
121
- #Takes: TrackAndDistance object with bearing and distance.
122
- #Returns new Vincenty object with the destination coordinates.
134
+ # spherical earth estimate of calculation for finding target coordinate from start coordinate, bearing and distance
135
+ # Used to run checks on the Vincenty algorithm
136
+ # @param [TrackAndDistance] track_and_distance specifying bearing and distance.
137
+ # @return [Vincenty] with the destination coordinates.
123
138
  def sphereDestination( track_and_distance )
124
- a = 6378137 #equatorial radius in meters (+/-2 m)
125
- b = 6356752.31424518 #polar radius in meters
126
- r = (a+b)/2 #average diametre as a rough estimate for our tests.
127
-
139
+ a = 6378137 # equatorial radius in meters (+/-2 m)
140
+ b = 6356752.31424518 # polar radius in meters
141
+ r = (a + b) / 2 # average diametre as a rough estimate for our tests.
142
+
128
143
  d = track_and_distance.distance.abs
129
- sin_dor = Math.sin(d/r)
130
- cos_dor = Math.cos(d/r)
131
- sin_lat1 = Math.sin(@latitude.to_r)
132
- cos_lat1 = Math.cos(@latitude.to_r)
133
- lat2 = Math.asin( sin_lat1 * cos_dor + cos_lat1 * sin_dor * Math.cos(track_and_distance.bearing.to_r) )
134
- lon2 = @longitude.to_r + Math.atan2(Math.sin(track_and_distance.bearing.to_r) * sin_dor * cos_lat1, cos_dor-sin_lat1 * Math.sin(lat2))
135
-
136
- Vincenty.new(lat2, lon2, 0, true);
144
+ sin_dor = Math.sin(d / r)
145
+ cos_dor = Math.cos(d / r)
146
+ sin_lat1 = Math.sin(@latitude.to_rad)
147
+ cos_lat1 = Math.cos(@latitude.to_rad)
148
+ lat2 = Math.asin( sin_lat1 * cos_dor + cos_lat1 * sin_dor * Math.cos(track_and_distance.bearing.to_rad) )
149
+ lon2 = @longitude.to_rad + Math.atan2(Math.sin(track_and_distance.bearing.to_rad) * sin_dor * cos_lat1, cos_dor - sin_lat1 * Math.sin(lat2))
150
+
151
+ Vincenty.new(lat2, lon2, 0, true)
137
152
  end
138
153
 
139
154
  #
140
155
  # Calculate destination point given start point lat/long, bearing and distance.
141
- #Assumes earth is a WGS-84 Ellipsod.
142
- #Takes: TrackAndDistance object with bearing and distance.
143
- #Returns: new Vincenty object with the destination coordinates.
144
- def destination( track_and_distance )
156
+ # Assumes earth is a WGS-84 Ellipsod.
157
+ # @param [TrackAndDistance] specifying bearing and distance.
158
+ # @return [Vincenty] with the destination coordinates.
159
+ def destination( track_and_distance )
145
160
  # a, b = major & minor semiaxes of the ellipsoid
146
- a = 6378137 #equatorial radius in meters (+/-2 m)
147
- b = 6356752.31424518 #polar radius in meters
148
- f = (a-b)/a # flattening
149
-
150
- s = track_and_distance.distance.abs;
151
- alpha1 = track_and_distance.bearing.to_r
161
+ a = 6378137 # equatorial radius in meters (+/-2 m)
162
+ b = 6356752.31424518 # polar radius in meters
163
+ f = (a - b) / a # flattening
164
+
165
+ s = track_and_distance.distance.abs
166
+ alpha1 = track_and_distance.bearing.to_rad
152
167
  sin_alpha1 = Math.sin(alpha1)
153
168
  cos_alpha1 = Math.cos(alpha1)
154
-
155
- tanU1 = (1-f) * Math.tan(@latitude.to_r);
156
- cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1))
169
+
170
+ tanU1 = (1 - f) * Math.tan(@latitude.to_rad)
171
+ cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1))
157
172
  sinU1 = tanU1 * cosU1
158
173
  sigma1 = Math.atan2(tanU1, cos_alpha1)
159
- sin_alpha = cosU1 * sin_alpha1
160
- cos_2_alpha = 1 - sin_alpha * sin_alpha #Trig identity
161
- u_2 = cos_2_alpha * (a * a - b * b) / (b * b)
162
- a1 = 1 + u_2/16384 * (4096 + u_2 * (-768 + u_2 * (320-175 * u_2)))
163
- b1 = u_2/1024 * (256 + u_2 * (-128 + u_2 * (74-47 * u_2)))
164
-
174
+ sin_alpha = cosU1 * sin_alpha1
175
+ cos_2_alpha = 1 - sin_alpha * sin_alpha # Trig identity
176
+ u_2 = cos_2_alpha * (a * a - b * b) / (b * b)
177
+ a1 = 1 + u_2 / 16384 * (4096 + u_2 * (-768 + u_2 * (320 - 175 * u_2)))
178
+ b1 = u_2 / 1024 * (256 + u_2 * (-128 + u_2 * (74 - 47 * u_2)))
179
+
165
180
  sigma = s / (b * a1)
166
181
  sigma_dash = 2 * Math::PI
167
- while ((sigma-sigma_dash).abs > 1.0e-12) #i.e 0.06mm
168
- cos_2_sigma_m = Math.cos(2 * sigma1 + sigma)
182
+ while (sigma - sigma_dash).abs > 1.0e-12 # i.e 0.06mm
183
+ cos_2_sigma_m = Math.cos(2 * sigma1 + sigma)
169
184
  sin_sigma = Math.sin(sigma)
170
185
  cos_sigma = Math.cos(sigma)
171
- delta_sigma = b1 * sin_sigma * (cos_2_sigma_m + b1/4 * (cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) - b1/6 * cos_2_sigma_m * (-3 + 4 * sin_sigma * sin_sigma) * (-3 + 4 * cos_2_sigma_m * cos_2_sigma_m)))
186
+ delta_sigma = b1 * sin_sigma * (cos_2_sigma_m + b1 / 4 * (cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) - b1 / 6 * cos_2_sigma_m * (-3 + 4 * sin_sigma * sin_sigma) * (-3 + 4 * cos_2_sigma_m * cos_2_sigma_m)))
172
187
  sigma_dash = sigma
173
- sigma = s / (b * a1) + delta_sigma
188
+ sigma = s / (b * a1) + delta_sigma
174
189
  end
175
190
 
176
191
  tmp = sinU1 * sin_sigma - cosU1 * cos_sigma * cos_alpha1
177
- lat2 = Math.atan2(sinU1 * cos_sigma + cosU1 * sin_sigma * cos_alpha1, (1-f) * Math.sqrt(sin_alpha * sin_alpha + tmp * tmp))
192
+ lat2 = Math.atan2(sinU1 * cos_sigma + cosU1 * sin_sigma * cos_alpha1, (1 - f) * Math.sqrt(sin_alpha * sin_alpha + tmp * tmp))
178
193
  lambda_v = Math.atan2(sin_sigma * sin_alpha1, cosU1 * cos_sigma - sinU1 * sin_sigma * cos_alpha1)
179
- c = f/16 * cos_2_alpha * (4 + f * (4-3 * cos_2_alpha))
180
- l = lambda_v - (1-c) * f * sin_alpha * (sigma + c * sin_sigma * (cos_2_sigma_m + c * cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m))) #difference in longitude
181
-
182
- #sigma2 = Math.atan2(sin_alpha, -tmp) # reverse azimuth
183
-
184
- return Vincenty.new(lat2, @longitude + l, 0, true);
185
- end
194
+ c = f / 16 * cos_2_alpha * (4 + f * (4 - 3 * cos_2_alpha))
195
+ l = lambda_v - (1 - c) * f * sin_alpha * (sigma + c * sin_sigma * (cos_2_sigma_m + c * cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m))) # difference in longitude
186
196
 
197
+ # sigma2 = Math.atan2(sin_alpha, -tmp) # reverse azimuth
187
198
 
199
+ return Vincenty.new(lat2, @longitude + l, 0, true)
200
+ end
188
201
  end
189
-
190
-
191
-
data/test/ts_all.rb CHANGED
@@ -1,10 +1,10 @@
1
- require 'ts_angle.rb'
2
- require 'ts_vincenty.rb'
3
- require 'ts_latitude.rb'
4
- require 'ts_longitude.rb'
5
- require 'ts_coordinate.rb'
6
- require 'ts_track_and_distance.rb'
1
+ #!/usr/bin/env ruby
2
+ require_relative 'ts_angle.rb'
3
+ require_relative 'ts_vincenty.rb'
4
+ require_relative 'ts_latitude.rb'
5
+ require_relative 'ts_longitude.rb'
6
+ require_relative 'ts_coordinate.rb'
7
+ require_relative 'ts_track_and_distance.rb'
7
8
 
9
+ puts 'Testing from source'
8
10
  puts Vincenty.new.version
9
-
10
-
data/test/ts_angle.rb CHANGED
@@ -1,81 +1,82 @@
1
+ #!/usr/bin/env ruby
1
2
  require 'test/unit'
2
- require 'vincenty.rb'
3
+ require_relative '../lib/vincenty.rb'
3
4
 
4
- class TestAngle< Test::Unit::TestCase
5
- #test Angle creation
5
+ class TestAngle < Test::Unit::TestCase
6
+ # test Angle creation
6
7
  def test_angle
7
- assert_equal(Angle.new(), 0)
8
- assert_equal(Angle.new("S37 01'7.5\"").to_d, -37.01875) #Leading NSEW
9
- assert_equal(Angle.new("37 01'7.5\"S").to_d , -37.01875) #Trailing NSEW
10
- assert_equal(Angle.new("-37 01'7.5\"").to_d, -37.01875) #Use of - rather than S or W
11
- assert_equal(Angle.new("-37 1.125'").to_d, -37.01875) #Decimal minutes, rather than minutes and seconds.
12
- assert_equal(Angle.new("-37 01'.125").to_d, -37.01875) #Nb. the minute marker ' between the minutes and fraction
13
- assert_equal(Angle.new("S37 01'.125").to_d, -37.01875) #Nb. the minute marker ' between the minutes and fraction
14
- assert_equal(Angle.new("37 01'.125S").to_d, -37.01875) #Nb. the minute marker ' between the minutes and fraction
15
- assert_equal(Angle.new("-37.01875 ").to_d, -37.01875) #decimal degrees, rather than deg, min, sec.
16
- assert_equal(Angle.new([-37, 1, 7.5]).to_d, -37.01875) #an array of deg,min,sec
17
- assert_equal(Angle.new(-37.01875).to_d, -37.01875)
18
- assert_equal(Angle.degrees(-37.01875).to_d, -37.01875)
19
- assert_equal(Angle.degrees(-37.01875).to_r.round(15), -0.646099072472651)
20
- assert_equal(Angle.new(-0.646099072472651, true).to_d.round(5), -37.01875)
21
- assert_equal(Angle.new(-0.646099072472651, true).to_r, -0.646099072472651)
22
- assert_equal(Angle.radians(-0.646099072472651).to_d.round(5), -37.01875)
8
+ assert_equal(Angle.new, 0)
9
+ assert_equal(Angle.new("S37 01'7.5\"").to_deg, -37.01875) # Leading NSEW
10
+ assert_equal(Angle.new("37 01'7.5\"S").to_deg, -37.01875) # Trailing NSEW
11
+ assert_equal(Angle.new("-37 01'7.5\"").to_deg, -37.01875) # Use of - rather than S or W
12
+ assert_equal(Angle.new("-37 1.125'").to_deg, -37.01875) # Decimal minutes, rather than minutes and seconds.
13
+ assert_equal(Angle.new("-37 01'.125").to_deg, -37.01875) # Nb. the minute marker ' between the minutes and fraction
14
+ assert_equal(Angle.new("S37 01'.125").to_deg, -37.01875) # Nb. the minute marker ' between the minutes and fraction
15
+ assert_equal(Angle.new("37 01'.125S").to_deg, -37.01875) # Nb. the minute marker ' between the minutes and fraction
16
+ assert_equal(Angle.new('-37.01875 ').to_deg, -37.01875) # decimal degrees, rather than deg, min, sec.
17
+ assert_equal(Angle.new([ -37, 1, 7.5 ]).to_deg, -37.01875) # an array of deg,min,sec
18
+ assert_equal(Angle.new(-37.01875).to_deg, -37.01875)
19
+ assert_equal(Angle.degrees(-37.01875).to_deg, -37.01875)
20
+ assert_equal(Angle.degrees(-37.01875).to_rad.round(15), -0.646099072472651)
21
+ assert_equal(Angle.new(-0.646099072472651, true).to_deg.round(5), -37.01875)
22
+ assert_equal(Angle.new(-0.646099072472651, true).to_rad, -0.646099072472651)
23
+ assert_equal(Angle.radians(-0.646099072472651).to_deg.round(5), -37.01875)
23
24
  assert_equal(Angle.radians(-0.646099072472651).value, Angle.radians(-0.646099072472651).angle)
24
- assert_equal(Angle.decimal_deg(1,2,3,'S'), -(1.0 + 2/60.0 + 3/3600.0))
25
- assert_equal(Angle.decimal_deg(1,2,3,'E'), (1.0 + 2/60.0 + 3/3600.0))
26
- assert_equal(Angle.decimal_deg(1,2,4,'N'), (1.0 + 2/60.0 + 4/3600.0))
27
- assert_equal(Angle.decimal_deg(1,5,4,'W'), -(1.0 + 5/60.0 + 4/3600.0))
28
- assert_equal(Angle.decimal_deg_from_ary([1,5,4,'W']), -(1.0 + 5/60.0 + 4/3600.0))
29
- assert_equal(Angle.decimal_deg_from_ary(Angle.dms( -(1.0 + 5/60.0 + 1.0/3600.0) )),-(1.0 + 5/60.0 + 1.0/3600.0)) #double call, rounding error always produced a failure.
25
+ assert_equal(Angle.decimal_deg(1, 2, 3, 'S'), -(1.0 + 2 / 60.0 + 3 / 3600.0))
26
+ assert_equal(Angle.decimal_deg(1, 2, 3, 'E'), (1.0 + 2 / 60.0 + 3 / 3600.0))
27
+ assert_equal(Angle.decimal_deg(1, 2, 4, 'N'), (1.0 + 2 / 60.0 + 4 / 3600.0))
28
+ assert_equal(Angle.decimal_deg(1, 5, 4, 'W'), -(1.0 + 5 / 60.0 + 4 / 3600.0))
29
+ assert_equal(Angle.decimal_deg_from_ary([ 1, 5, 4, 'W' ]), -(1.0 + 5 / 60.0 + 4 / 3600.0))
30
+ assert_equal(Angle.decimal_deg_from_ary(Angle.dms( -(1.0 + 5 / 60.0 + 1.0 / 3600.0) )), -(1.0 + 5 / 60.0 + 1.0 / 3600.0)) # double call, rounding error always produced a failure.
30
31
  end
31
-
32
+
32
33
  def test_strf
33
34
  a = Angle.new("S37 01'7.5\"")
34
- assert_equal("-37 01'07.5000\"", a.strf) #default format of strf
35
+ assert_equal("-37 01'07.5000\"", a.strf) # default format of strf
35
36
  assert_equal("37 01'07.50000\"S", a.strf( "%d %2m'%2.5s\"%N" ))
36
37
  assert_equal("37 01'07.50000\"W", a.strf("%d %2m'%2.5s\"%E" ))
37
38
  assert_equal("-37 01'07.5000\"", a.strf("%d %2m'%2.4s\"" ))
38
39
  assert_equal("-37 01.1250'\n", a.strf("%d %2.4M'\n" ))
39
- assert_equal("*** -37 01'.1250", a.strf( "*** %d %2m'%4W" )) #puting the minute ' before decimal point.
40
- assert_equal("-37.01875 ", a.strf("%0.5D " ))
40
+ assert_equal("*** -37 01'.1250", a.strf( "*** %d %2m'%4W" )) # puting the minute ' before decimal point.
41
+ assert_equal('-37.01875 ', a.strf('%0.5D ' ))
41
42
  assert_equal("-0.64610 radians\n", a.strf("%0.5r radians\n" ))
42
43
 
43
- assert_equal("-037 01'7.5000\"", Angle.new("-37 01'7.5\"").to_s('%3d %2m\'%1.4s"')) #testing leading 0 with -deg, no leading 0 %s
44
- assert_equal("00 01'07.5000\"S", Angle.new("0 01'7.5\"S").to_s('%2d %2m\'%2.4s"%N')) #testing 0 degrees and leading 0 %s
45
- assert_equal("00 -01'07.5000\"", Angle.new("0 01'7.5\"S").to_s('%2d %2m\'%2.4s"')) #testing 0 degrees and -min
46
- assert_equal("00 -01'07.5000\"", Angle.new("0 01'7.5\"S").to_s('%2d %2m\'%2.4s"') ) #test of 0 degrees, -min, no NSEW
47
- assert_equal("000 00'07.5000\"W", Angle.new("0 0'7.5\"W").to_s('%3d %2m\'%2.4s"%E') ) #testing E W 0 deg and 0 min and -sec
48
- assert_equal("00 00'-07.5000\"", Angle.new("0 0'7.5\"S").to_s('%2d %2m\'%2.4s"') ) #testing 0 deg and 0 min and -sec no NSEW
44
+ assert_equal("-037 01'7.5000\"", Angle.new("-37 01'7.5\"").to_s('%3d %2m\'%1.4s"')) # testing leading 0 with -deg, no leading 0 %s
45
+ assert_equal("00 01'07.5000\"S", Angle.new("0 01'7.5\"S").to_s('%2d %2m\'%2.4s"%N')) # testing 0 degrees and leading 0 %s
46
+ assert_equal("00 -01'07.5000\"", Angle.new("0 01'7.5\"S").to_s('%2d %2m\'%2.4s"')) # testing 0 degrees and -min
47
+ assert_equal("00 -01'07.5000\"", Angle.new("0 01'7.5\"S").to_s('%2d %2m\'%2.4s"') ) # test of 0 degrees, -min, no NSEW
48
+ assert_equal("000 00'07.5000\"W", Angle.new("0 0'7.5\"W").to_s('%3d %2m\'%2.4s"%E') ) # testing E W 0 deg and 0 min and -sec
49
+ assert_equal("00 00'-07.5000\"", Angle.new("0 0'7.5\"S").to_s('%2d %2m\'%2.4s"') ) # testing 0 deg and 0 min and -sec no NSEW
49
50
  end
50
51
 
51
52
  def test_operators
52
- #Comparable.
53
- assert_equal(Angle.radians(-0.646099072472651), Angle.radians(-0.646099072472651)) #<=>
54
- #unary-op Angle
55
- assert_equal(+Angle.radians(-0.646099072472651), Angle.radians(-0.646099072472651)) #unary +
56
- assert_equal(-Angle.radians(-0.646099072472651), Angle.radians(0.646099072472651)) #unary -
57
- #Angle op Numeric
58
- assert_equal(5, Angle.radians(2) + 3) # +
59
- assert_equal(-1, Angle.radians(2) - 3) # -
60
- assert_equal(6, Angle.radians(2) * 3) # *
61
- assert_equal(2, Angle.radians(4) /2) # /
53
+ # Comparable.
54
+ assert_equal(Angle.radians(-0.646099072472651), Angle.radians(-0.646099072472651)) # <=>
55
+ # unary-op Angle
56
+ assert_equal(+Angle.radians(-0.646099072472651), Angle.radians(-0.646099072472651)) # unary +
57
+ assert_equal(-Angle.radians(-0.646099072472651), Angle.radians(0.646099072472651)) # unary -
58
+ # Angle op Numeric
59
+ assert_equal(5, Angle.radians(2) + 3) # +
60
+ assert_equal(-1, Angle.radians(2) - 3) # -
61
+ assert_equal(6, Angle.radians(2) * 3) # *
62
+ assert_equal(2, Angle.radians(4) / 2) # /
62
63
  assert_equal(1, Angle.radians(4) % 3) # %
63
- assert_equal(64, Angle.radians(4) ** 3) # **
64
- #Numeric op Angle
64
+ assert_equal(64, Angle.radians(4)**3) # **
65
+ # Numeric op Angle
65
66
  assert_equal(5.1, 3.1 + Angle.radians(2) ) # +
66
67
  assert_equal(2.646099072472651, 2 - Angle.radians(-0.646099072472651) ) # -
67
68
  assert_equal(6, 3 * Angle.radians(2) ) # *
68
69
  assert_equal(2, 4 / Angle.radians(2) ) # /
69
- #Angle op Angle
70
- assert_equal(Angle.radians(3.2+2.1), Angle.radians(3.2) + Angle.radians(2.1) ) # +
71
- #Sign method.
70
+ # Angle op Angle
71
+ assert_equal(Angle.radians(3.2 + 2.1), Angle.radians(3.2) + Angle.radians(2.1) ) # +
72
+ # Sign method.
72
73
  assert_equal(1, Angle.radians(3).sign)
73
74
  assert_equal(-1, Angle.radians(-3).sign)
74
- #abs
75
+ # abs
75
76
  assert_equal(3, Angle.radians(-3).abs)
76
- #reverse
77
+ # reverse
77
78
  assert_equal(Angle.degrees(90), Angle.degrees(270).reverse)
78
- #bearing
79
+ # bearing
79
80
  assert_equal(Angle.degrees(340), Angle.degrees(20).to_bearing)
80
81
  end
81
- end
82
+ end
@@ -1,18 +1,19 @@
1
+ #!/usr/bin/env ruby
1
2
  require 'test/unit'
2
- require 'vincenty.rb'
3
+ require_relative '../lib/vincenty.rb'
3
4
 
4
- class TestAngle< Test::Unit::TestCase
5
- #test Coordinate
5
+ class TestCoordinate < Test::Unit::TestCase
6
+ # test Coordinate
6
7
  def test_coordinate
7
- c = Coordinate.new(-36.9923293459124, 174.485341187381,13.5)
8
+ c = Coordinate.new(-36.9923293459124, 174.485341187381, 13.5)
8
9
  ca = c.to_ary
9
- assert_equal(-36.9923293459124, ca[0].to_d)
10
- assert_equal(174.485341187381, ca[1].to_d)
10
+ assert_equal(-36.9923293459124, ca[0].to_deg)
11
+ assert_equal(174.485341187381, ca[1].to_deg)
11
12
  assert_equal(13.5, ca[2])
12
13
  ch = c.to_hash
13
- assert_equal(-36.9923293459124, ch[:latitude].to_d)
14
- assert_equal(174.485341187381, ch[:longitude].to_d)
14
+ assert_equal(-36.9923293459124, ch[:latitude].to_deg)
15
+ assert_equal(174.485341187381, ch[:longitude].to_deg)
15
16
  assert_equal(13.5, ch[:altitude])
16
17
  assert_equal("36 59'32.3856\"S 174 29'07.2283\"E 13.5m", c.to_s)
17
18
  end
18
- end
19
+ end
data/test/ts_latitude.rb CHANGED
@@ -1,7 +1,8 @@
1
+ #!/usr/bin/env ruby
1
2
  require 'test/unit'
2
- require 'vincenty.rb'
3
+ require_relative '../lib/vincenty.rb'
3
4
 
4
- class TestAngle< Test::Unit::TestCase
5
+ class TestLatitude < Test::Unit::TestCase
5
6
  def test_strf
6
7
  assert_equal("37 01'07.5000\"S", Latitude.new("S37 01'7.5\"").to_s)
7
8
  assert_equal("37 01'07.5000\"S", Latitude.new("-37 01'7.5\"").to_s)
@@ -10,22 +11,24 @@ class TestAngle< Test::Unit::TestCase
10
11
  assert_equal("37 01'07.5000\"N", Latitude.new("37 01'7.5\"").to_s)
11
12
  assert_equal("37 01'07.5000\"N", Latitude.new("37 01'7.5\"N").to_s)
12
13
  end
14
+
13
15
  def test_to_radians
14
- assert_equal(Math::PI/4, Latitude.degrees(45).to_r)
15
- assert_equal(Math::PI/4, Latitude.degrees(135).to_r)
16
- assert_equal(-Math::PI/4, Latitude.degrees(225).to_r)
17
- assert_equal(-Math::PI/4, Latitude.degrees(315).to_r)
16
+ assert_equal(Math::PI / 4, Latitude.degrees(45).to_rad)
17
+ assert_equal(Math::PI / 4, Latitude.degrees(135).to_rad)
18
+ assert_equal(-Math::PI / 4, Latitude.degrees(225).to_rad)
19
+ assert_equal(-Math::PI / 4, Latitude.degrees(315).to_rad)
18
20
  end
21
+
19
22
  def test_to_degrees
20
- assert_equal(45, Latitude.degrees(45).to_d)
21
- assert_equal(45, Latitude.degrees(135).to_d)
22
- assert_equal(-45, Latitude.degrees(225).to_d)
23
- assert_equal(-45, Latitude.degrees(315).to_d)
24
- assert_equal(1, Latitude.degrees(179).to_d)
25
- assert_equal(-1, Latitude.degrees(181).to_d)
26
- assert_equal(-89, Latitude.degrees(269).to_d)
27
- assert_equal(-89, Latitude.degrees(271).to_d)
28
- assert_equal(89, Latitude.degrees(89).to_d)
29
- assert_equal(89, Latitude.degrees(91).to_d)
23
+ assert_equal(45, Latitude.degrees(45).to_deg)
24
+ assert_equal(45, Latitude.degrees(135).to_deg)
25
+ assert_equal(-45, Latitude.degrees(225).to_deg)
26
+ assert_equal(-45, Latitude.degrees(315).to_deg)
27
+ assert_equal(1, Latitude.degrees(179).to_deg)
28
+ assert_equal(-1, Latitude.degrees(181).to_deg)
29
+ assert_equal(-89, Latitude.degrees(269).to_deg)
30
+ assert_equal(-89, Latitude.degrees(271).to_deg)
31
+ assert_equal(89, Latitude.degrees(89).to_deg)
32
+ assert_equal(89, Latitude.degrees(91).to_deg)
30
33
  end
31
- end
34
+ end
data/test/ts_longitude.rb CHANGED
@@ -1,7 +1,8 @@
1
+ #!/usr/bin/env ruby
1
2
  require 'test/unit'
2
- require 'vincenty.rb'
3
+ require_relative '../lib/vincenty.rb'
3
4
 
4
- class TestAngle< Test::Unit::TestCase
5
+ class TestLongitude < Test::Unit::TestCase
5
6
  def test_strf
6
7
  assert_equal("037 01'07.5000\"W", Longitude.new("W37 01'7.5\"").to_s)
7
8
  assert_equal("037 01'07.5000\"W", Longitude.new("-37 01'7.5\"").to_s)
@@ -10,16 +11,18 @@ class TestAngle< Test::Unit::TestCase
10
11
  assert_equal("037 01'07.5000\"E", Longitude.new("37 01'7.5\"").to_s)
11
12
  assert_equal("037 01'07.5000\"E", Longitude.new("37 01'7.5\"E").to_s)
12
13
  end
14
+
13
15
  def test_to_radians
14
- assert_equal(Math::PI/4, Longitude.degrees(45).to_r)
15
- assert_equal(3*Math::PI/4, Longitude.degrees(135).to_r)
16
- assert_equal(-3*Math::PI/4, Longitude.degrees(225).to_r)
17
- assert_equal(-Math::PI/4, Longitude.degrees(315).to_r)
16
+ assert_equal(Math::PI / 4, Longitude.degrees(45).to_rad)
17
+ assert_equal(3 * Math::PI / 4, Longitude.degrees(135).to_rad)
18
+ assert_equal(-3 * Math::PI / 4, Longitude.degrees(225).to_rad)
19
+ assert_equal(-Math::PI / 4, Longitude.degrees(315).to_rad)
18
20
  end
21
+
19
22
  def test_to_degrees
20
- assert_equal(45, Longitude.degrees(45).to_d)
21
- assert_equal(135, Longitude.degrees(135).to_d)
22
- assert_equal(-135, Longitude.degrees(225).to_d)
23
- assert_equal(-45, Longitude.degrees(315).to_d)
23
+ assert_equal(45, Longitude.degrees(45).to_deg)
24
+ assert_equal(135, Longitude.degrees(135).to_deg)
25
+ assert_equal(-135, Longitude.degrees(225).to_deg)
26
+ assert_equal(-45, Longitude.degrees(315).to_deg)
24
27
  end
25
- end
28
+ end