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
@@ -1,7 +1,7 @@
1
1
  using AIXM::Refinements
2
2
 
3
3
  module AIXM
4
- class Component
4
+ module Component
5
5
 
6
6
  # Runways are landing and takeoff strips for forward propelled aircraft.
7
7
  #
@@ -44,8 +44,11 @@ module AIXM
44
44
  # runway.forth.geographic_orientation = 165
45
45
  # runway.back = nil
46
46
  #
47
- # @see https://github.com/openflightmaps/ofmx/wiki/Airport#rwy-runway
47
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Airport#rwy-runway
48
48
  class Runway
49
+ include AIXM::Association
50
+ include AIXM::Memoize
51
+
49
52
  STATUSES = {
50
53
  CLSD: :closed,
51
54
  WIP: :work_in_progress, # e.g. construction work
@@ -55,8 +58,21 @@ module AIXM
55
58
  OTHER: :other # specify in remarks
56
59
  }
57
60
 
58
- # @return [AIXM::Feature::Airport] airport the runway belongs to
59
- attr_reader :airport
61
+ # @!method forth
62
+ # @return [AIXM::Component::Runway::Direction] main direction
63
+ # @!method forth=(forth)
64
+ # @param forth [AIXM::Component::Runway::Direction]
65
+ has_one :forth, accept: 'AIXM::Component::Runway::Direction'
66
+
67
+ # @!method back
68
+ # @return [AIXM::Component::Runway::Direction, nil] reverse direction
69
+ # @!method back=(back)
70
+ # @param back [AIXM::Component::Runway::Direction, nil]
71
+ has_one :back, accept: 'AIXM::Component::Runway::Direction', allow_nil: true
72
+
73
+ # @!method airport
74
+ # @return [AIXM::Feature::Airport] airport the runway belongs to
75
+ belongs_to :airport
60
76
 
61
77
  # @return [String] full name of runway (e.g. "12/30" or "16L/34R")
62
78
  attr_reader :name
@@ -76,18 +92,12 @@ module AIXM
76
92
  # @return [String, nil] free text remarks
77
93
  attr_reader :remarks
78
94
 
79
- # @return [AIXM::Component::Runway::Direction] main direction
80
- attr_accessor :forth
81
-
82
- # @return [AIXM::Component::Runway::Direction] reverse direction
83
- attr_accessor :back
84
-
85
95
  def initialize(name:)
86
96
  self.name = name
87
- @name.split("/").tap do |forth, back|
88
- @forth = Direction.new(runway: self, name: AIXM.a(forth))
89
- @back = Direction.new(runway: self, name: AIXM.a(back)) if back
90
- fail(ArgumentError, "invalid name") unless !@back || @back.name.inverse_of?(@forth.name)
97
+ @name.split("/").tap do |forth_name, back_name|
98
+ self.forth = Direction.new(name: AIXM.a(forth_name))
99
+ self.back = Direction.new(name: AIXM.a(back_name)) if back_name
100
+ fail(ArgumentError, "invalid name") unless !back || back.name.inverse_of?(@forth.name)
91
101
  end
92
102
  @surface = AIXM.surface
93
103
  end
@@ -97,12 +107,6 @@ module AIXM
97
107
  %Q(#<#{self.class} airport=#{airport&.id.inspect} name=#{name.inspect}>)
98
108
  end
99
109
 
100
- def airport=(value)
101
- fail(ArgumentError, "invalid airport") unless value.is_a? AIXM::Feature::Airport
102
- @airport = value
103
- end
104
- private :airport=
105
-
106
110
  def name=(value)
107
111
  fail(ArgumentError, "invalid name") unless value.is_a? String
108
112
  @name = value.uptrans
@@ -112,7 +116,7 @@ module AIXM
112
116
  @length = if value
113
117
  fail(ArgumentError, "invalid length") unless value.is_a?(AIXM::D) && value.dist > 0
114
118
  fail(ArgumentError, "invalid length unit") if width && width.unit != value.unit
115
- @length = value
119
+ value
116
120
  end
117
121
  end
118
122
 
@@ -120,7 +124,7 @@ module AIXM
120
124
  @width = if value
121
125
  fail(ArgumentError, "invalid width") unless value.is_a?(AIXM::D) && value.dist > 0
122
126
  fail(ArgumentError, "invalid width unit") if length && length.unit != value.unit
123
- @width = value
127
+ value
124
128
  end
125
129
  end
126
130
 
@@ -140,6 +144,7 @@ module AIXM
140
144
  rwy_uid.txtDesig(name)
141
145
  end
142
146
  end
147
+ memoize :to_uid
143
148
 
144
149
  # @return [String] AIXM or OFMX markup
145
150
  def to_xml
@@ -167,16 +172,27 @@ module AIXM
167
172
  # Runway directions further describe each direction {#forth} and {#back}
168
173
  # of a runway.
169
174
  #
170
- # @see https://github.com/openflightmaps/ofmx/wiki/Airport#rdn-runway-direction
175
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Airport#rdn-runway-direction
171
176
  class Direction
177
+ include AIXM::Association
178
+ include AIXM::Memoize
179
+
172
180
  VFR_PATTERNS = {
173
181
  L: :left,
174
182
  R: :right,
175
183
  E: :left_or_right
176
184
  }
177
185
 
178
- # @return [AIXM::Component::Runway] runway the runway direction is further describing
179
- attr_reader :runway
186
+ # @!method lightings
187
+ # @return [Array<AIXM::Component::Lighting>] installed lighting systems
188
+ # @!method add_lighting(lighting)
189
+ # @param lighting [AIXM::Component::Lighting]
190
+ # @return [self]
191
+ has_many :lightings, as: :lightable
192
+
193
+ # @!method runway
194
+ # @return [AIXM::Component::Runway] runway the runway direction is further describing
195
+ belongs_to :runway, readonly: true
180
196
 
181
197
  # @return [AIXM::A] partial name of runway (e.g. "12" or "16L")
182
198
  attr_reader :name
@@ -201,12 +217,8 @@ module AIXM
201
217
  # @return [String, nil] free text remarks
202
218
  attr_reader :remarks
203
219
 
204
- # @return [Array<AIXM::Component::Lighting>] installed lighting systems
205
- attr_reader :lightings
206
-
207
- def initialize(runway:, name:)
208
- self.runway, self.name = runway, name
209
- @lightings = []
220
+ def initialize(name:)
221
+ self.name = name
210
222
  end
211
223
 
212
224
  # @return [String]
@@ -214,12 +226,6 @@ module AIXM
214
226
  %Q(#<#{self.class} airport=#{runway&.airport&.id.inspect} name=#{name.inspect}>)
215
227
  end
216
228
 
217
- def runway=(value)
218
- fail(ArgumentError, "invalid runway") unless value.is_a? AIXM::Component::Runway
219
- @runway = value
220
- end
221
- private :runway
222
-
223
229
  def name=(value)
224
230
  fail(ArgumentError, "invalid name") unless value.is_a? AIXM::A
225
231
  @name = value
@@ -263,21 +269,10 @@ module AIXM
263
269
  @remarks = value&.to_s
264
270
  end
265
271
 
266
- # Add a lighting system to the runway direction.
267
- #
268
- # @param lighting [AIXM::Component::Lighting] lighting instance
269
- # @return [self]
270
- def add_lighting(lighting)
271
- fail(ArgumentError, "invalid lighting") unless lighting.is_a? AIXM::Component::Lighting
272
- lighting.send(:lightable=, self)
273
- @lightings << lighting
274
- self
275
- end
276
-
277
272
  # @return [AIXM::A] magnetic orientation (magnetic bearing) in degrees
278
273
  def magnetic_orientation
279
274
  if geographic_orientation && runway.airport.declination
280
- geographic_orientation + runway.airport.declination
275
+ geographic_orientation - runway.airport.declination
281
276
  end
282
277
  end
283
278
 
@@ -289,6 +284,7 @@ module AIXM
289
284
  rdn_uid.txtDesig(name)
290
285
  end
291
286
  end
287
+ memoize :to_uid
292
288
 
293
289
  # @return [String] AIXM or OFMX markup
294
290
  def to_xml
@@ -309,10 +305,7 @@ module AIXM
309
305
  if displaced_threshold
310
306
  builder.Rdd do |rdd|
311
307
  rdd.RddUid do |rdd_uid|
312
- rdd_uid.RdnUid do |rdn_uid|
313
- rdn_uid << runway.to_uid.indent(6)
314
- rdn_uid.txtDesig(name)
315
- end
308
+ rdd_uid << to_uid.indent(4)
316
309
  rdd_uid.codeType('DPLM')
317
310
  rdd_uid.codeDayPeriod('A')
318
311
  end
@@ -1,22 +1,22 @@
1
1
  using AIXM::Refinements
2
2
 
3
3
  module AIXM
4
- class Feature
4
+ module Component
5
5
 
6
6
  # Service provided by a unit.
7
7
  #
8
8
  # ===Cheat Sheet in Pseudo Code:
9
9
  # service = AIXM.service(
10
- # source: String or nil
11
10
  # type: TYPES
12
11
  # )
13
12
  # service.timetable = AIXM.timetable or nil
14
13
  # service.remarks = String or nil
15
14
  # service.add_frequency(AIXM.frequency)
16
15
  #
17
- # @see https://github.com/openflightmaps/ofmx/wiki/Organisation#ser-service
18
- class Service < Feature
19
- public_class_method :new
16
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Organisation#ser-service
17
+ class Service
18
+ include AIXM::Association
19
+ include AIXM::Memoize
20
20
 
21
21
  TYPES = {
22
22
  ACS: :area_control_service,
@@ -125,10 +125,21 @@ module AIXM
125
125
  :vhf_direction_finding_service => :vdf_direction_finding_station,
126
126
  :volmet_service => :meteorological_office,
127
127
  :other => :other
128
- }
128
+ }.freeze
129
+
130
+ # @!method frequencies
131
+ # @return [Array<AIXM::Component::Frequency>] frequencies used by this service
132
+ # @!method add_frequency(frequency)
133
+ # @param frequency [AIXM::Component::Frequency]
134
+ has_many :frequencies
129
135
 
130
- # @return [AIXM::Feature::Unit] unit providing this service
131
- attr_reader :unit
136
+ # @!method unit
137
+ # @return [AIXM::Feature::Unit] unit providing this service
138
+ belongs_to :unit
139
+
140
+ # @!method layer
141
+ # @return [AIXM::Component::Layer] airspace layer this service is provided within
142
+ belongs_to :layer
132
143
 
133
144
  # @return [Symbol] type of service (see {TYPES})
134
145
  attr_reader :type
@@ -139,13 +150,9 @@ module AIXM
139
150
  # @return [String, nil] free text remarks
140
151
  attr_reader :remarks
141
152
 
142
- # @return [Array<AIXM::Component::Frequency>] frequencies used by this service
143
- attr_reader :frequencies
144
-
145
- def initialize(source: nil, type:)
146
- super(source: source)
153
+ def initialize(type:)
147
154
  self.type = type
148
- @frequencies = []
155
+ @sequence = 1
149
156
  end
150
157
 
151
158
  # @return [String]
@@ -153,12 +160,6 @@ module AIXM
153
160
  %Q(#<#{self.class} type=#{type.inspect}>)
154
161
  end
155
162
 
156
- def unit=(value)
157
- fail(ArgumentError, "invalid unit") unless value.is_a? AIXM::Feature::Unit
158
- @unit = value
159
- end
160
- private :unit=
161
-
162
163
  def type=(value)
163
164
  @type = TYPES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid type")
164
165
  end
@@ -172,17 +173,6 @@ module AIXM
172
173
  @remarks = value&.to_s
173
174
  end
174
175
 
175
- # Add a frequency used by this service
176
- #
177
- # @param frequency [AIXM::Component::Frequency] frequency instance
178
- # @return [self]
179
- def add_frequency(frequency)
180
- fail(ArgumentError, "invalid frequency") unless frequency.is_a? AIXM::Component::Frequency
181
- frequency.send(:service=, self)
182
- @frequencies << frequency
183
- self
184
- end
185
-
186
176
  # Guess the unit type for this service
187
177
  #
188
178
  # @return [Symbol, nil] guessed unit type or +nil+ if unmappable
@@ -192,6 +182,7 @@ module AIXM
192
182
 
193
183
  # @return [String] UID markup
194
184
  def to_uid
185
+ resequence!
195
186
  builder = Builder::XmlMarkup.new(indent: 2)
196
187
  builder.SerUid do |ser_uid|
197
188
  ser_uid << unit.to_uid.indent(2)
@@ -199,13 +190,13 @@ module AIXM
199
190
  ser_uid.noSeq(@sequence)
200
191
  end
201
192
  end
193
+ memoize :to_uid
202
194
 
203
195
  # @return [String] AIXM or OFMX markup
204
- def to_xml(sequence:)
205
- @sequence = sequence
196
+ def to_xml
206
197
  builder = Builder::XmlMarkup.new(indent: 2)
207
- builder.comment! ["Service: #{TYPES.key(type)}", unit&.name].compact.join(' by ')
208
- builder.Ser({ source: (source if AIXM.ofmx?) }.compact) do |ser|
198
+ builder.comment! ["Service: #{TYPES.key(type)}", unit&.send(:name_with_type)].compact.join(' by ')
199
+ builder.Ser do |ser|
209
200
  ser << to_uid.indent(2)
210
201
  ser << timetable.to_xml(as: :Stt).indent(2) if timetable
211
202
  ser.txtRmk(remarks) if remarks
@@ -215,6 +206,16 @@ module AIXM
215
206
  end
216
207
  builder.target!
217
208
  end
209
+
210
+ private
211
+
212
+ def resequence!
213
+ unit.services.sort { |a, b| a.type <=> b.type }.each.with_object({}) do |service, sequences|
214
+ sequences[service.type] = (sequences[service.type] || 0) + 1
215
+ service.instance_variable_set(:@sequence, sequences[service.type])
216
+ end
217
+ end
218
+
218
219
  end
219
220
 
220
221
  end
@@ -1,7 +1,7 @@
1
1
  using AIXM::Refinements
2
2
 
3
3
  module AIXM
4
- class Component
4
+ module Component
5
5
 
6
6
  # Surface of a runway, helipad etc
7
7
  #
@@ -21,7 +21,7 @@ module AIXM
21
21
  # * +AIXM::PCN_RE+ - regular expression to match PCN notations
22
22
  #
23
23
  #
24
- # @see https://github.com/openflightmaps/ofmx/wiki/Airport#rwy-runway
24
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Airport#rwy-runway
25
25
  class Surface
26
26
  COMPOSITIONS = {
27
27
  ASPH: :asphalt,
@@ -109,7 +109,7 @@ module AIXM
109
109
  def pcn=(value)
110
110
  return @pcn = {} if value.nil?
111
111
  fail(ArgumentError, "invalid PCN") unless match = value.to_s.upcase.match(PCN_RE)
112
- @pcn = match.named_captures.reject{ |k| k == 'pcn' }
112
+ @pcn = match.named_captures.reject{ _1 == 'pcn' }
113
113
  end
114
114
 
115
115
  def siwl_weight=(value)
@@ -1,7 +1,7 @@
1
1
  using AIXM::Refinements
2
2
 
3
3
  module AIXM
4
- class Component
4
+ module Component
5
5
 
6
6
  # Timetables define activity time windows.
7
7
  #
@@ -17,7 +17,7 @@ module AIXM
17
17
  # * +AIXM::H24+ - continuous, all day and all night
18
18
  # * +AIXM::H_RE+ - pattern matching working hour codes
19
19
  #
20
- # @see https://github.com/openflightmaps/ofmx/wiki/Timetable#predefined-timetable
20
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Timetable#predefined-timetable
21
21
  class Timetable
22
22
  CODES = {
23
23
  H24: :continuous, # all day and all night
@@ -45,7 +45,9 @@ module AIXM
45
45
  end
46
46
 
47
47
  def code=(value)
48
- @code = CODES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid code")
48
+ @code = if value
49
+ CODES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid code")
50
+ end
49
51
  end
50
52
 
51
53
  def remarks=(value)
@@ -1,9 +1,9 @@
1
1
  using AIXM::Refinements
2
2
 
3
3
  module AIXM
4
- class Component
4
+ module Component
5
5
 
6
- # Vertical limits define a 3D airspace vertically. They are often noted in
6
+ # Vertical limit defines a 3D airspace vertically. It is often noted in
7
7
  # AIP as follows:
8
8
  #
9
9
  # upper_z
@@ -13,7 +13,7 @@ module AIXM
13
13
  # (min_z) whichever is lower
14
14
  #
15
15
  # ===Cheat Sheet in Pseudo Code:
16
- # vertical_limits = AIXM.vertical_limits(
16
+ # vertical_limit = AIXM.vertical_limit(
17
17
  # upper_z: AIXM.z
18
18
  # max_z: AIXM.z or nil
19
19
  # lower_z: AIXM.z
@@ -24,14 +24,20 @@ module AIXM
24
24
  # * +AIXM::GROUND+ - surface expressed as "0 ft QFE"
25
25
  # * +AIXM::UNLIMITED+ - no upper limit expressed as "FL 999"
26
26
  #
27
- # @see https://github.com/openflightmaps/ofmx/wiki/Airspace#ase-airspace
28
- class VerticalLimits
27
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Airspace#ase-airspace
28
+ class VerticalLimit
29
+ include AIXM::Association
30
+
29
31
  # @api private
30
32
  TAGS = { upper_z: :Upper, lower_z: :Lower, max_z: :Max, min_z: :Mnm }.freeze
31
33
 
32
34
  # @api private
33
35
  CODES = { qfe: :HEI, qnh: :ALT, qne: :STD }.freeze
34
36
 
37
+ # @!method layer
38
+ # @return [AIXM::Component::Layer] layer to which this vertical limit applies
39
+ belongs_to :layer
40
+
35
41
  # @return [AIXM::Z] upper limit
36
42
  attr_reader :upper_z
37
43
 
@@ -50,7 +56,7 @@ module AIXM
50
56
 
51
57
  # @return [String]
52
58
  def inspect
53
- payload = %i(upper_z max_z lower_z min_z).map { |l| %Q(#{l}="#{send(l)}") if send(l) }.compact
59
+ payload = %i(upper_z max_z lower_z min_z).map { %Q(#{_1}="#{send(_1)}") if send(_1) }.compact
54
60
  %Q(#<#{self.class} #{payload.join(' ')}>)
55
61
  end
56
62
 
data/lib/aixm/config.rb CHANGED
@@ -9,8 +9,8 @@ module AIXM
9
9
  },
10
10
  ofmx: {
11
11
  version: '0',
12
- namespace: 'http://schema.openflightmaps.org/0/OFMX-Snapshot.xsd',
13
- xsd: Pathname(__dir__).join('..', '..', 'schemas', 'ofmx', '0', 'OFMX-Snapshot.xsd'),
12
+ namespace: 'http://schema.openflightmaps.org/0.1/OFMX-Snapshot.xsd',
13
+ xsd: Pathname(__dir__).join('..', '..', 'schemas', 'ofmx', '0.1', 'OFMX-Snapshot.xsd'),
14
14
  root: 'OFMX-Snapshot'
15
15
  }
16
16
  }.freeze
@@ -77,7 +77,10 @@ module AIXM
77
77
  # @return [OpenStruct]
78
78
  def initialize_config
79
79
  @@config = OpenStruct.new(
80
- schema: :aixm
80
+ schema: :aixm,
81
+ voice_channel_separation: :any,
82
+ mid: false,
83
+ inflector: Dry::Inflector.new
81
84
  )
82
85
  end
83
86
 
data/lib/aixm/document.rb CHANGED
@@ -11,16 +11,21 @@ module AIXM
11
11
  # created_at: Time or Date or String
12
12
  # effective_at: Time or Date or String
13
13
  # )
14
- # document.features << AIXM::Feature
14
+ # document.add_feature(AIXM::Feature)
15
15
  #
16
- # @see https://github.com/openflightmaps/ofmx/wiki/Snapshot
16
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Snapshot
17
17
  class Document
18
- REGION_RE = /\A[A-Z]{2}\z/.freeze
18
+ include AIXM::Association
19
19
 
20
20
  NAMESPACE_RE = /\A[a-f\d]{8}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{12}\z/.freeze
21
21
 
22
- # @return [String] OFMX region all features in this document belong to
23
- attr_reader :region
22
+ # @!method features
23
+ # @return [Array<AIXM::Feature>] features (e.g. airport or airspace) present
24
+ # in this document
25
+ # @!method add_feature(feature)
26
+ # @param feature [AIXM::Feature]
27
+ # @return [self]
28
+ has_many :features, accept: ['AIXM::Feature']
24
29
 
25
30
  # @return [String] UUID to namespace the data contained in this document
26
31
  attr_reader :namespace
@@ -31,12 +36,8 @@ module AIXM
31
36
  # @return [Time] effective after date and time (default: {#created_at} or now)
32
37
  attr_reader :effective_at
33
38
 
34
- # @return [Array<AIXM::Feature>] airspaces, airports and other features
35
- attr_accessor :features
36
-
37
- def initialize(region: nil, namespace: nil, created_at: nil, effective_at: nil)
38
- self.region, self.namespace, self.created_at, self.effective_at = region, namespace, created_at, effective_at
39
- @features = []
39
+ def initialize(namespace: nil, created_at: nil, effective_at: nil)
40
+ self.namespace, self.created_at, self.effective_at = namespace, created_at, effective_at
40
41
  end
41
42
 
42
43
  # @return [String]
@@ -44,11 +45,6 @@ module AIXM
44
45
  %Q(#<#{self.class} created_at=#{created_at.inspect}>)
45
46
  end
46
47
 
47
- def region=(value)
48
- fail(ArgumentError, "invalid region") unless value.nil? || value&.upcase&.match?(REGION_RE)
49
- @region = value&.upcase
50
- end
51
-
52
48
  def namespace=(value)
53
49
  fail(ArgumentError, "invalid namespace") unless value.nil? || value.match?(NAMESPACE_RE)
54
50
  @namespace = value || SecureRandom.uuid
@@ -62,50 +58,30 @@ module AIXM
62
58
  @effective_at = value&.to_time || created_at || Time.now
63
59
  end
64
60
 
65
- # Search features and return those matching the given class and attribute
66
- # values
67
- #
68
- # @example
69
- # select_features(:airport, id: "LFNT")
70
- #
71
- # @param klass [Class, Symbol] feature class like AIXM::Feature::Airport or
72
- # AIXM::Feature::NavigationalAid::VOR, shorthand notations as symbols
73
- # e.g. :airport or :vor as listed in AIXM::CLASSES are recognized as well
74
- # @param attributes [Hash] search attributes by their values
75
- # @return [Array<AIXM::Feature>]
76
- def select_features(klass, attributes={})
77
- if klass.is_a? Symbol
78
- klass = AIXM::CLASSES.fetch(klass, nil)
79
- fail(ArgumentError, "unknown feature shortcut") unless klass
80
- end
81
- features.select do |feature|
82
- if feature.is_a? klass
83
- attributes.reduce(true) do |memo, (attribute, value)|
84
- memo && feature.send(attribute) == value
85
- end
86
- end
87
- end
88
- end
89
-
90
61
  # Compare all ungrouped obstacles and create new obstacle groups whose
91
62
  # members are located within +max_distance+ pairwise.
92
63
  #
64
+ # @note OFMX requires every obstacle, even single ones, to be part of an
65
+ # obstacle group which has a region assigned. For this to work, you must
66
+ # assure every obstacle has a region assigned when using this method.
67
+ #
93
68
  # @param max_distance [AIXM::D] max distance between obstacle group member
94
69
  # pairs (default: 1 NM)
95
70
  # @return [Integer] number of obstacle groups added
96
71
  def group_obstacles!(max_distance: AIXM.d(1, :nm))
97
- obstacles, list = select_features(:obstacle), {}
98
- while subject = obstacles.shift
72
+ obstacles, list = features.find_by(:obstacle), {}
73
+ while subject = obstacles.send(:shift)
99
74
  obstacles.each do |obstacle|
100
75
  if subject.xy.distance(obstacle.xy) <= max_distance
101
- [subject, obstacle].each { |o| list[o] = list[subject] || SecureRandom.uuid }
76
+ [subject, obstacle].each { list[_1] = list[subject] || SecureRandom.uuid }
102
77
  end
103
78
  end
104
79
  end
105
80
  list.group_by(&:last).each do |_, grouped_list|
106
- obstacle_group = AIXM.obstacle_group(source: grouped_list.first.first.source)
107
- grouped_list.each { |o, _| obstacle_group.obstacles << features.delete(o) }
108
- features << obstacle_group
81
+ first_obstacle = grouped_list.first.first
82
+ obstacle_group = AIXM.obstacle_group(source: first_obstacle.source, region: first_obstacle.region)
83
+ grouped_list.each { |o, _| obstacle_group.add_obstacle features.send(:delete, o) }
84
+ add_feature obstacle_group
109
85
  end.count
110
86
  end
111
87
 
@@ -133,7 +109,6 @@ module AIXM
133
109
  'xmlns:xsi': AIXM.schema(:namespace),
134
110
  version: AIXM.schema(:version),
135
111
  origin: "rubygem aixm-#{AIXM::VERSION}",
136
- region: (region if AIXM.ofmx?),
137
112
  namespace: (namespace if AIXM.ofmx?),
138
113
  created: @created_at.xmlschema,
139
114
  effective: @effective_at.xmlschema
@@ -141,7 +116,14 @@ module AIXM
141
116
  builder = Builder::XmlMarkup.new(indent: 2)
142
117
  builder.instruct!
143
118
  builder.tag!(AIXM.schema(:root), meta) do |root|
144
- root << features.map { |f| f.to_xml }.join.indent(2)
119
+ AIXM::Memoize.method :to_uid do
120
+ root << features.map { _1.to_xml }.join.indent(2)
121
+ end
122
+ end
123
+ if AIXM.ofmx? && AIXM.config.mid
124
+ AIXM::PayloadHash::Mid.new(builder.target!).insert_mid.to_xml
125
+ else
126
+ builder.target!
145
127
  end
146
128
  end
147
129