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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +1 -0
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +33 -3
- data/README.md +166 -56
- data/exe/ckmid +14 -0
- data/exe/mkmid +14 -0
- data/lib/aixm.rb +16 -6
- data/lib/aixm/association.rb +369 -0
- data/lib/aixm/classes.rb +43 -0
- data/lib/aixm/component/fato.rb +45 -53
- data/lib/aixm/component/frequency.rb +11 -12
- data/lib/aixm/component/geometry.rb +36 -38
- data/lib/aixm/component/geometry/arc.rb +2 -2
- data/lib/aixm/component/geometry/border.rb +6 -3
- data/lib/aixm/component/geometry/circle.rb +8 -2
- data/lib/aixm/component/geometry/point.rb +8 -2
- data/lib/aixm/component/helipad.rb +30 -38
- data/lib/aixm/component/layer.rb +28 -19
- data/lib/aixm/component/lighting.rb +12 -13
- data/lib/aixm/component/runway.rb +44 -48
- data/lib/aixm/{feature → component}/service.rb +37 -36
- data/lib/aixm/component/surface.rb +3 -3
- data/lib/aixm/component/timetable.rb +2 -2
- data/lib/aixm/component/{vertical_limits.rb → vertical_limit.rb} +12 -6
- data/lib/aixm/config.rb +2 -1
- data/lib/aixm/document.rb +27 -50
- data/lib/aixm/executables.rb +85 -0
- data/lib/aixm/feature.rb +13 -3
- data/lib/aixm/feature/address.rb +12 -13
- data/lib/aixm/feature/airport.rb +103 -128
- data/lib/aixm/feature/airspace.rb +44 -17
- data/lib/aixm/feature/navigational_aid.rb +7 -9
- data/lib/aixm/feature/navigational_aid/designated_point.rb +13 -15
- data/lib/aixm/feature/navigational_aid/dme.rb +11 -12
- data/lib/aixm/feature/navigational_aid/marker.rb +7 -3
- data/lib/aixm/feature/navigational_aid/ndb.rb +7 -3
- data/lib/aixm/feature/navigational_aid/tacan.rb +7 -3
- data/lib/aixm/feature/navigational_aid/vor.rb +23 -15
- data/lib/aixm/feature/obstacle.rb +29 -43
- data/lib/aixm/feature/obstacle_group.rb +37 -34
- data/lib/aixm/feature/organisation.rb +21 -5
- data/lib/aixm/feature/unit.rb +36 -46
- data/lib/aixm/memoize.rb +89 -0
- data/lib/aixm/object.rb +9 -0
- data/lib/aixm/payload_hash.rb +114 -0
- data/lib/aixm/refinements.rb +29 -76
- data/lib/aixm/shortcuts.rb +5 -42
- data/lib/aixm/version.rb +1 -1
- data/lib/aixm/xy.rb +1 -1
- data/schemas/ofmx/0/OFMX-Features.xsd +152 -20
- data/schemas/ofmx/0/OFMX-Snapshot.xsd +0 -5
- metadata +107 -156
- metadata.gz.sig +2 -0
- data/.github/workflows/test.yml +0 -26
- data/.gitignore +0 -6
- data/.ruby-version +0 -1
- data/.yardopts +0 -3
- data/Guardfile +0 -8
- data/aixm.gemspec +0 -35
- data/gems.rb +0 -3
- data/lib/aixm/component.rb +0 -6
- data/rakefile.rb +0 -36
- data/spec/factory.rb +0 -559
- data/spec/lib/aixm/a_spec.rb +0 -203
- data/spec/lib/aixm/component/fato_spec.rb +0 -267
- data/spec/lib/aixm/component/frequency_spec.rb +0 -74
- data/spec/lib/aixm/component/geometry/arc_spec.rb +0 -73
- data/spec/lib/aixm/component/geometry/border_spec.rb +0 -38
- data/spec/lib/aixm/component/geometry/circle_spec.rb +0 -68
- data/spec/lib/aixm/component/geometry/point_spec.rb +0 -37
- data/spec/lib/aixm/component/geometry_spec.rb +0 -316
- data/spec/lib/aixm/component/helipad_spec.rb +0 -193
- data/spec/lib/aixm/component/layer_spec.rb +0 -135
- data/spec/lib/aixm/component/lighting_spec.rb +0 -94
- data/spec/lib/aixm/component/runway_spec.rb +0 -479
- data/spec/lib/aixm/component/surface_spec.rb +0 -124
- data/spec/lib/aixm/component/timetable_spec.rb +0 -47
- data/spec/lib/aixm/component/vertical_limits_spec.rb +0 -94
- data/spec/lib/aixm/config_spec.rb +0 -41
- data/spec/lib/aixm/d_spec.rb +0 -150
- data/spec/lib/aixm/document_spec.rb +0 -1884
- data/spec/lib/aixm/errors_spec.rb +0 -14
- data/spec/lib/aixm/f_spec.rb +0 -85
- data/spec/lib/aixm/feature/address_spec.rb +0 -60
- data/spec/lib/aixm/feature/airport_spec.rb +0 -776
- data/spec/lib/aixm/feature/airspace_spec.rb +0 -394
- data/spec/lib/aixm/feature/navigational_aid/designated_point_spec.rb +0 -103
- data/spec/lib/aixm/feature/navigational_aid/dme_spec.rb +0 -98
- data/spec/lib/aixm/feature/navigational_aid/marker_spec.rb +0 -85
- data/spec/lib/aixm/feature/navigational_aid/ndb_spec.rb +0 -95
- data/spec/lib/aixm/feature/navigational_aid/tacan_spec.rb +0 -94
- data/spec/lib/aixm/feature/navigational_aid/vor_spec.rb +0 -251
- data/spec/lib/aixm/feature/navigational_aid_spec.rb +0 -52
- data/spec/lib/aixm/feature/obstacle_group_spec.rb +0 -330
- data/spec/lib/aixm/feature/obstacle_spec.rb +0 -284
- data/spec/lib/aixm/feature/organisation_spec.rb +0 -83
- data/spec/lib/aixm/feature/service_spec.rb +0 -59
- data/spec/lib/aixm/feature/unit_spec.rb +0 -238
- data/spec/lib/aixm/feature_spec.rb +0 -38
- data/spec/lib/aixm/p_spec.rb +0 -189
- data/spec/lib/aixm/refinements_spec.rb +0 -430
- data/spec/lib/aixm/version_spec.rb +0 -7
- data/spec/lib/aixm/w_spec.rb +0 -150
- data/spec/lib/aixm/xy_spec.rb +0 -180
- data/spec/lib/aixm/z_spec.rb +0 -94
- data/spec/macros/marking.rb +0 -12
- data/spec/macros/organisation.rb +0 -11
- data/spec/macros/remarks.rb +0 -12
- data/spec/macros/timetable.rb +0 -11
- data/spec/macros/xy.rb +0 -11
- data/spec/macros/z_qnh.rb +0 -11
- data/spec/sounds/failure.mp3 +0 -0
- data/spec/sounds/success.mp3 +0 -0
- data/spec/spec_helper.rb +0 -62
data/lib/aixm/config.rb
CHANGED
data/lib/aixm/document.rb
CHANGED
|
@@ -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.
|
|
14
|
+
# document.add_feature(AIXM::Feature)
|
|
16
15
|
#
|
|
17
|
-
# @see https://
|
|
16
|
+
# @see https://gitlab.com/openflightmaps/ofmx/wikis/Snapshot
|
|
18
17
|
class Document
|
|
19
|
-
|
|
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
|
-
#
|
|
24
|
-
|
|
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
|
-
|
|
36
|
-
|
|
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 =
|
|
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 {
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
features
|
|
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
|
-
|
|
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
|
data/lib/aixm/feature.rb
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
32
|
+
self.__class__ === other && self.to_uid == other.to_uid
|
|
23
33
|
end
|
|
24
34
|
end
|
|
25
35
|
|
data/lib/aixm/feature/address.rb
CHANGED
|
@@ -13,8 +13,11 @@ module AIXM
|
|
|
13
13
|
# )
|
|
14
14
|
# service.remarks = String or nil
|
|
15
15
|
#
|
|
16
|
-
# @see https://
|
|
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
|
-
#
|
|
37
|
-
|
|
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?) {
|
|
80
|
+
tag.codeType(TYPES.key(type).to_s.then_if(AIXM.aixm?) { _1.sub(/-\w+$/, '') })
|
|
83
81
|
tag.noSeq(sequence)
|
|
84
|
-
end
|
|
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:)
|
data/lib/aixm/feature/airport.rb
CHANGED
|
@@ -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://
|
|
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
|
-
#
|
|
48
|
-
|
|
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
|
-
|
|
97
|
-
|
|
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::
|
|
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
|
|
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
|
|
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://
|
|
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
|
-
#
|
|
363
|
-
|
|
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://
|
|
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
|
|