aixm 0.2.3 → 0.3.0
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
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/.yardopts +3 -0
- data/CHANGELOG.md +34 -14
- data/Guardfile +1 -0
- data/README.md +64 -257
- data/lib/aixm.rb +16 -7
- data/lib/aixm/component.rb +6 -0
- data/lib/aixm/component/frequency.rb +135 -0
- data/lib/aixm/component/geometry.rb +34 -23
- data/lib/aixm/component/geometry/arc.rb +37 -22
- data/lib/aixm/component/geometry/border.rb +29 -20
- data/lib/aixm/component/geometry/circle.rb +39 -22
- data/lib/aixm/component/geometry/point.rb +29 -13
- data/lib/aixm/component/helipad.rb +154 -0
- data/lib/aixm/component/layer.rb +91 -0
- data/lib/aixm/component/runway.rb +294 -0
- data/lib/aixm/component/service.rb +170 -0
- data/lib/aixm/component/timetable.rb +65 -0
- data/lib/aixm/component/vertical_limits.rb +65 -29
- data/lib/aixm/config.rb +87 -0
- data/lib/aixm/document.rb +66 -42
- data/lib/aixm/errors.rb +11 -0
- data/lib/aixm/f.rb +34 -20
- data/lib/aixm/feature.rb +38 -0
- data/lib/aixm/feature/airport.rb +473 -0
- data/lib/aixm/feature/airspace.rb +145 -92
- data/lib/aixm/feature/navigational_aid.rb +94 -0
- data/lib/aixm/feature/navigational_aid/designated_point.rb +50 -54
- data/lib/aixm/feature/navigational_aid/dme.rb +48 -40
- data/lib/aixm/feature/navigational_aid/marker.rb +55 -45
- data/lib/aixm/feature/navigational_aid/ndb.rb +54 -50
- data/lib/aixm/feature/navigational_aid/tacan.rb +38 -31
- data/lib/aixm/feature/navigational_aid/vor.rb +84 -76
- data/lib/aixm/feature/organisation.rb +97 -0
- data/lib/aixm/feature/unit.rb +152 -0
- data/lib/aixm/refinements.rb +132 -47
- data/lib/aixm/shortcuts.rb +11 -6
- data/lib/aixm/version.rb +1 -1
- data/lib/aixm/xy.rb +64 -20
- data/lib/aixm/z.rb +51 -22
- data/{lib/aixm/schemas → schemas/aixm}/4.5/AIXM-DataTypes.xsd +0 -0
- data/{lib/aixm/schemas → schemas/aixm}/4.5/AIXM-Features.xsd +0 -0
- data/{lib/aixm/schemas → schemas/aixm}/4.5/AIXM-Snapshot.xsd +0 -0
- data/schemas/ofmx/0/OFMX-DataTypes.xsd +5077 -0
- data/schemas/ofmx/0/OFMX-Features.xsd +9955 -0
- data/schemas/ofmx/0/OFMX-Snapshot.xsd +217 -0
- data/spec/factory.rb +209 -33
- data/spec/lib/aixm/component/frequency_spec.rb +75 -0
- data/spec/lib/aixm/component/geometry/arc_spec.rb +28 -22
- data/spec/lib/aixm/component/geometry/border_spec.rb +23 -20
- data/spec/lib/aixm/component/geometry/circle_spec.rb +31 -22
- data/spec/lib/aixm/component/geometry/point_spec.rb +11 -14
- data/spec/lib/aixm/component/geometry_spec.rb +150 -69
- data/spec/lib/aixm/component/helipad_spec.rb +136 -0
- data/spec/lib/aixm/component/layer_spec.rb +110 -0
- data/spec/lib/aixm/component/runway_spec.rb +402 -0
- data/spec/lib/aixm/component/service_spec.rb +61 -0
- data/spec/lib/aixm/component/timetable_spec.rb +49 -0
- data/spec/lib/aixm/component/vertical_limits_spec.rb +39 -20
- data/spec/lib/aixm/config_spec.rb +41 -0
- data/spec/lib/aixm/document_spec.rb +637 -147
- data/spec/lib/aixm/errors_spec.rb +14 -0
- data/spec/lib/aixm/f_spec.rb +17 -10
- data/spec/lib/aixm/feature/airport_spec.rb +546 -0
- data/spec/lib/aixm/feature/airspace_spec.rb +349 -226
- data/spec/lib/aixm/feature/navigational_aid/designated_point_spec.rb +47 -36
- data/spec/lib/aixm/feature/navigational_aid/dme_spec.rb +61 -36
- data/spec/lib/aixm/feature/navigational_aid/marker_spec.rb +61 -113
- data/spec/lib/aixm/feature/navigational_aid/ndb_spec.rb +65 -79
- data/spec/lib/aixm/feature/navigational_aid/tacan_spec.rb +57 -36
- data/spec/lib/aixm/feature/navigational_aid/vor_spec.rb +86 -112
- data/spec/lib/aixm/feature/navigational_aid_spec.rb +52 -0
- data/spec/lib/aixm/feature/organisation_spec.rb +77 -0
- data/spec/lib/aixm/feature/unit_spec.rb +227 -0
- data/spec/lib/aixm/feature_spec.rb +58 -0
- data/spec/lib/aixm/refinements_spec.rb +187 -178
- data/spec/lib/aixm/xy_spec.rb +45 -34
- data/spec/lib/aixm/z_spec.rb +19 -21
- data/spec/macros/organisation.rb +11 -0
- data/spec/macros/remarks.rb +12 -0
- data/spec/macros/timetable.rb +11 -0
- data/spec/macros/xy.rb +11 -0
- data/spec/macros/z_qnh.rb +11 -0
- data/spec/spec_helper.rb +26 -0
- metadata +60 -19
- data/lib/aixm/base.rb +0 -10
- data/lib/aixm/component/base.rb +0 -6
- data/lib/aixm/component/class_layer.rb +0 -46
- data/lib/aixm/component/geometry/base.rb +0 -8
- data/lib/aixm/component/schedule.rb +0 -43
- data/lib/aixm/feature/base.rb +0 -6
- data/lib/aixm/feature/navigational_aid/base.rb +0 -79
- data/spec/lib/aixm/component/class_layer_spec.rb +0 -74
- data/spec/lib/aixm/component/schedule_spec.rb +0 -33
- data/spec/lib/aixm/feature/navigational_aid/base_spec.rb +0 -41
|
@@ -1,30 +1,46 @@
|
|
|
1
|
+
using AIXM::Refinements
|
|
2
|
+
|
|
1
3
|
module AIXM
|
|
2
|
-
|
|
4
|
+
class Component
|
|
3
5
|
class Geometry
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
#
|
|
7
|
-
|
|
7
|
+
# Points are defined by {#xy} coordinates.
|
|
8
|
+
#
|
|
9
|
+
# ===Cheat Sheet in Pseudo Code:
|
|
10
|
+
# point = AIXM.point(
|
|
11
|
+
# xy: AIXM.xy
|
|
12
|
+
# )
|
|
13
|
+
#
|
|
14
|
+
# @see https://github.com/openflightmaps/ofmx/wiki/Airspace#point
|
|
15
|
+
class Point
|
|
8
16
|
extend Forwardable
|
|
9
|
-
using AIXM::Refinements
|
|
10
17
|
|
|
11
|
-
def_delegators :xy
|
|
18
|
+
def_delegators :xy
|
|
12
19
|
|
|
20
|
+
# @return [AIXM::XY] (starting) point
|
|
13
21
|
attr_reader :xy
|
|
14
22
|
|
|
15
23
|
def initialize(xy:)
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
self.xy = xy
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [String]
|
|
28
|
+
def inspect
|
|
29
|
+
%Q(#<#{self.class} xy="#{xy.to_s}">)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def xy=(value)
|
|
33
|
+
fail(ArgumentError, "invalid xy") unless value.is_a? AIXM::XY
|
|
34
|
+
@xy = value
|
|
18
35
|
end
|
|
19
36
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def to_aixm(*extensions)
|
|
37
|
+
# @return [String] AIXM or OFMX markup
|
|
38
|
+
def to_xml
|
|
23
39
|
builder = Builder::XmlMarkup.new(indent: 2)
|
|
24
40
|
builder.Avx do |avx|
|
|
25
41
|
avx.codeType('GRC')
|
|
26
|
-
avx.geoLat(xy.lat(
|
|
27
|
-
avx.geoLong(xy.long(
|
|
42
|
+
avx.geoLat(xy.lat(AIXM.schema))
|
|
43
|
+
avx.geoLong(xy.long(AIXM.schema))
|
|
28
44
|
avx.codeDatum('WGE')
|
|
29
45
|
end
|
|
30
46
|
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
using AIXM::Refinements
|
|
2
|
+
|
|
3
|
+
module AIXM
|
|
4
|
+
class Component
|
|
5
|
+
|
|
6
|
+
# Helipads are TLOF (touch-down and lift-off areas) e.g. for helicopters.
|
|
7
|
+
#
|
|
8
|
+
# ===Cheat Sheet in Pseudo Code:
|
|
9
|
+
# helipad = AIXM.helipad(
|
|
10
|
+
# name: String
|
|
11
|
+
# )
|
|
12
|
+
# helipad.xy = AIXM.xy
|
|
13
|
+
# helipad.z = AIXM.z or nil
|
|
14
|
+
# helipad.length = Integer or nil # meters
|
|
15
|
+
# helipad.width = Integer or nil # meters
|
|
16
|
+
# helipad.composition = COMPOSITIONS or nil
|
|
17
|
+
# helipad.status = STATUSES or nil
|
|
18
|
+
# helipad.remarks = String or nil
|
|
19
|
+
#
|
|
20
|
+
# @see https://github.com/openflightmaps/ofmx/wiki/Airport#tla-helipad-tlof
|
|
21
|
+
class Helipad
|
|
22
|
+
COMPOSITIONS = {
|
|
23
|
+
ASPH: :asphalt,
|
|
24
|
+
BITUM: :bitumen, # dug up, bound and rolled ground
|
|
25
|
+
CONC: :concrete,
|
|
26
|
+
GRAVE: :gravel, # small and midsize rounded stones
|
|
27
|
+
MACADAM: :macadam, # small rounded stones
|
|
28
|
+
SAND: :sand,
|
|
29
|
+
GRADE: :graded_earth, # graded or rolled earth possibly with some grass
|
|
30
|
+
GRASS: :grass, # lawn
|
|
31
|
+
WATER: :water,
|
|
32
|
+
OTHER: :other # specify in remarks
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
STATUSES = {
|
|
36
|
+
CLSD: :closed,
|
|
37
|
+
WIP: :work_in_progress, # e.g. construction work
|
|
38
|
+
PARKED: :parked_aircraft, # parked or disabled aircraft on helipad
|
|
39
|
+
FAILAID: :visual_aids_failure, # failure or irregular operation of visual aids
|
|
40
|
+
SPOWER: :secondary_power, # secondary power supply in operation
|
|
41
|
+
OTHER: :other # specify in remarks
|
|
42
|
+
}.freeze
|
|
43
|
+
|
|
44
|
+
# @return [AIXM::Feature::Airport] airport this helipad belongs to
|
|
45
|
+
attr_reader :airport
|
|
46
|
+
|
|
47
|
+
# @return [String] full name (e.g. "H1")
|
|
48
|
+
attr_reader :name
|
|
49
|
+
|
|
50
|
+
# @return [AIXM::XY] center point
|
|
51
|
+
attr_reader :xy
|
|
52
|
+
|
|
53
|
+
# @return [AIXM:Z, nil] elevation in +:qnh+
|
|
54
|
+
attr_reader :z
|
|
55
|
+
|
|
56
|
+
# @return [Integer, nil] length in meters
|
|
57
|
+
attr_reader :length
|
|
58
|
+
|
|
59
|
+
# @return [Integer, nil] width in meters
|
|
60
|
+
attr_reader :width
|
|
61
|
+
|
|
62
|
+
# @return [Symbol, nil] composition of the surface (see {COMPOSITIONS})
|
|
63
|
+
attr_reader :composition
|
|
64
|
+
|
|
65
|
+
# @return [Symbol, nil] status of the helipad (see {STATUSES}) or +nil+ for normal operation
|
|
66
|
+
attr_reader :status
|
|
67
|
+
|
|
68
|
+
# @return [String, nil] free text remarks
|
|
69
|
+
attr_reader :remarks
|
|
70
|
+
|
|
71
|
+
def initialize(name:)
|
|
72
|
+
self.name = name
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @return [String]
|
|
76
|
+
def inspect
|
|
77
|
+
%Q(#<#{self.class} name=#{name.inspect}>)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def airport=(value)
|
|
81
|
+
fail(ArgumentError, "invalid airport") unless value.is_a? AIXM::Feature::Airport
|
|
82
|
+
@airport = value
|
|
83
|
+
end
|
|
84
|
+
private :airport=
|
|
85
|
+
|
|
86
|
+
def name=(value)
|
|
87
|
+
fail(ArgumentError, "invalid name") unless value.is_a? String
|
|
88
|
+
@name = value.uptrans
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def xy=(value)
|
|
92
|
+
fail(ArgumentError, "invalid xy") unless value.is_a? AIXM::XY
|
|
93
|
+
@xy = value
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def z=(value)
|
|
97
|
+
fail(ArgumentError, "invalid z") unless value.nil? || (value.is_a?(AIXM::Z) && value.qnh?)
|
|
98
|
+
@z = value
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def length=(value)
|
|
102
|
+
fail(ArgumentError, "invalid length") unless value.nil? || (value.is_a?(Numeric) && value > 0)
|
|
103
|
+
@length = value.nil? ? nil : value.to_i
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def width=(value)
|
|
107
|
+
fail(ArgumentError, "invalid width") unless value.nil? || (value.is_a?(Numeric) && value > 0)
|
|
108
|
+
@width = value.nil? ? nil : value.to_i
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def composition=(value)
|
|
112
|
+
@composition = value.nil? ? nil : COMPOSITIONS.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid composition")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def status=(value)
|
|
116
|
+
@status = value.nil? ? nil : (STATUSES.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid status"))
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def remarks=(value)
|
|
120
|
+
@remarks = value&.to_s
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# @return [String] UID markup
|
|
124
|
+
def to_uid
|
|
125
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
|
126
|
+
builder.TlaUid do |tla_uid|
|
|
127
|
+
tla_uid << airport.to_uid.indent(2)
|
|
128
|
+
tla_uid.txtDesig(name)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# @return [String] AIXM or OFMX markup
|
|
133
|
+
def to_xml
|
|
134
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
|
135
|
+
builder.Tla do |tla|
|
|
136
|
+
tla << to_uid.indent(2)
|
|
137
|
+
tla.geoLat(xy.lat(AIXM.schema))
|
|
138
|
+
tla.geoLong(xy.long(AIXM.schema))
|
|
139
|
+
tla.codeDatum('WGE')
|
|
140
|
+
if z
|
|
141
|
+
tla.valElev(z.alt)
|
|
142
|
+
tla.uomDistVer(z.unit.upcase.to_s)
|
|
143
|
+
end
|
|
144
|
+
tla.valLen(length) if length
|
|
145
|
+
tla.valWid(width) if width
|
|
146
|
+
tla.uomDim('M') if length || width
|
|
147
|
+
tla.codeComposition(COMPOSITIONS.key(composition).to_s) if composition
|
|
148
|
+
tla.codeSts(STATUSES.key(status).to_s) if status
|
|
149
|
+
tla.txtRmk(remarks) if remarks
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
using AIXM::Refinements
|
|
2
|
+
|
|
3
|
+
module AIXM
|
|
4
|
+
class Component
|
|
5
|
+
|
|
6
|
+
# Each airspace has one or more layers with optional airspace class and
|
|
7
|
+
# mandatory vertical limits.
|
|
8
|
+
#
|
|
9
|
+
# ===Cheat Sheet in Pseudo Code:
|
|
10
|
+
# layer = AIXM.layer(
|
|
11
|
+
# class: String or nil
|
|
12
|
+
# vertical_limits: AIXM.vertical_limits
|
|
13
|
+
# )
|
|
14
|
+
# layer.timetable = AIXM.timetable or nil
|
|
15
|
+
# layer.selective = true or false (default)
|
|
16
|
+
# layer.remarks = String or nil
|
|
17
|
+
#
|
|
18
|
+
# @see https://github.com/openflightmaps/ofmx/wiki/Airspace
|
|
19
|
+
class Layer
|
|
20
|
+
CLASSES = (:A..:G).freeze
|
|
21
|
+
|
|
22
|
+
# @return [AIXM::Component::VerticalLimits] vertical limits of this layer
|
|
23
|
+
attr_reader :vertical_limits
|
|
24
|
+
|
|
25
|
+
# @return [AIXM::Component::Timetable, nil] activation hours
|
|
26
|
+
attr_reader :timetable
|
|
27
|
+
|
|
28
|
+
# @return [String, nil] free text remarks
|
|
29
|
+
attr_reader :remarks
|
|
30
|
+
|
|
31
|
+
def initialize(class: nil, vertical_limits:)
|
|
32
|
+
self.class = binding.local_variable_get(:class)
|
|
33
|
+
self.vertical_limits = vertical_limits
|
|
34
|
+
self.selective = false
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [String]
|
|
38
|
+
def inspect
|
|
39
|
+
%Q(#<#{self.class} class=#{@klass.inspect}>)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @!attribute class
|
|
43
|
+
# @return [Symbol] class of layer (see {CLASSES})
|
|
44
|
+
def class
|
|
45
|
+
@klass
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def class=(value)
|
|
49
|
+
@klass = value&.to_sym&.upcase
|
|
50
|
+
fail(ArgumentError, "invalid class") unless @klass.nil? || CLASSES.include?(@klass)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def vertical_limits=(value)
|
|
54
|
+
fail(ArgumentError, "invalid vertical limits") unless value.is_a? AIXM::Component::VerticalLimits
|
|
55
|
+
@vertical_limits = value
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def timetable=(value)
|
|
59
|
+
fail(ArgumentError, "invalid timetable") unless value.nil? || value.is_a?(AIXM::Component::Timetable)
|
|
60
|
+
@timetable = value
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @!attribute [w] selective
|
|
64
|
+
# @return [Boolean] whether the layer may be activated selectively
|
|
65
|
+
def selective?
|
|
66
|
+
@selective
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def selective=(value)
|
|
70
|
+
fail(ArgumentError, "invalid selective") unless [true, false].include? value
|
|
71
|
+
@selective = value
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def remarks=(value)
|
|
75
|
+
@remarks = value&.to_s
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @return [String] AIXM or OFMX markup
|
|
79
|
+
def to_xml
|
|
80
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
|
81
|
+
builder.codeClass(self.class.to_s) if self.class
|
|
82
|
+
builder << vertical_limits.to_xml
|
|
83
|
+
builder << timetable.to_xml(as: :Att) if timetable
|
|
84
|
+
builder.codeSelAvbl(selective? ? 'Y' : 'N') if AIXM.ofmx?
|
|
85
|
+
builder.txtRmk(remarks) if remarks
|
|
86
|
+
builder.target!
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
using AIXM::Refinements
|
|
2
|
+
|
|
3
|
+
module AIXM
|
|
4
|
+
class Component
|
|
5
|
+
|
|
6
|
+
# Runways are landing and takeoff strips for forward propelled aircraft.
|
|
7
|
+
#
|
|
8
|
+
# By convention, the runway name is usually the composition of the runway
|
|
9
|
+
# forth name (smaller number) and the runway back name (bigger number)
|
|
10
|
+
# joined with a forward slash e.g. "12/30" or "16R/34L".
|
|
11
|
+
#
|
|
12
|
+
# A runway has one or to directions accessible as +runway.forth+ (mandatory)
|
|
13
|
+
# and +runway.back+ (optional). Both have identical properties.
|
|
14
|
+
#
|
|
15
|
+
# ===Cheat Sheet in Pseudo Code:
|
|
16
|
+
# runway = AIXM.runway(
|
|
17
|
+
# name: String
|
|
18
|
+
# )
|
|
19
|
+
# runway.length = Integer or nil # meters
|
|
20
|
+
# runway.width = Integer or nil # meters
|
|
21
|
+
# runway.composition = COMPOSITIONS or nil
|
|
22
|
+
# runway.status = STATUSES or nil
|
|
23
|
+
# runway.remarks = String or nil
|
|
24
|
+
# runway.forth.name = String # preset based on the runway name
|
|
25
|
+
# runway.forth.geographic_orientation = Integer or nil # degrees
|
|
26
|
+
# runway.forth.xy = AIXM.xy
|
|
27
|
+
# runway.forth.z = AIXM.z or nil
|
|
28
|
+
# runway.forth.displaced_threshold = Integer or nil # meters
|
|
29
|
+
# runway.forth.remarks = String or nil
|
|
30
|
+
#
|
|
31
|
+
# @example Bidirectional runway
|
|
32
|
+
# runway = AIXM.runway(name: '16L/34R')
|
|
33
|
+
# runway.name # => '16L/34R'
|
|
34
|
+
# runway.forth.name = '16L'
|
|
35
|
+
# runway.forth.geographic_orientation = 165
|
|
36
|
+
# runway.back.name = '34R'
|
|
37
|
+
# runway.back.geographic_orientation = 345
|
|
38
|
+
#
|
|
39
|
+
# @example Unidirectional runway:
|
|
40
|
+
# runway = AIXM.runway(name: '16L')
|
|
41
|
+
# runway.name # => '16L'
|
|
42
|
+
# runway.forth.name = '16L'
|
|
43
|
+
# runway.forth.geographic_orientation = 165
|
|
44
|
+
# runway.back = nil
|
|
45
|
+
#
|
|
46
|
+
# @see https://github.com/openflightmaps/ofmx/wiki/Airport#rwy-runway
|
|
47
|
+
class Runway
|
|
48
|
+
COMPOSITIONS = {
|
|
49
|
+
ASPH: :asphalt,
|
|
50
|
+
BITUM: :bitumen, # dug up, bound and rolled ground
|
|
51
|
+
CONC: :concrete,
|
|
52
|
+
GRAVE: :gravel, # small and midsize rounded stones
|
|
53
|
+
MACADAM: :macadam, # small rounded stones
|
|
54
|
+
SAND: :sand,
|
|
55
|
+
GRADE: :graded_earth, # graded or rolled earth possibly with some grass
|
|
56
|
+
GRASS: :grass, # lawn
|
|
57
|
+
WATER: :water,
|
|
58
|
+
OTHER: :other # specify in remarks
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
STATUSES = {
|
|
62
|
+
CLSD: :closed,
|
|
63
|
+
WIP: :work_in_progress, # e.g. construction work
|
|
64
|
+
PARKED: :parked_aircraft, # parked or disabled aircraft on helipad
|
|
65
|
+
FAILAID: :visual_aids_failure, # failure or irregular operation of visual aids
|
|
66
|
+
SPOWER: :secondary_power, # secondary power supply in operation
|
|
67
|
+
OTHER: :other # specify in remarks
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# @return [AIXM::Feature::Airport] airport the runway belongs to
|
|
71
|
+
attr_reader :airport
|
|
72
|
+
|
|
73
|
+
# @return [String] full name of runway (e.g. "12/30" or "16L/34R")
|
|
74
|
+
attr_reader :name
|
|
75
|
+
|
|
76
|
+
# @return [Integer, nil] length in meters
|
|
77
|
+
attr_reader :length
|
|
78
|
+
|
|
79
|
+
# @return [Integer, nil] width in meters
|
|
80
|
+
attr_reader :width
|
|
81
|
+
|
|
82
|
+
# @return [Symbol, nil] composition of the surface (see {COMPOSITIONS})
|
|
83
|
+
attr_reader :composition
|
|
84
|
+
|
|
85
|
+
# @return [Symbol, nil] status of the runway (see {STATUSES}) or +nil+ for normal operation
|
|
86
|
+
attr_reader :status
|
|
87
|
+
|
|
88
|
+
# @return [String, nil] free text remarks
|
|
89
|
+
attr_reader :remarks
|
|
90
|
+
|
|
91
|
+
# @return [AIXM::Component::Runway::Direction] main direction
|
|
92
|
+
attr_accessor :forth
|
|
93
|
+
|
|
94
|
+
# @return [AIXM::Component::Runway::Direction] reverse direction
|
|
95
|
+
attr_accessor :back
|
|
96
|
+
|
|
97
|
+
def initialize(name:)
|
|
98
|
+
self.name = name
|
|
99
|
+
@name.split('/').tap do |forth, back|
|
|
100
|
+
@forth = Direction.new(runway: self, name: forth)
|
|
101
|
+
@back = Direction.new(runway: self, name: back) if back
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# @return [String]
|
|
106
|
+
def inspect
|
|
107
|
+
%Q(#<#{self.class} name=#{name.inspect}>)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def airport=(value)
|
|
111
|
+
fail(ArgumentError, "invalid airport") unless value.is_a? AIXM::Feature::Airport
|
|
112
|
+
@airport = value
|
|
113
|
+
end
|
|
114
|
+
private :airport=
|
|
115
|
+
|
|
116
|
+
def name=(value)
|
|
117
|
+
fail(ArgumentError, "invalid name") unless value.is_a? String
|
|
118
|
+
@name = value.uptrans
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def length=(value)
|
|
122
|
+
fail(ArgumentError, "invalid length") unless value.nil? || (value.is_a?(Numeric) && value > 0)
|
|
123
|
+
@length = value.nil? ? nil : value.to_i
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def width=(value)
|
|
127
|
+
fail(ArgumentError, "invalid width") unless value.nil? || (value.is_a?(Numeric) && value > 0)
|
|
128
|
+
@width = value.nil? ? nil : value.to_i
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def composition=(value)
|
|
132
|
+
@composition = value.nil? ? nil : COMPOSITIONS.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid composition")
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def status=(value)
|
|
136
|
+
@status = value.nil? ? nil : (STATUSES.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid status"))
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def remarks=(value)
|
|
140
|
+
@remarks = value&.to_s
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @return [String] UID markup
|
|
144
|
+
def to_uid
|
|
145
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
|
146
|
+
builder.RwyUid do |rwy_uid|
|
|
147
|
+
rwy_uid << airport.to_uid.indent(2)
|
|
148
|
+
rwy_uid.txtDesig(name)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# @return [String] AIXM or OFMX markup
|
|
153
|
+
def to_xml
|
|
154
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
|
155
|
+
builder.Rwy do |rwy|
|
|
156
|
+
rwy << to_uid.indent(2)
|
|
157
|
+
rwy.valLen(length) if length
|
|
158
|
+
rwy.valWid(width) if width
|
|
159
|
+
rwy.uomDimRwy('M') if length || width
|
|
160
|
+
rwy.codeComposition(COMPOSITIONS.key(composition).to_s) if composition
|
|
161
|
+
rwy.codeSts(STATUSES.key(status).to_s) if status
|
|
162
|
+
rwy.txtRmk(remarks) if remarks
|
|
163
|
+
end
|
|
164
|
+
%i(@forth @back).each do |direction|
|
|
165
|
+
direction = instance_variable_get(direction)
|
|
166
|
+
builder << direction.to_xml if direction
|
|
167
|
+
end
|
|
168
|
+
builder.target!
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Runway directions further describe each direction {#forth} and {#back}
|
|
172
|
+
# of a runway.
|
|
173
|
+
#
|
|
174
|
+
# @see https://github.com/openflightmaps/ofmx/wiki/Airport#rdn-runway-direction
|
|
175
|
+
class Direction
|
|
176
|
+
# @return [AIXM::Component::Runway] runway the runway direction is further describing
|
|
177
|
+
attr_reader :runway
|
|
178
|
+
|
|
179
|
+
# @return [String] partial name of runway (e.g. "12" or "16L")
|
|
180
|
+
attr_reader :name
|
|
181
|
+
|
|
182
|
+
# @return [Integer, nil] geographic orientation (true bearing) in degrees
|
|
183
|
+
attr_reader :geographic_orientation
|
|
184
|
+
|
|
185
|
+
# @return [AIXM::XY] beginning point (middle of the runway width)
|
|
186
|
+
attr_reader :xy
|
|
187
|
+
|
|
188
|
+
# @return [AIXM::Z, nil] elevation of the touch down zone in +qnh+
|
|
189
|
+
attr_reader :z
|
|
190
|
+
|
|
191
|
+
# @return [AIXM::XY, Integer, nil] displaced threshold point either as
|
|
192
|
+
# coordinates (AIXM::XY) or distance (Integer) in meters from the
|
|
193
|
+
# beginning point
|
|
194
|
+
attr_reader :displaced_threshold
|
|
195
|
+
|
|
196
|
+
# @return [String, nil] free text remarks
|
|
197
|
+
attr_reader :remarks
|
|
198
|
+
|
|
199
|
+
def initialize(runway:, name:)
|
|
200
|
+
self.runway, self.name = runway, name
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# @return [String]
|
|
204
|
+
def inspect
|
|
205
|
+
%Q(#<#{self.class} name=#{name.inspect}>)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def runway=(value)
|
|
209
|
+
fail(ArgumentError, "invalid runway") unless value.is_a? AIXM::Component::Runway
|
|
210
|
+
@runway = value
|
|
211
|
+
end
|
|
212
|
+
private :runway
|
|
213
|
+
|
|
214
|
+
def name=(value)
|
|
215
|
+
fail(ArgumentError, "invalid name") unless value.is_a? String
|
|
216
|
+
@name = value.uptrans
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def geographic_orientation=(value)
|
|
220
|
+
return @geographic_orientation = nil if value.nil?
|
|
221
|
+
fail(ArgumentError, "invalid geographic orientation") unless value.is_a? Numeric
|
|
222
|
+
@geographic_orientation = value.to_i
|
|
223
|
+
fail(ArgumentError, "invalid geographic orientation") unless (0..359).include? @geographic_orientation
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def xy=(value)
|
|
227
|
+
fail(ArgumentError, "invalid xy") unless value.is_a? AIXM::XY
|
|
228
|
+
@xy = value
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def z=(value)
|
|
232
|
+
fail(ArgumentError, "invalid z") unless value.nil? || (value.is_a?(AIXM::Z) && value.qnh?)
|
|
233
|
+
@z = value
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def displaced_threshold=(value)
|
|
237
|
+
@displaced_threshold = case value
|
|
238
|
+
when AIXM::XY then @xy.distance(value).to_i
|
|
239
|
+
when Numeric then value.to_i
|
|
240
|
+
when NilClass then nil
|
|
241
|
+
else fail(ArgumentError, "invalid displaced threshold")
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def remarks=(value)
|
|
246
|
+
@remarks = value&.to_s
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# @return [Integer] magnetic orientation (magnetic bearing) in degrees
|
|
250
|
+
def magnetic_orientation
|
|
251
|
+
if geographic_orientation && runway.airport.declination
|
|
252
|
+
(geographic_orientation + runway.airport.declination).round
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# @return [String] AIXM or OFMX markup
|
|
257
|
+
def to_xml
|
|
258
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
|
259
|
+
builder.Rdn do |rdn|
|
|
260
|
+
rdn.RdnUid do |rdn_uid|
|
|
261
|
+
rdn_uid << runway.to_uid.indent(4)
|
|
262
|
+
rdn_uid.txtDesig(name)
|
|
263
|
+
end
|
|
264
|
+
rdn.geoLat(xy.lat(AIXM.schema))
|
|
265
|
+
rdn.geoLong(xy.long(AIXM.schema))
|
|
266
|
+
rdn.valTrueBrg(geographic_orientation) if geographic_orientation
|
|
267
|
+
rdn.valMagBrg(magnetic_orientation) if magnetic_orientation
|
|
268
|
+
if z
|
|
269
|
+
rdn.valElevTdz(z.alt)
|
|
270
|
+
rdn.uomElevTdz(z.unit.upcase.to_s)
|
|
271
|
+
end
|
|
272
|
+
rdn.txtRmk(remarks) if remarks
|
|
273
|
+
end
|
|
274
|
+
if displaced_threshold
|
|
275
|
+
builder.Rdd do |rdd|
|
|
276
|
+
rdd.RddUid do |rdd_uid|
|
|
277
|
+
rdd_uid.RdnUid do |rdn_uid|
|
|
278
|
+
rdn_uid << runway.to_uid.indent(6)
|
|
279
|
+
rdn_uid.txtDesig(name)
|
|
280
|
+
end
|
|
281
|
+
rdd_uid.codeType('DPLM')
|
|
282
|
+
rdd_uid.codeDayPeriod('A')
|
|
283
|
+
end
|
|
284
|
+
rdd.valDist(displaced_threshold)
|
|
285
|
+
rdd.uomDist('M')
|
|
286
|
+
rdd.txtRmk(remarks) if remarks
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
builder.target!
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|