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
@@ -78,7 +78,8 @@ module AIXM
78
78
  def initialize_config
79
79
  @@config = OpenStruct.new(
80
80
  schema: :aixm,
81
- mid: false
81
+ mid: false,
82
+ inflector: Dry::Inflector.new
82
83
  )
83
84
  end
84
85
 
@@ -7,21 +7,25 @@ module AIXM
7
7
  #
8
8
  # ===Cheat Sheet in Pseudo Code:
9
9
  # document = AIXM.document(
10
- # region: String
11
10
  # namespace: String (UUID)
12
11
  # created_at: Time or Date or String
13
12
  # effective_at: Time or Date or String
14
13
  # )
15
- # document.features << AIXM::Feature
14
+ # document.add_feature(AIXM::Feature)
16
15
  #
17
- # @see https://github.com/openflightmaps/ofmx/wiki/Snapshot
16
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Snapshot
18
17
  class Document
19
- REGION_RE = /\A[A-Z]{2}\z/.freeze
18
+ include AIXM::Association
20
19
 
21
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
22
21
 
23
- # @return [String] OFMX region all features in this document belong to
24
- 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']
25
29
 
26
30
  # @return [String] UUID to namespace the data contained in this document
27
31
  attr_reader :namespace
@@ -32,12 +36,8 @@ module AIXM
32
36
  # @return [Time] effective after date and time (default: {#created_at} or now)
33
37
  attr_reader :effective_at
34
38
 
35
- # @return [Array<AIXM::Feature>] airspaces, airports and other features
36
- attr_accessor :features
37
-
38
- def initialize(region: nil, namespace: nil, created_at: nil, effective_at: nil)
39
- self.region, self.namespace, self.created_at, self.effective_at = region, namespace, created_at, effective_at
40
- @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
41
41
  end
42
42
 
43
43
  # @return [String]
@@ -45,11 +45,6 @@ module AIXM
45
45
  %Q(#<#{self.class} created_at=#{created_at.inspect}>)
46
46
  end
47
47
 
48
- def region=(value)
49
- fail(ArgumentError, "invalid region") unless value.nil? || value&.upcase&.match?(REGION_RE)
50
- @region = value&.upcase
51
- end
52
-
53
48
  def namespace=(value)
54
49
  fail(ArgumentError, "invalid namespace") unless value.nil? || value.match?(NAMESPACE_RE)
55
50
  @namespace = value || SecureRandom.uuid
@@ -63,31 +58,6 @@ module AIXM
63
58
  @effective_at = value&.to_time || created_at || Time.now
64
59
  end
65
60
 
66
- # Search features and return those matching the given class and attribute
67
- # values
68
- #
69
- # @example
70
- # select_features(:airport, id: "LFNT")
71
- #
72
- # @param klass [Class, Symbol] feature class like AIXM::Feature::Airport or
73
- # AIXM::Feature::NavigationalAid::VOR, shorthand notations as symbols
74
- # e.g. :airport or :vor as listed in AIXM::CLASSES are recognized as well
75
- # @param attributes [Hash] search attributes by their values
76
- # @return [Array<AIXM::Feature>]
77
- def select_features(klass, attributes={})
78
- if klass.is_a? Symbol
79
- klass = AIXM::CLASSES.fetch(klass, nil)
80
- fail(ArgumentError, "unknown feature shortcut") unless klass
81
- end
82
- features.select do |feature|
83
- if feature.is_a? klass
84
- attributes.reduce(true) do |memo, (attribute, value)|
85
- memo && feature.send(attribute) == value
86
- end
87
- end
88
- end
89
- end
90
-
91
61
  # Compare all ungrouped obstacles and create new obstacle groups whose
92
62
  # members are located within +max_distance+ pairwise.
93
63
  #
@@ -95,18 +65,19 @@ module AIXM
95
65
  # pairs (default: 1 NM)
96
66
  # @return [Integer] number of obstacle groups added
97
67
  def group_obstacles!(max_distance: AIXM.d(1, :nm))
98
- obstacles, list = select_features(:obstacle), {}
99
- while subject = obstacles.shift
68
+ obstacles, list = features.find_by(:obstacle), {}
69
+ while subject = obstacles.send(:shift)
100
70
  obstacles.each do |obstacle|
101
71
  if subject.xy.distance(obstacle.xy) <= max_distance
102
- [subject, obstacle].each { |o| list[o] = list[subject] || SecureRandom.uuid }
72
+ [subject, obstacle].each { list[_1] = list[subject] || SecureRandom.uuid }
103
73
  end
104
74
  end
105
75
  end
106
76
  list.group_by(&:last).each do |_, grouped_list|
107
- obstacle_group = AIXM.obstacle_group(source: grouped_list.first.first.source)
108
- grouped_list.each { |o, _| obstacle_group.obstacles << features.delete(o) }
109
- features << obstacle_group
77
+ first_obstacle = grouped_list.first.first
78
+ obstacle_group = AIXM.obstacle_group(source: first_obstacle.source, region: first_obstacle.region)
79
+ grouped_list.each { |o, _| obstacle_group.add_obstacle features.send(:delete, o) }
80
+ add_feature obstacle_group
110
81
  end.count
111
82
  end
112
83
 
@@ -134,7 +105,6 @@ module AIXM
134
105
  'xmlns:xsi': AIXM.schema(:namespace),
135
106
  version: AIXM.schema(:version),
136
107
  origin: "rubygem aixm-#{AIXM::VERSION}",
137
- region: (region if AIXM.ofmx?),
138
108
  namespace: (namespace if AIXM.ofmx?),
139
109
  created: @created_at.xmlschema,
140
110
  effective: @effective_at.xmlschema
@@ -142,7 +112,14 @@ module AIXM
142
112
  builder = Builder::XmlMarkup.new(indent: 2)
143
113
  builder.instruct!
144
114
  builder.tag!(AIXM.schema(:root), meta) do |root|
145
- root << features.map { |f| f.to_xml }.join.indent(2)
115
+ AIXM::Memoize.method :to_uid do
116
+ root << features.map { _1.to_xml }.join.indent(2)
117
+ end
118
+ end
119
+ if AIXM.ofmx? && AIXM.config.mid
120
+ AIXM::PayloadHash::Mid.new(builder.target!).insert_mid.to_xml
121
+ else
122
+ builder.target!
146
123
  end
147
124
  end
148
125
 
@@ -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
@@ -2,24 +2,34 @@ module AIXM
2
2
 
3
3
  # @abstract
4
4
  class Feature
5
+ REGION_RE = /\A[A-Z]{2}\z/.freeze
6
+
5
7
  private_class_method :new
6
8
 
7
9
  # @return [String] reference to source of the feature data
8
10
  attr_reader :source
9
11
 
10
- def initialize(source: nil)
12
+ # @return [String] OFMX region all features in this document belong to
13
+ attr_reader :region
14
+
15
+ def initialize(source: nil, region: nil)
11
16
  self.source = source
17
+ self.region = region || AIXM.config.region
12
18
  end
13
19
 
14
- # @return [String] reference to source of the feature data
15
20
  def source=(value)
16
21
  fail(ArgumentError, "invalid source") unless value.nil? || value.is_a?(String)
17
22
  @source = value
18
23
  end
19
24
 
25
+ def region=(value)
26
+ fail(ArgumentError, "invalid region") unless value.nil? || (value.is_a?(String) && value.upcase.match?(REGION_RE))
27
+ @region = value&.upcase
28
+ end
29
+
20
30
  # @return [Boolean]
21
31
  def ==(other)
22
- self.class === other && self.to_uid == other.to_uid
32
+ self.__class__ === other && self.to_uid == other.to_uid
23
33
  end
24
34
  end
25
35
 
@@ -13,8 +13,11 @@ module AIXM
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,12 +59,6 @@ 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
@@ -79,10 +77,11 @@ module AIXM
79
77
  builder = Builder::XmlMarkup.new(indent: 2)
80
78
  builder.tag!(as) do |tag|
81
79
  tag << addressable.to_uid.indent(2) if addressable
82
- tag.codeType(TYPES.key(type).to_s.then_if(AIXM.aixm?) { |t| t.sub(/-\w+$/, '') })
80
+ tag.codeType(TYPES.key(type).to_s.then_if(AIXM.aixm?) { _1.sub(/-\w+$/, '') })
83
81
  tag.noSeq(sequence)
84
- end.insert_payload_hash(region: AIXM.config.mid_region)
82
+ end
85
83
  end
84
+ memoize :to_uid
86
85
 
87
86
  # @return [String] AIXM or OFMX markup
88
87
  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
  #
@@ -93,26 +142,10 @@ module AIXM
93
142
  # @return [String, nil] free text remarks
94
143
  attr_reader :remarks
95
144
 
96
- # @return [Array<AIXM::Component::Runway>] runways present at this airport
97
- attr_reader :runways
98
-
99
- # @return [Array<AIXM::Component::FATO>] FATOs present at this airport
100
- attr_reader :fatos
101
-
102
- # @return [Array<AIXM::Component::Helipad>] helipads present at this airport
103
- attr_reader :helipads
104
-
105
- # @return [Array<AIXM::Feature::Airport::UsageLimitation>] usage limitations
106
- attr_accessor :usage_limitations
107
-
108
- # @return [Array<AIXM::Feature::Address>] postal address, url, A/A or A/G frequency etc
109
- attr_reader :addresses
110
-
111
- def initialize(source: nil, organisation:, id: nil, name:, xy:)
112
- super(source: source)
145
+ def initialize(source: nil, region: nil, organisation:, id: nil, name:, xy:)
146
+ super(source: source, region: region)
113
147
  self.organisation, self.name, self.xy = organisation, name, xy
114
148
  self.id = id # name must already be set
115
- @runways, @fatos, @helipads, @usage_limitations, @addresses = [], [], [], [], []
116
149
  end
117
150
 
118
151
  # @return [String]
@@ -120,15 +153,10 @@ module AIXM
120
153
  %Q(#<#{self.class} id=#{id.inspect}>)
121
154
  end
122
155
 
123
- def organisation=(value)
124
- fail(ArgumentError, "invalid organisation") unless value.is_a? AIXM::Feature::Organisation
125
- @organisation = value
126
- end
127
-
128
156
  # For airports without an +id+, you may assign the two character region
129
157
  # (e.g. "LF") which will be combined with an 8 character digest of +name+.
130
158
  def id=(value)
131
- 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
132
160
  fail(ArgumentError, "invalid id") unless value&.upcase&.match? ID_RE
133
161
  @id = value.upcase
134
162
  end
@@ -199,99 +227,21 @@ module AIXM
199
227
  @remarks = value&.to_s
200
228
  end
201
229
 
202
- # Add a runway to the airport.
203
- #
204
- # @param runway [AIXM::Component::Runway] runway instance
205
- # @return [self]
206
- def add_runway(runway)
207
- fail(ArgumentError, "invalid runway") unless runway.is_a? AIXM::Component::Runway
208
- runway.send(:airport=, self)
209
- @runways << runway
210
- self
211
- end
212
-
213
- # Add a FATO to the airport.
214
- #
215
- # @param FATO [AIXM::Component::FATO] FATO instance
216
- # @return [self]
217
- def add_fato(fato)
218
- fail(ArgumentError, "invalid FATO") unless fato.is_a? AIXM::Component::FATO
219
- fato.send(:airport=, self)
220
- @fatos << fato
221
- self
222
- end
223
-
224
- # Add a helipad to the airport.
225
- #
226
- # @param helipad [AIXM::Component::Helipad] helipad instance
227
- # @return [self]
228
- def add_helipad(helipad)
229
- fail(ArgumentError, "invalid helipad") unless helipad.is_a? AIXM::Component::Helipad
230
- helipad.send(:airport=, self)
231
- @helipads << helipad
232
- self
233
- end
234
-
235
- # Add an airport usage limitation.
236
- #
237
- # See {AIXM::Feature::Airport::UsageLimitation::TYPES UsageLimitation::TYPES}
238
- # for recognized limitations and {AIXM::Feature::Airport::UsageLimitation#add_condition UsageLimitation#add_condition}
239
- # for recognized conditions.
240
- #
241
- # Multiple conditions are joined with an implicit *or* whereas the
242
- # specifics of a condition (aircraft, rule etc) are joined with an
243
- # implicit *and*.
244
- #
245
- # @example Limitation applying to any traffic
246
- # airport.add_usage_limitation(:permitted)
247
- #
248
- # @example Limitation applying to specific traffic
249
- # airport.add_usage_limitation(:reservation_required) do |reservation_required|
250
- # reservation_required.add_condition do |condition|
251
- # condition.aircraft = :glider
252
- # end
253
- # reservation_required.add_condition do |condition|
254
- # condition.rule = :ifr
255
- # condition.origin = :international
256
- # end
257
- # reservation_required.timetable = AIXM::H24
258
- # reservation_required.remarks = "Reservation 24 HRS prior to arrival"
259
- # end
260
- #
261
- # @yieldparam usage_limitation [AIXM::Feature::Airport::UsageLimitation]
262
- # @return [self]
263
- def add_usage_limitation(type)
264
- usage_limitation = UsageLimitation.new(type: type)
265
- yield(usage_limitation) if block_given?
266
- @usage_limitations << usage_limitation
267
- self
268
- end
269
-
270
- # Add an address (postal address, url, A/A or A/G frequency etc) to the airport.
271
- #
272
- # @params address [AIXM::Feature::Address] address instance
273
- # @return [self]
274
- def add_address(address)
275
- fail(ArgumentError, "invalid address") unless address.is_a? AIXM::Feature::Address
276
- address.send(:addressable=, self)
277
- @addresses << address
278
- self
279
- end
280
-
281
230
  # @return [String] UID markup
282
231
  def to_uid(as: :AhpUid)
283
232
  builder = Builder::XmlMarkup.new(indent: 2)
284
- builder.tag!(as) do |tag|
233
+ builder.tag!(as, ({ region: (region if AIXM.ofmx?) }.compact)) do |tag|
285
234
  tag.codeId(id)
286
- end.insert_payload_hash(region: AIXM.config.mid_region)
235
+ end
287
236
  end
237
+ memoize :to_uid
288
238
 
289
239
  # @return [String] UID markup
290
240
  def to_wrapped_uid(as: :AhpUid, with:)
291
241
  builder = Builder::XmlMarkup.new(indent: 2)
292
242
  builder.tag!(with) do |tag|
293
243
  tag << to_uid(as: as).indent(2)
294
- end.insert_payload_hash(region: AIXM.config.mid_region)
244
+ end
295
245
  end
296
246
 
297
247
  # @return [String] AIXM or OFMX markup
@@ -349,9 +299,35 @@ module AIXM
349
299
  # Limitations concerning the availability of an airport for certain flight
350
300
  # types, aircraft types etc during specific hours.
351
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
+ #
352
326
  # @see AIXM::Feature::Airport#add_usage_limitation
353
- # @see https://github.com/openflightmaps/ofmx/wiki/Airport#ahu-airport-usage
327
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Airport#ahu-airport-usage
354
328
  class UsageLimitation
329
+ include AIXM::Association
330
+
355
331
  TYPES = {
356
332
  PERMIT: :permitted,
357
333
  FORBID: :forbidden,
@@ -359,15 +335,20 @@ module AIXM
359
335
  OTHER: :other # specify in remarks
360
336
  }.freeze
361
337
 
362
- # @return [AIXM::Feature::Airport] airport this usage limitation is assigned to
363
- 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
364
348
 
365
349
  # @return [Symbol] type of limitation
366
350
  attr_reader :type
367
351
 
368
- # @return [Array<AIXM::Feature::Airport::UsageLimitation::Condition>] conditions for this limitation to apply
369
- attr_reader :conditions
370
-
371
352
  # @return [AIXM::Component::Timetable, nil] limitation application hours
372
353
  attr_reader :timetable
373
354
 
@@ -376,7 +357,6 @@ module AIXM
376
357
 
377
358
  def initialize(type:)
378
359
  self.type = type
379
- @conditions = []
380
360
  end
381
361
 
382
362
  # @return [String]
@@ -388,17 +368,6 @@ module AIXM
388
368
  @type = TYPES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid type")
389
369
  end
390
370
 
391
- # Add a condition to the usage limitation.
392
- #
393
- # @yieldparam condition [AIXM::Feature::Airport::UsageLimitation::Condition]
394
- # @return [self]
395
- def add_condition
396
- condition = Condition.new
397
- yield(condition)
398
- @conditions << condition
399
- self
400
- end
401
-
402
371
  def timetable=(value)
403
372
  fail(ArgumentError, "invalid timetable") unless value.nil? || value.is_a?(AIXM::Component::Timetable)
404
373
  @timetable = value
@@ -425,8 +394,10 @@ module AIXM
425
394
  # limitation.
426
395
  #
427
396
  # @see AIXM::Feature::Airport#add_usage_limitation
428
- # @see https://github.com/openflightmaps/ofmx/wiki/Airport#ahu-airport-usage
397
+ # @see https://gitlab.com/openflightmaps/ofmx/wikis/Airport#ahu-airport-usage
429
398
  class Condition
399
+ include AIXM::Association
400
+
430
401
  AIRCRAFT = {
431
402
  L: :landplane,
432
403
  S: :seaplane,
@@ -472,6 +443,10 @@ module AIXM
472
443
  OTHER: :other # specify in remarks
473
444
  }.freeze
474
445
 
446
+ # @!method usage_limitation
447
+ # @return [AIXM::Feature::Airport::UsageLimitation] usage limitation the condition belongs to
448
+ belongs_to :usage_limitation
449
+
475
450
  # @return [Symbol, nil] kind of aircraft (see {AIRCRAFT})
476
451
  attr_reader :aircraft
477
452