aixm 0.3.8 → 0.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +1 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +33 -3
  5. data/README.md +166 -56
  6. data/exe/ckmid +14 -0
  7. data/exe/mkmid +14 -0
  8. data/lib/aixm.rb +16 -6
  9. data/lib/aixm/association.rb +369 -0
  10. data/lib/aixm/classes.rb +43 -0
  11. data/lib/aixm/component/fato.rb +45 -53
  12. data/lib/aixm/component/frequency.rb +11 -12
  13. data/lib/aixm/component/geometry.rb +36 -38
  14. data/lib/aixm/component/geometry/arc.rb +2 -2
  15. data/lib/aixm/component/geometry/border.rb +6 -3
  16. data/lib/aixm/component/geometry/circle.rb +8 -2
  17. data/lib/aixm/component/geometry/point.rb +8 -2
  18. data/lib/aixm/component/helipad.rb +30 -38
  19. data/lib/aixm/component/layer.rb +28 -19
  20. data/lib/aixm/component/lighting.rb +12 -13
  21. data/lib/aixm/component/runway.rb +44 -48
  22. data/lib/aixm/{feature → component}/service.rb +37 -36
  23. data/lib/aixm/component/surface.rb +3 -3
  24. data/lib/aixm/component/timetable.rb +2 -2
  25. data/lib/aixm/component/{vertical_limits.rb → vertical_limit.rb} +12 -6
  26. data/lib/aixm/config.rb +2 -1
  27. data/lib/aixm/document.rb +27 -50
  28. data/lib/aixm/executables.rb +85 -0
  29. data/lib/aixm/feature.rb +13 -3
  30. data/lib/aixm/feature/address.rb +12 -13
  31. data/lib/aixm/feature/airport.rb +103 -128
  32. data/lib/aixm/feature/airspace.rb +44 -17
  33. data/lib/aixm/feature/navigational_aid.rb +7 -9
  34. data/lib/aixm/feature/navigational_aid/designated_point.rb +13 -15
  35. data/lib/aixm/feature/navigational_aid/dme.rb +11 -12
  36. data/lib/aixm/feature/navigational_aid/marker.rb +7 -3
  37. data/lib/aixm/feature/navigational_aid/ndb.rb +7 -3
  38. data/lib/aixm/feature/navigational_aid/tacan.rb +7 -3
  39. data/lib/aixm/feature/navigational_aid/vor.rb +23 -15
  40. data/lib/aixm/feature/obstacle.rb +29 -43
  41. data/lib/aixm/feature/obstacle_group.rb +37 -34
  42. data/lib/aixm/feature/organisation.rb +21 -5
  43. data/lib/aixm/feature/unit.rb +36 -46
  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 +29 -76
  48. data/lib/aixm/shortcuts.rb +5 -42
  49. data/lib/aixm/version.rb +1 -1
  50. data/lib/aixm/xy.rb +1 -1
  51. data/schemas/ofmx/0/OFMX-Features.xsd +152 -20
  52. data/schemas/ofmx/0/OFMX-Snapshot.xsd +0 -5
  53. metadata +107 -156
  54. metadata.gz.sig +2 -0
  55. data/.github/workflows/test.yml +0 -26
  56. data/.gitignore +0 -6
  57. data/.ruby-version +0 -1
  58. data/.yardopts +0 -3
  59. data/Guardfile +0 -8
  60. data/aixm.gemspec +0 -35
  61. data/gems.rb +0 -3
  62. data/lib/aixm/component.rb +0 -6
  63. data/rakefile.rb +0 -36
  64. data/spec/factory.rb +0 -559
  65. data/spec/lib/aixm/a_spec.rb +0 -203
  66. data/spec/lib/aixm/component/fato_spec.rb +0 -267
  67. data/spec/lib/aixm/component/frequency_spec.rb +0 -74
  68. data/spec/lib/aixm/component/geometry/arc_spec.rb +0 -73
  69. data/spec/lib/aixm/component/geometry/border_spec.rb +0 -38
  70. data/spec/lib/aixm/component/geometry/circle_spec.rb +0 -68
  71. data/spec/lib/aixm/component/geometry/point_spec.rb +0 -37
  72. data/spec/lib/aixm/component/geometry_spec.rb +0 -316
  73. data/spec/lib/aixm/component/helipad_spec.rb +0 -193
  74. data/spec/lib/aixm/component/layer_spec.rb +0 -135
  75. data/spec/lib/aixm/component/lighting_spec.rb +0 -94
  76. data/spec/lib/aixm/component/runway_spec.rb +0 -479
  77. data/spec/lib/aixm/component/surface_spec.rb +0 -124
  78. data/spec/lib/aixm/component/timetable_spec.rb +0 -47
  79. data/spec/lib/aixm/component/vertical_limits_spec.rb +0 -94
  80. data/spec/lib/aixm/config_spec.rb +0 -41
  81. data/spec/lib/aixm/d_spec.rb +0 -150
  82. data/spec/lib/aixm/document_spec.rb +0 -1884
  83. data/spec/lib/aixm/errors_spec.rb +0 -14
  84. data/spec/lib/aixm/f_spec.rb +0 -85
  85. data/spec/lib/aixm/feature/address_spec.rb +0 -60
  86. data/spec/lib/aixm/feature/airport_spec.rb +0 -776
  87. data/spec/lib/aixm/feature/airspace_spec.rb +0 -394
  88. data/spec/lib/aixm/feature/navigational_aid/designated_point_spec.rb +0 -103
  89. data/spec/lib/aixm/feature/navigational_aid/dme_spec.rb +0 -98
  90. data/spec/lib/aixm/feature/navigational_aid/marker_spec.rb +0 -85
  91. data/spec/lib/aixm/feature/navigational_aid/ndb_spec.rb +0 -95
  92. data/spec/lib/aixm/feature/navigational_aid/tacan_spec.rb +0 -94
  93. data/spec/lib/aixm/feature/navigational_aid/vor_spec.rb +0 -251
  94. data/spec/lib/aixm/feature/navigational_aid_spec.rb +0 -52
  95. data/spec/lib/aixm/feature/obstacle_group_spec.rb +0 -330
  96. data/spec/lib/aixm/feature/obstacle_spec.rb +0 -284
  97. data/spec/lib/aixm/feature/organisation_spec.rb +0 -83
  98. data/spec/lib/aixm/feature/service_spec.rb +0 -59
  99. data/spec/lib/aixm/feature/unit_spec.rb +0 -238
  100. data/spec/lib/aixm/feature_spec.rb +0 -38
  101. data/spec/lib/aixm/p_spec.rb +0 -189
  102. data/spec/lib/aixm/refinements_spec.rb +0 -430
  103. data/spec/lib/aixm/version_spec.rb +0 -7
  104. data/spec/lib/aixm/w_spec.rb +0 -150
  105. data/spec/lib/aixm/xy_spec.rb +0 -180
  106. data/spec/lib/aixm/z_spec.rb +0 -94
  107. data/spec/macros/marking.rb +0 -12
  108. data/spec/macros/organisation.rb +0 -11
  109. data/spec/macros/remarks.rb +0 -12
  110. data/spec/macros/timetable.rb +0 -11
  111. data/spec/macros/xy.rb +0 -11
  112. data/spec/macros/z_qnh.rb +0 -11
  113. data/spec/sounds/failure.mp3 +0 -0
  114. data/spec/sounds/success.mp3 +0 -0
  115. data/spec/spec_helper.rb +0 -62
@@ -9,11 +9,12 @@ module AIXM
9
9
  # ===Cheat Sheet in Pseudo Code:
10
10
  # obstacle = AIXM.obstacle(
11
11
  # source: String or nil
12
+ # region: String or nil
12
13
  # name: String or nil
13
14
  # type: TYPES
14
15
  # xy: AIXM.xy
15
16
  # z: AIXM.z
16
- # radius: AIXM.d
17
+ # radius: AIXM.d or nil
17
18
  # )
18
19
  # obstacle.lighting = true or false (default for AIXM) or nil (means: unknown, default for OFMX)
19
20
  # obstacle.lighting_remarks = String or nil
@@ -32,12 +33,15 @@ module AIXM
32
33
  # See {AIXM::Feature::ObstacleGroup} for how to define physical links
33
34
  # between two obstacles (e.g. cables between powerline towers).
34
35
  #
35
- # Please note: As soon as an obstacle is added to an obstacle group, the
36
- # +xy_accuracy+ and +z_accuracy+ of the obstacle group overwrite whatever
37
- # is set on the individual obstacles!
36
+ # Please note: Accuracies (+xy_accuracy+ and +z_accuracy+) set on an
37
+ # obstacle group are implicitly applied to all obstacles of the group
38
+ # unless they have their own, different accuracies set.
38
39
  #
39
- # @see https://github.com/openflightmaps/ofmx/wiki/Obstacle
40
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Obstacle
40
41
  class Obstacle < Feature
42
+ include AIXM::Association
43
+ include AIXM::Memoize
44
+
41
45
  public_class_method :new
42
46
 
43
47
  TYPES = {
@@ -57,6 +61,10 @@ module AIXM
57
61
  OTHER: :other
58
62
  }.freeze
59
63
 
64
+ # @!method obstacle_group
65
+ # @return [AIXM::Feature::ObstacleGroup] group this obstacle belongs to
66
+ belongs_to :obstacle_group
67
+
60
68
  # @return [String] full name
61
69
  attr_reader :name
62
70
 
@@ -108,19 +116,16 @@ module AIXM
108
116
  # @return [String, nil] free text remarks
109
117
  attr_reader :remarks
110
118
 
111
- # @return [AIXM::Feature::ObstacleGroup] group this obstacle belongs to
112
- attr_reader :obstacle_group
113
-
114
119
  # @return [Symbol, nil] another obstacle to which a physical link exists
115
120
  attr_reader :linked_to
116
121
 
117
122
  # @return [Symbol, nil] type of physical link between this and another obstacle
118
123
  attr_reader :link_type
119
124
 
120
- def initialize(source: nil, name: nil, type:, xy:, z:, radius:)
121
- super(source: source)
125
+ def initialize(source: nil, region: nil, name: nil, type:, xy:, z:, radius: nil)
126
+ super(source: source, region: region)
122
127
  self.name, self.type, self.xy, self.z, self.radius = name, type, xy, z, radius
123
- @lighting = @marking = @height_accurate = false
128
+ @lighting = @marking = false
124
129
  end
125
130
 
126
131
  # @return [String]
@@ -202,12 +207,6 @@ module AIXM
202
207
  @remarks = value&.to_s
203
208
  end
204
209
 
205
- def obstacle_group=(value)
206
- fail(ArgumentError, "invalid obstacle group") unless value.is_a?(AIXM::Feature::ObstacleGroup)
207
- @obstacle_group = value
208
- end
209
- private :obstacle_group=
210
-
211
210
  def linked_to=(value)
212
211
  fail(ArgumentError, "invalid linked to") unless value.is_a?(AIXM::Feature::Obstacle)
213
212
  @linked_to = value
@@ -219,11 +218,6 @@ module AIXM
219
218
  end
220
219
  private :link_type=
221
220
 
222
- # @return [Boolean] whether part of an obstacle group
223
- def grouped?
224
- obstacle_group && obstacle_group.obstacles.count > 1
225
- end
226
-
227
221
  # @return [Boolean] whether obstacle is linked to another one
228
222
  def linked?
229
223
  !!linked_to
@@ -231,30 +225,29 @@ module AIXM
231
225
 
232
226
  # @return [String] UID markup
233
227
  def to_uid(as: :ObsUid)
234
- self.obstacle_group ||= singleton_obstacle_group
235
228
  builder = Builder::XmlMarkup.new(indent: 2)
236
- builder.tag!(as) do |tag|
237
- tag << obstacle_group.to_uid.indent(2) if AIXM.ofmx?
229
+ builder.tag!(as, { region: (region if AIXM.ofmx?) }.compact) do |tag|
238
230
  tag.geoLat((xy.lat(AIXM.schema)))
239
231
  tag.geoLong((xy.long(AIXM.schema)))
240
- end.insert_payload_hash(region: AIXM.config.mid_region)
232
+ end
241
233
  end
234
+ memoize :to_uid
242
235
 
243
236
  # @return [String] AIXM or OFMX markup
244
237
  def to_xml(delegate: true)
245
- self.obstacle_group ||= singleton_obstacle_group
246
- return obstacle_group.to_xml if delegate && AIXM.ofmx?
238
+ return obstacle_group.to_xml if delegate && obstacle_group && AIXM.ofmx?
247
239
  builder = Builder::XmlMarkup.new(indent: 2)
248
240
  builder.comment! "Obstacle: [#{type}] #{xy.to_s} #{name}".strip
249
241
  builder.Obs do |obs|
250
242
  obs << to_uid.indent(2)
243
+ obs << obstacle_group.to_uid.indent(2) if obstacle_group && AIXM.ofmx?
251
244
  obs.txtName(name) if name
252
245
  if AIXM.ofmx?
253
246
  obs.codeType(TYPES.key(type).to_s)
254
247
  else
255
248
  obs.txtDescrType(TYPES.key(type).to_s)
256
249
  end
257
- obs.codeGroup(grouped? ? 'Y' : 'N')
250
+ obs.codeGroup(obstacle_group ? 'Y' : 'N') if AIXM.aixm?
258
251
  if AIXM.ofmx?
259
252
  obs.codeLgt(lighting ? 'Y' : 'N') unless lighting.nil?
260
253
  obs.codeMarking(marking ? 'Y' : 'N') unless marking.nil?
@@ -264,13 +257,14 @@ module AIXM
264
257
  obs.txtDescrLgt(lighting_remarks) if lighting_remarks
265
258
  obs.txtDescrMarking(marking_remarks) if marking_remarks
266
259
  obs.codeDatum('WGE')
267
- if AIXM.aixm? && obstacle_group.xy_accuracy
268
- obs.valGeoAccuracy(obstacle_group.xy_accuracy.dist.trim)
269
- obs.uomGeoAccuracy(obstacle_group.xy_accuracy.unit.upcase.to_s)
260
+ if accuracy = (xy_accuracy || (obstacle_group&.xy_accuracy if AIXM.aixm?))
261
+ obs.valGeoAccuracy(accuracy.dist.trim)
262
+ obs.uomGeoAccuracy(accuracy.unit.upcase.to_s)
270
263
  end
271
264
  obs.valElev(z.alt)
272
- if AIXM.aixm? && obstacle_group.z_accuracy
273
- obs.valElevAccuracy(obstacle_group.z_accuracy.to_ft.dist.round)
265
+ if accuracy = (z_accuracy || (obstacle_group&.z_accuracy if AIXM.aixm?))
266
+ obs.valElevAccuracy(accuracy.to_ft.dist.round)
267
+ obs.uomElevAccuracy('FT') if AIXM.ofmx?
274
268
  end
275
269
  obs.valHgt(height.to_ft.dist.round) if height
276
270
  obs.uomDistVer('FT')
@@ -282,7 +276,7 @@ module AIXM
282
276
  obs.valRadius(radius.dist.trim)
283
277
  obs.uomRadius(radius.unit.upcase.to_s)
284
278
  end
285
- if grouped? && linked?
279
+ if linked?
286
280
  obs << linked_to.to_uid(as: :ObsUidLink).indent(2)
287
281
  obs.codeLinkType(LINK_TYPES.key(link_type).to_s)
288
282
  end
@@ -293,14 +287,6 @@ module AIXM
293
287
  end
294
288
  end
295
289
 
296
- private
297
-
298
- # OFMX requires single, ungrouped obstacles to be the only member of a
299
- # singleton obstacle group.
300
- def singleton_obstacle_group
301
- AIXM.obstacle_group.add_obstacle self
302
- end
303
-
304
290
  end
305
291
  end
306
292
  end
@@ -6,9 +6,15 @@ module AIXM
6
6
  # Groups of obstacles which consist of either linked (e.g. power line
7
7
  # towers) or unlinked (e.g. wind turbines) members.
8
8
  #
9
+ # Obstacle group should contain at least two obstacles. However, if the
10
+ # details of each obstacle of the group are unknown, you may add only one
11
+ # virtual obstacle to the group and mention the number of real obstacles
12
+ # in it's remarks.
13
+ #
9
14
  # ===Cheat Sheet in Pseudo Code:
10
15
  # obstacle_group = AIXM.obstacle_group(
11
16
  # source: String or nil # see remarks below
17
+ # region: String or nil
12
18
  # name: String or nil
13
19
  # ).tap do |obstacle_group|
14
20
  # obstacle_group.xy_accuracy = AIXM.d or nil
@@ -29,14 +35,34 @@ module AIXM
29
35
  # link_type: LINK_TYPES
30
36
  # )
31
37
  #
32
- # Please note: As soon as an obstacle is added to an obstacle group, the
33
- # +xy_accuracy+ and +z_accuracy+ of the obstacle group overwrite whatever
34
- # is set on the individual obstacles!
38
+ # Please note: Accuracies (+xy_accuracy+ and +z_accuracy+) set on an
39
+ # obstacle group are implicitly applied to all obstacles of the group
40
+ # unless they have their own, different accuracies set.
35
41
  #
36
- # @see https://github.com/openflightmaps/ofmx/wiki/Obstacle
42
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Obstacle
37
43
  class ObstacleGroup < Feature
44
+ include AIXM::Association
45
+ include AIXM::Memoize
46
+
38
47
  public_class_method :new
39
48
 
49
+ # @!method obstacles
50
+ # @return [Array<AIXM::Feature::Obstacle>] obstacles in this obstacle group
51
+ # @!method add_obstacle(obstacle, linked_to: nil, link_type: nil)
52
+ # @param obstacle [AIXM::Feature::Obstacle] obstacle instance
53
+ # @param linked_to [Symbol, AIXM::Feature::Obstacle, nil] Either:
54
+ # * :previous - link to the obstacle last added to the obstacle group
55
+ # * AIXM::Feature::Obstacle - link to this specific obstacle
56
+ # @param link_type [Symbol, nil] type of link (see
57
+ # {AIXM::Feature::Obstacle::LINK_TYPES})
58
+ # @return [self]
59
+ has_many :obstacles do |obstacle, linked_to: nil, link_type: nil|
60
+ if linked_to
61
+ obstacle.send(:linked_to=, linked_to == :previous ? @obstacles.last : linked_to)
62
+ obstacle.send(:link_type=, (link_type || :other))
63
+ end
64
+ end
65
+
40
66
  # @!method source
41
67
  # @return [String] reference to source of the feature data
42
68
  # @!method name
@@ -54,13 +80,9 @@ module AIXM
54
80
  # @return [String, nil] free text remarks
55
81
  attr_reader :remarks
56
82
 
57
- # @return [Array<AIXM::Feature::Obstacle>] obstacles in this obstacle group
58
- attr_reader :obstacles
59
-
60
- def initialize(source: nil, name: nil)
61
- super(source: source)
83
+ def initialize(source: nil, region: nil, name: nil)
84
+ super(source: source, region: region)
62
85
  self.name = name
63
- @obstacles = []
64
86
  end
65
87
 
66
88
  # @return [String]
@@ -87,35 +109,15 @@ module AIXM
87
109
  @remarks = value&.to_s
88
110
  end
89
111
 
90
- # Add an obstacle to the obstacle group and optionally link it to another
91
- # obstacle from the obstacle group.
92
- #
93
- # @param obstacle [AIXM::Feature::Obstacle] obstacle instance
94
- # @param linked_to [Symbol, AIXM::Feature::Obstacle, nil] Either:
95
- # * :previous - link to the obstacle last added to the obstacle group
96
- # * AIXM::Feature::Obstacle - link to this specific obstacle
97
- # @param link_type [Symbol, nil] type of link (see
98
- # {AIXM::Feature::Obstacle::LINK_TYPES})
99
- # @return [self]
100
- def add_obstacle(obstacle, linked_to: nil, link_type: :other)
101
- obstacle.send(:obstacle_group=, self)
102
- if linked_to && link_type
103
- obstacle.send(:linked_to=, linked_to == :previous ? @obstacles.last : linked_to)
104
- obstacle.send(:link_type=, link_type)
105
- end
106
- @obstacles << obstacle
107
- self
108
- end
109
-
110
112
  # @return [String] UID markup
111
113
  def to_uid
112
114
  builder = Builder::XmlMarkup.new(indent: 2)
113
- builder.OgrUid do |ogr_uid|
114
- ogr_uid.txtName(name)
115
+ builder.OgrUid({ region: (region if AIXM.ofmx?) }.compact) do |ogr_uid|
115
116
  ogr_uid.geoLat(obstacles.first.xy.lat(AIXM.schema))
116
117
  ogr_uid.geoLong(obstacles.first.xy.long(AIXM.schema))
117
- end.insert_payload_hash(region: AIXM.config.mid_region)
118
+ end
118
119
  end
120
+ memoize :to_uid
119
121
 
120
122
  # @return [String] AIXM or OFMX markup
121
123
  def to_xml
@@ -124,6 +126,7 @@ module AIXM
124
126
  builder.comment! "Obstacle group: #{name}".strip
125
127
  builder.Ogr({ source: (source if AIXM.ofmx?) }.compact) do |ogr|
126
128
  ogr << to_uid.indent(2)
129
+ ogr.txtName(name)
127
130
  ogr.codeDatum('WGE')
128
131
  if xy_accuracy
129
132
  ogr.valGeoAccuracy(xy_accuracy.dist.trim)
@@ -136,7 +139,7 @@ module AIXM
136
139
  ogr.txtRmk(remarks) if remarks
137
140
  end
138
141
  end
139
- obstacles.each { |o| builder << o.to_xml(delegate: false) }
142
+ obstacles.each { builder << _1.to_xml(delegate: false) }
140
143
  builder.target!
141
144
  end
142
145
  end
@@ -9,14 +9,18 @@ module AIXM
9
9
  # ===Cheat Sheet in Pseudo Code:
10
10
  # organisation = AIXM.organisation(
11
11
  # source: String or nil
12
+ # region: String or nil
12
13
  # name: String
13
14
  # type: TYPES
14
15
  # )
15
16
  # organisation.id = String or nil
16
17
  # organisation.remarks = String or nil
17
18
  #
18
- # @see https://github.com/openflightmaps/ofmx/wiki/Organisation#org-organisation
19
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Organisation#org-organisation
19
20
  class Organisation < Feature
21
+ include AIXM::Association
22
+ include AIXM::Memoize
23
+
20
24
  public_class_method :new
21
25
 
22
26
  TYPES = {
@@ -31,6 +35,17 @@ module AIXM
31
35
  OTHER: :other # specify in remarks
32
36
  }.freeze
33
37
 
38
+ # @!method members
39
+ # @return [Array<AIXM::Feature::Airport,
40
+ # AIXM::Feature::Unit,
41
+ # AIXM::Feature::NavigationalAid>] aiports, units or navigational aids
42
+ # @!method add_member(member)
43
+ # @param member [AIXM::Feature::Airport,
44
+ # AIXM::Feature::Unit,
45
+ # AIXM::Feature::NavigationalAid]
46
+ # @return [self]
47
+ has_many :members, accept: [:airport, :unit, 'AIXM::Feature::NavigationalAid']
48
+
34
49
  # @return [String] name of organisation (e.g. "FRANCE")
35
50
  attr_reader :name
36
51
 
@@ -43,8 +58,8 @@ module AIXM
43
58
  # @return [String, nil] free text remarks
44
59
  attr_reader :remarks
45
60
 
46
- def initialize(source: nil, name:, type:)
47
- super(source: source)
61
+ def initialize(source: nil, region: nil, name:, type:)
62
+ super(source: source, region: region)
48
63
  self.name, self.type = name, type
49
64
  end
50
65
 
@@ -74,10 +89,11 @@ module AIXM
74
89
  # @return [String] UID markup
75
90
  def to_uid
76
91
  builder = Builder::XmlMarkup.new(indent: 2)
77
- builder.OrgUid do |org_uid|
92
+ builder.OrgUid({ region: (region if AIXM.ofmx?) }.compact) do |org_uid|
78
93
  org_uid.txtName(name)
79
- end.insert_payload_hash(region: AIXM.config.mid_region)
94
+ end
80
95
  end
96
+ memoize :to_uid
81
97
 
82
98
  # @return [String] AIXM or OFMX markup
83
99
  def to_xml
@@ -9,6 +9,7 @@ module AIXM
9
9
  # ===Cheat Sheet in Pseudo Code:
10
10
  # unit = AIXM.unit(
11
11
  # source: String or nil
12
+ # region: String or nil
12
13
  # organisation: AIXM.organisation
13
14
  # name: String
14
15
  # type: TYPES
@@ -18,8 +19,11 @@ module AIXM
18
19
  # unit.remarks = String or nil
19
20
  # unit.add_service(AIXM.service)
20
21
  #
21
- # @see https://github.com/openflightmaps/ofmx/wiki/Organisation#uni-unit
22
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Organisation#uni-unit
22
23
  class Unit < Feature
24
+ include AIXM::Association
25
+ include AIXM::Memoize
26
+
23
27
  public_class_method :new
24
28
 
25
29
  TYPES = {
@@ -76,8 +80,19 @@ module AIXM
76
80
  OTHER: :other # specify in remarks
77
81
  }.freeze
78
82
 
79
- # @return [AIXM::Feature::Organisation] superior organisation
80
- attr_reader :organisation
83
+ # @!method services
84
+ # @return [Array<AIXM::Component::Service>] services provided by this unit
85
+ # @!method add_service(service)
86
+ # @param service [AIXM::Component::Service]
87
+ has_many :services
88
+
89
+ # @!method organisation
90
+ # @return [AIXM::Feature::Organisation] superior organisation
91
+ belongs_to :organisation, as: :member
92
+
93
+ # @!method airport
94
+ # @return [AIXM::Feature::Airport, nil] airport
95
+ belongs_to :airport
81
96
 
82
97
  # @return [String] name of unit (e.g. "MARSEILLE ACS")
83
98
  attr_reader :name
@@ -85,27 +100,18 @@ module AIXM
85
100
  # @return [Symbol] type of unit (see {TYPES})
86
101
  attr_reader :type
87
102
 
88
- # @return [AIXM::Feature::Airport, nil] airport
89
- attr_reader :airport
90
-
91
103
  # @return [String, nil] free text remarks
92
104
  attr_reader :remarks
93
105
 
94
- def initialize(source: nil, organisation:, name:, type:, class:)
95
- super(source: source)
106
+ def initialize(source: nil, region: nil, organisation:, name:, type:, class:)
107
+ super(source: source, region: region)
96
108
  self.organisation, self.name, self.type = organisation, name, type
97
109
  self.class = binding.local_variable_get(:class)
98
- @services = []
99
110
  end
100
111
 
101
112
  # @return [String]
102
113
  def inspect
103
- %Q(#<#{original_class} name=#{name.inspect} type=#{type.inspect}>)
104
- end
105
-
106
- def organisation=(value)
107
- fail(ArgumentError, "invalid organisation") unless value.is_a? AIXM::Feature::Organisation
108
- @organisation = value
114
+ %Q(#<#{__class__} name=#{name.inspect} type=#{type.inspect}>)
109
115
  end
110
116
 
111
117
  def name=(value)
@@ -118,9 +124,8 @@ module AIXM
118
124
  end
119
125
 
120
126
  # @!attribute class
121
- # @note Use +original_class+ to query the Ruby object class.
127
+ # @note Use +Object#__class__+ alias to query the Ruby object class.
122
128
  # @return [Symbol] class of unit (see {CLASSES})
123
- alias_method :original_class, :class
124
129
  def class
125
130
  @klass
126
131
  end
@@ -129,58 +134,43 @@ module AIXM
129
134
  @klass = CLASSES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid class")
130
135
  end
131
136
 
132
- def airport=(value)
133
- fail(ArgumentError, "invalid airport") unless value.nil? || value.is_a?(AIXM::Feature::Airport)
134
- @airport = value
135
- end
136
-
137
137
  def remarks=(value)
138
138
  @remarks = value&.to_s
139
139
  end
140
140
 
141
- # Add a service provided by this unit.
142
- #
143
- # @param service [AIXM::Feature::Service] service instance
144
- # @return [self]
145
- def add_service(service)
146
- fail(ArgumentError, "invalid service") unless service.is_a? AIXM::Feature::Service
147
- service.send(:unit=, self)
148
- @services << service
149
- self
150
- end
151
-
152
- # @!attribute [r] services
153
- # @return [Array<AIXM::Feature::Service>] services provided by this unit
154
- def services
155
- @services.sort { |a, b| a.type <=> b.type }
156
- end
157
-
158
141
  # @return [String] UID markup
159
142
  def to_uid
160
143
  builder = Builder::XmlMarkup.new(indent: 2)
161
- builder.UniUid do |uni_uid|
144
+ builder.UniUid({ region: (region if AIXM.ofmx?) }.compact) do |uni_uid|
162
145
  uni_uid.txtName(name)
163
- end.insert_payload_hash(region: AIXM.config.mid_region)
146
+ uni_uid.codeType(TYPES.key(type).to_s) if AIXM.ofmx?
147
+ end
164
148
  end
149
+ memoize :to_uid
165
150
 
166
151
  # @return [String] AIXM or OFMX markup
167
152
  def to_xml
168
153
  builder = Builder::XmlMarkup.new(indent: 2)
169
- builder.comment! "Unit: #{name}"
154
+ builder.comment! "Unit: #{name_with_type}"
170
155
  builder.Uni({ source: (source if AIXM.ofmx?) }.compact) do |uni|
171
156
  uni << to_uid.indent(2)
172
157
  uni << organisation.to_uid.indent(2)
173
158
  uni << airport.to_uid.indent(2) if airport
174
- uni.codeType(TYPES.key(type).to_s)
159
+ uni.codeType(TYPES.key(type).to_s) unless AIXM.ofmx?
175
160
  uni.codeClass(CLASSES.key(self.class).to_s)
176
161
  uni.txtRmk(remarks) if remarks
177
162
  end
178
- services.each.with_object({}) do |service, sequences|
179
- sequences[service.type] = (sequences[service.type] || 0) + 1
180
- builder << service.to_xml(sequence: sequences[service.type])
163
+ services.sort { |a, b| a.type <=> b.type }.each do |service|
164
+ builder << service.to_xml
181
165
  end
182
166
  builder.target!
183
167
  end
168
+
169
+ private
170
+
171
+ def name_with_type
172
+ [name, TYPES.key(type)].join(' ')
173
+ end
184
174
  end
185
175
 
186
176
  end