aixm 0.3.5 → 0.3.6

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.
@@ -111,10 +111,10 @@ module AIXM
111
111
  %Q(#<#{original_class} class=#{@klass.inspect}>)
112
112
  end
113
113
 
114
- alias_method :original_class, :class
115
-
116
114
  # @!attribute class
115
+ # @note Use +original_class+ to query the Ruby object class.
117
116
  # @return [Symbol] class of layer (see {CLASSES})
117
+ alias_method :original_class, :class
118
118
  def class
119
119
  @klass
120
120
  end
@@ -0,0 +1,133 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+ class Component
5
+
6
+ # Lighting of a runway, helipad etc
7
+ #
8
+ # ===Cheat Sheet in Pseudo Code:
9
+ # lighting = AIXM.lighting(
10
+ # position: POSITIONS
11
+ # )
12
+ # lighting.description = String or nil
13
+ # lighting.intensity = INTENSITIES or nil
14
+ # lighting.color = COLORS or nil
15
+ # lighting.remarks = String or nil
16
+ #
17
+ # @see https://github.com/openflightmaps/ofmx/wiki/Airport#rls-runway-direction-lighting
18
+ # @see https://github.com/openflightmaps/ofmx/wiki/Airport#fls-fato-direction-lighting
19
+ # @see https://github.com/openflightmaps/ofmx/wiki/Airport#tls-helipad-tlof-lighting
20
+ class Lighting
21
+ POSITIONS = {
22
+ TDZ: :touch_down_zone,
23
+ AIM: :aiming_point,
24
+ CL: :center_line,
25
+ EDGE: :edge,
26
+ THR: :threshold,
27
+ SWYEDGE: :stopway_edge,
28
+ DESIG: :runway_designation,
29
+ AFTTHR: :after_threshold,
30
+ DISPTHR: :displaced_threshold,
31
+ SWYCL: :stopway_center_line,
32
+ END: :runway_end,
33
+ SWYEND: :stopway_end,
34
+ TWYINT: :taxiway_intersection,
35
+ HOLDBAY: :taxyway_hold_bay,
36
+ RTWYINT: :rapid_taxiway_intersection,
37
+ OTHER: :other # specify in remarks
38
+ }
39
+
40
+ INTENSITIES = {
41
+ LIL: :low,
42
+ LIM: :medium,
43
+ LIH: :high,
44
+ OTHER: :other # specify in remarks
45
+ }
46
+
47
+ COLORS = {
48
+ YEL: :yellow,
49
+ RED: :red,
50
+ WHI: :white,
51
+ BLU: :blue,
52
+ GRN: :green,
53
+ PRP: :purple,
54
+ OTHER: :other # specify in remarks
55
+ }
56
+
57
+ # @return [AIXM::Component::Runway::Direction, AIXM::Component::FATO::Direction, AIXM::Component::Helipad] lighted entity
58
+ attr_reader :lightable
59
+
60
+ # @return [Symbol, nil] position of the lighting system (see {POSITIONS})
61
+ attr_reader :position
62
+
63
+ # @return [String, nil] detailed description
64
+ attr_reader :description
65
+
66
+ # @return [Symbol, nil] intensity of lights (see {INTENSITIES})
67
+ attr_reader :intensity
68
+
69
+ # @return [Symbol, nil] color of lights (see {COLORS})
70
+ attr_reader :color
71
+
72
+ # @return [String, nil] free text remarks
73
+ attr_reader :remarks
74
+
75
+ def initialize(position:)
76
+ self.position = position
77
+ end
78
+
79
+ # @return [String]
80
+ def inspect
81
+ %Q(#<#{self.class} position=#{position.inspect}>)
82
+ end
83
+
84
+ def lightable=(value)
85
+ fail(ArgumentError, "invalid lightable") unless value.respond_to? :lightings
86
+ @lightable = value
87
+ end
88
+ private :lightable=
89
+
90
+ def position=(value)
91
+ @position = POSITIONS.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid position")
92
+ end
93
+
94
+ def description=(value)
95
+ @description = value&.to_s
96
+ end
97
+
98
+ def intensity=(value)
99
+ @intensity = value.nil? ? nil : INTENSITIES.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid intensity")
100
+ end
101
+
102
+ def color=(value)
103
+ @color = value.nil? ? nil : COLORS.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid color")
104
+ end
105
+
106
+ def remarks=(value)
107
+ @remarks = value&.to_s
108
+ end
109
+
110
+ # @return [String] UID markup
111
+ def to_uid(as:)
112
+ builder = Builder::XmlMarkup.new(indent: 2)
113
+ builder.tag!(as) do |tag|
114
+ tag << lightable.to_uid.indent(2)
115
+ tag.codePsn(POSITIONS.key(position).to_s)
116
+ end
117
+ end
118
+
119
+ # @return [String] AIXM or OFMX markup
120
+ def to_xml(as:)
121
+ builder = Builder::XmlMarkup.new(indent: 2)
122
+ builder.tag!(as) do |tag|
123
+ tag << to_uid(as: "#{as}Uid").indent(2)
124
+ tag.txtDescr(description) if description
125
+ tag.codeIntst(INTENSITIES.key(intensity).to_s) if intensity
126
+ tag.codeColour(COLORS.key(color).to_s) if color
127
+ tag.txtRmk(remarks) if remarks
128
+ end
129
+ builder.target!
130
+ end
131
+ end
132
+ end
133
+ end
@@ -157,8 +157,9 @@ module AIXM
157
157
  rwy.txtRmk(remarks) if remarks
158
158
  end
159
159
  %i(@forth @back).each do |direction|
160
- direction = instance_variable_get(direction)
161
- builder << direction.to_xml if direction
160
+ if direction = instance_variable_get(direction)
161
+ builder << direction.to_xml
162
+ end
162
163
  end
163
164
  builder.target!
164
165
  end
@@ -200,8 +201,12 @@ module AIXM
200
201
  # @return [String, nil] free text remarks
201
202
  attr_reader :remarks
202
203
 
204
+ # @return [Array<AIXM::Component::Lighting>] installed lighting systems
205
+ attr_reader :lightings
206
+
203
207
  def initialize(runway:, name:)
204
208
  self.runway, self.name = runway, name
209
+ @lightings = []
205
210
  end
206
211
 
207
212
  # @return [String]
@@ -258,6 +263,17 @@ module AIXM
258
263
  @remarks = value&.to_s
259
264
  end
260
265
 
266
+ # Add a lighting system to the runway direction.
267
+ #
268
+ # @param lighting [AIXM::Component::Lighting] lighting instance
269
+ # @return [self]
270
+ def add_lighting(lighting)
271
+ fail(ArgumentError, "invalid lighting") unless lighting.is_a? AIXM::Component::Lighting
272
+ lighting.send(:lightable=, self)
273
+ @lightings << lighting
274
+ self
275
+ end
276
+
261
277
  # @return [AIXM::A] magnetic orientation (magnetic bearing) in degrees
262
278
  def magnetic_orientation
263
279
  if geographic_orientation && runway.airport.declination
@@ -265,14 +281,20 @@ module AIXM
265
281
  end
266
282
  end
267
283
 
284
+ # @return [String] UID markup
285
+ def to_uid
286
+ builder = Builder::XmlMarkup.new(indent: 2)
287
+ builder.RdnUid do |rdn_uid|
288
+ rdn_uid << runway.to_uid.indent(2)
289
+ rdn_uid.txtDesig(name)
290
+ end
291
+ end
292
+
268
293
  # @return [String] AIXM or OFMX markup
269
294
  def to_xml
270
295
  builder = Builder::XmlMarkup.new(indent: 2)
271
296
  builder.Rdn do |rdn|
272
- rdn.RdnUid do |rdn_uid|
273
- rdn_uid << runway.to_uid.indent(4)
274
- rdn_uid.txtDesig(name)
275
- end
297
+ rdn << to_uid.indent(2)
276
298
  rdn.geoLat(xy.lat(AIXM.schema))
277
299
  rdn.geoLong(xy.long(AIXM.schema))
278
300
  rdn.valTrueBrg(geographic_orientation) if geographic_orientation
@@ -299,6 +321,9 @@ module AIXM
299
321
  rdd.txtRmk(remarks) if remarks
300
322
  end
301
323
  end
324
+ lightings.each do |lighting|
325
+ builder << lighting.to_xml(as: :Rls)
326
+ end
302
327
  builder.target!
303
328
  end
304
329
  end
@@ -6,12 +6,15 @@ module AIXM
6
6
  # Surface of a runway, helipad etc
7
7
  #
8
8
  # ===Cheat Sheet in Pseudo Code:
9
- # surface = AIXM.surfaceservice(
9
+ # surface = AIXM.surface(
10
10
  # composition: COMPOSITIONS or nil
11
11
  # preparation: PREPARATIONS or nil
12
12
  # condition: CONDITIONS or nil
13
13
  # )
14
- # surface.pcn = String
14
+ # surface.pcn = String or nil
15
+ # surface.siwl_weight = AIXM.w
16
+ # surface.siwl_tire_pressure = AIXM.p
17
+ # surface.auw_weight = AIXM.w
15
18
  # surface.remarks = String or nil
16
19
  #
17
20
  # ===Constants:
@@ -24,11 +27,15 @@ module AIXM
24
27
  ASPH: :asphalt,
25
28
  BITUM: :bitumen, # dug up, bound and rolled ground
26
29
  CONC: :concrete,
30
+ 'CONC+ASPH': :concrete_and_asphalt,
31
+ 'CONC+GRS': :concrete_and_grass,
27
32
  GRADE: :graded_earth, # graded or rolled earth possibly with some grass
28
33
  GRASS: :grass, # lawn
29
34
  GRAVE: :gravel, # small and midsize rounded stones
30
35
  MACADAM: :macadam, # small rounded stones
36
+ METAL: :metal,
31
37
  SAND: :sand,
38
+ SNOW: :snow,
32
39
  WATER: :water,
33
40
  OTHER: :other # specify in remarks
34
41
  }
@@ -61,6 +68,15 @@ module AIXM
61
68
  # @return [Symbol, nil] condition of the surface (see {CONDITIONS})
62
69
  attr_reader :condition
63
70
 
71
+ # @return [AIXM::W, nil] single isolated wheel load weight
72
+ attr_reader :siwl_weight
73
+
74
+ # @return [AIXM::P, nil] single isolated wheel load tire pressure
75
+ attr_reader :siwl_tire_pressure
76
+
77
+ # @return [AIXM::W, nil] all-up wheel weight
78
+ attr_reader :auw_weight
79
+
64
80
  # @return [String, nil] free text remarks
65
81
  attr_reader :remarks
66
82
 
@@ -96,6 +112,21 @@ module AIXM
96
112
  @pcn = match.named_captures.reject{ |k| k == 'pcn' }
97
113
  end
98
114
 
115
+ def siwl_weight=(value)
116
+ fail(ArgumentError, "invalid siwl_weight") unless value.nil? || value.is_a?(AIXM::W)
117
+ @siwl_weight = value
118
+ end
119
+
120
+ def siwl_tire_pressure=(value)
121
+ fail(ArgumentError, "invalid siwl_tire_pressure") unless value.nil? || value.is_a?(AIXM::P)
122
+ @siwl_tire_pressure = value
123
+ end
124
+
125
+ def auw_weight=(value)
126
+ fail(ArgumentError, "invalid auw_weight") unless value.nil? || value.is_a?(AIXM::W)
127
+ @auw_weight = value
128
+ end
129
+
99
130
  def remarks=(value)
100
131
  @remarks = value&.to_s
101
132
  end
@@ -114,6 +145,18 @@ module AIXM
114
145
  builder.codePcnEvalMethod(@pcn['evaluation_method'])
115
146
  end
116
147
  builder.txtPcnNote(@remarks) if remarks
148
+ if siwl_weight
149
+ builder.valSiwlWeight(siwl_weight.wgt.trim)
150
+ builder.uomSiwlWeight(siwl_weight.unit.to_s.upcase)
151
+ end
152
+ if siwl_tire_pressure
153
+ builder.valSiwlTirePressure(siwl_tire_pressure.pres.trim)
154
+ builder.uomSiwlTirePressure(siwl_tire_pressure.unit.to_s.upcase)
155
+ end
156
+ if auw_weight
157
+ builder.valAuwWeight(auw_weight.wgt.trim)
158
+ builder.uomAuwWeight(auw_weight.unit.to_s.upcase)
159
+ end
117
160
  builder.target!
118
161
  end
119
162
  end
@@ -18,7 +18,7 @@ module AIXM
18
18
  }.freeze
19
19
 
20
20
  # @!method zero?
21
- # @return [Boolean] whether length is zero
21
+ # @return [Boolean] whether distance is zero
22
22
  def_delegator :@dist, :zero?
23
23
 
24
24
  # @return [Float] distance
@@ -56,7 +56,7 @@ module AIXM
56
56
  # @!method to_km
57
57
  # @!method to_m
58
58
  # @!method to_nm
59
- # @return [AIXM::d] convert distance
59
+ # @return [AIXM::D] convert distance
60
60
  UNITS.each_key do |target_unit|
61
61
  define_method "to_#{target_unit}" do
62
62
  return self if unit == target_unit
@@ -67,7 +67,7 @@ module AIXM
67
67
  # @see Object#<=>
68
68
  # @return [Integer]
69
69
  def <=>(other)
70
- to_m.dist <=> other.to_m.dist
70
+ dist <=> other.send(:"to_#{unit}").dist
71
71
  end
72
72
 
73
73
  # @see Object#==
@@ -15,6 +15,8 @@ module AIXM
15
15
  #
16
16
  # @see https://github.com/openflightmaps/ofmx/wiki/Snapshot
17
17
  class Document
18
+ REGION_RE = /\A[A-Z]{2}\z/.freeze
19
+
18
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
19
21
 
20
22
  # @return [String] OFMX region all features in this document belong to
@@ -43,7 +45,7 @@ module AIXM
43
45
  end
44
46
 
45
47
  def region=(value)
46
- fail(ArgumentError, "invalid region") unless value.nil? || value.is_a?(String)
48
+ fail(ArgumentError, "invalid region") unless value.nil? || value&.upcase&.match?(REGION_RE)
47
49
  @region = value&.upcase
48
50
  end
49
51
 
@@ -20,12 +20,17 @@ module AIXM
20
20
  # airport.declination = Float or nil
21
21
  # airport.transition_z = AIXM.z or nil
22
22
  # airport.timetable = AIXM.timetable or nil
23
+ # airport.operator = String or nil
23
24
  # airport.remarks = String or nil
24
25
  # airport.add_runway(AIXM.runway)
26
+ # airport.add_fato(AIXM.fato)
25
27
  # airport.add_helipad(AIXM.helipad)
26
28
  # airport.add_usage_limitation(UsageLimitation::TYPES)
27
29
  # airport.add_address(AIXM.address)
28
30
  #
31
+ # For airports without an +id+, you may assign the two character region
32
+ # (e.g. "LF") which will be combined with an 8 character digest of +name+.
33
+ #
29
34
  # @see https://github.com/openflightmaps/ofmx/wiki/Airport#ahp-airport
30
35
  class Airport < Feature
31
36
  public_class_method :new
@@ -79,12 +84,18 @@ module AIXM
79
84
  # @return [AIXM::Component::Timetable, nil] operating hours
80
85
  attr_reader :timetable
81
86
 
87
+ # @return [String, nil] operator of the airport
88
+ attr_reader :operator
89
+
82
90
  # @return [String, nil] free text remarks
83
91
  attr_reader :remarks
84
92
 
85
93
  # @return [Array<AIXM::Component::Runway>] runways present at this airport
86
94
  attr_reader :runways
87
95
 
96
+ # @return [Array<AIXM::Component::FATO>] FATOs present at this airport
97
+ attr_reader :fatos
98
+
88
99
  # @return [Array<AIXM::Component::Helipad>] helipads present at this airport
89
100
  attr_reader :helipads
90
101
 
@@ -94,10 +105,10 @@ module AIXM
94
105
  # @return [Array<AIXM::Feature::Address>] postal address, url, A/A or A/G frequency etc
95
106
  attr_reader :addresses
96
107
 
97
- def initialize(source: nil, organisation:, id:, name:, xy:)
108
+ def initialize(source: nil, organisation:, id: nil, name:, xy:)
98
109
  super(source: source)
99
110
  self.organisation, self.id, self.name, self.xy = organisation, id, name, xy
100
- @runways, @helipads, @usage_limitations, @addresses = [], [], [], []
111
+ @runways, @fatos, @helipads, @usage_limitations, @addresses = [], [], [], [], []
101
112
  end
102
113
 
103
114
  # @return [String]
@@ -110,8 +121,11 @@ module AIXM
110
121
  @organisation = value
111
122
  end
112
123
 
124
+ # For airports without an +id+, you may assign the two character region
125
+ # (e.g. "LF") which will be combined with an 8 character digest of +name+.
113
126
  def id=(value)
114
- fail(ArgumentError, "invalid id `#{id}'") unless value&.upcase&.match? ID_RE
127
+ value = [value, [name].to_digest].join.upcase if value&.upcase&.match? AIXM::Document::REGION_RE
128
+ fail(ArgumentError, "invalid id") unless value&.upcase&.match? ID_RE
115
129
  @id = value.upcase
116
130
  end
117
131
 
@@ -134,9 +148,9 @@ module AIXM
134
148
  def type
135
149
  @type = case
136
150
  when @type then @type
137
- when runways.any? && helipads.any? then :aerodrome_and_heliport
151
+ when runways.any? && (helipads.any? || fatos.any?) then :aerodrome_and_heliport
138
152
  when runways.any? then :aerodrome
139
- when helipads.any? then :heliport
153
+ when helipads.any? || fatos.any? then :heliport
140
154
  end
141
155
  end
142
156
 
@@ -172,6 +186,11 @@ module AIXM
172
186
  @timetable = value
173
187
  end
174
188
 
189
+ def operator=(value)
190
+ fail(ArgumentError, "invalid name") unless value.nil? || value.is_a?(String)
191
+ @operator = value&.uptrans
192
+ end
193
+
175
194
  def remarks=(value)
176
195
  @remarks = value&.to_s
177
196
  end
@@ -187,6 +206,17 @@ module AIXM
187
206
  self
188
207
  end
189
208
 
209
+ # Add a FATO to the airport.
210
+ #
211
+ # @param FATO [AIXM::Component::FATO] FATO instance
212
+ # @return [self]
213
+ def add_fato(fato)
214
+ fail(ArgumentError, "invalid FATO") unless fato.is_a? AIXM::Component::FATO
215
+ fato.send(:airport=, self)
216
+ @fatos << fato
217
+ self
218
+ end
219
+
190
220
  # Add a helipad to the airport.
191
221
  #
192
222
  # @param helipad [AIXM::Component::Helipad] helipad instance
@@ -272,6 +302,7 @@ module AIXM
272
302
  ahp.uomDistVer(z.unit.upcase.to_s)
273
303
  end
274
304
  ahp.valMagVar(declination) if declination
305
+ ahp.txtNameAdmin(operator) if operator
275
306
  if transition_z
276
307
  ahp.valTransitionAlt(transition_z.alt)
277
308
  ahp.uomTransitionAlt(transition_z.unit.upcase.to_s)
@@ -282,6 +313,9 @@ module AIXM
282
313
  runways.each do |runway|
283
314
  builder << runway.to_xml
284
315
  end
316
+ fatos.each do |fato|
317
+ builder << fato.to_xml
318
+ end
285
319
  helipads.each do |helipad|
286
320
  builder << helipad.to_xml
287
321
  end