aixm 0.3.5 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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