aixm 0.3.7 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +72 -6
  4. data/README.md +191 -53
  5. data/exe/ckmid +11 -0
  6. data/exe/mkmid +11 -0
  7. data/lib/aixm/association.rb +367 -0
  8. data/lib/aixm/classes.rb +44 -0
  9. data/lib/aixm/component/fato.rb +44 -52
  10. data/lib/aixm/component/frequency.rb +13 -14
  11. data/lib/aixm/component/geometry/arc.rb +2 -2
  12. data/lib/aixm/component/geometry/border.rb +14 -5
  13. data/lib/aixm/component/geometry/circle.rb +8 -2
  14. data/lib/aixm/component/geometry/point.rb +10 -3
  15. data/lib/aixm/component/geometry/rhumb_line.rb +54 -0
  16. data/lib/aixm/component/geometry.rb +38 -38
  17. data/lib/aixm/component/helipad.rb +29 -37
  18. data/lib/aixm/component/layer.rb +28 -19
  19. data/lib/aixm/component/lighting.rb +11 -12
  20. data/lib/aixm/component/runway.rb +46 -53
  21. data/lib/aixm/{feature → component}/service.rb +36 -35
  22. data/lib/aixm/component/surface.rb +3 -3
  23. data/lib/aixm/component/timetable.rb +5 -3
  24. data/lib/aixm/component/{vertical_limits.rb → vertical_limit.rb} +12 -6
  25. data/lib/aixm/config.rb +6 -3
  26. data/lib/aixm/document.rb +31 -49
  27. data/lib/aixm/executables.rb +85 -0
  28. data/lib/aixm/f.rb +28 -0
  29. data/lib/aixm/feature/address.rb +20 -15
  30. data/lib/aixm/feature/airport.rb +113 -129
  31. data/lib/aixm/feature/airspace.rb +54 -23
  32. data/lib/aixm/feature/navigational_aid/designated_point.rb +12 -14
  33. data/lib/aixm/feature/navigational_aid/dme.rb +10 -11
  34. data/lib/aixm/feature/navigational_aid/marker.rb +6 -2
  35. data/lib/aixm/feature/navigational_aid/ndb.rb +6 -2
  36. data/lib/aixm/feature/navigational_aid/tacan.rb +6 -2
  37. data/lib/aixm/feature/navigational_aid/vor.rb +22 -14
  38. data/lib/aixm/feature/navigational_aid.rb +7 -9
  39. data/lib/aixm/feature/obstacle.rb +22 -20
  40. data/lib/aixm/feature/obstacle_group.rb +30 -30
  41. data/lib/aixm/feature/organisation.rb +20 -4
  42. data/lib/aixm/feature/unit.rb +35 -45
  43. data/lib/aixm/feature.rb +13 -3
  44. data/lib/aixm/memoize.rb +89 -0
  45. data/lib/aixm/object.rb +9 -0
  46. data/lib/aixm/payload_hash.rb +114 -0
  47. data/lib/aixm/refinements.rb +34 -50
  48. data/lib/aixm/shortcuts.rb +6 -43
  49. data/lib/aixm/version.rb +1 -1
  50. data/lib/aixm/xy.rb +9 -1
  51. data/lib/aixm.rb +18 -7
  52. data/schemas/ofmx/{0 → 0.1}/OFMX-CSV-Obstacle.json +0 -0
  53. data/schemas/ofmx/{0 → 0.1}/OFMX-CSV.json +0 -0
  54. data/schemas/ofmx/{0 → 0.1}/OFMX-DataTypes.xsd +52 -2
  55. data/schemas/ofmx/{0 → 0.1}/OFMX-Features.xsd +225 -14
  56. data/schemas/ofmx/{0 → 0.1}/OFMX-Snapshot.xsd +0 -5
  57. data.tar.gz.sig +0 -0
  58. metadata +116 -164
  59. metadata.gz.sig +0 -0
  60. data/.gitignore +0 -6
  61. data/.ruby-version +0 -1
  62. data/.travis.yml +0 -8
  63. data/.yardopts +0 -3
  64. data/Guardfile +0 -8
  65. data/aixm.gemspec +0 -35
  66. data/gems.rb +0 -3
  67. data/lib/aixm/component.rb +0 -6
  68. data/rakefile.rb +0 -22
  69. data/spec/factory.rb +0 -559
  70. data/spec/lib/aixm/a_spec.rb +0 -203
  71. data/spec/lib/aixm/component/fato_spec.rb +0 -260
  72. data/spec/lib/aixm/component/frequency_spec.rb +0 -75
  73. data/spec/lib/aixm/component/geometry/arc_spec.rb +0 -75
  74. data/spec/lib/aixm/component/geometry/border_spec.rb +0 -33
  75. data/spec/lib/aixm/component/geometry/circle_spec.rb +0 -70
  76. data/spec/lib/aixm/component/geometry/point_spec.rb +0 -39
  77. data/spec/lib/aixm/component/geometry_spec.rb +0 -321
  78. data/spec/lib/aixm/component/helipad_spec.rb +0 -187
  79. data/spec/lib/aixm/component/layer_spec.rb +0 -137
  80. data/spec/lib/aixm/component/lighting_spec.rb +0 -88
  81. data/spec/lib/aixm/component/runway_spec.rb +0 -472
  82. data/spec/lib/aixm/component/surface_spec.rb +0 -124
  83. data/spec/lib/aixm/component/timetable_spec.rb +0 -49
  84. data/spec/lib/aixm/component/vertical_limits_spec.rb +0 -97
  85. data/spec/lib/aixm/config_spec.rb +0 -41
  86. data/spec/lib/aixm/d_spec.rb +0 -150
  87. data/spec/lib/aixm/document_spec.rb +0 -1875
  88. data/spec/lib/aixm/errors_spec.rb +0 -14
  89. data/spec/lib/aixm/f_spec.rb +0 -85
  90. data/spec/lib/aixm/feature/address_spec.rb +0 -55
  91. data/spec/lib/aixm/feature/airport_spec.rb +0 -770
  92. data/spec/lib/aixm/feature/airspace_spec.rb +0 -390
  93. data/spec/lib/aixm/feature/navigational_aid/designated_point_spec.rb +0 -98
  94. data/spec/lib/aixm/feature/navigational_aid/dme_spec.rb +0 -92
  95. data/spec/lib/aixm/feature/navigational_aid/marker_spec.rb +0 -79
  96. data/spec/lib/aixm/feature/navigational_aid/ndb_spec.rb +0 -89
  97. data/spec/lib/aixm/feature/navigational_aid/tacan_spec.rb +0 -88
  98. data/spec/lib/aixm/feature/navigational_aid/vor_spec.rb +0 -245
  99. data/spec/lib/aixm/feature/navigational_aid_spec.rb +0 -52
  100. data/spec/lib/aixm/feature/obstacle_group_spec.rb +0 -326
  101. data/spec/lib/aixm/feature/obstacle_spec.rb +0 -279
  102. data/spec/lib/aixm/feature/organisation_spec.rb +0 -77
  103. data/spec/lib/aixm/feature/service_spec.rb +0 -59
  104. data/spec/lib/aixm/feature/unit_spec.rb +0 -230
  105. data/spec/lib/aixm/feature_spec.rb +0 -38
  106. data/spec/lib/aixm/p_spec.rb +0 -189
  107. data/spec/lib/aixm/refinements_spec.rb +0 -381
  108. data/spec/lib/aixm/version_spec.rb +0 -7
  109. data/spec/lib/aixm/w_spec.rb +0 -150
  110. data/spec/lib/aixm/xy_spec.rb +0 -180
  111. data/spec/lib/aixm/z_spec.rb +0 -94
  112. data/spec/macros/marking.rb +0 -12
  113. data/spec/macros/organisation.rb +0 -11
  114. data/spec/macros/remarks.rb +0 -12
  115. data/spec/macros/timetable.rb +0 -11
  116. data/spec/macros/xy.rb +0 -11
  117. data/spec/macros/z_qnh.rb +0 -11
  118. data/spec/sounds/failure.mp3 +0 -0
  119. data/spec/sounds/success.mp3 +0 -0
  120. data/spec/spec_helper.rb +0 -55
@@ -0,0 +1,85 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+ module Executables
5
+
6
+ class Mkmid
7
+ def initialize(**options)
8
+ @options = options
9
+ OptionParser.new do |o|
10
+ o.banner = <<~END
11
+ Add mid attributes to a schema valid OFMX file.
12
+ Usage: #{File.basename($0)} infile.ofmx
13
+ END
14
+ o.on('-i', '--[no-]in-place', 'overwrite file instead of dumping to STDOUT (default: false)') { @options[:in_place] = _1 }
15
+ o.on('-f', '--[no-]force', 'ignore XML schema validation errors (default: false)') { @options[:force] = _1 }
16
+ o.on('-A', '--about', 'show author/license information and exit') { AIXM::Executables.about }
17
+ o.on('-V', '--version', 'show version and exit') { AIXM::Executables.version }
18
+ end.parse!
19
+ @infile = ARGV.shift
20
+ end
21
+
22
+ def run
23
+ fail 'cannot read file' unless @infile && File.readable?(@infile)
24
+ fail 'file ist not OFMX' unless @infile.match?(/\.ofmx$/)
25
+ AIXM.ofmx!
26
+ document = File.open(@infile) { Nokogiri::XML(_1) }
27
+ AIXM::PayloadHash::Mid.new(document).insert_mid
28
+ errors = Nokogiri::XML::Schema(File.open(AIXM.schema(:xsd))).validate(document)
29
+ case
30
+ when errors.any? && !@options[:force]
31
+ puts errors
32
+ fail "OFMX file is not schema valid"
33
+ when @options[:in_place]
34
+ File.write(@infile, document.to_xml)
35
+ else
36
+ puts document.to_xml
37
+ end
38
+ rescue => error
39
+ puts "ERROR: #{error.message}"
40
+ exit 1
41
+ end
42
+ end
43
+
44
+ class Ckmid
45
+ def initialize(**options)
46
+ OptionParser.new do |o|
47
+ o.banner = <<~END
48
+ Check mid attributes of an OFMX file.
49
+ Usage: #{File.basename($0)} infile.ofmx
50
+ END
51
+ o.on('-A', '--about', 'show author/license information and exit') { AIXM::Executables.about }
52
+ o.on('-V', '--version', 'show version and exit') { AIXM::Executables.version }
53
+ end.parse!
54
+ @infile = ARGV.shift
55
+ end
56
+
57
+ def run
58
+ fail 'cannot read file' unless @infile && File.readable?(@infile)
59
+ fail 'file ist not OFMX' unless @infile.match?(/\.ofmx$/)
60
+ AIXM.ofmx!
61
+ document = File.open(@infile) { Nokogiri::XML(_1) }
62
+ errors = Nokogiri::XML::Schema(File.open(AIXM.schema(:xsd))).validate(document)
63
+ errors += AIXM::PayloadHash::Mid.new(document).check_mid
64
+ if errors.any?
65
+ puts errors
66
+ fail "OFMX file has errors"
67
+ end
68
+ rescue => error
69
+ puts "ERROR: #{error.message}"
70
+ exit 1
71
+ end
72
+ end
73
+
74
+ def self.about
75
+ puts 'Written by Sven Schwyn (bitcetera.com) and distributed under MIT license.'
76
+ exit
77
+ end
78
+
79
+ def self.version
80
+ puts AIXM::VERSION
81
+ exit
82
+ end
83
+
84
+ end
85
+ end
data/lib/aixm/f.rb CHANGED
@@ -64,6 +64,34 @@ module AIXM
64
64
  to_s.hash
65
65
  end
66
66
 
67
+ # @return [Boolean] whether this frequency is part of the voice airband
68
+ # for civil aviation using `AIXM.config.voice_channel_separation`
69
+ def voice?
70
+ return false unless unit == :mhz
71
+ case AIXM.config.voice_channel_separation
72
+ when 25 then voice_25?
73
+ when 833 then voice_833?
74
+ when :any then voice_25? || voice_833?
75
+ else fail(ArgumentError, "unknown voice channel separation")
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ # @return [Boolean] whether this frequency is part of the voice airband
82
+ # for civil aviation using 25 kHz channel separation
83
+ def voice_25?
84
+ return false unless unit == :mhz && freq == freq.round(3) && freq.between?(118, 136.975)
85
+ ((freq * 1000).round % 25).zero?
86
+ end
87
+
88
+ # @return [Boolean] whether this frequency is part of the voice airband
89
+ # for civil aviation using 8.33 kHz channel separation
90
+ def voice_833?
91
+ return false unless unit == :mhz && freq == freq.round(3) && freq.between?(118, 136.99)
92
+ [0.005, 0.01, 0.015].any? { |d| (((freq - d) * 1000).round % 25).zero? }
93
+ end
94
+
67
95
  end
68
96
 
69
97
  end
@@ -9,12 +9,15 @@ module AIXM
9
9
  # address = AIXM.address(
10
10
  # source: String or nil
11
11
  # type: TYPES
12
- # address: String
12
+ # address: AIXM.f (type :radio_frequency) or String (other types)
13
13
  # )
14
14
  # service.remarks = String or nil
15
15
  #
16
- # @see https://github.com/openflightmaps/ofmx/wiki/Airport#aha-airport-address
16
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Airport#aha-airport-address
17
17
  class Address < Feature
18
+ include AIXM::Association
19
+ include AIXM::Memoize
20
+
18
21
  public_class_method :new
19
22
 
20
23
  TYPES = {
@@ -33,8 +36,9 @@ module AIXM
33
36
  OTHER: :other # specify in remarks
34
37
  }
35
38
 
36
- # @return [AIXM::Feature] addressable feature
37
- attr_reader :addressable
39
+ # @!method addressable
40
+ # @return [AIXM::Feature] addressable feature
41
+ belongs_to :addressable
38
42
 
39
43
  # @return [Symbol] type of address (see {TYPES})
40
44
  attr_reader :type
@@ -45,8 +49,8 @@ module AIXM
45
49
  # @return [String, nil] free text remarks
46
50
  attr_reader :remarks
47
51
 
48
- def initialize(source: nil, type:, address:)
49
- super(source: source)
52
+ def initialize(source: nil, region: nil, type:, address:)
53
+ super(source: source, region: region)
50
54
  self.type, self.address = type, address
51
55
  end
52
56
 
@@ -55,19 +59,19 @@ module AIXM
55
59
  %Q(#<#{self.class} type=#{type.inspect}>)
56
60
  end
57
61
 
58
- def addressable=(value)
59
- fail(ArgumentError, "invalid addressable") unless value.is_a? AIXM::Feature
60
- @addressable = value
61
- end
62
- private :addressable=
63
-
64
62
  def type=(value)
65
63
  @type = TYPES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid type")
66
64
  end
67
65
 
68
66
  def address=(value)
69
- fail(ArgumentError, "invalid address") unless value.is_a? String
70
- @address = value&.to_s
67
+ case type
68
+ when :radio_frequency
69
+ fail(ArgumentError, "invalid address frequency") unless value.is_a?(AIXM::F)
70
+ @address = value
71
+ else
72
+ fail(ArgumentError, "invalid address") unless value
73
+ @address = value.to_s
74
+ end
71
75
  end
72
76
 
73
77
  def remarks=(value)
@@ -79,10 +83,11 @@ module AIXM
79
83
  builder = Builder::XmlMarkup.new(indent: 2)
80
84
  builder.tag!(as) do |tag|
81
85
  tag << addressable.to_uid.indent(2) if addressable
82
- tag.codeType(TYPES.key(type).to_s.then_if(AIXM.aixm?) { |t| t.sub(/-\w+$/, '') })
86
+ tag.codeType(TYPES.key(type).to_s.then_if(AIXM.aixm?) { _1.sub(/-\w+$/, '') })
83
87
  tag.noSeq(sequence)
84
88
  end
85
89
  end
90
+ memoize :to_uid
86
91
 
87
92
  # @return [String] AIXM or OFMX markup
88
93
  def to_xml(as:, sequence:)
@@ -9,6 +9,7 @@ module AIXM
9
9
  # ===Cheat Sheet in Pseudo Code:
10
10
  # airport = AIXM.airport(
11
11
  # source: String or nil
12
+ # region: String or nil
12
13
  # organisation: AIXM.organisation
13
14
  # id: String
14
15
  # name: String
@@ -31,8 +32,11 @@ module AIXM
31
32
  # For airports without an +id+, you may assign the two character region
32
33
  # (e.g. "LF") which will be combined with an 8 character digest of +name+.
33
34
  #
34
- # @see https://github.com/openflightmaps/ofmx/wiki/Airport#ahp-airport
35
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Airport#ahp-airport
35
36
  class Airport < Feature
37
+ include AIXM::Association
38
+ include AIXM::Memoize
39
+
36
40
  public_class_method :new
37
41
 
38
42
  ID_RE = /^([A-Z]{3,4}|[A-Z]{2}[A-Z\d]{4,})$/.freeze
@@ -44,8 +48,53 @@ module AIXM
44
48
  LS: :landing_site
45
49
  }.freeze
46
50
 
47
- # @return [AIXM::Feature::Organisation] superior organisation
48
- attr_reader :organisation
51
+ # @!method addresses
52
+ # @return [Array<AIXM::Feature::Address>] postal address, url, A/A or A/G frequency etc
53
+ # @!method add_address(address)
54
+ # @param address [AIXM::Feature::Address]
55
+ # @return [self]
56
+ has_many :addresses, as: :addressable
57
+
58
+ # @!method fatos
59
+ # @return [Array<AIXM::Component::FATO>] FATOs present at this airport
60
+ # @!method add_fato(fato)
61
+ # @param fato [AIXM::Component::FATO]
62
+ has_many :fatos
63
+
64
+ # @!method helipads
65
+ # @return [Array<AIXM::Component::Helipad>] helipads present at this airport
66
+ # @!method add_helipad(helipad)
67
+ # @param helipad [AIXM::Component::Helipad]
68
+ has_many :helipads
69
+
70
+ # @!method runways
71
+ # @return [Array<AIXM::Component::Runway>] runways present at this airport
72
+ # @!method add_runway(runway)
73
+ # @param runway [AIXM::Component::Runway]
74
+ has_many :runways
75
+
76
+ # @!method usage_limitations
77
+ # @return [Array<AIXM::Feature::Airport::UsageLimitation>] usage limitations
78
+ # @!method add_usage_limitation
79
+ # @yield [AIXM::Feature::Airport::UsageLimitation]
80
+ # @return [self]
81
+ has_many :usage_limitations, accept: 'AIXM::Feature::Airport::UsageLimitation' do |usage_limitation, type:| end
82
+
83
+ # @!method designated_points
84
+ # @return [Array<AIXM::Feature::NavigationalAid::DesignatedPoint>] designated points
85
+ # @!method add_designated_point(designated_point)
86
+ # @param designated_point [AIXM::Feature::NavigationalAid::DesignatedPoint]
87
+ has_many :designated_points
88
+
89
+ # @!method units
90
+ # @return [Array<AIXM::Feature::Unit>] units
91
+ # @!method add_unit(unit)
92
+ # @param unit [AIXM::Feature::Unit]
93
+ has_many :units
94
+
95
+ # @!method organisation
96
+ # @return [AIXM::Feature::Organisation] superior organisation
97
+ belongs_to :organisation, as: :member
49
98
 
50
99
  # ICAO indicator, IATA indicator or generated indicator
51
100
  #
@@ -74,6 +123,9 @@ module AIXM
74
123
  # declination represents the magnetic north is to the right (aka: east)
75
124
  # by this angle.
76
125
  #
126
+ # To convert a magnetic bearing to the corresponding geographic (aka:
127
+ # true) bearing, the declination has to be added.
128
+ #
77
129
  # @see https://en.wikipedia.org/wiki/Magnetic_declination
78
130
  # @return [Float, nil] magnetic declination in degrees
79
131
  attr_reader :declination
@@ -90,26 +142,10 @@ module AIXM
90
142
  # @return [String, nil] free text remarks
91
143
  attr_reader :remarks
92
144
 
93
- # @return [Array<AIXM::Component::Runway>] runways present at this airport
94
- attr_reader :runways
95
-
96
- # @return [Array<AIXM::Component::FATO>] FATOs present at this airport
97
- attr_reader :fatos
98
-
99
- # @return [Array<AIXM::Component::Helipad>] helipads present at this airport
100
- attr_reader :helipads
101
-
102
- # @return [Array<AIXM::Feature::Airport::UsageLimitation>] usage limitations
103
- attr_accessor :usage_limitations
104
-
105
- # @return [Array<AIXM::Feature::Address>] postal address, url, A/A or A/G frequency etc
106
- attr_reader :addresses
107
-
108
- def initialize(source: nil, organisation:, id: nil, name:, xy:)
109
- super(source: source)
145
+ def initialize(source: nil, region: nil, organisation:, id: nil, name:, xy:)
146
+ super(source: source, region: region)
110
147
  self.organisation, self.name, self.xy = organisation, name, xy
111
148
  self.id = id # name must already be set
112
- @runways, @fatos, @helipads, @usage_limitations, @addresses = [], [], [], [], []
113
149
  end
114
150
 
115
151
  # @return [String]
@@ -117,15 +153,10 @@ module AIXM
117
153
  %Q(#<#{self.class} id=#{id.inspect}>)
118
154
  end
119
155
 
120
- def organisation=(value)
121
- fail(ArgumentError, "invalid organisation") unless value.is_a? AIXM::Feature::Organisation
122
- @organisation = value
123
- end
124
-
125
156
  # For airports without an +id+, you may assign the two character region
126
157
  # (e.g. "LF") which will be combined with an 8 character digest of +name+.
127
158
  def id=(value)
128
- value = [value, [name].to_digest].join.upcase if value&.upcase&.match? AIXM::Document::REGION_RE
159
+ value = [value, [name].to_digest].join.upcase if value&.upcase&.match? AIXM::Feature::REGION_RE
129
160
  fail(ArgumentError, "invalid id") unless value&.upcase&.match? ID_RE
130
161
  @id = value.upcase
131
162
  end
@@ -196,92 +227,22 @@ module AIXM
196
227
  @remarks = value&.to_s
197
228
  end
198
229
 
199
- # Add a runway to the airport.
200
- #
201
- # @param runway [AIXM::Component::Runway] runway instance
202
- # @return [self]
203
- def add_runway(runway)
204
- fail(ArgumentError, "invalid runway") unless runway.is_a? AIXM::Component::Runway
205
- runway.send(:airport=, self)
206
- @runways << runway
207
- self
208
- end
209
-
210
- # Add a FATO to the airport.
211
- #
212
- # @param FATO [AIXM::Component::FATO] FATO instance
213
- # @return [self]
214
- def add_fato(fato)
215
- fail(ArgumentError, "invalid FATO") unless fato.is_a? AIXM::Component::FATO
216
- fato.send(:airport=, self)
217
- @fatos << fato
218
- self
219
- end
220
-
221
- # Add a helipad to the airport.
222
- #
223
- # @param helipad [AIXM::Component::Helipad] helipad instance
224
- # @return [self]
225
- def add_helipad(helipad)
226
- fail(ArgumentError, "invalid helipad") unless helipad.is_a? AIXM::Component::Helipad
227
- helipad.send(:airport=, self)
228
- @helipads << helipad
229
- self
230
- end
231
-
232
- # Add an airport usage limitation.
233
- #
234
- # See {AIXM::Feature::Airport::UsageLimitation::TYPES UsageLimitation::TYPES}
235
- # for recognized limitations and {AIXM::Feature::Airport::UsageLimitation#add_condition UsageLimitation#add_condition}
236
- # for recognized conditions.
237
- #
238
- # Multiple conditions are joined with an implicit *or* whereas the
239
- # specifics of a condition (aircraft, rule etc) are joined with an
240
- # implicit *and*.
241
- #
242
- # @example Limitation applying to any traffic
243
- # airport.add_usage_limitation(:permitted)
244
- #
245
- # @example Limitation applying to specific traffic
246
- # airport.add_usage_limitation(:reservation_required) do |reservation_required|
247
- # reservation_required.add_condition do |condition|
248
- # condition.aircraft = :glider
249
- # end
250
- # reservation_required.add_condition do |condition|
251
- # condition.rule = :ifr
252
- # condition.origin = :international
253
- # end
254
- # reservation_required.timetable = AIXM::H24
255
- # reservation_required.remarks = "Reservation 24 HRS prior to arrival"
256
- # end
257
- #
258
- # @yieldparam usage_limitation [AIXM::Feature::Airport::UsageLimitation]
259
- # @return [self]
260
- def add_usage_limitation(type)
261
- usage_limitation = UsageLimitation.new(type: type)
262
- yield(usage_limitation) if block_given?
263
- @usage_limitations << usage_limitation
264
- self
265
- end
266
-
267
- # Add an address (postal address, url, A/A or A/G frequency etc) to the airport.
268
- #
269
- # @params address [AIXM::Feature::Address] address instance
270
- # @return [self]
271
- def add_address(address)
272
- fail(ArgumentError, "invalid address") unless address.is_a? AIXM::Feature::Address
273
- address.send(:addressable=, self)
274
- @addresses << address
275
- self
276
- end
277
-
278
230
  # @return [String] UID markup
279
231
  def to_uid(as: :AhpUid)
280
232
  builder = Builder::XmlMarkup.new(indent: 2)
281
- builder.tag!(as) do |tag|
233
+ builder.tag!(as, ({ region: (region if AIXM.ofmx?) }.compact)) do |tag|
282
234
  tag.codeId(id)
283
235
  end
284
236
  end
237
+ memoize :to_uid
238
+
239
+ # @return [String] UID markup
240
+ def to_wrapped_uid(as: :AhpUid, with:)
241
+ builder = Builder::XmlMarkup.new(indent: 2)
242
+ builder.tag!(with) do |tag|
243
+ tag << to_uid(as: as).indent(2)
244
+ end
245
+ end
285
246
 
286
247
  # @return [String] AIXM or OFMX markup
287
248
  def to_xml
@@ -322,9 +283,7 @@ module AIXM
322
283
  end
323
284
  if usage_limitations.any?
324
285
  builder.Ahu do |ahu|
325
- ahu.AhuUid do |ahu_uid|
326
- ahu_uid << to_uid.indent(4)
327
- end
286
+ ahu << to_wrapped_uid(with: :AhuUid).indent(2)
328
287
  usage_limitations.each do |usage_limitation|
329
288
  ahu << usage_limitation.to_xml.indent(2)
330
289
  end
@@ -340,9 +299,35 @@ module AIXM
340
299
  # Limitations concerning the availability of an airport for certain flight
341
300
  # types, aircraft types etc during specific hours.
342
301
  #
302
+ # See {AIXM::Feature::Airport::UsageLimitation::TYPES UsageLimitation::TYPES}
303
+ # for recognized limitations and {AIXM::Feature::Airport::UsageLimitation#add_condition UsageLimitation#add_condition}
304
+ # for recognized conditions.
305
+ #
306
+ # Multiple conditions are joined with an implicit *or* whereas the
307
+ # specifics of a condition (aircraft, rule etc) are joined with an
308
+ # implicit *and*.
309
+ #
310
+ # @example Limitation applying to any traffic
311
+ # airport.add_usage_limitation(type: :permitted)
312
+ #
313
+ # @example Limitation applying to specific traffic
314
+ # airport.add_usage_limitation(type: :reservation_required) do |reservation_required|
315
+ # reservation_required.add_condition do |condition|
316
+ # condition.aircraft = :glider
317
+ # end
318
+ # reservation_required.add_condition do |condition|
319
+ # condition.rule = :ifr
320
+ # condition.origin = :international
321
+ # end
322
+ # reservation_required.timetable = AIXM::H24
323
+ # reservation_required.remarks = "Reservation 24 HRS prior to arrival"
324
+ # end
325
+ #
343
326
  # @see AIXM::Feature::Airport#add_usage_limitation
344
- # @see https://github.com/openflightmaps/ofmx/wiki/Airport#ahu-airport-usage
327
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Airport#ahu-airport-usage
345
328
  class UsageLimitation
329
+ include AIXM::Association
330
+
346
331
  TYPES = {
347
332
  PERMIT: :permitted,
348
333
  FORBID: :forbidden,
@@ -350,15 +335,20 @@ module AIXM
350
335
  OTHER: :other # specify in remarks
351
336
  }.freeze
352
337
 
353
- # @return [AIXM::Feature::Airport] airport this usage limitation is assigned to
354
- attr_reader :airport
338
+ # @!method conditions
339
+ # @return [Array<AIXM::Feature::Airport::UsageLimitation::Condition>] conditions for this limitation to apply
340
+ # @!method add_condition
341
+ # @yield [AIXM::Feature::Airport::UsageLimitation::Condition]
342
+ # @return [self]
343
+ has_many :conditions, accept: 'AIXM::Feature::Airport::UsageLimitation::Condition' do |condition| end
344
+
345
+ # @!method airport
346
+ # @return [AIXM::Feature::Airport] airport this usage limitation is assigned to
347
+ belongs_to :airport
355
348
 
356
349
  # @return [Symbol] type of limitation
357
350
  attr_reader :type
358
351
 
359
- # @return [Array<AIXM::Feature::Airport::UsageLimitation::Condition>] conditions for this limitation to apply
360
- attr_reader :conditions
361
-
362
352
  # @return [AIXM::Component::Timetable, nil] limitation application hours
363
353
  attr_reader :timetable
364
354
 
@@ -367,7 +357,6 @@ module AIXM
367
357
 
368
358
  def initialize(type:)
369
359
  self.type = type
370
- @conditions = []
371
360
  end
372
361
 
373
362
  # @return [String]
@@ -379,17 +368,6 @@ module AIXM
379
368
  @type = TYPES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid type")
380
369
  end
381
370
 
382
- # Add a condition to the usage limitation.
383
- #
384
- # @yieldparam condition [AIXM::Feature::Airport::UsageLimitation::Condition]
385
- # @return [self]
386
- def add_condition
387
- condition = Condition.new
388
- yield(condition)
389
- @conditions << condition
390
- self
391
- end
392
-
393
371
  def timetable=(value)
394
372
  fail(ArgumentError, "invalid timetable") unless value.nil? || value.is_a?(AIXM::Component::Timetable)
395
373
  @timetable = value
@@ -416,8 +394,10 @@ module AIXM
416
394
  # limitation.
417
395
  #
418
396
  # @see AIXM::Feature::Airport#add_usage_limitation
419
- # @see https://github.com/openflightmaps/ofmx/wiki/Airport#ahu-airport-usage
397
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Airport#ahu-airport-usage
420
398
  class Condition
399
+ include AIXM::Association
400
+
421
401
  AIRCRAFT = {
422
402
  L: :landplane,
423
403
  S: :seaplane,
@@ -463,6 +443,10 @@ module AIXM
463
443
  OTHER: :other # specify in remarks
464
444
  }.freeze
465
445
 
446
+ # @!method usage_limitation
447
+ # @return [AIXM::Feature::Airport::UsageLimitation] usage limitation the condition belongs to
448
+ belongs_to :usage_limitation
449
+
466
450
  # @return [Symbol, nil] kind of aircraft (see {AIRCRAFT})
467
451
  attr_reader :aircraft
468
452