aixm 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|