aixm 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/.yardopts +3 -0
- data/CHANGELOG.md +34 -14
- data/Guardfile +1 -0
- data/README.md +64 -257
- data/lib/aixm.rb +16 -7
- data/lib/aixm/component.rb +6 -0
- data/lib/aixm/component/frequency.rb +135 -0
- data/lib/aixm/component/geometry.rb +34 -23
- data/lib/aixm/component/geometry/arc.rb +37 -22
- data/lib/aixm/component/geometry/border.rb +29 -20
- data/lib/aixm/component/geometry/circle.rb +39 -22
- data/lib/aixm/component/geometry/point.rb +29 -13
- data/lib/aixm/component/helipad.rb +154 -0
- data/lib/aixm/component/layer.rb +91 -0
- data/lib/aixm/component/runway.rb +294 -0
- data/lib/aixm/component/service.rb +170 -0
- data/lib/aixm/component/timetable.rb +65 -0
- data/lib/aixm/component/vertical_limits.rb +65 -29
- data/lib/aixm/config.rb +87 -0
- data/lib/aixm/document.rb +66 -42
- data/lib/aixm/errors.rb +11 -0
- data/lib/aixm/f.rb +34 -20
- data/lib/aixm/feature.rb +38 -0
- data/lib/aixm/feature/airport.rb +473 -0
- data/lib/aixm/feature/airspace.rb +145 -92
- data/lib/aixm/feature/navigational_aid.rb +94 -0
- data/lib/aixm/feature/navigational_aid/designated_point.rb +50 -54
- data/lib/aixm/feature/navigational_aid/dme.rb +48 -40
- data/lib/aixm/feature/navigational_aid/marker.rb +55 -45
- data/lib/aixm/feature/navigational_aid/ndb.rb +54 -50
- data/lib/aixm/feature/navigational_aid/tacan.rb +38 -31
- data/lib/aixm/feature/navigational_aid/vor.rb +84 -76
- data/lib/aixm/feature/organisation.rb +97 -0
- data/lib/aixm/feature/unit.rb +152 -0
- data/lib/aixm/refinements.rb +132 -47
- data/lib/aixm/shortcuts.rb +11 -6
- data/lib/aixm/version.rb +1 -1
- data/lib/aixm/xy.rb +64 -20
- data/lib/aixm/z.rb +51 -22
- data/{lib/aixm/schemas → schemas/aixm}/4.5/AIXM-DataTypes.xsd +0 -0
- data/{lib/aixm/schemas → schemas/aixm}/4.5/AIXM-Features.xsd +0 -0
- data/{lib/aixm/schemas → schemas/aixm}/4.5/AIXM-Snapshot.xsd +0 -0
- data/schemas/ofmx/0/OFMX-DataTypes.xsd +5077 -0
- data/schemas/ofmx/0/OFMX-Features.xsd +9955 -0
- data/schemas/ofmx/0/OFMX-Snapshot.xsd +217 -0
- data/spec/factory.rb +209 -33
- data/spec/lib/aixm/component/frequency_spec.rb +75 -0
- data/spec/lib/aixm/component/geometry/arc_spec.rb +28 -22
- data/spec/lib/aixm/component/geometry/border_spec.rb +23 -20
- data/spec/lib/aixm/component/geometry/circle_spec.rb +31 -22
- data/spec/lib/aixm/component/geometry/point_spec.rb +11 -14
- data/spec/lib/aixm/component/geometry_spec.rb +150 -69
- data/spec/lib/aixm/component/helipad_spec.rb +136 -0
- data/spec/lib/aixm/component/layer_spec.rb +110 -0
- data/spec/lib/aixm/component/runway_spec.rb +402 -0
- data/spec/lib/aixm/component/service_spec.rb +61 -0
- data/spec/lib/aixm/component/timetable_spec.rb +49 -0
- data/spec/lib/aixm/component/vertical_limits_spec.rb +39 -20
- data/spec/lib/aixm/config_spec.rb +41 -0
- data/spec/lib/aixm/document_spec.rb +637 -147
- data/spec/lib/aixm/errors_spec.rb +14 -0
- data/spec/lib/aixm/f_spec.rb +17 -10
- data/spec/lib/aixm/feature/airport_spec.rb +546 -0
- data/spec/lib/aixm/feature/airspace_spec.rb +349 -226
- data/spec/lib/aixm/feature/navigational_aid/designated_point_spec.rb +47 -36
- data/spec/lib/aixm/feature/navigational_aid/dme_spec.rb +61 -36
- data/spec/lib/aixm/feature/navigational_aid/marker_spec.rb +61 -113
- data/spec/lib/aixm/feature/navigational_aid/ndb_spec.rb +65 -79
- data/spec/lib/aixm/feature/navigational_aid/tacan_spec.rb +57 -36
- data/spec/lib/aixm/feature/navigational_aid/vor_spec.rb +86 -112
- data/spec/lib/aixm/feature/navigational_aid_spec.rb +52 -0
- data/spec/lib/aixm/feature/organisation_spec.rb +77 -0
- data/spec/lib/aixm/feature/unit_spec.rb +227 -0
- data/spec/lib/aixm/feature_spec.rb +58 -0
- data/spec/lib/aixm/refinements_spec.rb +187 -178
- data/spec/lib/aixm/xy_spec.rb +45 -34
- data/spec/lib/aixm/z_spec.rb +19 -21
- data/spec/macros/organisation.rb +11 -0
- data/spec/macros/remarks.rb +12 -0
- data/spec/macros/timetable.rb +11 -0
- data/spec/macros/xy.rb +11 -0
- data/spec/macros/z_qnh.rb +11 -0
- data/spec/spec_helper.rb +26 -0
- metadata +60 -19
- data/lib/aixm/base.rb +0 -10
- data/lib/aixm/component/base.rb +0 -6
- data/lib/aixm/component/class_layer.rb +0 -46
- data/lib/aixm/component/geometry/base.rb +0 -8
- data/lib/aixm/component/schedule.rb +0 -43
- data/lib/aixm/feature/base.rb +0 -6
- data/lib/aixm/feature/navigational_aid/base.rb +0 -79
- data/spec/lib/aixm/component/class_layer_spec.rb +0 -74
- data/spec/lib/aixm/component/schedule_spec.rb +0 -33
- data/spec/lib/aixm/feature/navigational_aid/base_spec.rb +0 -41
data/lib/aixm/refinements.rb
CHANGED
|
@@ -20,54 +20,81 @@ module AIXM
|
|
|
20
20
|
ft: 0.0003048
|
|
21
21
|
}.freeze
|
|
22
22
|
|
|
23
|
+
# @!method to_digest
|
|
24
|
+
# Builds a 4 byte hex digest from the Array payload.
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# ['foo', :bar, nil, [123]].to_digest
|
|
28
|
+
# # => "f3920098"
|
|
29
|
+
#
|
|
30
|
+
# @note This is a refinement for +Array+
|
|
31
|
+
# @return [String] 4 byte hex
|
|
23
32
|
refine Array do
|
|
24
|
-
##
|
|
25
|
-
# Shortcut for +include?+
|
|
26
|
-
#
|
|
27
|
-
# Example:
|
|
28
|
-
# extensions.include?(:ofm) # => true
|
|
29
|
-
# extensions >> :ofm # => true
|
|
30
|
-
alias_method :>>, :include?
|
|
31
|
-
|
|
32
|
-
##
|
|
33
|
-
# Build a 1 to 9 digit integer digest (which fits in signed 32bit) from payload
|
|
34
33
|
def to_digest
|
|
35
|
-
::Digest::SHA512.hexdigest(flatten.join('|'))
|
|
34
|
+
::Digest::SHA512.hexdigest(flatten.map(&:to_s).join('|'))[0, 8]
|
|
36
35
|
end
|
|
37
36
|
end
|
|
38
37
|
|
|
38
|
+
# @!method lookup(key_or_value, fallback=omitted=true)
|
|
39
|
+
# Fetch a value from the hash, but unlike +Hash#fetch+, if +key_or_value+
|
|
40
|
+
# is no hash key, check whether +key_or_value+ is a hash value and if so
|
|
41
|
+
# return it.
|
|
42
|
+
#
|
|
43
|
+
# @example
|
|
44
|
+
# h = { one: 1, two: 2, three: 3, four: :three }
|
|
45
|
+
# h.lookup(:one) # => 1
|
|
46
|
+
# h.lookup(1) # => 1
|
|
47
|
+
# h.lookup(:three) # => 3 (key has priority over value)
|
|
48
|
+
# h.lookup(:foo) # => KeyError
|
|
49
|
+
# h.lookup(:foo, :fallback) # => :fallback
|
|
50
|
+
# h.lookup(:foo, nil) # => nil
|
|
51
|
+
#
|
|
52
|
+
# @note This is a refinement for +Hash+
|
|
53
|
+
# @param key_or_value [Object] key or value of the hash
|
|
54
|
+
# @param fallback [Object] fallback value
|
|
55
|
+
# @raise [KeyError] if neither a matching hash key nor hash value are
|
|
56
|
+
# found and no fallback value has been passed
|
|
39
57
|
refine Hash do
|
|
40
|
-
|
|
41
|
-
# Fetch a value from the hash, but unlike +fetch+, if +key_or_value+ is
|
|
42
|
-
# no hash key, check whether +key_or_value+ is a hash value and if so
|
|
43
|
-
# return it.
|
|
44
|
-
#
|
|
45
|
-
# Examples:
|
|
46
|
-
# h = { one: 1, two: 2, three: 3, four: :three }
|
|
47
|
-
# h.lookup(:one) # => 1
|
|
48
|
-
# h.lookup(1) # => 1
|
|
49
|
-
# h.lookup(:three) # => 3 (key has priority over value)
|
|
50
|
-
# h.lookup(:foo) # => KeyError
|
|
51
|
-
# h.lookup(:foo, :default) # => :default
|
|
52
|
-
# h.lookup(:foo, nil) # => nil
|
|
53
|
-
def lookup(key_or_value, default = omitted = true)
|
|
58
|
+
def lookup(key_or_value, fallback=omitted=true)
|
|
54
59
|
self[key_or_value] ||
|
|
55
60
|
(key_or_value if has_value?(key_or_value)) ||
|
|
56
|
-
(omitted ? fail(KeyError, "key or value `#{key_or_value}' not found") :
|
|
61
|
+
(omitted ? fail(KeyError, "key or value `#{key_or_value}' not found") : fallback)
|
|
57
62
|
end
|
|
58
63
|
end
|
|
59
64
|
|
|
65
|
+
# @!method indent(number)
|
|
66
|
+
# Indent every line of a string with +number+ spaces.
|
|
67
|
+
#
|
|
68
|
+
# @example
|
|
69
|
+
# "foo\nbar".indent(2)
|
|
70
|
+
# # => " foo\n bar"
|
|
71
|
+
#
|
|
72
|
+
# @note This is a refinement for +String+
|
|
73
|
+
# @param number [Integer] number of spaces
|
|
74
|
+
# @return [String] line indended string
|
|
60
75
|
refine String do
|
|
61
|
-
##
|
|
62
|
-
# Indent every line of a string with +number+ spaces
|
|
63
76
|
def indent(number)
|
|
64
77
|
whitespace = ' ' * number
|
|
65
78
|
gsub(/^/, whitespace)
|
|
66
79
|
end
|
|
80
|
+
end
|
|
67
81
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
82
|
+
# @!method uptrans
|
|
83
|
+
# Upcase and transliterate to match the reduced character set for
|
|
84
|
+
# AIXM names and titles.
|
|
85
|
+
#
|
|
86
|
+
# See {UPTRANS_MAP} for supported diacryts and {UPTRANS_FILTER} for the
|
|
87
|
+
# list of allowed characters in the returned value.
|
|
88
|
+
#
|
|
89
|
+
# @example
|
|
90
|
+
# "Nîmes-Alès".uptrans
|
|
91
|
+
# # => "NIMES-ALES"
|
|
92
|
+
# "Zürich".uptrans
|
|
93
|
+
# # => "ZUERICH"
|
|
94
|
+
#
|
|
95
|
+
# @note This is a refinement for +String+
|
|
96
|
+
# @return [String] upcased and transliterated String
|
|
97
|
+
refine String do
|
|
71
98
|
def uptrans
|
|
72
99
|
self.dup.tap do |string|
|
|
73
100
|
string.upcase!
|
|
@@ -76,14 +103,25 @@ module AIXM
|
|
|
76
103
|
string.gsub!(UPTRANS_FILTER, '')
|
|
77
104
|
end
|
|
78
105
|
end
|
|
106
|
+
end
|
|
79
107
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
108
|
+
# @!method to_dd
|
|
109
|
+
# Convert DMS angle to DD or +nil+ if the notation is not recognized.
|
|
110
|
+
#
|
|
111
|
+
# @example
|
|
112
|
+
# %q(43°12'77.92").to_dd
|
|
113
|
+
# # => 43.22164444444445
|
|
114
|
+
# %q(-123).to_dd
|
|
115
|
+
# # => nil
|
|
116
|
+
#
|
|
117
|
+
# Supported notations:
|
|
118
|
+
# * +{-}{DD}D°MM'SS{.SS}"+
|
|
119
|
+
# * +{-}{DD}D MM SS{.SS}+
|
|
120
|
+
# * +{-}{DD}DMMSS{.SS}+
|
|
121
|
+
#
|
|
122
|
+
# @note This is a refinement for +String+
|
|
123
|
+
# @return [Float] angle in DD notation
|
|
124
|
+
refine String do
|
|
87
125
|
def to_dd
|
|
88
126
|
if self =~ /\A(-)?(\d{1,3})[° ]?(\d{2})[' ]?(\d{2}\.?\d{0,2})"?\z/
|
|
89
127
|
("#{$1}1".to_i * ($2.to_f + ($3.to_f/60) + ($4.to_f/3600)))
|
|
@@ -91,18 +129,52 @@ module AIXM
|
|
|
91
129
|
end
|
|
92
130
|
end
|
|
93
131
|
|
|
132
|
+
# @!method trim
|
|
133
|
+
# Convert whole numbers to Integer and leave all other untouched.
|
|
134
|
+
#
|
|
135
|
+
# @example
|
|
136
|
+
# 3.0.trim
|
|
137
|
+
# # => 3
|
|
138
|
+
# 3.3.trim
|
|
139
|
+
# # => 3.3
|
|
140
|
+
#
|
|
141
|
+
# @note This is a refinement for +Float+
|
|
142
|
+
# @return [Integer, Float] converted Float
|
|
94
143
|
refine Float do
|
|
95
|
-
##
|
|
96
|
-
# Convert whole numbers to Integer and leave all other untouched
|
|
97
144
|
def trim
|
|
98
145
|
(self % 1).zero? ? self.to_i : self
|
|
99
146
|
end
|
|
147
|
+
end
|
|
100
148
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
149
|
+
# @!method to_rad
|
|
150
|
+
# Convert an angle from degree to radian.
|
|
151
|
+
#
|
|
152
|
+
# @example
|
|
153
|
+
# 45.to_rad
|
|
154
|
+
# # => 0.7853981633974483
|
|
155
|
+
#
|
|
156
|
+
# @note This is a refinement for +Float+
|
|
157
|
+
# @return [Float] radian angle
|
|
158
|
+
refine Float do
|
|
159
|
+
def to_rad
|
|
160
|
+
self * Math::PI / 180
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# @!method to_dms(padding=3)
|
|
165
|
+
# Convert DD angle to DMS with the degrees zero padded to +padding+
|
|
166
|
+
# length.
|
|
167
|
+
#
|
|
168
|
+
# @example
|
|
169
|
+
# 43.22164444444445.to_dms(2)
|
|
170
|
+
# # => "43°12'77.92\""
|
|
171
|
+
# 43.22164444444445.to_dms
|
|
172
|
+
# # => "043°12'77.92\""
|
|
173
|
+
#
|
|
174
|
+
# @note This is a refinement for +Float+
|
|
175
|
+
# @param padding [Integer] number of digits for the degree part
|
|
176
|
+
# @return [String] angle in DMS notation +{-}D°MM'SS.SS"+
|
|
177
|
+
refine Float do
|
|
106
178
|
def to_dms(padding=3)
|
|
107
179
|
degrees = self.abs.floor
|
|
108
180
|
minutes = ((self.abs - degrees) * 60).floor
|
|
@@ -116,9 +188,22 @@ module AIXM
|
|
|
116
188
|
seconds.abs
|
|
117
189
|
]
|
|
118
190
|
end
|
|
191
|
+
end
|
|
119
192
|
|
|
120
|
-
|
|
121
|
-
|
|
193
|
+
# @!method to_km(from:)
|
|
194
|
+
# Convert a distance from the source unit +from+ to kilometers.
|
|
195
|
+
#
|
|
196
|
+
# @example
|
|
197
|
+
# 10.to_km(from: :nm)
|
|
198
|
+
# # => 18.52
|
|
199
|
+
# 10.to_km(from: :foobar)
|
|
200
|
+
# # => ArgumentError
|
|
201
|
+
#
|
|
202
|
+
# @note This is a refinement for +Float+
|
|
203
|
+
# @param from [Symbol] source unit (see {KM_FACTORS})
|
|
204
|
+
# @raise [ArgumentError] if the specified unit is not supported
|
|
205
|
+
# @return [Float] value converted to kilometers
|
|
206
|
+
refine Float do
|
|
122
207
|
def to_km(from:)
|
|
123
208
|
self * KM_FACTORS.fetch(from.downcase.to_sym)
|
|
124
209
|
rescue KeyError
|
data/lib/aixm/shortcuts.rb
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
module AIXM
|
|
2
2
|
|
|
3
|
-
SCHEMA = Pathname(__dir__).join('schemas', '4.5', 'AIXM-Snapshot.xsd').freeze
|
|
4
|
-
|
|
5
3
|
ELEMENTS = {
|
|
6
4
|
document: Document,
|
|
7
5
|
xy: XY,
|
|
8
6
|
z: Z,
|
|
9
7
|
f: F,
|
|
8
|
+
organisation: Feature::Organisation,
|
|
9
|
+
unit: Feature::Unit,
|
|
10
|
+
service: Component::Service,
|
|
11
|
+
frequency: Component::Frequency,
|
|
12
|
+
airport: Feature::Airport,
|
|
13
|
+
runway: Component::Runway,
|
|
14
|
+
helipad: Component::Helipad,
|
|
10
15
|
airspace: Feature::Airspace,
|
|
11
|
-
|
|
16
|
+
layer: Component::Layer,
|
|
12
17
|
geometry: Component::Geometry,
|
|
13
|
-
schedule: Component::Schedule,
|
|
14
18
|
vertical_limits: Component::VerticalLimits,
|
|
15
19
|
arc: Component::Geometry::Arc,
|
|
16
20
|
border: Component::Geometry::Border,
|
|
@@ -21,7 +25,8 @@ module AIXM
|
|
|
21
25
|
marker: Feature::NavigationalAid::Marker,
|
|
22
26
|
tacan: Feature::NavigationalAid::TACAN,
|
|
23
27
|
ndb: Feature::NavigationalAid::NDB,
|
|
24
|
-
vor: Feature::NavigationalAid::VOR
|
|
28
|
+
vor: Feature::NavigationalAid::VOR,
|
|
29
|
+
timetable: Component::Timetable
|
|
25
30
|
}.freeze
|
|
26
31
|
|
|
27
32
|
ELEMENTS.each do |element, klass|
|
|
@@ -32,6 +37,6 @@ module AIXM
|
|
|
32
37
|
|
|
33
38
|
GROUND = z(0, :qfe).freeze
|
|
34
39
|
UNLIMITED = z(999, :qne).freeze
|
|
35
|
-
H24 =
|
|
40
|
+
H24 = timetable(code: :H24).freeze
|
|
36
41
|
|
|
37
42
|
end
|
data/lib/aixm/version.rb
CHANGED
data/lib/aixm/xy.rb
CHANGED
|
@@ -1,42 +1,86 @@
|
|
|
1
|
+
using AIXM::Refinements
|
|
2
|
+
|
|
1
3
|
module AIXM
|
|
2
4
|
|
|
3
|
-
##
|
|
4
5
|
# Geographical coordinates
|
|
5
6
|
#
|
|
6
|
-
#
|
|
7
|
+
# ===Recognized notations:
|
|
7
8
|
# * DD - examples: 12.12345678 (north or east), -12.12345678 (south or west)
|
|
8
9
|
# * DMS - examples: 11°22'33.44"N, 1112233.44W
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
#
|
|
11
|
+
# @example All of the below are equivalent
|
|
12
|
+
# AIXM.xy(lat: %q(11°22'33.44"), long: %q(-111°22'33.44"))
|
|
13
|
+
# AIXM.xy(lat: '112233.44N', long: '1112233.44W')
|
|
14
|
+
# AIXM.xy(lat: 11.375955555555556, long: -111.37595555555555)
|
|
15
|
+
#
|
|
16
|
+
# @see https://github.com/openflightmaps/ofmx/wiki/Coordinates
|
|
17
|
+
class XY
|
|
18
|
+
EARTH_RADIUS = 6_371_008.8
|
|
11
19
|
|
|
12
20
|
def initialize(lat:, long:)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
self.lat, self.long = lat, long
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [String]
|
|
25
|
+
def inspect
|
|
26
|
+
%Q(#<#{self.class} #{to_s}>)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @return [String] human readable representation
|
|
30
|
+
def to_s
|
|
31
|
+
[lat(:ofmx), long(:ofmx)].join(' ')
|
|
16
32
|
end
|
|
17
33
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
34
|
+
# @!attribute lat
|
|
35
|
+
def lat=(value)
|
|
36
|
+
@lat = float_for value
|
|
37
|
+
fail(ArgumentError, "invalid lat") unless (-90..90).include? @lat
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @param schema [Symbol, nil] either nil, +:aixm+ or +:ofmx+
|
|
41
|
+
# @return [String, Float] latitude
|
|
42
|
+
def lat(schema=nil)
|
|
43
|
+
case schema
|
|
44
|
+
when :ofmx then ("%011.8f" % @lat.abs.round(8)) + (@lat.negative? ? 'S' : 'N')
|
|
21
45
|
when :aixm then @lat.to_dms(2).gsub(/[^\d.]/, '') + (@lat.negative? ? 'S' : 'N')
|
|
22
46
|
else @lat.round(8)
|
|
23
47
|
end
|
|
24
48
|
end
|
|
25
49
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
50
|
+
# @!attribute long
|
|
51
|
+
def long=(value)
|
|
52
|
+
@long = float_for value
|
|
53
|
+
fail(ArgumentError, "invalid long") unless (-180..180).include? @long
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @param schema [Symbol, nil] either nil, +:aixm+ or +:ofmx+
|
|
57
|
+
# @return [Float, String] longitude
|
|
58
|
+
def long(schema=nil)
|
|
59
|
+
case schema
|
|
60
|
+
when :ofmx then ("%012.8f" % @long.abs.round(8)) + (@long.negative? ? 'W' : 'E')
|
|
29
61
|
when :aixm then @long.to_dms(3).gsub(/[^\d.]/, '') + (@long.negative? ? 'W' : 'E')
|
|
30
62
|
else @long.round(8)
|
|
31
63
|
end
|
|
32
64
|
end
|
|
33
65
|
|
|
34
|
-
|
|
35
|
-
|
|
66
|
+
# @return [Boolean]
|
|
67
|
+
def ==(other)
|
|
68
|
+
other.is_a?(self.class) && lat == other.lat && long == other.long
|
|
36
69
|
end
|
|
37
70
|
|
|
38
|
-
|
|
39
|
-
|
|
71
|
+
# @return [Float] distance in meters as calculated by use of the Haversine formula
|
|
72
|
+
def distance(other)
|
|
73
|
+
if self == other
|
|
74
|
+
0
|
|
75
|
+
else
|
|
76
|
+
2 * EARTH_RADIUS * Math.asin(
|
|
77
|
+
Math.sqrt(
|
|
78
|
+
Math.sin((other.lat.to_rad - lat.to_rad) / 2) ** 2 +
|
|
79
|
+
Math.cos(lat.to_rad) * Math.cos(other.lat.to_rad) *
|
|
80
|
+
Math.sin((other.long.to_rad - long.to_rad) / 2) ** 2
|
|
81
|
+
)
|
|
82
|
+
)
|
|
83
|
+
end.round
|
|
40
84
|
end
|
|
41
85
|
|
|
42
86
|
private
|
|
@@ -44,11 +88,11 @@ module AIXM
|
|
|
44
88
|
def float_for(value)
|
|
45
89
|
case value
|
|
46
90
|
when Numeric then value.to_f
|
|
47
|
-
when String then value[0..-2].to_dd * (value
|
|
48
|
-
else fail(ArgumentError, "
|
|
91
|
+
when String then value[0..-2].to_dd * (value.match?(/[SW]$/) ? -1 : 1)
|
|
92
|
+
else fail(ArgumentError, "invalid value class `#{value.class}'")
|
|
49
93
|
end
|
|
50
94
|
rescue
|
|
51
|
-
fail(ArgumentError, "
|
|
95
|
+
fail(ArgumentError, "invalid value `#{value}'")
|
|
52
96
|
end
|
|
53
97
|
|
|
54
98
|
end
|
data/lib/aixm/z.rb
CHANGED
|
@@ -1,48 +1,77 @@
|
|
|
1
|
+
using AIXM::Refinements
|
|
2
|
+
|
|
1
3
|
module AIXM
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
# Elevation or altitude
|
|
5
|
+
# Height, elevation or altitude
|
|
5
6
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
# @example
|
|
8
|
+
# AIXM.z(1000, :qfe) # height: 1000 ft above ground
|
|
9
|
+
# AIXM.z(2000, :qnh) # elevation or altitude: 2000 ft above mean sea level
|
|
10
|
+
# AIXM.z(45, :qne) # altitude: flight level 45
|
|
11
|
+
#
|
|
12
|
+
# ===Shortcuts:
|
|
13
|
+
# * +AIXM::GROUND+ - surface expressed as "0 ft QFE"
|
|
14
|
+
# * +AIXM::UNLIMITED+ - no upper limit expressed as "FL 999"
|
|
15
|
+
class Z
|
|
13
16
|
CODES = %i(qfe qnh qne).freeze
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
# @return [Integer] elevation or altitude value
|
|
19
|
+
attr_reader :alt
|
|
20
|
+
|
|
21
|
+
# @return [Symbol] Q code - either +:qfe+ (height in feet), +:qnh+ (altitude in feet or +:qne+ (altitude as flight level)
|
|
22
|
+
attr_reader :code
|
|
16
23
|
|
|
17
24
|
def initialize(alt, code)
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
self.alt, self.code = alt, code
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [String]
|
|
29
|
+
def inspect
|
|
30
|
+
%Q(#<#{self.class} #{to_s}>)
|
|
20
31
|
end
|
|
21
32
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
[alt, code].to_digest
|
|
33
|
+
# @return [String] human readable representation (e.g. "FL045" or "1350 ft QNH")
|
|
34
|
+
def to_s
|
|
35
|
+
qne? ? "FL%03i" % alt : [alt, unit, code.upcase].join(' ')
|
|
26
36
|
end
|
|
27
37
|
|
|
38
|
+
def alt=(value)
|
|
39
|
+
fail(ArgumentError, "invalid alt") unless value.is_a? Numeric
|
|
40
|
+
@alt = value.to_i
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def code=(value)
|
|
44
|
+
fail(ArgumentError, "invalid code") unless value.respond_to? :to_sym
|
|
45
|
+
@code = value.to_sym.downcase
|
|
46
|
+
fail(ArgumentError, "invalid code") unless CODES.include? @code
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @return [Boolean]
|
|
28
50
|
def ==(other)
|
|
29
|
-
other.is_a?(
|
|
51
|
+
other.is_a?(self.class) && alt == other.alt && code == other.code
|
|
30
52
|
end
|
|
31
53
|
|
|
54
|
+
# @example
|
|
55
|
+
# z = AIXM.z(123, :qnh)
|
|
56
|
+
# z.qnh? # => true
|
|
57
|
+
# z.qfe? # => false
|
|
58
|
+
#
|
|
59
|
+
# @!method qfe?
|
|
60
|
+
# @!method qnh?
|
|
61
|
+
# @!method qne?
|
|
62
|
+
# @return [Boolean]
|
|
32
63
|
CODES.each do |code|
|
|
33
64
|
define_method(:"#{code}?") { @code == code }
|
|
34
65
|
end
|
|
35
66
|
|
|
67
|
+
# @return [Boolean] whether ground level or not
|
|
36
68
|
def ground?
|
|
37
69
|
qfe? && @alt == 0
|
|
38
70
|
end
|
|
39
71
|
|
|
40
|
-
|
|
41
|
-
qfe? ? :ASFC : :AMSL
|
|
42
|
-
end
|
|
43
|
-
|
|
72
|
+
# @return [Symbol] unit - either +:fl+ (flight level) or +:ft+ (feet)
|
|
44
73
|
def unit
|
|
45
|
-
qne? ? :
|
|
74
|
+
qne? ? :fl : :ft
|
|
46
75
|
end
|
|
47
76
|
|
|
48
77
|
end
|