vincenty 1.0.4 → 1.0.9
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 +7 -0
- data/History.txt +62 -52
- data/README.md +3 -3
- data/Rakefile +17 -10
- data/lib/angle.rb +215 -203
- data/lib/coordinate.rb +24 -23
- data/lib/core_extensions.rb +67 -59
- data/lib/latitude.rb +30 -29
- data/lib/longitude.rb +22 -23
- data/lib/track_and_distance.rb +36 -32
- data/lib/vincenty.rb +153 -143
- data/test/ts_all.rb +8 -8
- data/test/ts_angle.rb +55 -54
- data/test/ts_coordinate.rb +10 -9
- data/test/ts_latitude.rb +20 -17
- data/test/ts_longitude.rb +14 -11
- data/test/ts_track_and_distance.rb +12 -9
- data/test/ts_vincenty.rb +80 -71
- metadata +33 -39
- data/.gemtest +0 -0
data/lib/angle.rb
CHANGED
@@ -1,246 +1,259 @@
|
|
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
|
7
7
|
# * Conversion to string as radians or DMS format
|
8
|
-
|
9
8
|
class Angle
|
10
9
|
include Comparable
|
11
|
-
|
12
|
-
|
13
|
-
attr_accessor :angle
|
14
|
-
alias
|
15
|
-
alias
|
16
|
-
|
17
|
-
#angle may be anything that has a to_f and to_radians.
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@angle = angle.to_radians #we have a String and Numeric class version of this. Another Angle will work too.
|
29
|
-
end
|
30
|
-
end
|
10
|
+
|
11
|
+
# @return [Float] stored in radians
|
12
|
+
attr_accessor :angle
|
13
|
+
alias value angle # Older version of angle used volue rather than angle
|
14
|
+
alias value= angle= # Older version of angle used volue rather than angle
|
15
|
+
|
16
|
+
# @param [#to_f,#to_radians] angle may be anything that has a to_f and to_radians.
|
17
|
+
# @param [true,false, :radians] radians Angle is in degrees unless radians == true (or set to :radians).
|
18
|
+
def initialize(angle = 0, radians = false)
|
19
|
+
# assumes that we are getting an angle in degrees.
|
20
|
+
@angle = if radians == true || radians == :radians
|
21
|
+
angle.to_f # works for String, Fixed, other Angles and for Float.
|
22
|
+
elsif angle.instance_of?(Array)
|
23
|
+
self.class.decimal_deg(*angle).to_radians
|
24
|
+
else
|
25
|
+
angle.to_radians # we have a String and Numeric class version of this. Another Angle will work too.
|
26
|
+
end
|
31
27
|
end
|
32
|
-
|
33
|
-
|
34
|
-
#Class level function that converts 4 arguments into decimal degrees.
|
35
|
-
#Dircection is one of 'N', 'S', 'E', 'W'.
|
36
|
-
# direction is optional, as you might want to specify a negative value for degrees
|
28
|
+
|
29
|
+
# Class level function that converts 4 arguments into decimal degrees.
|
37
30
|
# @return [Float] signed decimal degrees.
|
38
|
-
|
39
|
-
|
31
|
+
# @param [Numeric] degrees
|
32
|
+
# @param [Numeric] minutes
|
33
|
+
# @param [Numeric] seconds
|
34
|
+
# @param ['N', 'S', 'E', 'W'] direction Optional, as the direction might be specified by a negative value for degrees
|
35
|
+
def self.decimal_deg(degrees = 0, minutes = 0, seconds = 0, direction = 'N')
|
36
|
+
s = { 'N' => 1, 'S' => -1, 'E' => 1, 'W' => -1, 'n' => 1, 's' => -1, 'e' => 1, 'w' => -1 }
|
40
37
|
sign = s[direction]
|
41
|
-
sign =
|
42
|
-
#Shouldn't have a negative value for degrees if the direction is specified.
|
43
|
-
#I am defaulting to the degrees sign if it is set, otherwise the direction given
|
44
|
-
sign = degrees.sign == -1 || (degrees == 0 && (minutes < 0 || (minutes == 0 && seconds < 0)))
|
45
|
-
sign * (degrees.abs + minutes/60.0 + seconds/3600.0)
|
38
|
+
sign = 1 if sign.nil? # Assume 'N' or 'E' if the direction is not set.
|
39
|
+
# Shouldn't have a negative value for degrees if the direction is specified.
|
40
|
+
# I am defaulting to the degrees sign if it is set, otherwise the direction given
|
41
|
+
sign = -1 if degrees.sign == -1 || (degrees == 0 && (minutes < 0 || (minutes == 0 && seconds < 0)))
|
42
|
+
return sign * (degrees.abs + (minutes / 60.0) + (seconds / 3600.0))
|
46
43
|
end
|
47
|
-
|
48
|
-
#Class level function that takes an array of [degress,minutes, seconds, direction]
|
49
|
-
# and returns decimal degrees
|
44
|
+
|
45
|
+
# Class level function that takes an array of [degress,minutes, seconds, direction] or [degrees,minutes,seconds]
|
50
46
|
# @return [Float] signed decimal degrees.
|
47
|
+
# @param [Array] a Array is expanded and passed as degrees,minutes,seconds,direction to Angle#decimal_deg
|
51
48
|
def self.decimal_deg_from_ary(a)
|
52
|
-
self.decimal_deg(*a)
|
49
|
+
return self.decimal_deg(*a)
|
53
50
|
end
|
54
|
-
|
55
|
-
#Class level utility function to return the angle as deg,min,sec
|
56
|
-
#Assumes decimal degress unless radians == true .
|
51
|
+
|
52
|
+
# Class level utility function to return the angle as deg,min,sec
|
53
|
+
# Assumes decimal degress unless radians == true .
|
57
54
|
# @return [Array] of signed deg, min, sec.
|
58
|
-
#Nb. * That min will be negative if the angle is negative and deg == 0
|
55
|
+
# Nb. * That min will be negative if the angle is negative and deg == 0
|
59
56
|
# * That sec will be negative if the angle is negative and deg == 0 && min == 0
|
57
|
+
# @param [#to_f,#to_degrees] angle may be anything that has a to_f and to_radians.
|
58
|
+
# @param [true,false, :radians] radians Angle is in degrees unless radians == true (or set to :radians).
|
60
59
|
def self.dms(angle, radians = false)
|
61
|
-
angle = angle.
|
60
|
+
angle = angle.to_f # means Strings, Numeric, and anything accepting a #to_f will work.
|
61
|
+
angle = angle.to_degrees if radians == true || radians == :radians
|
62
62
|
v = angle.abs
|
63
63
|
deg = v.floor
|
64
|
-
min = ((v-deg)*60.0).floor
|
65
|
-
sec = ((v-deg-min/60.0)*3600.0)
|
66
|
-
|
64
|
+
min = ((v - deg) * 60.0).floor
|
65
|
+
sec = ((v - deg - (min / 60.0)) * 3600.0)
|
66
|
+
|
67
67
|
if angle < 0
|
68
68
|
if deg == 0
|
69
69
|
if min == 0
|
70
70
|
sec = -sec
|
71
71
|
else
|
72
|
-
min = -min
|
72
|
+
min = -min
|
73
73
|
end
|
74
74
|
else
|
75
75
|
deg = -deg
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
-
return deg,min,sec
|
79
|
+
return deg, min, sec
|
80
80
|
end
|
81
|
-
|
82
|
-
#Class level function equivalent to Angle.new(r, true)
|
81
|
+
|
82
|
+
# Class level function equivalent to Angle.new(r, true)
|
83
83
|
# @return [Angle]
|
84
|
-
|
85
|
-
|
84
|
+
# @param [#to_f] r Value in radians to create the new Angle class with.
|
85
|
+
def self.radians(r = 0)
|
86
|
+
return 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.
|
86
87
|
end
|
87
|
-
|
88
|
-
#Class level function equivalent to Angle.new(d, false) or just Angle.new(d)
|
88
|
+
|
89
|
+
# Class level function equivalent to Angle.new(d, false) or just Angle.new(d)
|
89
90
|
# @return [Angle]
|
90
|
-
|
91
|
-
|
91
|
+
# @param [#to_radians] d Angle in degrees, to convert to radians, to create new Angle object from.
|
92
|
+
def self.degrees(d = 0)
|
93
|
+
return self.new(d.to_radians, true)
|
92
94
|
end
|
93
|
-
|
94
|
-
#Provides test for Module Comparable, giving us <,>,<=,>=,== between angles
|
95
|
+
|
96
|
+
# Provides test for Module Comparable, giving us <,>,<=,>=,== between angles
|
95
97
|
# @return [true,false]
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
98
|
+
# @param [Angle,Float] angle Can be another Angle, or a Numeric value to compare @angle with.
|
99
|
+
def <=>(other)
|
100
|
+
@angle <=> if other.instance_of?(Angle)
|
101
|
+
other.angle
|
102
|
+
else
|
103
|
+
other
|
104
|
+
end
|
102
105
|
end
|
103
106
|
|
104
|
-
#unary +
|
105
|
-
# @return [Angle] equivalent to @angle
|
107
|
+
# unary +
|
108
|
+
# @return [Angle,self] equivalent to @angle
|
106
109
|
def +@
|
107
|
-
self.class.radians(@angle) #Nb. Not Angle.new, as we want subclasses to return their class, not Angle.
|
110
|
+
return self.class.radians(@angle) # Nb. Not Angle.new, as we want subclasses to return their class, not Angle.
|
108
111
|
end
|
109
|
-
|
110
|
-
#Unary -
|
111
|
-
# @return [Angle] -@angle
|
112
|
+
|
113
|
+
# Unary -
|
114
|
+
# @return [Angle,self] -@angle
|
112
115
|
def -@
|
113
|
-
self.class.radians(-@angle)
|
116
|
+
return self.class.radians(-@angle)
|
114
117
|
end
|
115
|
-
|
118
|
+
|
116
119
|
# Binary addition operator. Can add angles and numbers, or two angles.
|
117
|
-
# @return [Angle]
|
118
|
-
|
119
|
-
|
120
|
+
# @return [Angle,self]
|
121
|
+
# @param [Angle,Numeric] angle
|
122
|
+
def +(other)
|
123
|
+
return self.class.radians(@angle + other)
|
120
124
|
end
|
121
|
-
|
125
|
+
|
122
126
|
# Binary subtraction operator. Can add angles and numbers, or two angles.
|
123
|
-
# @return [Angle]
|
124
|
-
|
125
|
-
|
127
|
+
# @return [Angle,self]
|
128
|
+
# @param [Angle,Numeric] angle
|
129
|
+
def -(other)
|
130
|
+
return self.class.radians(@angle - other)
|
126
131
|
end
|
127
|
-
|
132
|
+
|
128
133
|
# Binary multiply operator. Can add angles and numbers, or two angles.
|
129
|
-
# @return [Angle]
|
130
|
-
|
131
|
-
|
134
|
+
# @return [Angle,self]
|
135
|
+
# @param [Angle,Numeric] angle
|
136
|
+
def *(other)
|
137
|
+
self.class.radians(@angle * other)
|
132
138
|
end
|
133
|
-
|
139
|
+
|
134
140
|
# Binary power of operator. Can add angles and numbers, or two angles.
|
135
|
-
# @return [Angle]
|
136
|
-
|
137
|
-
|
141
|
+
# @return [Angle,self]
|
142
|
+
# @param [Angle,Numeric] angle
|
143
|
+
def **(other)
|
144
|
+
self.class.radians(@angle**other)
|
138
145
|
end
|
139
|
-
|
146
|
+
|
140
147
|
# Binary division operator. Can add angles and numbers, or two angles.
|
141
|
-
# @return [Angle]
|
142
|
-
|
143
|
-
|
148
|
+
# @return [Angle,self]
|
149
|
+
# @param [Angle,Numeric] angle
|
150
|
+
def /(other)
|
151
|
+
self.class.radians(@angle / other)
|
144
152
|
end
|
145
|
-
|
153
|
+
|
146
154
|
# Binary mod operator. Can add angles and numbers, or two angles.
|
147
|
-
# @return [Angle]
|
148
|
-
|
149
|
-
|
155
|
+
# @return [Angle,self]
|
156
|
+
# @param [Angle,Numeric] angle
|
157
|
+
def %(other)
|
158
|
+
self.class.radians(@angle % other)
|
150
159
|
end
|
151
|
-
|
160
|
+
|
152
161
|
# @return [Float] angle in degrees
|
153
162
|
def to_degrees
|
154
163
|
@angle.to_degrees
|
155
164
|
end
|
156
|
-
|
165
|
+
|
157
166
|
# @return [Float] angle in degrees
|
158
|
-
alias
|
159
|
-
|
160
|
-
|
167
|
+
# alias to_deg to_degrees
|
168
|
+
alias to_deg to_degrees
|
169
|
+
|
170
|
+
# @return [Float] angle in radians
|
161
171
|
def to_radians
|
162
172
|
@angle
|
163
173
|
end
|
164
174
|
|
175
|
+
alias to_rad to_radians
|
165
176
|
alias to_r to_radians
|
166
|
-
|
177
|
+
|
167
178
|
# Returns @angle as decimal_degrees
|
168
179
|
# @return [Array] of signed Floats: degrees,minutes,seconds
|
169
|
-
#Nb. * That min will be negative if the angle is negative and deg == 0
|
180
|
+
# Nb. * That min will be negative if the angle is negative and deg == 0
|
170
181
|
# * That sec will be negative if the angle is negative and deg == 0 && min == 0
|
171
|
-
def to_dms
|
182
|
+
def to_dms
|
172
183
|
d = to_degrees.abs
|
173
184
|
deg = d.floor
|
174
|
-
min = ((d-deg)*60).floor
|
175
|
-
sec = ((d-deg-min/60.0)*3600.0)
|
176
|
-
|
185
|
+
min = ((d - deg) * 60).floor
|
186
|
+
sec = ((d - deg - (min / 60.0)) * 3600.0)
|
187
|
+
|
177
188
|
if @angle < 0
|
178
189
|
if deg == 0
|
179
190
|
if min == 0
|
180
191
|
sec = -sec
|
181
192
|
else
|
182
|
-
min = -min
|
193
|
+
min = -min
|
183
194
|
end
|
184
195
|
else
|
185
196
|
deg = -deg
|
186
197
|
end
|
187
198
|
end
|
188
|
-
|
199
|
+
|
189
200
|
return deg, min, sec
|
190
201
|
end
|
191
|
-
|
202
|
+
|
192
203
|
# @return [Float] the angle in radians as a float (equivalent to to_radians)
|
193
204
|
alias to_f to_radians
|
194
|
-
|
205
|
+
|
195
206
|
# @return [Fixnum] the angle truncated to an integer, in radians.
|
196
207
|
def to_i
|
197
208
|
to_radians.to_i
|
198
209
|
end
|
199
|
-
|
210
|
+
|
200
211
|
# @return [Fixnum] the angle truncated to an integer, in radians.
|
201
212
|
alias to_int to_i
|
202
|
-
|
213
|
+
|
203
214
|
# @return [Array] the angle parameter as a Float and the @angle parameter from this class.
|
215
|
+
# @param [Numeric] angle
|
204
216
|
def coerce(angle)
|
205
|
-
[Float(angle), @angle]
|
217
|
+
[ Float(angle), @angle ]
|
206
218
|
end
|
207
|
-
|
219
|
+
|
208
220
|
# @return [Fixnum] the sign of the angle. 1 for positive, -1 for negative.
|
209
221
|
def sign
|
210
222
|
@angle.sign
|
211
223
|
end
|
212
|
-
|
224
|
+
|
213
225
|
# @return [Float] the absolute angle of the angle in radians
|
214
226
|
def abs
|
215
227
|
@angle.abs
|
216
228
|
end
|
217
|
-
|
229
|
+
|
218
230
|
# @return [Float] angle as compass bearing in radians.
|
219
|
-
#Compass bearings are clockwise, Math angles are counter clockwise.
|
231
|
+
# Compass bearings are clockwise, Math angles are counter clockwise.
|
220
232
|
def to_bearing
|
221
|
-
self.class.new(Math::PI * 2 - @angle,true)
|
233
|
+
self.class.new((Math::PI * 2) - @angle, true)
|
222
234
|
end
|
223
|
-
|
235
|
+
|
224
236
|
# @return [Float] the reverse angle in radians. i.e. angle + PI (or angle + 180 degrees)
|
225
237
|
def reverse
|
226
238
|
if (angle = @angle + Math::PI) > Math::PI * 2
|
227
239
|
angle -= Math::PI * 2
|
228
240
|
end
|
229
|
-
return self.class.new(angle,true)
|
241
|
+
return self.class.new(angle, true)
|
230
242
|
end
|
231
|
-
|
232
|
-
|
243
|
+
|
233
244
|
# @return [String] angle in radians as a string.
|
245
|
+
# @param [String] fmt Optional format string passed to Angle#strf
|
234
246
|
def to_s(fmt = nil)
|
235
|
-
return to_radians.to_s if
|
247
|
+
return to_radians.to_s if fmt.nil?
|
248
|
+
|
236
249
|
return strf(fmt)
|
237
250
|
end
|
238
|
-
|
239
|
-
#formated output of the angle.
|
240
|
-
#The default format is a signed deg
|
241
|
-
#formats are:
|
251
|
+
|
252
|
+
# formated output of the angle.
|
253
|
+
# @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.
|
254
|
+
# formats are:
|
242
255
|
# * %wd output the degrees as an integer.
|
243
|
-
# ** where w is 0, 1, 2 or 3 and represents the field width.
|
256
|
+
# ** where w is 0, 1, 2 or 3 and represents the field width.
|
244
257
|
# *** 1 is the default, which indicates that at least 1 digit is displayed
|
245
258
|
# *** 2 indicates that at least 2 digits are displayed. 1 to 9 will be displayed as 01 0 to 09 0
|
246
259
|
# *** 3 indicates that at least 4 digits are displayed. 10 to 99 will be displayed as 010 0 to 099 0
|
@@ -254,8 +267,8 @@ class Angle
|
|
254
267
|
# * %w.pM outputs minutes as a float .e.g. 01.125'.
|
255
268
|
# * p is the number of decimal places.
|
256
269
|
#
|
257
|
-
# * %wW outputs secs/60 as a float without the leading '0.'.
|
258
|
-
# Used with %m like this %2m'%4W , to get minute marker before the decimal places.
|
270
|
+
# * %wW outputs secs/60 as a float without the leading '0.'.
|
271
|
+
# Used with %m like this %2m'%4W , to get minute marker before the decimal places.
|
259
272
|
# e.g. -37 001'.1167 rather than -37 001.1167'
|
260
273
|
# * p is the number of decimal places.
|
261
274
|
#
|
@@ -272,133 +285,132 @@ class Angle
|
|
272
285
|
#
|
273
286
|
# Other strings in the format are printed as is.
|
274
287
|
# @return [String]
|
275
|
-
def strf(fmt="%d %2m'%2.4s\"")
|
276
|
-
tokens = fmt.scan(/%[0-9
|
288
|
+
def strf(fmt = "%d %2m'%2.4s\"")
|
289
|
+
tokens = fmt.scan(/%[0-9.]*[%dmsDMNErW]|[^%]*/)
|
277
290
|
have_dir = have_dms = false
|
278
291
|
tokens.collect! do |t|
|
279
|
-
if t[0,1] == '%' #format
|
292
|
+
if t[0, 1] == '%' # format
|
280
293
|
had_dot = false
|
281
294
|
decimals = -1
|
282
295
|
width = -1
|
283
296
|
format = nil
|
284
297
|
t[1..-1].scan(/[0-9]+|\.|[0-9]+|[%dmsDMNErW]/) do |t2|
|
285
298
|
case t2
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
else
|
290
|
-
width = t2.to_i
|
291
|
-
end
|
292
|
-
when '%'
|
293
|
-
format = t2
|
294
|
-
when /[dmsMwW]/
|
295
|
-
have_dms = true
|
296
|
-
format = t2
|
297
|
-
when /[NE]/
|
298
|
-
have_dir = true
|
299
|
-
format = t2
|
300
|
-
when '.'
|
301
|
-
had_dot = true
|
302
|
-
when /[Dr]/
|
303
|
-
format = t2
|
299
|
+
when /[0-9]+/
|
300
|
+
if had_dot
|
301
|
+
decimals = t2.to_i
|
304
302
|
else
|
305
|
-
|
303
|
+
width = t2.to_i
|
304
|
+
end
|
305
|
+
when '%', /[Dr]/
|
306
|
+
format = t2
|
307
|
+
when /[dmsMwW]/
|
308
|
+
have_dms = true
|
309
|
+
format = t2
|
310
|
+
when /[NE]/
|
311
|
+
have_dir = true
|
312
|
+
format = t2
|
313
|
+
when '.'
|
314
|
+
had_dot = true
|
315
|
+
else
|
316
|
+
raise "unknown format character '#{t2}'" # shouldn't be able to get here.
|
306
317
|
end
|
307
318
|
end
|
308
|
-
[:format, width, decimals, format]
|
319
|
+
[ :format, width, decimals, format ]
|
309
320
|
else
|
310
|
-
[:filler, t]
|
321
|
+
[ :filler, t ]
|
311
322
|
end
|
312
323
|
end
|
313
324
|
|
314
|
-
deg,min,sec = to_dms if have_dms
|
325
|
+
deg, min, sec = to_dms if have_dms
|
315
326
|
|
316
|
-
s =
|
327
|
+
s = ''
|
317
328
|
tokens.each do |t|
|
318
|
-
if
|
329
|
+
if t[0] == :format
|
319
330
|
case t[3]
|
320
331
|
when '%'
|
321
332
|
s += '%'
|
322
333
|
when 'd'
|
323
334
|
s += s_int(deg, t[1], have_dir)
|
324
335
|
when 'D'
|
325
|
-
s += s_float(@angle.
|
336
|
+
s += s_float(@angle.to_deg, t[1], t[2], have_dir)
|
326
337
|
when 'm'
|
327
338
|
s += s_int(min, t[1], have_dir)
|
328
339
|
when 'M'
|
329
|
-
s += s_float(min + sec/60, t[1], t[2], have_dir)
|
340
|
+
s += s_float(min + (sec / 60), t[1], t[2], have_dir)
|
330
341
|
when 'W'
|
331
|
-
s += s_only_places(sec/60,
|
342
|
+
s += s_only_places(sec / 60, t[1])
|
332
343
|
when 's'
|
333
344
|
s += s_float(sec, t[1], t[2], have_dir)
|
334
345
|
when 'r'
|
335
346
|
s += s_float(@angle, t[1], t[2], have_dir)
|
336
347
|
when 'N'
|
337
|
-
s +=
|
348
|
+
s += (@angle < 0 ? 'S' : 'N')
|
338
349
|
when 'E'
|
339
350
|
s += (@angle < 0 ? 'W' : 'E')
|
340
351
|
end
|
341
352
|
else
|
342
|
-
s += t[1] #the fillers.
|
353
|
+
s += t[1] # the fillers.
|
343
354
|
end
|
344
355
|
end
|
345
|
-
|
356
|
+
|
346
357
|
return s
|
347
358
|
end
|
348
|
-
|
349
|
-
|
350
|
-
#
|
351
|
-
#
|
352
|
-
#
|
353
|
-
#If ploces is -1, then all decimal places are returned.
|
359
|
+
|
360
|
+
# Output angle_dec as a string to the number of decimal places specified by places.
|
361
|
+
# Assumes the angle is 0 <= angle_dec < 1
|
362
|
+
# No leading '0' is output. The string starts with a '.' if places is non-zero.
|
363
|
+
# If ploces is -1, then all decimal places are returned.
|
354
364
|
# @return [String]
|
355
|
-
|
356
|
-
|
357
|
-
|
365
|
+
# @param [Float] angle_dec Angle's fractional part
|
366
|
+
# @param [Fixnum] places Number of decimal places to output
|
367
|
+
private def s_places(angle_dec, places)
|
368
|
+
if places == -1
|
369
|
+
angle_dec.to_s[1..-1] # Output all decimal places stripping the leading 0
|
358
370
|
else
|
359
|
-
|
371
|
+
places > 0 ? ".%0#{places}d" % (angle_dec * (10**places)).round : ''
|
360
372
|
end
|
361
373
|
end
|
362
374
|
|
363
|
-
#return the angle as a string with fixed width decimal portion with leading 0s
|
364
|
-
#to get at least the width specified
|
365
|
-
#Prints the number of places after the decimal point rounded to places places.
|
375
|
+
# return the angle as a string with fixed width decimal portion with leading 0s
|
376
|
+
# to get at least the width specified
|
377
|
+
# Prints the number of places after the decimal point rounded to places places.
|
366
378
|
#-1 width means no width format
|
367
379
|
#-1 places means print all decimal places.
|
368
|
-
#abs means print the absolute value.
|
380
|
+
# abs means print the absolute value.
|
369
381
|
# @return [String]
|
370
|
-
|
371
|
-
|
382
|
+
# @param [Float] angle In radians
|
383
|
+
# @param [Fixnum] width Output field width, padded with leading 0's
|
384
|
+
# @param [Fixnum] places Number of decimal places to output
|
385
|
+
# @param [true,false] abs Output absolute value.
|
386
|
+
private def s_float(angle, width, places, abs)
|
387
|
+
_angle_int, angle_dec = angle.abs.divmod(1)
|
372
388
|
f = "%0#{width > 0 ? width : ''}d"
|
373
|
-
s =
|
374
|
-
s
|
389
|
+
s = abs == false && angle.sign == -1 ? '-' : '' # catch the case of -0
|
390
|
+
return s + (f % angle.abs) + s_places(angle_dec, places)
|
375
391
|
end
|
376
392
|
|
377
|
-
#Return the integer part of angle as a string of fixed width
|
378
|
-
#If abs == true, then return the absolute value.
|
393
|
+
# Return the integer part of angle as a string of fixed width
|
394
|
+
# If abs == true, then return the absolute value.
|
379
395
|
# @return [Fixnum]
|
380
|
-
|
396
|
+
# @param [Float] angle In radians
|
397
|
+
# @param [Fixnum] width Output field width, padded with leading 0's
|
398
|
+
# @param [true,false] abs Output absolute value.
|
399
|
+
private def s_int(angle, width, abs)
|
381
400
|
f = "%0#{width > 0 ? width : ''}d"
|
382
|
-
s =
|
383
|
-
s
|
401
|
+
s = abs == false && angle.sign == -1 ? '-' : '' # catch the case of -0
|
402
|
+
return s + (f % angle.abs)
|
384
403
|
end
|
385
|
-
|
386
|
-
#Return the fractional part of angle as a string,
|
387
|
-
#to the number of decimal places specified by 'places'.
|
388
|
-
#No leading '0' is output. The string starts with a '.'
|
389
|
-
#If ploces is -1, then all decimal places are returned.
|
404
|
+
|
405
|
+
# Return the fractional part of angle as a string,
|
406
|
+
# to the number of decimal places specified by 'places'.
|
407
|
+
# No leading '0' is output. The string starts with a '.'
|
408
|
+
# If ploces is -1, then all decimal places are returned.
|
390
409
|
# @return [String]
|
391
|
-
|
392
|
-
|
410
|
+
# @param [Float] angle In radians
|
411
|
+
# @param [Fixnum] places Number of decimal places to output
|
412
|
+
private def s_only_places(angle, places)
|
413
|
+
_angle_int, angle_dec = angle.abs.divmod(1)
|
393
414
|
s_places(angle_dec, places)
|
394
415
|
end
|
395
416
|
end
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
data/lib/coordinate.rb
CHANGED
@@ -1,35 +1,36 @@
|
|
1
|
+
require_relative 'angle.rb'
|
1
2
|
|
2
|
-
|
3
|
-
|
4
|
-
#Holds the latitude, longitude, and the altitude for the coordinate
|
3
|
+
# Holds the latitude, longitude, and the altitude for the coordinate
|
5
4
|
class Coordinate
|
6
|
-
|
7
|
-
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
# @return [Latitude]
|
6
|
+
attr_accessor :latitude
|
7
|
+
# @return [Longitude]
|
8
|
+
attr_accessor :longitude
|
9
|
+
# @return [Numeric]
|
10
|
+
attr_accessor :altitude
|
11
|
+
|
12
|
+
# latitude and longitude can be Strings or Numeric, or anything else with to_radians and to_f
|
13
|
+
# latitude and longitude are in degrees unless radians == true (or set to :radians)
|
14
|
+
def initialize(latitude = 0, longitude = 0, altitude = 0, radians = false)
|
15
|
+
@latitude = Latitude.new(latitude, radians)
|
16
|
+
@longitude = Longitude.new(longitude, radians)
|
13
17
|
@altitude = altitude.to_f
|
14
18
|
end
|
15
|
-
|
16
|
-
#
|
17
|
-
#Should add an optional format string to this.
|
19
|
+
|
20
|
+
# @return [String] Latitude longitude and altitude as a single space separated string.
|
18
21
|
def to_s
|
19
|
-
"#{@latitude
|
22
|
+
"#{@latitude} #{@longitude} #{@altitude}m"
|
20
23
|
end
|
21
|
-
|
22
|
-
#
|
23
|
-
#members, latitude, longitude and altitude
|
24
|
+
|
25
|
+
# @return [Latitude, Longitude, Float] with members, latitude, longitude and altitude
|
24
26
|
def to_ary
|
25
27
|
[ @latitude, @longitude, @altitude ]
|
26
28
|
end
|
27
|
-
|
29
|
+
|
28
30
|
alias to_a to_ary
|
29
|
-
|
30
|
-
#return
|
31
|
-
#keys :latitude, :longitude, and :altitude
|
31
|
+
|
32
|
+
# @return [Hash] with keys :latitude, :longitude, and :altitude
|
32
33
|
def to_hash
|
33
|
-
{ :
|
34
|
+
{ latitude: @latitude, longitude: @longitude, altitude: @altitude }
|
34
35
|
end
|
35
|
-
end
|
36
|
+
end
|