aixm 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.ruby-version +1 -1
  4. data/.yardopts +3 -0
  5. data/CHANGELOG.md +34 -14
  6. data/Guardfile +1 -0
  7. data/README.md +64 -257
  8. data/lib/aixm.rb +16 -7
  9. data/lib/aixm/component.rb +6 -0
  10. data/lib/aixm/component/frequency.rb +135 -0
  11. data/lib/aixm/component/geometry.rb +34 -23
  12. data/lib/aixm/component/geometry/arc.rb +37 -22
  13. data/lib/aixm/component/geometry/border.rb +29 -20
  14. data/lib/aixm/component/geometry/circle.rb +39 -22
  15. data/lib/aixm/component/geometry/point.rb +29 -13
  16. data/lib/aixm/component/helipad.rb +154 -0
  17. data/lib/aixm/component/layer.rb +91 -0
  18. data/lib/aixm/component/runway.rb +294 -0
  19. data/lib/aixm/component/service.rb +170 -0
  20. data/lib/aixm/component/timetable.rb +65 -0
  21. data/lib/aixm/component/vertical_limits.rb +65 -29
  22. data/lib/aixm/config.rb +87 -0
  23. data/lib/aixm/document.rb +66 -42
  24. data/lib/aixm/errors.rb +11 -0
  25. data/lib/aixm/f.rb +34 -20
  26. data/lib/aixm/feature.rb +38 -0
  27. data/lib/aixm/feature/airport.rb +473 -0
  28. data/lib/aixm/feature/airspace.rb +145 -92
  29. data/lib/aixm/feature/navigational_aid.rb +94 -0
  30. data/lib/aixm/feature/navigational_aid/designated_point.rb +50 -54
  31. data/lib/aixm/feature/navigational_aid/dme.rb +48 -40
  32. data/lib/aixm/feature/navigational_aid/marker.rb +55 -45
  33. data/lib/aixm/feature/navigational_aid/ndb.rb +54 -50
  34. data/lib/aixm/feature/navigational_aid/tacan.rb +38 -31
  35. data/lib/aixm/feature/navigational_aid/vor.rb +84 -76
  36. data/lib/aixm/feature/organisation.rb +97 -0
  37. data/lib/aixm/feature/unit.rb +152 -0
  38. data/lib/aixm/refinements.rb +132 -47
  39. data/lib/aixm/shortcuts.rb +11 -6
  40. data/lib/aixm/version.rb +1 -1
  41. data/lib/aixm/xy.rb +64 -20
  42. data/lib/aixm/z.rb +51 -22
  43. data/{lib/aixm/schemas → schemas/aixm}/4.5/AIXM-DataTypes.xsd +0 -0
  44. data/{lib/aixm/schemas → schemas/aixm}/4.5/AIXM-Features.xsd +0 -0
  45. data/{lib/aixm/schemas → schemas/aixm}/4.5/AIXM-Snapshot.xsd +0 -0
  46. data/schemas/ofmx/0/OFMX-DataTypes.xsd +5077 -0
  47. data/schemas/ofmx/0/OFMX-Features.xsd +9955 -0
  48. data/schemas/ofmx/0/OFMX-Snapshot.xsd +217 -0
  49. data/spec/factory.rb +209 -33
  50. data/spec/lib/aixm/component/frequency_spec.rb +75 -0
  51. data/spec/lib/aixm/component/geometry/arc_spec.rb +28 -22
  52. data/spec/lib/aixm/component/geometry/border_spec.rb +23 -20
  53. data/spec/lib/aixm/component/geometry/circle_spec.rb +31 -22
  54. data/spec/lib/aixm/component/geometry/point_spec.rb +11 -14
  55. data/spec/lib/aixm/component/geometry_spec.rb +150 -69
  56. data/spec/lib/aixm/component/helipad_spec.rb +136 -0
  57. data/spec/lib/aixm/component/layer_spec.rb +110 -0
  58. data/spec/lib/aixm/component/runway_spec.rb +402 -0
  59. data/spec/lib/aixm/component/service_spec.rb +61 -0
  60. data/spec/lib/aixm/component/timetable_spec.rb +49 -0
  61. data/spec/lib/aixm/component/vertical_limits_spec.rb +39 -20
  62. data/spec/lib/aixm/config_spec.rb +41 -0
  63. data/spec/lib/aixm/document_spec.rb +637 -147
  64. data/spec/lib/aixm/errors_spec.rb +14 -0
  65. data/spec/lib/aixm/f_spec.rb +17 -10
  66. data/spec/lib/aixm/feature/airport_spec.rb +546 -0
  67. data/spec/lib/aixm/feature/airspace_spec.rb +349 -226
  68. data/spec/lib/aixm/feature/navigational_aid/designated_point_spec.rb +47 -36
  69. data/spec/lib/aixm/feature/navigational_aid/dme_spec.rb +61 -36
  70. data/spec/lib/aixm/feature/navigational_aid/marker_spec.rb +61 -113
  71. data/spec/lib/aixm/feature/navigational_aid/ndb_spec.rb +65 -79
  72. data/spec/lib/aixm/feature/navigational_aid/tacan_spec.rb +57 -36
  73. data/spec/lib/aixm/feature/navigational_aid/vor_spec.rb +86 -112
  74. data/spec/lib/aixm/feature/navigational_aid_spec.rb +52 -0
  75. data/spec/lib/aixm/feature/organisation_spec.rb +77 -0
  76. data/spec/lib/aixm/feature/unit_spec.rb +227 -0
  77. data/spec/lib/aixm/feature_spec.rb +58 -0
  78. data/spec/lib/aixm/refinements_spec.rb +187 -178
  79. data/spec/lib/aixm/xy_spec.rb +45 -34
  80. data/spec/lib/aixm/z_spec.rb +19 -21
  81. data/spec/macros/organisation.rb +11 -0
  82. data/spec/macros/remarks.rb +12 -0
  83. data/spec/macros/timetable.rb +11 -0
  84. data/spec/macros/xy.rb +11 -0
  85. data/spec/macros/z_qnh.rb +11 -0
  86. data/spec/spec_helper.rb +26 -0
  87. metadata +60 -19
  88. data/lib/aixm/base.rb +0 -10
  89. data/lib/aixm/component/base.rb +0 -6
  90. data/lib/aixm/component/class_layer.rb +0 -46
  91. data/lib/aixm/component/geometry/base.rb +0 -8
  92. data/lib/aixm/component/schedule.rb +0 -43
  93. data/lib/aixm/feature/base.rb +0 -6
  94. data/lib/aixm/feature/navigational_aid/base.rb +0 -79
  95. data/spec/lib/aixm/component/class_layer_spec.rb +0 -74
  96. data/spec/lib/aixm/component/schedule_spec.rb +0 -33
  97. data/spec/lib/aixm/feature/navigational_aid/base_spec.rb +0 -41
@@ -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('|')).gsub(/\D/, '')[0, 9].to_i
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") : default)
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
- # Upcase and transliterate to match the reduced character set for
70
- # AIXM names and titles
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
- # Convert DMS angle to DD or +nil+ if the format is not recognized
82
- #
83
- # Supported formats:
84
- # * {-}{DD}D°MM'SS{.SS}"
85
- # * {-}{DD}D MM SS{.SS}
86
- # * {-}{DD}DMMSS{.SS}
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
- # Convert DD angle to DMS with the degrees zero padded to +padding+ length
103
- #
104
- # Output format:
105
- # * {-}D°MM'SS.SS"
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
- # Convert a distance +from+ unit (+:km+, +:m+, +:nm+ or +:ft+) to kilometers
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
@@ -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
- class_layer: Component::ClassLayer,
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 = schedule(code: :H24).freeze
40
+ H24 = timetable(code: :H24).freeze
36
41
 
37
42
  end
@@ -1,3 +1,3 @@
1
1
  module AIXM
2
- VERSION = "0.2.3".freeze
2
+ VERSION = "0.3.0".freeze
3
3
  end
@@ -1,42 +1,86 @@
1
+ using AIXM::Refinements
2
+
1
3
  module AIXM
2
4
 
3
- ##
4
5
  # Geographical coordinates
5
6
  #
6
- # The following notations for longitude and latitude are recognized:
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
- class XY < Base
10
- using AIXM::Refinements
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
- @lat, @long = float_for(lat), float_for(long)
14
- fail(ArgumentError, "illegal latitude") unless (-90..90).include? @lat
15
- fail(ArgumentError, "illegal longitude") unless (-180..180).include? @long
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
- def lat(format=nil)
19
- case format
20
- when :ofm then ("%.8f" % @lat.abs.round(8)) + (@lat.negative? ? 'S' : 'N')
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
- def long(format=nil)
27
- case format
28
- when :ofm then ("%.8f" % @long.abs.round(8)) + (@long.negative? ? 'W' : 'E')
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
- def to_digest
35
- [lat, long].to_digest
66
+ # @return [Boolean]
67
+ def ==(other)
68
+ other.is_a?(self.class) && lat == other.lat && long == other.long
36
69
  end
37
70
 
38
- def ==(other)
39
- other.is_a?(XY) && lat == other.lat && long == other.long
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 =~ /[SW]$/ ? -1 : 1)
48
- else fail(ArgumentError, "illegal value class `#{value.class}'")
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, "illegal value `#{value}'")
95
+ fail(ArgumentError, "invalid value `#{value}'")
52
96
  end
53
97
 
54
98
  end
@@ -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
- # The following Q codes are recognized:
7
- # * +:qfe+ - height in feet
8
- # * +:qnh+ - altitude in feet
9
- # * +:qne+ - altitude as flight level
10
- class Z < Base
11
- using AIXM::Refinements
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
- attr_reader :alt, :code
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
- @alt, @code = alt, code&.to_sym&.downcase
19
- fail(ArgumentError, "unrecognized Q code `#{code}'") unless CODES.include? @code
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
- # Digest to identify the payload
24
- def to_digest
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?(Z) && alt == other.alt && code == other.code
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
- def base
41
- qfe? ? :ASFC : :AMSL
42
- end
43
-
72
+ # @return [Symbol] unit - either +:fl+ (flight level) or +:ft+ (feet)
44
73
  def unit
45
- qne? ? :FL : :FT
74
+ qne? ? :fl : :ft
46
75
  end
47
76
 
48
77
  end