vincenty 1.0.7 → 1.0.8
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.
- checksums.yaml +4 -4
- data/History.txt +4 -0
- data/Rakefile +9 -7
- data/lib/angle.rb +57 -66
- data/lib/coordinate.rb +11 -11
- data/lib/latitude.rb +5 -5
- data/lib/longitude.rb +7 -7
- data/lib/track_and_distance.rb +5 -5
- data/lib/vincenty.rb +49 -38
- data/test/ts_all.rb +7 -8
- data/test/ts_angle.rb +5 -5
- data/test/ts_coordinate.rb +3 -3
- data/test/ts_latitude.rb +3 -3
- data/test/ts_longitude.rb +3 -3
- data/test/ts_track_and_distance.rb +5 -2
- data/test/ts_vincenty.rb +25 -16
- metadata +5 -6
- data/.gemtest +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b6741523510878f999a8ca01552a2c964f224d9
|
4
|
+
data.tar.gz: 3bf382d568ef7a48af1e25cf2ea959a1b59a4126
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b00f63be8f205622eb84632bad057c87f52f494bdae88f21869738703871b94fde50e342a507bc24c0a0cbd7c3fde13833964beead39a849f7e402da6c8d4af5
|
7
|
+
data.tar.gz: 6e49b7d4338d6a437809777f8dbe06cfc6b95035d8561e6d3d5de6eae1951742439681988fa9e93eb904886a71508c5c10d66fdbe77a592d7a2f27cae0b18268
|
data/History.txt
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
rbur004 Sat Dec 3 17:36:05 2016 +1300
|
2
|
+
Code is old. Needed require change to require_relative
|
3
|
+
Rob Burrowes Fri May 22 14:07:09 2015 +1200
|
4
|
+
Dropped this, as it is auto generated.
|
1
5
|
Rob Burrowes Mon May 18 16:51:05 2015 +1200
|
2
6
|
|
3
7
|
Rob Burrowes Mon May 18 14:33:16 2015 +1200
|
data/Rakefile
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
#!/usr/local/bin/ruby
|
2
2
|
# -*- ruby -*-
|
3
|
-
|
4
|
-
|
3
|
+
#need
|
4
|
+
# gem install yard
|
5
|
+
# gem install hoe
|
6
|
+
# gem install hoe-yard
|
7
|
+
require 'yard'
|
5
8
|
require 'hoe'
|
6
9
|
#require 'lib/vincenty.rb'
|
7
10
|
Hoe.plugin :yard
|
8
11
|
|
9
|
-
Hoe.spec 'vincenty' do
|
10
|
-
#self.rubyforge_name = "vincenty"
|
12
|
+
Hoe.spec 'vincenty' do
|
13
|
+
#self.rubyforge_name = "vincenty"
|
11
14
|
self.developer( "Rob Burrowes","r.burrowes@auckland.ac.nz")
|
12
|
-
|
15
|
+
|
13
16
|
self.yard_title = 'Vincenty'
|
14
17
|
self.yard_options = ['--markup', 'markdown', '--protected']
|
15
18
|
#self.url = "http://www.wikarekare.org"
|
16
|
-
#self.summary = "Vincenty Algorithm for Distance, Bearing between Map Coordinates."
|
19
|
+
#self.summary = "Vincenty Algorithm for Distance, Bearing between Map Coordinates."
|
17
20
|
#self.description = s.paragraphs_of('README.txt', 1..4).join("\n\n")
|
18
21
|
#self.remote_rdoc_dir = '' # Release to root
|
19
22
|
end
|
20
|
-
|
data/lib/angle.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
|
1
|
+
require_relative 'core_extensions.rb'
|
2
2
|
|
3
|
-
#Class Angle is a utility class that allows
|
3
|
+
#Class Angle is a utility class that allows
|
4
4
|
# * Angle arithmetic ( +,-,*,/,**,% and unary +,-)
|
5
5
|
# * Angle comparison ( <=>, hence, <, >, >=, <=, == )
|
6
6
|
# * Conversion to and from degrees and radians
|
@@ -8,13 +8,13 @@ require 'core_extensions.rb'
|
|
8
8
|
|
9
9
|
class Angle
|
10
10
|
include Comparable
|
11
|
-
|
11
|
+
|
12
12
|
# @return [Float] stored in radians
|
13
|
-
attr_accessor :angle
|
13
|
+
attr_accessor :angle
|
14
14
|
alias :value :angle #Older version of angle used volue rather than angle
|
15
15
|
alias :value= :angle= #Older version of angle used volue rather than angle
|
16
|
-
|
17
|
-
# @param [#to_f,#to_radians] angle may be anything that has a to_f and to_radians.
|
16
|
+
|
17
|
+
# @param [#to_f,#to_radians] angle may be anything that has a to_f and to_radians.
|
18
18
|
# @param [true,false, :radians] radians Angle is in degrees unless radians == true (or set to :radians).
|
19
19
|
def initialize(angle=0, radians=false)
|
20
20
|
#assumes that we are getting an angle in degrees.
|
@@ -28,8 +28,8 @@ class Angle
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
-
|
32
|
-
|
31
|
+
|
32
|
+
|
33
33
|
#Class level function that converts 4 arguments into decimal degrees.
|
34
34
|
# @return [Float] signed decimal degrees.
|
35
35
|
# @param [Numeric] degrees
|
@@ -43,22 +43,22 @@ class Angle
|
|
43
43
|
#Shouldn't have a negative value for degrees if the direction is specified.
|
44
44
|
#I am defaulting to the degrees sign if it is set, otherwise the direction given
|
45
45
|
sign = degrees.sign == -1 || (degrees == 0 && (minutes < 0 || (minutes == 0 && seconds < 0))) ? -1 : sign
|
46
|
-
sign * (degrees.abs + minutes/60.0 + seconds/3600.0)
|
46
|
+
sign * (degrees.abs + minutes/60.0 + seconds/3600.0)
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
#Class level function that takes an array of [degress,minutes, seconds, direction] or [degrees,minutes,seconds]
|
50
50
|
# @return [Float] signed decimal degrees.
|
51
51
|
# @param [Array] a Array is expanded and passed as degrees,minutes,seconds,direction to Angle#decimal_deg
|
52
52
|
def self.decimal_deg_from_ary(a)
|
53
53
|
self.decimal_deg(*a)
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
#Class level utility function to return the angle as deg,min,sec
|
57
57
|
#Assumes decimal degress unless radians == true .
|
58
58
|
# @return [Array] of signed deg, min, sec.
|
59
59
|
#Nb. * That min will be negative if the angle is negative and deg == 0
|
60
60
|
# * That sec will be negative if the angle is negative and deg == 0 && min == 0
|
61
|
-
# @param [#to_f,#to_degrees] angle may be anything that has a to_f and to_radians.
|
61
|
+
# @param [#to_f,#to_degrees] angle may be anything that has a to_f and to_radians.
|
62
62
|
# @param [true,false, :radians] radians Angle is in degrees unless radians == true (or set to :radians).
|
63
63
|
def self.dms(angle, radians = false)
|
64
64
|
ongle = angle.to_f #means Strings, Numeric, and anything accepting a #to_f will work.
|
@@ -66,14 +66,14 @@ class Angle
|
|
66
66
|
v = angle.abs
|
67
67
|
deg = v.floor
|
68
68
|
min = ((v-deg)*60.0).floor
|
69
|
-
sec = ((v-deg-min/60.0)*3600.0)
|
70
|
-
|
69
|
+
sec = ((v-deg-min/60.0)*3600.0)
|
70
|
+
|
71
71
|
if angle < 0
|
72
72
|
if deg == 0
|
73
73
|
if min == 0
|
74
74
|
sec = -sec
|
75
75
|
else
|
76
|
-
min = -min
|
76
|
+
min = -min
|
77
77
|
end
|
78
78
|
else
|
79
79
|
deg = -deg
|
@@ -82,28 +82,28 @@ class Angle
|
|
82
82
|
|
83
83
|
return deg,min,sec
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
#Class level function equivalent to Angle.new(r, true)
|
87
87
|
# @return [Angle]
|
88
88
|
# @param [#to_f] r Value in radians to create the new Angle class with.
|
89
89
|
def self.radians(r=0) #passed in radians.
|
90
90
|
self.new(r.to_f, true) #Nb. self is Angle, be we don't Angle.new, as we want subclasses to return their class, not Angle.
|
91
91
|
end
|
92
|
-
|
92
|
+
|
93
93
|
#Class level function equivalent to Angle.new(d, false) or just Angle.new(d)
|
94
94
|
# @return [Angle]
|
95
95
|
# @param [#to_radians] d Angle to convert to radians, to create new Angle object from.
|
96
96
|
def self.degrees(d=0) #passed in degrees.
|
97
97
|
self.new(d.to_radians, true)
|
98
98
|
end
|
99
|
-
|
99
|
+
|
100
100
|
#Provides test for Module Comparable, giving us <,>,<=,>=,== between angles
|
101
101
|
# @return [true,false]
|
102
102
|
# @param [Angle,Float] angle Can be another Angle, or a Numeric value to compare @angle with.
|
103
103
|
def <=>(angle)
|
104
104
|
if angle.class == Angle
|
105
105
|
@angle <=> angle.angle
|
106
|
-
else
|
106
|
+
else
|
107
107
|
@angle <=> angle
|
108
108
|
end
|
109
109
|
end
|
@@ -113,64 +113,64 @@ class Angle
|
|
113
113
|
def +@
|
114
114
|
self.class.radians(@angle) #Nb. Not Angle.new, as we want subclasses to return their class, not Angle.
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
#Unary -
|
118
118
|
# @return [Angle,self] -@angle
|
119
119
|
def -@
|
120
120
|
self.class.radians(-@angle)
|
121
121
|
end
|
122
|
-
|
122
|
+
|
123
123
|
# Binary addition operator. Can add angles and numbers, or two angles.
|
124
124
|
# @return [Angle,self]
|
125
125
|
# @param [Angle,Numeric] angle
|
126
126
|
def +(angle)
|
127
127
|
self.class.radians(@angle + angle)
|
128
128
|
end
|
129
|
-
|
129
|
+
|
130
130
|
# Binary subtraction operator. Can add angles and numbers, or two angles.
|
131
131
|
# @return [Angle,self]
|
132
132
|
# @param [Angle,Numeric] angle
|
133
133
|
def -(angle)
|
134
134
|
self.class.radians(@angle - angle)
|
135
135
|
end
|
136
|
-
|
136
|
+
|
137
137
|
# Binary multiply operator. Can add angles and numbers, or two angles.
|
138
138
|
# @return [Angle,self]
|
139
139
|
# @param [Angle,Numeric] angle
|
140
140
|
def *(angle)
|
141
141
|
self.class.radians(@angle * angle)
|
142
142
|
end
|
143
|
-
|
143
|
+
|
144
144
|
# Binary power of operator. Can add angles and numbers, or two angles.
|
145
145
|
# @return [Angle,self]
|
146
146
|
# @param [Angle,Numeric] angle
|
147
147
|
def **(angle)
|
148
148
|
self.class.radians(@angle ** angle)
|
149
149
|
end
|
150
|
-
|
150
|
+
|
151
151
|
# Binary division operator. Can add angles and numbers, or two angles.
|
152
152
|
# @return [Angle,self]
|
153
153
|
# @param [Angle,Numeric] angle
|
154
154
|
def /(angle)
|
155
155
|
self.class.radians(@angle / angle)
|
156
156
|
end
|
157
|
-
|
157
|
+
|
158
158
|
# Binary mod operator. Can add angles and numbers, or two angles.
|
159
159
|
# @return [Angle,self]
|
160
160
|
# @param [Angle,Numeric] angle
|
161
161
|
def %(angle)
|
162
162
|
self.class.radians(@angle % angle)
|
163
163
|
end
|
164
|
-
|
164
|
+
|
165
165
|
# @return [Float] angle in degrees
|
166
166
|
def to_degrees
|
167
167
|
@angle.to_degrees
|
168
168
|
end
|
169
|
-
|
169
|
+
|
170
170
|
# @return [Float] angle in degrees
|
171
|
-
#alias
|
171
|
+
#alias to_deg to_degrees
|
172
172
|
alias to_deg to_degrees
|
173
|
-
|
173
|
+
|
174
174
|
# @return [Float] angle in radians
|
175
175
|
def to_radians
|
176
176
|
@angle
|
@@ -178,65 +178,65 @@ class Angle
|
|
178
178
|
|
179
179
|
alias to_rad to_radians
|
180
180
|
alias to_r to_radians
|
181
|
-
|
181
|
+
|
182
182
|
# Returns @angle as decimal_degrees
|
183
183
|
# @return [Array] of signed Floats: degrees,minutes,seconds
|
184
184
|
#Nb. * That min will be negative if the angle is negative and deg == 0
|
185
185
|
# * That sec will be negative if the angle is negative and deg == 0 && min == 0
|
186
|
-
def to_dms
|
186
|
+
def to_dms
|
187
187
|
d = to_degrees.abs
|
188
188
|
deg = d.floor
|
189
189
|
min = ((d-deg)*60).floor
|
190
|
-
sec = ((d-deg-min/60.0)*3600.0)
|
191
|
-
|
190
|
+
sec = ((d-deg-min/60.0)*3600.0)
|
191
|
+
|
192
192
|
if @angle < 0
|
193
193
|
if deg == 0
|
194
194
|
if min == 0
|
195
195
|
sec = -sec
|
196
196
|
else
|
197
|
-
min = -min
|
197
|
+
min = -min
|
198
198
|
end
|
199
199
|
else
|
200
200
|
deg = -deg
|
201
201
|
end
|
202
202
|
end
|
203
|
-
|
203
|
+
|
204
204
|
return deg, min, sec
|
205
205
|
end
|
206
|
-
|
206
|
+
|
207
207
|
# @return [Float] the angle in radians as a float (equivalent to to_radians)
|
208
208
|
alias to_f to_radians
|
209
|
-
|
209
|
+
|
210
210
|
# @return [Fixnum] the angle truncated to an integer, in radians.
|
211
211
|
def to_i
|
212
212
|
to_radians.to_i
|
213
213
|
end
|
214
|
-
|
214
|
+
|
215
215
|
# @return [Fixnum] the angle truncated to an integer, in radians.
|
216
216
|
alias to_int to_i
|
217
|
-
|
217
|
+
|
218
218
|
# @return [Array] the angle parameter as a Float and the @angle parameter from this class.
|
219
219
|
# @param [Numeric] angle
|
220
220
|
def coerce(angle)
|
221
221
|
[Float(angle), @angle]
|
222
222
|
end
|
223
|
-
|
223
|
+
|
224
224
|
# @return [Fixnum] the sign of the angle. 1 for positive, -1 for negative.
|
225
225
|
def sign
|
226
226
|
@angle.sign
|
227
227
|
end
|
228
|
-
|
228
|
+
|
229
229
|
# @return [Float] the absolute angle of the angle in radians
|
230
230
|
def abs
|
231
231
|
@angle.abs
|
232
232
|
end
|
233
|
-
|
233
|
+
|
234
234
|
# @return [Float] angle as compass bearing in radians.
|
235
235
|
#Compass bearings are clockwise, Math angles are counter clockwise.
|
236
236
|
def to_bearing
|
237
237
|
self.class.new(Math::PI * 2 - @angle,true)
|
238
238
|
end
|
239
|
-
|
239
|
+
|
240
240
|
# @return [Float] the reverse angle in radians. i.e. angle + PI (or angle + 180 degrees)
|
241
241
|
def reverse
|
242
242
|
if (angle = @angle + Math::PI) > Math::PI * 2
|
@@ -244,20 +244,20 @@ class Angle
|
|
244
244
|
end
|
245
245
|
return self.class.new(angle,true)
|
246
246
|
end
|
247
|
-
|
248
|
-
|
247
|
+
|
248
|
+
|
249
249
|
# @return [String] angle in radians as a string.
|
250
250
|
# @param [String] fmt Optional format string passed to Angle#strf
|
251
251
|
def to_s(fmt = nil)
|
252
252
|
return to_radians.to_s if(fmt == nil)
|
253
253
|
return strf(fmt)
|
254
254
|
end
|
255
|
-
|
255
|
+
|
256
256
|
#formated output of the angle.
|
257
257
|
# @param [String] fmt The default format is a signed deg minutes'seconds" with leading 0's in the minutes and seconds and 4 decimal places for seconds.
|
258
258
|
#formats are:
|
259
259
|
# * %wd output the degrees as an integer.
|
260
|
-
# ** where w is 0, 1, 2 or 3 and represents the field width.
|
260
|
+
# ** where w is 0, 1, 2 or 3 and represents the field width.
|
261
261
|
# *** 1 is the default, which indicates that at least 1 digit is displayed
|
262
262
|
# *** 2 indicates that at least 2 digits are displayed. 1 to 9 will be displayed as 01 0 to 09 0
|
263
263
|
# *** 3 indicates that at least 4 digits are displayed. 10 to 99 will be displayed as 010 0 to 099 0
|
@@ -271,8 +271,8 @@ class Angle
|
|
271
271
|
# * %w.pM outputs minutes as a float .e.g. 01.125'.
|
272
272
|
# * p is the number of decimal places.
|
273
273
|
#
|
274
|
-
# * %wW outputs secs/60 as a float without the leading '0.'.
|
275
|
-
# Used with %m like this %2m'%4W , to get minute marker before the decimal places.
|
274
|
+
# * %wW outputs secs/60 as a float without the leading '0.'.
|
275
|
+
# Used with %m like this %2m'%4W , to get minute marker before the decimal places.
|
276
276
|
# e.g. -37 001'.1167 rather than -37 001.1167'
|
277
277
|
# * p is the number of decimal places.
|
278
278
|
#
|
@@ -314,7 +314,7 @@ class Angle
|
|
314
314
|
when /[NE]/
|
315
315
|
have_dir = true
|
316
316
|
format = t2
|
317
|
-
when '.'
|
317
|
+
when '.'
|
318
318
|
had_dot = true
|
319
319
|
when /[Dr]/
|
320
320
|
format = t2
|
@@ -339,7 +339,7 @@ class Angle
|
|
339
339
|
when 'd'
|
340
340
|
s += s_int(deg, t[1], have_dir)
|
341
341
|
when 'D'
|
342
|
-
s += s_float(@angle.
|
342
|
+
s += s_float(@angle.to_deg, t[1], t[2], have_dir)
|
343
343
|
when 'm'
|
344
344
|
s += s_int(min, t[1], have_dir)
|
345
345
|
when 'M'
|
@@ -359,11 +359,11 @@ class Angle
|
|
359
359
|
s += t[1] #the fillers.
|
360
360
|
end
|
361
361
|
end
|
362
|
-
|
362
|
+
|
363
363
|
return s
|
364
364
|
end
|
365
|
-
|
366
|
-
private
|
365
|
+
|
366
|
+
private
|
367
367
|
#Output angle_dec as a string to the number of decimal places specified by places.
|
368
368
|
#Assumes the angle is 0 <= angle_dec < 1
|
369
369
|
#No leading '0' is output. The string starts with a '.' if places is non-zero.
|
@@ -379,7 +379,7 @@ class Angle
|
|
379
379
|
end
|
380
380
|
end
|
381
381
|
|
382
|
-
#return the angle as a string with fixed width decimal portion with leading 0s
|
382
|
+
#return the angle as a string with fixed width decimal portion with leading 0s
|
383
383
|
#to get at least the width specified
|
384
384
|
#Prints the number of places after the decimal point rounded to places places.
|
385
385
|
#-1 width means no width format
|
@@ -408,7 +408,7 @@ class Angle
|
|
408
408
|
s = (abs == false && angle.sign == -1) ? '-' : '' #catch the case of -0
|
409
409
|
s += f % angle.abs
|
410
410
|
end
|
411
|
-
|
411
|
+
|
412
412
|
#Return the fractional part of angle as a string,
|
413
413
|
#to the number of decimal places specified by 'places'.
|
414
414
|
#No leading '0' is output. The string starts with a '.'
|
@@ -421,12 +421,3 @@ class Angle
|
|
421
421
|
s_places(angle_dec, places)
|
422
422
|
end
|
423
423
|
end
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
data/lib/coordinate.rb
CHANGED
@@ -1,37 +1,37 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
require_relative 'angle.rb'
|
3
3
|
|
4
4
|
#Holds the latitude, longitude, and the altitude for the coordinate
|
5
5
|
class Coordinate
|
6
6
|
# @return [Latitude]
|
7
|
-
attr_accessor :latitude
|
7
|
+
attr_accessor :latitude
|
8
8
|
# @return [Longitude]
|
9
|
-
attr_accessor :longitude
|
9
|
+
attr_accessor :longitude
|
10
10
|
# @return [Numeric]
|
11
|
-
attr_accessor :altitude
|
12
|
-
|
11
|
+
attr_accessor :altitude
|
12
|
+
|
13
13
|
#latitude and longitude can be Strings or Numeric, or anything else with to_radians and to_f
|
14
14
|
#latitude and longitude are in degrees unless radians == true (or set to :radians)
|
15
15
|
def initialize(latitude=0, longitude=0, altitude=0, radians = false)
|
16
|
-
@latitude = Latitude.new(latitude,radians)
|
16
|
+
@latitude = Latitude.new(latitude,radians)
|
17
17
|
@longitude = Longitude.new(longitude,radians)
|
18
18
|
@altitude = altitude.to_f
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
# @return [String] Latitude longitude and altitude as a single space separated string.
|
22
22
|
def to_s
|
23
23
|
"#{@latitude.to_s } #{@longitude.to_s} #{@altitude}m"
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
# @return [Latitude, Longitude, Float] with members, latitude, longitude and altitude
|
27
27
|
def to_ary
|
28
28
|
[ @latitude, @longitude, @altitude ]
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
alias to_a to_ary
|
32
|
-
|
32
|
+
|
33
33
|
# @return [Hash] with keys :latitude, :longitude, and :altitude
|
34
34
|
def to_hash
|
35
35
|
{ :latitude => @latitude, :longitude => @longitude, :altitude => @altitude }
|
36
36
|
end
|
37
|
-
end
|
37
|
+
end
|
data/lib/latitude.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
require_relative 'angle.rb'
|
3
3
|
|
4
4
|
#Subclass of Angle to add in special treatment of to_d, to_r , to_s
|
5
5
|
#Latitude degrees are between -PI and PI, South to North (+/- 90 degrees)
|
6
6
|
|
7
7
|
class Latitude < Angle
|
8
|
-
|
8
|
+
|
9
9
|
# @return [Float] angle as degrees in range -90 and 90
|
10
10
|
def to_degrees
|
11
11
|
degrees = super
|
@@ -17,7 +17,7 @@ class Latitude < Angle
|
|
17
17
|
else degrees
|
18
18
|
end
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
# @return [Float] angle as degrees in range -PI and PI
|
22
22
|
def to_radians
|
23
23
|
case
|
@@ -35,10 +35,10 @@ class Latitude < Angle
|
|
35
35
|
def to_s(fmt="%2d %2m'%2.4s\"%N")
|
36
36
|
super(fmt)
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
alias to_r to_radians
|
40
40
|
alias to_rad to_radians
|
41
41
|
#alias to_d to_degrees
|
42
42
|
alias to_deg to_degrees
|
43
43
|
|
44
|
-
end
|
44
|
+
end
|
data/lib/longitude.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
require_relative 'angle.rb'
|
3
3
|
|
4
4
|
#Subclass of Angle to add in special treatment of to_d, to_r and to_s
|
5
5
|
#Longitude degrees are between -2PI and 2PI, West to East (+/- 180 degrees)
|
6
6
|
|
7
7
|
class Longitude < Angle
|
8
|
-
|
8
|
+
|
9
9
|
# @return [Float] angle as degrees in range -180 and 180
|
10
10
|
def to_degrees
|
11
11
|
degrees = super
|
@@ -14,7 +14,7 @@ class Longitude < Angle
|
|
14
14
|
else degrees
|
15
15
|
end
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
# @return [Float] angle as degrees in range -2PI and 2PI
|
19
19
|
def to_radians
|
20
20
|
case
|
@@ -22,17 +22,17 @@ class Longitude < Angle
|
|
22
22
|
else @angle
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
# @return [String] angle as string in degrees minutes seconds direction.
|
27
27
|
#A West angle is negative, East is Positive.
|
28
28
|
# @param [String] fmt Optional format string passed to Angle#to_s
|
29
29
|
def to_s(fmt="%3d %2m'%2.4s\"%E")
|
30
30
|
super(fmt)
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
alias to_r to_radians
|
34
34
|
alias to_rad to_radians
|
35
35
|
#alias to_d to_degrees
|
36
36
|
alias to_deg to_degrees
|
37
|
-
|
38
|
-
end
|
37
|
+
|
38
|
+
end
|
data/lib/track_and_distance.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
require_relative 'angle.rb'
|
3
3
|
|
4
4
|
#Holds a bearing and distance
|
5
5
|
class TrackAndDistance
|
@@ -7,7 +7,7 @@ class TrackAndDistance
|
|
7
7
|
attr_accessor :bearing
|
8
8
|
# @return [Float]
|
9
9
|
attr_accessor :distance
|
10
|
-
|
10
|
+
|
11
11
|
# @param [String, Numeric, #to_radian, #to_f] Bearing can be a String or Numeric or any object with to_radians and to_f
|
12
12
|
# @param [Numeric] distance
|
13
13
|
# @param [true,false, :radians] radians Bearing is in degrees unless radians == true (or set to :radians).
|
@@ -15,7 +15,7 @@ class TrackAndDistance
|
|
15
15
|
@bearing = Angle.new(bearing, radians)
|
16
16
|
@distance = distance
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
#format string fmt is currently just for the bearing angle.
|
20
20
|
#Need to change this to include the distance is single format string.
|
21
21
|
# @return [String] Bearing angle and distance in meters.
|
@@ -33,9 +33,9 @@ class TrackAndDistance
|
|
33
33
|
def to_ary
|
34
34
|
[ @bearing, @distance ]
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
# @return [Hash] with keys :bearing and :distance
|
38
38
|
def to_hash
|
39
39
|
{ :bearing => @bearing, :distance => @distance }
|
40
40
|
end
|
41
|
-
end
|
41
|
+
end
|
data/lib/vincenty.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
7
|
|
8
8
|
#Vincenty's algorithms for finding the bearing and distance between two coordinates and
|
9
9
|
#for finding the latitude and longitude, given a start coordinate, distance and bearing.
|
@@ -12,28 +12,38 @@ require 'coordinate.rb'
|
|
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.
|
16
|
-
|
15
|
+
VERSION = '1.0.8'
|
16
|
+
|
17
17
|
# @return [String] constant VERSION
|
18
18
|
def version
|
19
19
|
VERSION
|
20
20
|
end
|
21
|
-
|
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
|
+
|
22
27
|
#Great Circle formulae http://en.wikipedia.org/wiki/Great-circle_distance
|
23
|
-
#Reference calculation for testing, assumes the earth is a sphere, which it isn't.
|
28
|
+
#Reference calculation for testing, assumes the earth is a sphere, which it isn't.
|
24
29
|
#This gives us an approximation to verify Vincenty algorithm.
|
25
30
|
# @param [Coordinate] p2 is target coordinate that we want the bearing to.
|
26
31
|
# @return [TrackAndDistance] with the compass bearing and distance in meters to P2
|
27
|
-
def sphericalDistanceAndAngle( p2 )
|
28
|
-
|
29
|
-
|
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
|
30
40
|
r = (a+b)/2 #average diametre as a rough estimate for our tests.
|
31
|
-
|
41
|
+
|
32
42
|
sin_lat1 = Math.sin(@latitude.to_rad)
|
33
43
|
sin_lat2 = Math.sin(p2.latitude.to_rad)
|
34
44
|
cos_lat1 = Math.cos(@latitude.to_rad)
|
35
45
|
atan1_2 = Math.atan(1) * 2
|
36
|
-
t1 = cos_lat1 * Math.cos(p2.latitude.to_rad) * ( Math.cos(@longitude.to_rad - p2.longitude.to_rad) ) + sin_lat1 * sin_lat2
|
46
|
+
t1 = cos_lat1 * Math.cos(p2.latitude.to_rad) * ( Math.cos(@longitude.to_rad - p2.longitude.to_rad) ) + sin_lat1 * sin_lat2
|
37
47
|
angular_distance = Math.atan(-t1/Math.sqrt(-t1 * t1 +1)) + atan1_2 #central angle in radians so we can calculate the arc length.
|
38
48
|
|
39
49
|
t2 = (sin_lat2 - sin_lat1 * Math.cos(angular_distance)) / (cos_lat1 * Math.sin(angular_distance))
|
@@ -43,7 +53,7 @@ class Vincenty < Coordinate
|
|
43
53
|
bearing = Math.atan(-t2 / Math.sqrt(-t2 * t2 + 1)) + atan1_2 #Compass Bearing in radians (clockwise)
|
44
54
|
end
|
45
55
|
|
46
|
-
#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.
|
47
57
|
return TrackAndDistance.new(bearing, angular_distance * r, true)
|
48
58
|
end
|
49
59
|
|
@@ -52,11 +62,15 @@ class Vincenty < Coordinate
|
|
52
62
|
# @param [Coordinate] p2 is target coordinate that we want the bearing to.
|
53
63
|
# @return [TrackAndDistance] with the compass bearing and distance in meters to P2
|
54
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
|
+
|
55
69
|
# a, b = major & minor semiaxes of the ellipsoid
|
56
|
-
a = 6378137 #equatorial radius in meters (+/-2 m)
|
70
|
+
a = 6378137 #equatorial radius in meters (+/-2 m)
|
57
71
|
b = 6356752.31424518 #polar radius in meters
|
58
|
-
f = (a-b)/a # flattening
|
59
|
-
|
72
|
+
f = (a-b)/a # flattening
|
73
|
+
|
60
74
|
lat1 = @latitude.to_rad
|
61
75
|
lon1 = @longitude.to_rad
|
62
76
|
lat2 = p2.latitude.to_rad
|
@@ -65,7 +79,7 @@ class Vincenty < Coordinate
|
|
65
79
|
lat2 = lat2.sign * (Math::PI/2-(1e-10)) if (Math::PI/2-lat2.abs).abs < 1.0e-10
|
66
80
|
|
67
81
|
# lat1, lat2 = geodetic latitude
|
68
|
-
|
82
|
+
|
69
83
|
l = (lon2 - lon1).abs #difference in longitude
|
70
84
|
l = 2*Math::PI - l if l > Math::PI
|
71
85
|
u1 = Math.atan( ( 1 - f) * Math.tan( lat1 ) ) #U is 'reduced latitude'
|
@@ -74,7 +88,7 @@ class Vincenty < Coordinate
|
|
74
88
|
cos_u1 = Math.cos(u1)
|
75
89
|
sin_u2 = Math.sin(u2)
|
76
90
|
cos_u2 = Math.cos(u2)
|
77
|
-
|
91
|
+
|
78
92
|
lambda_v = l
|
79
93
|
lambda_dash = Math::PI * 2
|
80
94
|
while( (lambda_v - lambda_dash).abs > 1.0e-12 ) #i.e. 0.06 mm error
|
@@ -83,8 +97,8 @@ class Vincenty < Coordinate
|
|
83
97
|
sin_sigma = Math.sqrt( ( cos_u2 * sin_lambda_v ) ** 2 + ( cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v ) ** 2 )
|
84
98
|
cos_sigma = sin_u1 * sin_u2 + cos_u1 * cos_u2 * cos_lambda_v
|
85
99
|
sigma = Math.atan2(sin_sigma, cos_sigma)
|
86
|
-
sin_alpha= cos_u1 * cos_u2 * sin_lambda_v / sin_sigma
|
87
|
-
cos_2_alpha = 1 - sin_alpha * sin_alpha #trig identity
|
100
|
+
sin_alpha= cos_u1 * cos_u2 * sin_lambda_v / sin_sigma
|
101
|
+
cos_2_alpha = 1 - sin_alpha * sin_alpha #trig identity
|
88
102
|
cos_2_sigma_m = cos_sigma - 2 * sin_u1 * sin_u2/cos_2_alpha
|
89
103
|
c = f / 16 * cos_2_alpha * (4 + f*(4-3*cos_2_alpha))
|
90
104
|
lambda_dash = lambda_v
|
@@ -102,7 +116,7 @@ class Vincenty < Coordinate
|
|
102
116
|
s = b * a1 * (sigma - delta_sigma)
|
103
117
|
sin_lambda_v = Math.sin(lambda_v)
|
104
118
|
cos_lambda_v = Math.cos(lambda_v)
|
105
|
-
|
119
|
+
|
106
120
|
#This test isn't in original formulae, and fixes the problem of all angles returned being between 0 - PI (0-180)
|
107
121
|
#Also converts the result to compass bearing, rather than the mathmatical anticlockwise angles.
|
108
122
|
if(Math.sin(p2.longitude.to_rad - @longitude.to_rad) < 0)
|
@@ -112,7 +126,7 @@ class Vincenty < Coordinate
|
|
112
126
|
alpha_1 = Math.atan2( cos_u2 * sin_lambda_v, cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v)
|
113
127
|
#alpha_2 = Math.atan2(cos_u1 * sin_lambda_v, -sin_u1 * cos_u2 + cos_u1 * sin_u2 * cos_lambda_v)
|
114
128
|
end
|
115
|
-
|
129
|
+
|
116
130
|
#Note that the bearing is a compass (i.e. clockwise) angle.
|
117
131
|
return TrackAndDistance.new(alpha_1, s, true) #What to do with alpha_2?
|
118
132
|
end
|
@@ -122,10 +136,10 @@ class Vincenty < Coordinate
|
|
122
136
|
# @param [TrackAndDistance] track_and_distance specifying bearing and distance.
|
123
137
|
# @return [Vincenty] with the destination coordinates.
|
124
138
|
def sphereDestination( track_and_distance )
|
125
|
-
a = 6378137 #equatorial radius in meters (+/-2 m)
|
139
|
+
a = 6378137 #equatorial radius in meters (+/-2 m)
|
126
140
|
b = 6356752.31424518 #polar radius in meters
|
127
141
|
r = (a+b)/2 #average diametre as a rough estimate for our tests.
|
128
|
-
|
142
|
+
|
129
143
|
d = track_and_distance.distance.abs
|
130
144
|
sin_dor = Math.sin(d/r)
|
131
145
|
cos_dor = Math.cos(d/r)
|
@@ -133,7 +147,7 @@ class Vincenty < Coordinate
|
|
133
147
|
cos_lat1 = Math.cos(@latitude.to_rad)
|
134
148
|
lat2 = Math.asin( sin_lat1 * cos_dor + cos_lat1 * sin_dor * Math.cos(track_and_distance.bearing.to_rad) )
|
135
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))
|
136
|
-
|
150
|
+
|
137
151
|
Vincenty.new(lat2, lon2, 0, true);
|
138
152
|
end
|
139
153
|
|
@@ -142,17 +156,17 @@ class Vincenty < Coordinate
|
|
142
156
|
#Assumes earth is a WGS-84 Ellipsod.
|
143
157
|
# @param [TrackAndDistance] specifying bearing and distance.
|
144
158
|
# @return [Vincenty] with the destination coordinates.
|
145
|
-
def destination( track_and_distance )
|
159
|
+
def destination( track_and_distance )
|
146
160
|
# a, b = major & minor semiaxes of the ellipsoid
|
147
|
-
a = 6378137 #equatorial radius in meters (+/-2 m)
|
161
|
+
a = 6378137 #equatorial radius in meters (+/-2 m)
|
148
162
|
b = 6356752.31424518 #polar radius in meters
|
149
|
-
f = (a-b)/a # flattening
|
150
|
-
|
163
|
+
f = (a-b)/a # flattening
|
164
|
+
|
151
165
|
s = track_and_distance.distance.abs;
|
152
166
|
alpha1 = track_and_distance.bearing.to_rad
|
153
167
|
sin_alpha1 = Math.sin(alpha1)
|
154
168
|
cos_alpha1 = Math.cos(alpha1)
|
155
|
-
|
169
|
+
|
156
170
|
tanU1 = (1-f) * Math.tan(@latitude.to_rad);
|
157
171
|
cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1))
|
158
172
|
sinU1 = tanU1 * cosU1
|
@@ -162,7 +176,7 @@ class Vincenty < Coordinate
|
|
162
176
|
u_2 = cos_2_alpha * (a * a - b * b) / (b * b)
|
163
177
|
a1 = 1 + u_2/16384 * (4096 + u_2 * (-768 + u_2 * (320-175 * u_2)))
|
164
178
|
b1 = u_2/1024 * (256 + u_2 * (-128 + u_2 * (74-47 * u_2)))
|
165
|
-
|
179
|
+
|
166
180
|
sigma = s / (b * a1)
|
167
181
|
sigma_dash = 2 * Math::PI
|
168
182
|
while ((sigma-sigma_dash).abs > 1.0e-12) #i.e 0.06mm
|
@@ -181,12 +195,9 @@ class Vincenty < Coordinate
|
|
181
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
|
182
196
|
|
183
197
|
#sigma2 = Math.atan2(sin_alpha, -tmp) # reverse azimuth
|
184
|
-
|
198
|
+
|
185
199
|
return Vincenty.new(lat2, @longitude + l, 0, true);
|
186
200
|
end
|
187
201
|
|
188
202
|
|
189
203
|
end
|
190
|
-
|
191
|
-
|
192
|
-
|
data/test/ts_all.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
|
8
9
|
puts "Testing from source"
|
9
10
|
puts Vincenty.new.version
|
10
|
-
|
11
|
-
|
data/test/ts_angle.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
|
2
|
+
require_relative '../lib/vincenty.rb'
|
3
3
|
|
4
4
|
class TestAngle< Test::Unit::TestCase
|
5
5
|
#test Angle creation
|
@@ -28,7 +28,7 @@ class TestAngle< Test::Unit::TestCase
|
|
28
28
|
assert_equal(Angle.decimal_deg_from_ary([1,5,4,'W']), -(1.0 + 5/60.0 + 4/3600.0))
|
29
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.
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def test_strf
|
33
33
|
a = Angle.new("S37 01'7.5\"")
|
34
34
|
assert_equal("-37 01'07.5000\"", a.strf) #default format of strf
|
@@ -50,7 +50,7 @@ class TestAngle< Test::Unit::TestCase
|
|
50
50
|
|
51
51
|
def test_operators
|
52
52
|
#Comparable.
|
53
|
-
assert_equal(Angle.radians(-0.646099072472651), Angle.radians(-0.646099072472651)) #<=>
|
53
|
+
assert_equal(Angle.radians(-0.646099072472651), Angle.radians(-0.646099072472651)) #<=>
|
54
54
|
#unary-op Angle
|
55
55
|
assert_equal(+Angle.radians(-0.646099072472651), Angle.radians(-0.646099072472651)) #unary +
|
56
56
|
assert_equal(-Angle.radians(-0.646099072472651), Angle.radians(0.646099072472651)) #unary -
|
@@ -67,7 +67,7 @@ class TestAngle< Test::Unit::TestCase
|
|
67
67
|
assert_equal(6, 3 * Angle.radians(2) ) # *
|
68
68
|
assert_equal(2, 4 / Angle.radians(2) ) # /
|
69
69
|
#Angle op Angle
|
70
|
-
assert_equal(Angle.radians(3.2+2.1), Angle.radians(3.2) + Angle.radians(2.1) ) # +
|
70
|
+
assert_equal(Angle.radians(3.2+2.1), Angle.radians(3.2) + Angle.radians(2.1) ) # +
|
71
71
|
#Sign method.
|
72
72
|
assert_equal(1, Angle.radians(3).sign)
|
73
73
|
assert_equal(-1, Angle.radians(-3).sign)
|
@@ -78,4 +78,4 @@ class TestAngle< Test::Unit::TestCase
|
|
78
78
|
#bearing
|
79
79
|
assert_equal(Angle.degrees(340), Angle.degrees(20).to_bearing)
|
80
80
|
end
|
81
|
-
end
|
81
|
+
end
|
data/test/ts_coordinate.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
|
2
|
+
require_relative '../lib/vincenty.rb'
|
3
3
|
|
4
|
-
class
|
4
|
+
class TestCoordinate< Test::Unit::TestCase
|
5
5
|
#test Coordinate
|
6
6
|
def test_coordinate
|
7
7
|
c = Coordinate.new(-36.9923293459124, 174.485341187381,13.5)
|
@@ -15,4 +15,4 @@ class TestAngle< Test::Unit::TestCase
|
|
15
15
|
assert_equal(13.5, ch[:altitude])
|
16
16
|
assert_equal("36 59'32.3856\"S 174 29'07.2283\"E 13.5m", c.to_s)
|
17
17
|
end
|
18
|
-
end
|
18
|
+
end
|
data/test/ts_latitude.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
|
2
|
+
require_relative '../lib/vincenty.rb'
|
3
3
|
|
4
|
-
class
|
4
|
+
class TestLatitude< Test::Unit::TestCase
|
5
5
|
def test_strf
|
6
6
|
assert_equal("37 01'07.5000\"S", Latitude.new("S37 01'7.5\"").to_s)
|
7
7
|
assert_equal("37 01'07.5000\"S", Latitude.new("-37 01'7.5\"").to_s)
|
@@ -28,4 +28,4 @@ class TestAngle< Test::Unit::TestCase
|
|
28
28
|
assert_equal(89, Latitude.degrees(89).to_deg)
|
29
29
|
assert_equal(89, Latitude.degrees(91).to_deg)
|
30
30
|
end
|
31
|
-
end
|
31
|
+
end
|
data/test/ts_longitude.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
|
2
|
+
require_relative '../lib/vincenty.rb'
|
3
3
|
|
4
|
-
class
|
4
|
+
class TestLongitude< Test::Unit::TestCase
|
5
5
|
def test_strf
|
6
6
|
assert_equal("037 01'07.5000\"W", Longitude.new("W37 01'7.5\"").to_s)
|
7
7
|
assert_equal("037 01'07.5000\"W", Longitude.new("-37 01'7.5\"").to_s)
|
@@ -22,4 +22,4 @@ class TestAngle< Test::Unit::TestCase
|
|
22
22
|
assert_equal(-135, Longitude.degrees(225).to_deg)
|
23
23
|
assert_equal(-45, Longitude.degrees(315).to_deg)
|
24
24
|
end
|
25
|
-
end
|
25
|
+
end
|
@@ -1,16 +1,19 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
|
2
|
+
require_relative '../lib/vincenty.rb'
|
3
3
|
|
4
|
-
class
|
4
|
+
class TestTrackAndDistance< Test::Unit::TestCase
|
5
5
|
#test TrackAndDistance
|
6
6
|
def test_track_and_distance
|
7
7
|
assert_equal("140 14'10.0000\" 12.0m", TrackAndDistance.new(Angle.new("320,14,10").reverse, 12.0).to_s)
|
8
8
|
assert_equal("215 03'00.0000\" 19.73m", TrackAndDistance.new("215,3,0", 19.73 ).to_s)
|
9
|
+
|
9
10
|
a = TrackAndDistance.new("215,3,0", 19.73 ).to_ary
|
10
11
|
assert_equal("215 03'00.0000\"", a[0].strf)
|
11
12
|
assert_equal("19.73", a[1].to_s)
|
13
|
+
|
12
14
|
a = TrackAndDistance.new("215,3,0", 19.73 ).to_hash
|
13
15
|
assert_equal("215 03'00.0000\"", a[:bearing].strf)
|
14
16
|
assert_equal("19.73", a[:distance].to_s)
|
17
|
+
|
15
18
|
end
|
16
19
|
end
|
data/test/ts_vincenty.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
|
2
|
+
require_relative '../lib/vincenty.rb'
|
3
3
|
|
4
4
|
class TestVincenty< Test::Unit::TestCase
|
5
5
|
|
6
6
|
def initialize(x)
|
7
7
|
super(x)
|
8
|
-
|
9
|
-
@path = [ #Path starting at peg by kanaka at end of drive
|
8
|
+
|
9
|
+
@path = [ #Path starting at peg by kanaka at end of drive
|
10
10
|
TrackAndDistance.new("215,3,0", 19.73 ) ,
|
11
11
|
TrackAndDistance.new(Angle.new("320,14,10").reverse, 12.0), #Note don't need to add the radians=true argument as Angle has to_radians function
|
12
12
|
TrackAndDistance.new(Angle.new("281,44,40").reverse, 35.23 ),
|
@@ -32,20 +32,20 @@ class TestVincenty< Test::Unit::TestCase
|
|
32
32
|
Vincenty.new(-36.9917932943506, 174.485664544705),
|
33
33
|
Vincenty.new(-36.9920268289562, 174.485617028991),
|
34
34
|
Vincenty.new(-36.9921837292671, 174.485468381511),
|
35
|
-
]
|
35
|
+
]
|
36
36
|
end
|
37
37
|
|
38
38
|
#The path in @path was entered from the property survey map, with distance and bearings which should form a closed loop
|
39
39
|
#verified on google map of my property by creating a KML file and loading the map over the satellite image and checking the
|
40
|
-
#coordinates in google earth, and visually checking the route created was a closed loop (it was with a tiny error).
|
40
|
+
#coordinates in google earth, and visually checking the route created was a closed loop (it was with a tiny error).
|
41
41
|
def test_vincenty_destination
|
42
|
-
start = Vincenty.new(-36.9921838030711, 174.485468469841)
|
42
|
+
start = Vincenty.new(-36.9921838030711, 174.485468469841)
|
43
43
|
|
44
44
|
next_p = start
|
45
45
|
# print "Start at coordinate #{next_p.longitude.to_deg}, #{next_p.latitude.to_deg}\n"
|
46
46
|
@path.each_with_index do |leg,i|
|
47
47
|
next_p, spherical_ans = next_p.destination( leg ) , next_p.sphereDestination(leg)
|
48
|
-
|
48
|
+
|
49
49
|
assert_equal(@waypoints[i].longitude.to_deg.round(12), next_p.longitude.to_deg.round(12))
|
50
50
|
assert_equal(@waypoints[i].latitude.to_deg.round(12), next_p.latitude.to_deg.round(12))
|
51
51
|
# print "Expect #{waypoints[i].longitude.to_deg.round(4)}, #{waypoints[i].latitude.to_deg.round(4)}\n"
|
@@ -56,18 +56,18 @@ class TestVincenty< Test::Unit::TestCase
|
|
56
56
|
# assert_equal(0, next_p.distanceAndAngle(start).distance)
|
57
57
|
# puts "distance from end to start should be 0. Actual #{next_p.distanceAndAngle(start)}"
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
#The waypoints are the latitudes and longitudes of the corners of my property.
|
61
61
|
#The resulting bearing and distances between them should match those in @path.
|
62
62
|
def test_vincenty_distance_and_angle
|
63
|
-
start = Vincenty.new(-36.9921838030711, 174.485468469841)
|
63
|
+
start = Vincenty.new(-36.9921838030711, 174.485468469841)
|
64
64
|
next_p = start
|
65
65
|
# print "\nReverse test, c\n"
|
66
66
|
# print "Start at coordinate #{next_p.longitude.to_deg}, #{next_p.latitude.to_deg}\n"
|
67
67
|
@waypoints.each_with_index do |point,i|
|
68
|
-
vtrack_and_bearing = next_p.distanceAndAngle( point )
|
68
|
+
vtrack_and_bearing = next_p.distanceAndAngle( point )
|
69
69
|
# strack_and_bearing = next_p.sphericalDistanceAndAngle( point )
|
70
|
-
|
70
|
+
|
71
71
|
assert_equal(@path[i].bearing.to_deg.round(4), vtrack_and_bearing.bearing.to_deg.round(4))
|
72
72
|
assert_equal(@path[i].distance.round(4), vtrack_and_bearing.distance.round(4))
|
73
73
|
# print "Expected #{path[i].bearing.to_deg.round(4)}(#{((path[i].bearing.to_deg+180)%360).round(4)}), #{path[i].distance.round(4)}m\n"
|
@@ -79,18 +79,27 @@ class TestVincenty< Test::Unit::TestCase
|
|
79
79
|
# assert_equal(0, next_p.distanceAndAngle(start).distance)
|
80
80
|
# puts "distance from end to start should be 0. Actual #{next_p.distanceAndAngle(start)}\n"
|
81
81
|
end
|
82
|
-
|
82
|
+
|
83
|
+
#Edge case, when points are at same location
|
84
|
+
def test_vincenty_distance_and_angle_when_0
|
85
|
+
start = Vincenty.new(-36.9921838030711, 174.485468469841)
|
86
|
+
vtrack_and_bearing = start.distanceAndAngle( start )
|
87
|
+
|
88
|
+
assert_equal(0, vtrack_and_bearing.bearing.to_deg.round(4))
|
89
|
+
assert_equal(0, vtrack_and_bearing.distance.round(4))
|
90
|
+
end
|
91
|
+
|
83
92
|
#Run the Australian Geoscience site example.
|
84
93
|
def test_geoscience_au
|
85
94
|
flindersPeak = Vincenty.new("-37 57'3.72030″", "144 25'29.52440″" )
|
86
95
|
buninyong = Vincenty.new("-37 39 ' 10.15610 ''", "143 55 ' 35.38390 ''") #Buninyong
|
87
|
-
track_and_bearing = flindersPeak.distanceAndAngle( buninyong )
|
96
|
+
track_and_bearing = flindersPeak.distanceAndAngle( buninyong )
|
88
97
|
assert_equal(Angle.new("306 52 ' 5.37 ''").to_deg.round(4), track_and_bearing.bearing.to_deg.round(4))
|
89
98
|
assert_equal(54972.271, track_and_bearing.distance.round(3))
|
90
|
-
|
99
|
+
|
91
100
|
destination = flindersPeak.destination(TrackAndDistance.new("306 52 ' 5.37 ''", 54972.271))
|
92
101
|
assert_equal(buninyong.latitude.to_deg.round(4), destination.latitude.to_deg.round(4))
|
93
102
|
assert_equal(buninyong.longitude.to_deg.round(4), destination.longitude.to_deg.round(4))
|
94
103
|
end
|
95
|
-
|
96
|
-
end
|
104
|
+
|
105
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vincenty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Burrowes
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hoe-yard
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '3.
|
33
|
+
version: '3.15'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '3.
|
40
|
+
version: '3.15'
|
41
41
|
description: "* Vincenty wrote an algorithm for calculating the bearing and distance
|
42
42
|
between two coordinates on the earth\n and an algorithm for finding a second coordinate,
|
43
43
|
given a starting coordinate, bearing and destination.\n The algorithms model the
|
@@ -70,7 +70,6 @@ extra_rdoc_files:
|
|
70
70
|
- Manifest.txt
|
71
71
|
- README.md
|
72
72
|
files:
|
73
|
-
- ".gemtest"
|
74
73
|
- History.txt
|
75
74
|
- Manifest.txt
|
76
75
|
- README.md
|
@@ -115,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
114
|
version: '0'
|
116
115
|
requirements: []
|
117
116
|
rubyforge_project:
|
118
|
-
rubygems_version: 2.
|
117
|
+
rubygems_version: 2.5.1
|
119
118
|
signing_key:
|
120
119
|
specification_version: 4
|
121
120
|
summary: "* Vincenty wrote an algorithm for calculating the bearing and distance between
|
data/.gemtest
DELETED
File without changes
|