aixm 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +1 -2
  4. data/CHANGELOG.md +20 -0
  5. data/README.md +7 -2
  6. data/aixm.gemspec +2 -3
  7. data/lib/aixm.rb +6 -2
  8. data/lib/aixm/a.rb +151 -0
  9. data/lib/aixm/component/frequency.rb +2 -2
  10. data/lib/aixm/component/geometry.rb +4 -0
  11. data/lib/aixm/component/geometry/point.rb +0 -4
  12. data/lib/aixm/component/helipad.rb +8 -22
  13. data/lib/aixm/component/runway.rb +36 -36
  14. data/lib/aixm/component/surface.rb +121 -0
  15. data/lib/aixm/component/timetable.rb +1 -0
  16. data/lib/aixm/constants.rb +40 -0
  17. data/lib/aixm/d.rb +5 -0
  18. data/lib/aixm/document.rb +2 -2
  19. data/lib/aixm/f.rb +6 -0
  20. data/lib/aixm/feature/address.rb +100 -0
  21. data/lib/aixm/feature/airport.rb +26 -7
  22. data/lib/aixm/feature/airspace.rb +10 -1
  23. data/lib/aixm/feature/navigational_aid.rb +1 -1
  24. data/lib/aixm/feature/navigational_aid/designated_point.rb +20 -5
  25. data/lib/aixm/feature/navigational_aid/dme.rb +2 -2
  26. data/lib/aixm/{component → feature}/service.rb +67 -16
  27. data/lib/aixm/feature/unit.rb +40 -6
  28. data/lib/aixm/refinements.rb +63 -6
  29. data/lib/aixm/shortcuts.rb +12 -4
  30. data/lib/aixm/version.rb +1 -1
  31. data/lib/aixm/xy.rb +6 -1
  32. data/lib/aixm/z.rb +6 -0
  33. data/schemas/ofmx/0/OFMX-DataTypes.xsd +5 -2
  34. data/schemas/ofmx/0/OFMX-Features.xsd +2 -0
  35. data/spec/factory.rb +32 -10
  36. data/spec/lib/aixm/a_spec.rb +203 -0
  37. data/spec/lib/aixm/component/helipad_spec.rb +11 -17
  38. data/spec/lib/aixm/component/runway_spec.rb +46 -32
  39. data/spec/lib/aixm/component/surface_spec.rb +88 -0
  40. data/spec/lib/aixm/d_spec.rb +10 -0
  41. data/spec/lib/aixm/document_spec.rb +104 -32
  42. data/spec/lib/aixm/f_spec.rb +10 -0
  43. data/spec/lib/aixm/feature/address_spec.rb +55 -0
  44. data/spec/lib/aixm/feature/airport_spec.rb +73 -3
  45. data/spec/lib/aixm/feature/navigational_aid/designated_point_spec.rb +43 -6
  46. data/spec/lib/aixm/feature/navigational_aid/dme_spec.rb +2 -2
  47. data/spec/lib/aixm/feature/navigational_aid/marker_spec.rb +2 -2
  48. data/spec/lib/aixm/feature/navigational_aid/ndb_spec.rb +2 -2
  49. data/spec/lib/aixm/feature/navigational_aid/tacan_spec.rb +2 -2
  50. data/spec/lib/aixm/feature/navigational_aid/vor_spec.rb +6 -6
  51. data/spec/lib/aixm/{component → feature}/service_spec.rb +12 -14
  52. data/spec/lib/aixm/feature/unit_spec.rb +7 -4
  53. data/spec/lib/aixm/refinements_spec.rb +100 -15
  54. data/spec/lib/aixm/z_spec.rb +10 -0
  55. metadata +17 -25
  56. data/lib/aixm/h.rb +0 -87
  57. data/spec/lib/aixm/h_spec.rb +0 -113
@@ -0,0 +1,121 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+ class Component
5
+
6
+ # Surface of a runway, helipad etc
7
+ #
8
+ # ===Cheat Sheet in Pseudo Code:
9
+ # surface = AIXM.surfaceservice(
10
+ # composition: COMPOSITIONS or nil
11
+ # preparation: PREPARATIONS or nil
12
+ # condition: CONDITIONS or nil
13
+ # )
14
+ # surface.pcn = String
15
+ # surface.remarks = String or nil
16
+ #
17
+ # ===Constants:
18
+ # * +AIXM::PCN_RE+ - regular expression to match PCN notations
19
+ #
20
+ #
21
+ # @see https://github.com/openflightmaps/ofmx/wiki/Airport#rwy-runway
22
+ class Surface
23
+ COMPOSITIONS = {
24
+ ASPH: :asphalt,
25
+ BITUM: :bitumen, # dug up, bound and rolled ground
26
+ CONC: :concrete,
27
+ GRADE: :graded_earth, # graded or rolled earth possibly with some grass
28
+ GRASS: :grass, # lawn
29
+ GRAVE: :gravel, # small and midsize rounded stones
30
+ MACADAM: :macadam, # small rounded stones
31
+ SAND: :sand,
32
+ WATER: :water,
33
+ OTHER: :other # specify in remarks
34
+ }
35
+
36
+ PREPARATIONS = {
37
+ AFSC: :aggregate_friction_seal_coat,
38
+ GROOVED: :grooved, # cut or plastic grooved
39
+ NATURAL: :natural, # no treatment
40
+ OILED: :oiled,
41
+ PAVED: :paved,
42
+ PFC: :porous_friction_course,
43
+ RFSC: :rubberized_friction_seal_coat,
44
+ ROLLED: :rolled,
45
+ OTHER: :other
46
+ }
47
+
48
+ CONDITIONS = {
49
+ GOOD: :good,
50
+ FAIR: :fair,
51
+ POOR: :poor,
52
+ OTHER: :other
53
+ }
54
+
55
+ # @return [Symbol, nil] composition of the surface (see {COMPOSITIONS})
56
+ attr_reader :composition
57
+
58
+ # @return [Symbol, nil] preparation of the surface (see {PREPARATIONS})
59
+ attr_reader :preparation
60
+
61
+ # @return [Symbol, nil] condition of the surface (see {CONDITIONS})
62
+ attr_reader :condition
63
+
64
+ # @return [String, nil] free text remarks
65
+ attr_reader :remarks
66
+
67
+ def initialize
68
+ @pcn = {}
69
+ end
70
+
71
+ # @return [String]
72
+ def inspect
73
+ %Q(#<#{self.class} composition=#{composition.inspect} preparation=#{preparation.inspect} condition=#{condition.inspect} pcn=#{pcn.inspect}>)
74
+ end
75
+
76
+ def composition=(value)
77
+ @composition = value.nil? ? nil : COMPOSITIONS.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid composition")
78
+ end
79
+
80
+ def preparation=(value)
81
+ @preparation = value.nil? ? nil : PREPARATIONS.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid preparation")
82
+ end
83
+
84
+ def condition=(value)
85
+ @condition = value.nil? ? nil : CONDITIONS.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid condition")
86
+ end
87
+
88
+ # @return [String, nil] pavement classification number (e.g. "59/F/A/W/T")
89
+ def pcn
90
+ @pcn.none? ? nil : @pcn.values.join("/")
91
+ end
92
+
93
+ def pcn=(value)
94
+ return @pcn = {} if value.nil?
95
+ fail(ArgumentError, "invalid PCN") unless match = value.to_s.upcase.match(PCN_RE)
96
+ @pcn = match.named_captures.reject{ |k| k == 'pcn' }
97
+ end
98
+
99
+ def remarks=(value)
100
+ @remarks = value&.to_s
101
+ end
102
+
103
+ # @return [String] AIXM or OFMX markup
104
+ def to_xml
105
+ builder = Builder::XmlMarkup.new(indent: true)
106
+ builder.codeComposition(COMPOSITIONS.key(composition).to_s) if composition
107
+ builder.codePreparation(PREPARATIONS.key(preparation).to_s) if preparation
108
+ builder.codeCondSfc(CONDITIONS.key(condition).to_s) if condition
109
+ if pcn
110
+ builder.valPcnClass(@pcn['capacity'])
111
+ builder.codePcnPavementType(@pcn['type'])
112
+ builder.codePcnPavementSubgrade(@pcn['subgrade'])
113
+ builder.codePcnMaxTirePressure(@pcn['tire_pressure'])
114
+ builder.codePcnEvalMethod(@pcn['evaluation_method'])
115
+ end
116
+ builder.txtPcnNote(@remarks) if remarks
117
+ builder.target!
118
+ end
119
+ end
120
+ end
121
+ end
@@ -15,6 +15,7 @@ module AIXM
15
15
  #
16
16
  # ===Shortcuts:
17
17
  # * +AIXM::H24+ - continuous, all day and all night
18
+ # * +AIXM::H_RE+ - pattern matching working hour codes
18
19
  #
19
20
  # @see https://github.com/openflightmaps/ofmx/wiki/Timetable#predefined-timetable
20
21
  class Timetable
@@ -0,0 +1,40 @@
1
+ module AIXM
2
+
3
+ # Characters recognized as symbols for "minute" in DMS notations
4
+ MIN = %Q('\u2018\u2019\u00b4).freeze
5
+
6
+ # Characters recognized as symbols for "second" in DMS notations
7
+ SEC = %Q("\u201c\u201d\u201f).freeze
8
+
9
+ # Pattern matching geographical coordinates in various DMS notations
10
+ DMS_RE = %r(
11
+ (?<dms>
12
+ (?<sgn>-)?
13
+ (?<deg>\d{1,3})[° ]{1,2}
14
+ (?<min>\d{2})[#{MIN}#{SEC} ]{1,2}
15
+ (?<sec>\d{2}(?:[\.,]\d{0,2})?)[#{SEC}#{MIN} ]{0,2}
16
+ (?<hem_ne>[NE])?(?<hem_sw>[SW])?
17
+ |
18
+ (?<sgn>-)?
19
+ (?<deg>\d{1,3})
20
+ (?<min>\d{2})
21
+ (?<sec>\d{2}(?:[\.,]\d{0,2})?)
22
+ (?:(?<hem_ne>[NE])|(?<hem_sw>[SW]))
23
+ )
24
+ )xi.freeze
25
+
26
+ # Pattern matching PCN surface strength notations
27
+ PCN_RE = %r(
28
+ (?<pcn>
29
+ (?<capacity>\d+)\W+
30
+ (?<type>[RF])\W+
31
+ (?<subgrade>[A-D])\W+
32
+ (?<tire_pressure>[W-Z])\W+
33
+ (?<evaluation_method>[TU])
34
+ )
35
+ )x.freeze
36
+
37
+ # Pattern matching timetable working hour codes
38
+ H_RE = /H(?:24|J|N|X|O)/.freeze
39
+
40
+ end
@@ -8,6 +8,7 @@ module AIXM
8
8
  # AIXM.d(123, :m)
9
9
  class D
10
10
  include Comparable
11
+ extend Forwardable
11
12
 
12
13
  UNITS = {
13
14
  ft: { km: 0.0003048, m: 0.3048, nm: 0.000164578833554 },
@@ -16,6 +17,10 @@ module AIXM
16
17
  nm: { ft: 6076.11548554, km: 1.852, m: 1852 }
17
18
  }.freeze
18
19
 
20
+ # @!method zero?
21
+ # @return [Boolean] whether length is zero
22
+ def_delegator :@dist, :zero?
23
+
19
24
  # @return [Float] distance
20
25
  attr_reader :dist
21
26
 
@@ -15,7 +15,7 @@ module AIXM
15
15
  #
16
16
  # @see https://github.com/openflightmaps/ofmx/wiki/Snapshot
17
17
  class Document
18
- NAMESPACE_PATTERN = /\A[a-f\d]{8}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{12}\z/.freeze
18
+ 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
19
 
20
20
  # @return [String] OFMX region all features in this document belong to
21
21
  attr_reader :region
@@ -48,7 +48,7 @@ module AIXM
48
48
  end
49
49
 
50
50
  def namespace=(value)
51
- fail(ArgumentError, "invalid namespace") unless value.nil? || value.match?(NAMESPACE_PATTERN)
51
+ fail(ArgumentError, "invalid namespace") unless value.nil? || value.match?(NAMESPACE_RE)
52
52
  @namespace = value || SecureRandom.uuid
53
53
  end
54
54
 
@@ -7,8 +7,14 @@ module AIXM
7
7
  # @example
8
8
  # AIXM.f(123.35, :mhz)
9
9
  class F
10
+ extend Forwardable
11
+
10
12
  UNITS = %i(ghz mhz khz).freeze
11
13
 
14
+ # @!method zero?
15
+ # @return [Boolean] whether frequency is zero
16
+ def_delegator :@freq, :zero?
17
+
12
18
  # @return [Float] frequency
13
19
  attr_reader :freq
14
20
 
@@ -0,0 +1,100 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+ class Feature
5
+
6
+ # Address or similar means to contact an entity.
7
+ #
8
+ # ===Cheat Sheet in Pseudo Code:
9
+ # address = AIXM.address(
10
+ # source: String or nil
11
+ # type: TYPES
12
+ # address: String
13
+ # )
14
+ # service.remarks = String or nil
15
+ #
16
+ # @see https://github.com/openflightmaps/ofmx/wiki/Airport#aha-airport-address
17
+ class Address < Feature
18
+ public_class_method :new
19
+
20
+ TYPES = {
21
+ POST: :postal_address,
22
+ PHONE: :phone,
23
+ 'PHONE-MET': :weather_phone,
24
+ FAX: :fax,
25
+ TLX: :telex,
26
+ SITA: :sita,
27
+ AFS: :aeronautical_fixed_service_address,
28
+ EMAIL: :email,
29
+ URL: :url,
30
+ 'URL-CAM': :webcam,
31
+ 'URL-MET': :weather_url,
32
+ RADIO: :radio_frequency,
33
+ OTHER: :other # specify in remarks
34
+ }
35
+
36
+ # @return [AIXM::Feature] addressable feature
37
+ attr_reader :addressable
38
+
39
+ # @return [Symbol] type of address (see {TYPES})
40
+ attr_reader :type
41
+
42
+ # @return [String] postal address, phone number, radio frequency etc
43
+ attr_reader :address
44
+
45
+ # @return [String, nil] free text remarks
46
+ attr_reader :remarks
47
+
48
+ def initialize(source: nil, type:, address:)
49
+ super(source: source)
50
+ self.type, self.address = type, address
51
+ end
52
+
53
+ # @return [String]
54
+ def inspect
55
+ %Q(#<#{self.class} type=#{type.inspect}>)
56
+ end
57
+
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
+ def type=(value)
65
+ @type = TYPES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid type")
66
+ end
67
+
68
+ def address=(value)
69
+ fail(ArgumentError, "invalid address") unless value.is_a? String
70
+ @address = value&.to_s
71
+ end
72
+
73
+ def remarks=(value)
74
+ @remarks = value&.to_s
75
+ end
76
+
77
+ # @return [String] UID markup
78
+ def to_uid(as:, sequence:)
79
+ builder = Builder::XmlMarkup.new(indent: 2)
80
+ builder.tag!(as) do |tag|
81
+ tag << addressable.to_uid.indent(2) if addressable
82
+ tag.codeType(TYPES.key(type).to_s.then { |t| AIXM.aixm? ? t.sub(/-\w+$/, '') : t })
83
+ tag.noSeq(sequence)
84
+ end
85
+ end
86
+
87
+ # @return [String] AIXM or OFMX markup
88
+ def to_xml(as:, sequence:)
89
+ builder = Builder::XmlMarkup.new(indent: 2)
90
+ builder.comment! ["Address: #{TYPES.key(type)}", addressable&.id].compact.join(' for ')
91
+ builder.tag!(as, { source: (source if AIXM.ofmx?) }.compact) do |tag|
92
+ tag << to_uid(as: :"#{as}Uid", sequence: sequence).indent(2)
93
+ tag.txtAddress(address)
94
+ tag.txtRmk(remarks) if remarks
95
+ end
96
+ end
97
+ end
98
+
99
+ end
100
+ end
@@ -24,12 +24,13 @@ module AIXM
24
24
  # airport.add_runway(AIXM.runway)
25
25
  # airport.add_helipad(AIXM.helipad)
26
26
  # airport.add_usage_limitation(UsageLimitation::TYPES)
27
+ # airport.add_address(AIXM.address)
27
28
  #
28
29
  # @see https://github.com/openflightmaps/ofmx/wiki/Airport#ahp-airport
29
30
  class Airport < Feature
30
31
  public_class_method :new
31
32
 
32
- ID_PATTERN = /^([A-Z]{3,4}|[A-Z]{2}[A-Z\d]{4,})$/.freeze
33
+ ID_RE = /^([A-Z]{3,4}|[A-Z]{2}[A-Z\d]{4,})$/.freeze
33
34
 
34
35
  TYPES = {
35
36
  AD: :aerodrome,
@@ -90,10 +91,13 @@ module AIXM
90
91
  # @return [Array<AIXM::Feature::Airport::UsageLimitation>] usage limitations
91
92
  attr_accessor :usage_limitations
92
93
 
94
+ # @return [Array<AIXM::Feature::Address>] postal address, url, A/A or A/G frequency etc
95
+ attr_reader :addresses
96
+
93
97
  def initialize(source: nil, organisation:, id:, name:, xy:)
94
98
  super(source: source)
95
99
  self.organisation, self.id, self.name, self.xy = organisation, id, name, xy
96
- @runways, @helipads, @usage_limitations = [], [], []
100
+ @runways, @helipads, @usage_limitations, @addresses = [], [], [], []
97
101
  end
98
102
 
99
103
  # @return [String]
@@ -107,7 +111,7 @@ module AIXM
107
111
  end
108
112
 
109
113
  def id=(value)
110
- fail(ArgumentError, "invalid id `#{id}'") unless value&.upcase&.match? ID_PATTERN
114
+ fail(ArgumentError, "invalid id `#{id}'") unless value&.upcase&.match? ID_RE
111
115
  @id = value.upcase
112
116
  end
113
117
 
@@ -155,7 +159,7 @@ module AIXM
155
159
  def declination=(value)
156
160
  return @declination = value if value.nil?
157
161
  fail(ArgumentError, "invalid declination") unless value.is_a?(Numeric) && (-180..180).include?(value)
158
- @declination = value.to_f
162
+ @declination = value.to_f + 0 # adding zero prevents -0.0
159
163
  end
160
164
 
161
165
  def transition_z=(value)
@@ -229,11 +233,22 @@ module AIXM
229
233
  self
230
234
  end
231
235
 
236
+ # Add an address (postal address, url, A/A or A/G frequency etc) to the airport.
237
+ #
238
+ # @params address [AIXM::Feature::Address] address instance
239
+ # @return [self]
240
+ def add_address(address)
241
+ fail(ArgumentError, "invalid address") unless address.is_a? AIXM::Feature::Address
242
+ address.send(:addressable=, self)
243
+ @addresses << address
244
+ self
245
+ end
246
+
232
247
  # @return [String] UID markup
233
- def to_uid
248
+ def to_uid(as: :AhpUid)
234
249
  builder = Builder::XmlMarkup.new(indent: 2)
235
- builder.AhpUid do |ahp_uid|
236
- ahp_uid.codeId(id)
250
+ builder.tag!(as) do |tag|
251
+ tag.codeId(id)
237
252
  end
238
253
  end
239
254
 
@@ -280,6 +295,10 @@ module AIXM
280
295
  end
281
296
  end
282
297
  end
298
+ addresses.each.with_object({}) do |address, sequences|
299
+ sequences[address.type] = (sequences[address.type] || 0) + 1
300
+ builder << address.to_xml(as: :Aha, sequence: sequences[address.type])
301
+ end
283
302
  builder.target!
284
303
  end
285
304
 
@@ -16,6 +16,13 @@ module AIXM
16
16
  # airspace.geometry << AIXM.point or AIXM.arc or AIXM.border or AIXM.circle
17
17
  # airspace.layers << AIXM.layer
18
18
  #
19
+ # Some regions define additional airspace types. In LF (France) for
20
+ # intance, the types RMZ (radio mandatory zone) and TMZ (transponder
21
+ # mandatory zone) exist. Such airspaces are usually specified together
22
+ # with a generic type such as +:regulated_airspace+:
23
+ #
24
+ # airspace= AIXM.airspace(type: :regulated_airspace, local_type: "RMZ")
25
+ #
19
26
  # @see https://github.com/openflightmaps/ofmx/wiki/Airspace#ase-airspace
20
27
  class Airspace < Feature
21
28
  public_class_method :new
@@ -71,7 +78,9 @@ module AIXM
71
78
  # @return [Symbol] type of airspace (see {TYPES})
72
79
  attr_reader :type
73
80
 
74
- # @return [String, nil] short name (e.g. "LF P 81")
81
+ # Some regions define additional types. They are usually specified with
82
+ #
83
+ # @return [String, nil] local type (e.g. "RMZ" or "TMZ")
75
84
  attr_reader :local_type
76
85
 
77
86
  # @return [String, nil] full name (e.g. "LF P 81 CHERBOURG")
@@ -85,7 +85,7 @@ module AIXM
85
85
 
86
86
  def to_builder
87
87
  builder = Builder::XmlMarkup.new(indent: 2)
88
- builder.comment! "NavigationalAid: [#{kind}] #{name || :UNNAMED}"
88
+ builder.comment! "NavigationalAid: [#{kind}] #{[id, name].compact.join(' / ')}"
89
89
  builder
90
90
  end
91
91
  end
@@ -15,6 +15,7 @@ module AIXM
15
15
  # xy: AIXM.xy
16
16
  # type: TYPES
17
17
  # )
18
+ # designated_point.airport = AIXM.airport or nil
18
19
  # designated_point.remarks = String or nil
19
20
  #
20
21
  # @see https://github.com/openflightmaps/ofmx/wiki/Navigational-aid#dpn-designated-point
@@ -24,15 +25,23 @@ module AIXM
24
25
  private :organisation
25
26
 
26
27
  TYPES = {
27
- ICAO: :icao, # five-letter ICAO name
28
- ADHP: :adhp, # airport related name
29
- COORD: :coordinates, # derived from geographical coordinates
30
- OTHER: :other # specify in remarks
28
+ ICAO: :icao, # five-letter ICAO id
29
+ ADHP: :adhp, # airport related id
30
+ COORD: :coordinates, # derived from geographical coordinates
31
+ 'VFR-RP': :vfr_reporting_point, # usually one or two letter id
32
+ 'VFR-MRP': :vfr_mandatory_reporting_point, # usually one or two letter id
33
+ 'VFR-ENR': :vfr_en_route_point,
34
+ 'VFR-GLD': :vfr_glider_point,
35
+ OTHER: :other # specify in remarks
31
36
  }.freeze
32
37
 
33
38
  # @return [Symbol] type of designated point
34
39
  attr_reader :type
35
40
 
41
+ # @return [AIXM::Feature::Airport] airport this designated point is
42
+ # associated with
43
+ attr_reader :airport
44
+
36
45
  def initialize(type:, **arguments)
37
46
  super(organisation: false, z: nil, **arguments)
38
47
  self.type = type
@@ -42,6 +51,11 @@ module AIXM
42
51
  @type = TYPES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid type")
43
52
  end
44
53
 
54
+ def airport=(value)
55
+ fail(ArgumentError, "invalid airport") unless value.nil? || value.is_a?(AIXM::Feature::Airport)
56
+ @airport = value
57
+ end
58
+
45
59
  # @return [String] UID markup
46
60
  def to_uid
47
61
  builder = Builder::XmlMarkup.new(indent: 2)
@@ -57,8 +71,9 @@ module AIXM
57
71
  builder = to_builder
58
72
  builder.Dpn({ source: (source if AIXM.ofmx?) }.compact) do |dpn|
59
73
  dpn << to_uid.indent(2)
74
+ dpn << airport.to_uid(as: :AhpUidAssoc).indent(2) if airport
60
75
  dpn.codeDatum('WGE')
61
- dpn.codeType(type_key.to_s)
76
+ dpn.codeType(AIXM.aixm? && type_key =~ /^VFR/ ? 'OTHER' : type_key.to_s)
62
77
  dpn.txtName(name) if name
63
78
  dpn.txtRmk(remarks) if remarks
64
79
  dpn.target!