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/errors.rb
ADDED
data/lib/aixm/f.rb
CHANGED
@@ -1,37 +1,51 @@
|
|
1
|
+
using AIXM::Refinements
|
2
|
+
|
1
3
|
module AIXM
|
2
4
|
|
3
|
-
|
4
|
-
# Frequency
|
5
|
+
# Radio frequency for communication, navigation and so forth.
|
5
6
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
using AIXM::Refinements
|
7
|
+
# @example
|
8
|
+
# AIXM.f(123.35, :mhz)
|
9
|
+
class F
|
10
|
+
UNITS = %i(ghz mhz khz).freeze
|
11
11
|
|
12
|
-
|
12
|
+
# @return [Float] frequency
|
13
|
+
attr_reader :freq
|
13
14
|
|
14
|
-
|
15
|
+
# @return [Symbol] unit (see {UNITS})
|
16
|
+
attr_reader :unit
|
15
17
|
|
16
18
|
def initialize(freq, unit)
|
17
|
-
|
18
|
-
|
19
|
+
self.freq, self.unit = freq, unit
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [String]
|
23
|
+
def inspect
|
24
|
+
%Q(#<#{self.class} #{to_s}>)
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String] human readable representation (e.g. "123.35 mhz")
|
28
|
+
def to_s
|
29
|
+
[freq, unit].join(' ')
|
30
|
+
end
|
31
|
+
|
32
|
+
def freq=(value)
|
33
|
+
fail(ArgumentError, "invalid freq") unless value.is_a? Numeric
|
34
|
+
@freq = value.to_f
|
19
35
|
end
|
20
36
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
37
|
+
def unit=(value)
|
38
|
+
fail(ArgumentError, "invalid unit") unless value.respond_to? :to_sym
|
39
|
+
@unit = value.to_sym.downcase
|
40
|
+
fail(ArgumentError, "invalid unit") unless UNITS.include? @unit
|
25
41
|
end
|
26
42
|
|
27
|
-
|
28
|
-
# Check whether two frequencies are identical
|
43
|
+
# @return [Boolean]
|
29
44
|
def ==(other)
|
30
|
-
other.is_a?(
|
45
|
+
other.is_a?(self.class) && freq == other.freq && unit == other.unit
|
31
46
|
end
|
32
47
|
|
33
|
-
|
34
|
-
# Check whether this frequency is part of a frequency band
|
48
|
+
# @return [Boolean] whether this frequency is part of a frequency band
|
35
49
|
def between?(lower_freq, upper_freq, unit)
|
36
50
|
freq.between?(lower_freq, upper_freq) && self.unit == unit
|
37
51
|
end
|
data/lib/aixm/feature.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module AIXM
|
2
|
+
|
3
|
+
# @abstract
|
4
|
+
class Feature
|
5
|
+
private_class_method :new
|
6
|
+
|
7
|
+
# @return [String] reference to source of the feature data
|
8
|
+
attr_reader :source
|
9
|
+
|
10
|
+
def initialize(source: nil, region: nil)
|
11
|
+
self.source, self.region = source, region
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String] reference to source of the feature data
|
15
|
+
def source=(value)
|
16
|
+
fail(ArgumentError, "invalid source") unless value.nil? || value.is_a?(String)
|
17
|
+
@source = value
|
18
|
+
end
|
19
|
+
|
20
|
+
# @!attribute region
|
21
|
+
# @note When assigning +nil+, the global default +AIXM.config.region+ is written instead.
|
22
|
+
# @return [String] region the feature belongs to
|
23
|
+
def region
|
24
|
+
@region || AIXM.config.region&.upcase
|
25
|
+
end
|
26
|
+
|
27
|
+
def region=(value)
|
28
|
+
fail(ArgumentError, "invalid region") unless value.nil? || value.is_a?(String)
|
29
|
+
@region = value&.upcase
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Boolean]
|
33
|
+
def ==(other)
|
34
|
+
other.is_a?(self.class) && self.to_uid == other.to_uid
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,473 @@
|
|
1
|
+
using AIXM::Refinements
|
2
|
+
|
3
|
+
module AIXM
|
4
|
+
class Feature
|
5
|
+
|
6
|
+
# Defined area on land or water to be used for the arrival, departure and
|
7
|
+
# surface movement of aircraft.
|
8
|
+
#
|
9
|
+
# ===Cheat Sheet in Pseudo Code:
|
10
|
+
# airport = AIXM.airport(
|
11
|
+
# source: String or nil
|
12
|
+
# region: String or nil (falls back to AIXM.config.region)
|
13
|
+
# organisation: AIXM.organisation
|
14
|
+
# code: String
|
15
|
+
# name: String
|
16
|
+
# xy: AIXM.xy
|
17
|
+
# )
|
18
|
+
# airport.gps = String or nil
|
19
|
+
# airport.type = TYPES
|
20
|
+
# airport.z = AIXM.z or nil
|
21
|
+
# airport.declination = Float or nil
|
22
|
+
# airport.transition_z = AIXM.z or nil
|
23
|
+
# airport.timetable = AIXM.timetable or nil
|
24
|
+
# airport.remarks = String or nil
|
25
|
+
# airport.add_runway(AIXM.runway)
|
26
|
+
# airport.add_helipad(AIXM.helipad)
|
27
|
+
# airport.add_usage_limitation(UsageLimitation::TYPES)
|
28
|
+
#
|
29
|
+
# @see https://github.com/openflightmaps/ofmx/wiki/Airport#ahp-airport
|
30
|
+
class Airport < Feature
|
31
|
+
public_class_method :new
|
32
|
+
|
33
|
+
CODE_PATTERN = /^[A-Z]{2}([A-Z]{1,2}|\d{4})$/.freeze
|
34
|
+
|
35
|
+
TYPES = {
|
36
|
+
AD: :aerodrome,
|
37
|
+
HP: :heliport,
|
38
|
+
AH: :aerodrome_and_heliport,
|
39
|
+
LS: :landing_site
|
40
|
+
}.freeze
|
41
|
+
|
42
|
+
# @return [AIXM::Feature::Organisation] superior organisation
|
43
|
+
attr_reader :organisation
|
44
|
+
|
45
|
+
# ICAO indicator, IATA indicator or ICAO serial number
|
46
|
+
#
|
47
|
+
# * four letter ICAO indicator (e.g. "LFMV")
|
48
|
+
# * three letter IATA indicator (e.g. "AVN")
|
49
|
+
# * two letter ICAO country code + four digit number (e.g. "LF1234")
|
50
|
+
#
|
51
|
+
# @return [String] airport indicator code
|
52
|
+
attr_reader :code
|
53
|
+
|
54
|
+
# @return [String] full name
|
55
|
+
attr_reader :name
|
56
|
+
|
57
|
+
# @return [AIXM::XY] reference point
|
58
|
+
attr_reader :xy
|
59
|
+
|
60
|
+
# @return [String, nil] GPS code
|
61
|
+
attr_reader :gps
|
62
|
+
|
63
|
+
# @return [AIXM::Z, nil] elevation in +:qnh+
|
64
|
+
attr_reader :z
|
65
|
+
|
66
|
+
# When looking towards the geographic (aka: true) north, a positive
|
67
|
+
# declination represents the magnetic north is to the right (aka: east)
|
68
|
+
# by this angle.
|
69
|
+
#
|
70
|
+
# @see https://en.wikipedia.org/wiki/Magnetic_declination
|
71
|
+
# @return [Float, nil] magnetic declination in degrees
|
72
|
+
attr_reader :declination
|
73
|
+
|
74
|
+
# @return [AIXM::Z, nil] transition altitude in +:qnh+
|
75
|
+
attr_reader :transition_z
|
76
|
+
|
77
|
+
# @return [AIXM::Component::Timetable, nil] operating hours
|
78
|
+
attr_reader :timetable
|
79
|
+
|
80
|
+
# @return [String, nil] free text remarks
|
81
|
+
attr_reader :remarks
|
82
|
+
|
83
|
+
# @return [Array<AIXM::Component::Runway>] runways present at this airport
|
84
|
+
attr_reader :runways
|
85
|
+
|
86
|
+
# @return [Array<AIXM::Component::Helipad>] helipads present at this airport
|
87
|
+
attr_reader :helipads
|
88
|
+
|
89
|
+
# @return [Array<AIXM::Feature::Airport::UsageLimitation>] usage limitations
|
90
|
+
attr_accessor :usage_limitations
|
91
|
+
|
92
|
+
def initialize(source: nil, region: nil, organisation:, code:, name:, xy:)
|
93
|
+
super(source: source, region: region)
|
94
|
+
self.organisation, self.code, self.name, self.xy = organisation, code, name, xy
|
95
|
+
@runways, @helipads, @usage_limitations = [], [], []
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [String]
|
99
|
+
def inspect
|
100
|
+
%Q(#<#{self.class} code=#{code.inspect}>)
|
101
|
+
end
|
102
|
+
|
103
|
+
def organisation=(value)
|
104
|
+
fail(ArgumentError, "invalid organisation") unless value.is_a? AIXM::Feature::Organisation
|
105
|
+
@organisation = value
|
106
|
+
end
|
107
|
+
|
108
|
+
def code=(value)
|
109
|
+
fail(ArgumentError, "invalid code `#{code}'") unless value&.upcase&.match? CODE_PATTERN
|
110
|
+
@code = value.upcase
|
111
|
+
end
|
112
|
+
|
113
|
+
def name=(value)
|
114
|
+
fail(ArgumentError, "invalid name") unless value.is_a? String
|
115
|
+
@name = value.uptrans
|
116
|
+
end
|
117
|
+
|
118
|
+
def gps=(value)
|
119
|
+
fail(ArgumentError, "invalid gps") unless value.nil? || value.is_a?(String)
|
120
|
+
@gps = value&.upcase
|
121
|
+
end
|
122
|
+
|
123
|
+
# The type is usually derived from the presence of runways and helipads,
|
124
|
+
# however, this may be overridden by setting an alternative value, most
|
125
|
+
# notably +:landing_site+.
|
126
|
+
#
|
127
|
+
# @return [Symbol] type of airport (see {TYPES})
|
128
|
+
def type
|
129
|
+
@type = case
|
130
|
+
when @type then @type
|
131
|
+
when runways.any? && helipads.any? then :aerodrome_and_heliport
|
132
|
+
when runways.any? then :aerodrome
|
133
|
+
when helipads.any? then :heliport
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def type=(value)
|
138
|
+
resolved_value = TYPES.lookup(value&.to_s&.to_sym, nil)
|
139
|
+
fail(ArgumentError, "invalid type") unless resolved_value == :landing_site
|
140
|
+
@type = resolved_value
|
141
|
+
end
|
142
|
+
|
143
|
+
def xy=(value)
|
144
|
+
fail(ArgumentError, "invalid xy") unless value.is_a? AIXM::XY
|
145
|
+
@xy = value
|
146
|
+
end
|
147
|
+
|
148
|
+
def z=(value)
|
149
|
+
fail(ArgumentError, "invalid z") unless value.nil? || (value.is_a?(AIXM::Z) && value.qnh?)
|
150
|
+
@z = value
|
151
|
+
end
|
152
|
+
|
153
|
+
def declination=(value)
|
154
|
+
return @declination = value if value.nil?
|
155
|
+
fail(ArgumentError, "invalid declination") unless value.is_a?(Numeric) && (-180..180).include?(value)
|
156
|
+
@declination = value.to_f
|
157
|
+
end
|
158
|
+
|
159
|
+
def transition_z=(value)
|
160
|
+
fail(ArgumentError, "invalid transition_z") unless value.nil? || (value.is_a?(AIXM::Z) && value.qnh?)
|
161
|
+
@transition_z = value
|
162
|
+
end
|
163
|
+
|
164
|
+
def timetable=(value)
|
165
|
+
fail(ArgumentError, "invalid timetable") unless value.nil? || value.is_a?(AIXM::Component::Timetable)
|
166
|
+
@timetable = value
|
167
|
+
end
|
168
|
+
|
169
|
+
def remarks=(value)
|
170
|
+
@remarks = value&.to_s
|
171
|
+
end
|
172
|
+
|
173
|
+
# Add a runway to the airport.
|
174
|
+
#
|
175
|
+
# @param runway [AIXM::Component::Runway] runway instance
|
176
|
+
# @return [self]
|
177
|
+
def add_runway(runway)
|
178
|
+
fail(ArgumentError, "invalid runway") unless runway.is_a? AIXM::Component::Runway
|
179
|
+
runway.send(:airport=, self)
|
180
|
+
@runways << runway
|
181
|
+
self
|
182
|
+
end
|
183
|
+
|
184
|
+
# Add a helipad to the airport.
|
185
|
+
#
|
186
|
+
# @param helipad [AIXM::Component::Helipad] helipad instance
|
187
|
+
# @return [self]
|
188
|
+
def add_helipad(helipad)
|
189
|
+
fail(ArgumentError, "invalid helipad") unless helipad.is_a? AIXM::Component::Helipad
|
190
|
+
helipad.send(:airport=, self)
|
191
|
+
@helipads << helipad
|
192
|
+
self
|
193
|
+
end
|
194
|
+
|
195
|
+
# Add an airport usage limitation.
|
196
|
+
#
|
197
|
+
# See {AIXM::Feature::Airport::UsageLimitation::TYPES UsageLimitation::TYPES}
|
198
|
+
# for recognized limitations and {AIXM::Feature::Airport::UsageLimitation#add_condition UsageLimitation#add_condition}
|
199
|
+
# for recognized conditions.
|
200
|
+
#
|
201
|
+
# Multiple conditions are joined with an implicit *or* whereas the
|
202
|
+
# specifics of a condition (aircraft, rule etc) are joined with an
|
203
|
+
# implicit *and*.
|
204
|
+
#
|
205
|
+
# @example Limitation applying to any traffic
|
206
|
+
# airport.add_usage_limitation(:permitted)
|
207
|
+
#
|
208
|
+
# @example Limitation applying to specific traffic
|
209
|
+
# airport.add_usage_limitation(:reservation_required) do |reservation_required|
|
210
|
+
# reservation_required.add_condition do |condition|
|
211
|
+
# condition.aircraft = :glider
|
212
|
+
# end
|
213
|
+
# reservation_required.add_condition do |condition|
|
214
|
+
# condition.rule = :ifr
|
215
|
+
# condition.origin = :international
|
216
|
+
# end
|
217
|
+
# reservation_required.timetable = AIXM::H24
|
218
|
+
# reservation_required.remarks = "Reservation 24 HRS prior to arrival"
|
219
|
+
# end
|
220
|
+
#
|
221
|
+
# @yieldparam usage_limitation [AIXM::Feature::Airport::UsageLimitation]
|
222
|
+
# @return [self]
|
223
|
+
def add_usage_limitation(type)
|
224
|
+
usage_limitation = UsageLimitation.new(type: type)
|
225
|
+
yield(usage_limitation) if block_given?
|
226
|
+
@usage_limitations << usage_limitation
|
227
|
+
self
|
228
|
+
end
|
229
|
+
|
230
|
+
# @return [String] UID markup
|
231
|
+
def to_uid
|
232
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
233
|
+
builder.AhpUid({ region: (region if AIXM.ofmx?) }.compact) do |ahp_uid|
|
234
|
+
ahp_uid.codeId(code)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# @return [String] AIXM or OFMX markup
|
239
|
+
def to_xml
|
240
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
241
|
+
builder.comment! "Airport: #{code} #{name}"
|
242
|
+
builder.Ahp({ source: (source if AIXM.ofmx?) }.compact) do |ahp|
|
243
|
+
ahp << to_uid.indent(2)
|
244
|
+
ahp << organisation.to_uid.indent(2)
|
245
|
+
ahp.txtName(name)
|
246
|
+
ahp.codeIcao(code) if code.length == 4
|
247
|
+
ahp.codeIata(code) if code.length == 3
|
248
|
+
ahp.codeGps(gps) if AIXM.ofmx? && gps
|
249
|
+
ahp.codeType(TYPES.key(type).to_s) if type
|
250
|
+
ahp.geoLat(xy.lat(AIXM.schema))
|
251
|
+
ahp.geoLong(xy.long(AIXM.schema))
|
252
|
+
ahp.codeDatum('WGE')
|
253
|
+
if z
|
254
|
+
ahp.valElev(z.alt)
|
255
|
+
ahp.uomDistVer(z.unit.upcase.to_s)
|
256
|
+
end
|
257
|
+
ahp.valMagVar(declination) if declination
|
258
|
+
if transition_z
|
259
|
+
ahp.valTransitionAlt(transition_z.alt)
|
260
|
+
ahp.uomTransitionAlt(transition_z.unit.upcase.to_s)
|
261
|
+
end
|
262
|
+
ahp << timetable.to_xml(as: :Aht).indent(2) if timetable
|
263
|
+
ahp.txtRmk(remarks) if remarks
|
264
|
+
end
|
265
|
+
runways.each do |runway|
|
266
|
+
builder << runway.to_xml
|
267
|
+
end
|
268
|
+
helipads.each do |helipad|
|
269
|
+
builder << helipad.to_xml
|
270
|
+
end
|
271
|
+
if usage_limitations.any?
|
272
|
+
builder.Ahu do |ahu|
|
273
|
+
ahu.AhuUid do |ahu_uid|
|
274
|
+
ahu_uid << to_uid.indent(4)
|
275
|
+
end
|
276
|
+
usage_limitations.each do |usage_limitation|
|
277
|
+
ahu << usage_limitation.to_xml.indent(2)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
builder.target!
|
282
|
+
end
|
283
|
+
|
284
|
+
# Limitations concerning the availability of an airport for certain flight
|
285
|
+
# types, aircraft types etc during specific hours.
|
286
|
+
#
|
287
|
+
# @see AIXM::Feature::Airport#add_usage_limitation
|
288
|
+
# @see https://github.com/openflightmaps/ofmx/wiki/Airport#ahu-airport-usage
|
289
|
+
class UsageLimitation
|
290
|
+
TYPES = {
|
291
|
+
PERMIT: :permitted,
|
292
|
+
FORBID: :forbidden,
|
293
|
+
RESERV: :reservation_required,
|
294
|
+
OTHER: :other # specify in remarks
|
295
|
+
}.freeze
|
296
|
+
|
297
|
+
# @return [AIXM::Feature::Airport] airport this usage limitation is assigned to
|
298
|
+
attr_reader :airport
|
299
|
+
|
300
|
+
# @return [Symbol] type of limitation
|
301
|
+
attr_reader :type
|
302
|
+
|
303
|
+
# @return [Array<AIXM::Feature::Airport::UsageLimitation::Condition>] conditions for this limitation to apply
|
304
|
+
attr_reader :conditions
|
305
|
+
|
306
|
+
# @return [AIXM::Component::Timetable, nil] limitation application hours
|
307
|
+
attr_reader :timetable
|
308
|
+
|
309
|
+
# @return [String, nil] free text remarks
|
310
|
+
attr_reader :remarks
|
311
|
+
|
312
|
+
def initialize(type:)
|
313
|
+
self.type = type
|
314
|
+
@conditions = []
|
315
|
+
end
|
316
|
+
|
317
|
+
# @return [String]
|
318
|
+
def inspect
|
319
|
+
%Q(#<#{self.class} type=#{type.inspect}>)
|
320
|
+
end
|
321
|
+
|
322
|
+
def type=(value)
|
323
|
+
@type = TYPES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid type")
|
324
|
+
end
|
325
|
+
|
326
|
+
# Add a condition to the usage limitation.
|
327
|
+
#
|
328
|
+
# @yieldparam condition [AIXM::Feature::Airport::UsageLimitation::Condition]
|
329
|
+
# @return [self]
|
330
|
+
def add_condition
|
331
|
+
condition = Condition.new
|
332
|
+
yield(condition)
|
333
|
+
@conditions << condition
|
334
|
+
self
|
335
|
+
end
|
336
|
+
|
337
|
+
def timetable=(value)
|
338
|
+
fail(ArgumentError, "invalid timetable") unless value.nil? || value.is_a?(AIXM::Component::Timetable)
|
339
|
+
@timetable = value
|
340
|
+
end
|
341
|
+
|
342
|
+
def remarks=(value)
|
343
|
+
@remarks = value&.to_s
|
344
|
+
end
|
345
|
+
|
346
|
+
# @return [String] AIXM or OFMX markup
|
347
|
+
def to_xml
|
348
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
349
|
+
builder.UsageLimitation do |usage_limitation|
|
350
|
+
usage_limitation.codeUsageLimitation(TYPES.key(type).to_s)
|
351
|
+
conditions.each do |condition|
|
352
|
+
usage_limitation << condition.to_xml.indent(2)
|
353
|
+
end
|
354
|
+
usage_limitation << timetable.to_xml(as: :Timetable).indent(2) if timetable
|
355
|
+
usage_limitation.txtRmk(remarks) if remarks
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
# Flight and/or aircraft characteristics used to target a usage
|
360
|
+
# limitation.
|
361
|
+
#
|
362
|
+
# @see AIXM::Feature::Airport#add_usage_limitation
|
363
|
+
# @see https://github.com/openflightmaps/ofmx/wiki/Airport#ahu-airport-usage
|
364
|
+
class Condition
|
365
|
+
AIRCRAFT = {
|
366
|
+
L: :landplane,
|
367
|
+
S: :seaplane,
|
368
|
+
A: :amphibian,
|
369
|
+
H: :helicopter,
|
370
|
+
G: :gyrocopter,
|
371
|
+
T: :tilt_wing,
|
372
|
+
R: :short_takeoff_and_landing,
|
373
|
+
E: :glider,
|
374
|
+
N: :hangglider,
|
375
|
+
P: :paraglider,
|
376
|
+
U: :ultra_light,
|
377
|
+
B: :balloon,
|
378
|
+
D: :unmanned_drone,
|
379
|
+
OTHER: :other # specify in remarks
|
380
|
+
}.freeze
|
381
|
+
|
382
|
+
RULES = {
|
383
|
+
I: :ifr,
|
384
|
+
V: :vfr,
|
385
|
+
IV: :ifr_and_vfr
|
386
|
+
}.freeze
|
387
|
+
|
388
|
+
REALMS = {
|
389
|
+
CIVIL: :civilian,
|
390
|
+
MIL: :military,
|
391
|
+
OTHER: :other # specify in remarks
|
392
|
+
}.freeze
|
393
|
+
|
394
|
+
ORIGINS = {
|
395
|
+
NTL: :national,
|
396
|
+
INTL: :international,
|
397
|
+
ANY: :any,
|
398
|
+
OTHER: :other # specify in remarks
|
399
|
+
}.freeze
|
400
|
+
|
401
|
+
PURPOSES = {
|
402
|
+
S: :scheduled,
|
403
|
+
NS: :not_scheduled,
|
404
|
+
P: :private,
|
405
|
+
TRG: :school_or_training,
|
406
|
+
WORK: :aerial_work,
|
407
|
+
OTHER: :other # specify in remarks
|
408
|
+
}.freeze
|
409
|
+
|
410
|
+
# @return [Symbol, nil] kind of aircraft (see {AIRCRAFT})
|
411
|
+
attr_reader :aircraft
|
412
|
+
|
413
|
+
# @return [String, nil] flight rule (see {RULES})
|
414
|
+
attr_reader :rule
|
415
|
+
|
416
|
+
# @return [String, nil] whether military or civil (see {REALMS})
|
417
|
+
attr_reader :realm
|
418
|
+
|
419
|
+
# @return [String, nil] geographic origin of the flight (see {ORIGINS})
|
420
|
+
attr_reader :origin
|
421
|
+
|
422
|
+
# @return [String, nil] purpose of the flight (see {PURPOSES})
|
423
|
+
attr_reader :purpose
|
424
|
+
|
425
|
+
# @return [String]
|
426
|
+
def inspect
|
427
|
+
%Q(#<#{self.class} aircraft=#{aircraft.inspect} rule=#{rule.inspect} realm=#{realm.inspect} origin=#{origin.inspect} purpose=#{purpose.inspect}>)
|
428
|
+
end
|
429
|
+
|
430
|
+
def aircraft=(value)
|
431
|
+
@aircraft = value.nil? ? nil : AIRCRAFT.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid aircraft")
|
432
|
+
end
|
433
|
+
|
434
|
+
def rule=(value)
|
435
|
+
@rule = value.nil? ? nil : RULES.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid rule")
|
436
|
+
end
|
437
|
+
|
438
|
+
def realm=(value)
|
439
|
+
@realm = value.nil? ? nil : REALMS.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid realm")
|
440
|
+
end
|
441
|
+
|
442
|
+
def origin=(value)
|
443
|
+
@origin = value.nil? ? nil : ORIGINS.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid origin")
|
444
|
+
end
|
445
|
+
|
446
|
+
def purpose=(value)
|
447
|
+
@purpose = value.nil? ? nil : PURPOSES.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid purpose")
|
448
|
+
end
|
449
|
+
|
450
|
+
# @return [String] AIXM or OFMX markup
|
451
|
+
def to_xml
|
452
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
453
|
+
builder.UsageCondition do |usage_condition|
|
454
|
+
if aircraft
|
455
|
+
usage_condition.AircraftClass do |aircraft_class|
|
456
|
+
aircraft_class.codeType(AIRCRAFT.key(aircraft).to_s)
|
457
|
+
end
|
458
|
+
end
|
459
|
+
if rule || realm || origin || purpose
|
460
|
+
usage_condition.FlightClass do |flight_class|
|
461
|
+
flight_class.codeRule(RULES.key(rule).to_s) if rule
|
462
|
+
flight_class.codeMil(REALMS.key(realm).to_s) if realm
|
463
|
+
flight_class.codeOrigin(ORIGINS.key(origin).to_s) if origin
|
464
|
+
flight_class.codePurpose(PURPOSES.key(purpose).to_s) if purpose
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|