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