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.
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