aixm 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +96 -72
  4. data/README.md +0 -1
  5. data/lib/aixm/component/geometry.rb +8 -2
  6. data/lib/aixm/component/layer.rb +83 -2
  7. data/lib/aixm/document.rb +11 -2
  8. data/lib/aixm/errors.rb +14 -2
  9. data/lib/aixm/feature.rb +2 -14
  10. data/lib/aixm/feature/airport.rb +19 -18
  11. data/lib/aixm/feature/airspace.rb +19 -21
  12. data/lib/aixm/feature/navigational_aid.rb +2 -2
  13. data/lib/aixm/feature/navigational_aid/designated_point.rb +1 -2
  14. data/lib/aixm/feature/navigational_aid/dme.rb +25 -4
  15. data/lib/aixm/feature/navigational_aid/marker.rb +1 -2
  16. data/lib/aixm/feature/navigational_aid/ndb.rb +1 -2
  17. data/lib/aixm/feature/navigational_aid/tacan.rb +5 -2
  18. data/lib/aixm/feature/navigational_aid/vor.rb +3 -4
  19. data/lib/aixm/feature/organisation.rb +3 -4
  20. data/lib/aixm/feature/unit.rb +3 -4
  21. data/lib/aixm/version.rb +1 -1
  22. data/schemas/ofmx/0/OFMX-CSV-Obstacle.json +209 -0
  23. data/schemas/ofmx/0/OFMX-CSV.json +12 -0
  24. data/schemas/ofmx/0/OFMX-DataTypes.xsd +64 -5
  25. data/schemas/ofmx/0/OFMX-Features.xsd +73 -23
  26. data/schemas/ofmx/0/OFMX-Snapshot.xsd +7 -2
  27. data/spec/factory.rb +8 -18
  28. data/spec/lib/aixm/component/geometry_spec.rb +27 -4
  29. data/spec/lib/aixm/component/helipad_spec.rb +2 -2
  30. data/spec/lib/aixm/component/layer_spec.rb +27 -0
  31. data/spec/lib/aixm/component/runway_spec.rb +13 -13
  32. data/spec/lib/aixm/document_spec.rb +56 -42
  33. data/spec/lib/aixm/feature/airport_spec.rb +15 -15
  34. data/spec/lib/aixm/feature/airspace_spec.rb +38 -30
  35. data/spec/lib/aixm/feature/navigational_aid/designated_point_spec.rb +2 -2
  36. data/spec/lib/aixm/feature/navigational_aid/dme_spec.rb +24 -7
  37. data/spec/lib/aixm/feature/navigational_aid/marker_spec.rb +4 -4
  38. data/spec/lib/aixm/feature/navigational_aid/ndb_spec.rb +4 -4
  39. data/spec/lib/aixm/feature/navigational_aid/tacan_spec.rb +24 -7
  40. data/spec/lib/aixm/feature/navigational_aid/vor_spec.rb +14 -14
  41. data/spec/lib/aixm/feature/organisation_spec.rb +2 -2
  42. data/spec/lib/aixm/feature/unit_spec.rb +11 -11
  43. data/spec/lib/aixm/feature_spec.rb +0 -20
  44. metadata +4 -2
@@ -7,8 +7,8 @@ module AIXM
7
7
  # @return [String] reference to source of the feature data
8
8
  attr_reader :source
9
9
 
10
- def initialize(source: nil, region: nil)
11
- self.source, self.region = source, region
10
+ def initialize(source: nil)
11
+ self.source = source
12
12
  end
13
13
 
14
14
  # @return [String] reference to source of the feature data
@@ -17,18 +17,6 @@ module AIXM
17
17
  @source = value
18
18
  end
19
19
 
20
- # @!attribute region
21
- # @note When assigning +nil+, the global default +AIXM.config.region+ is written instead.
22
- # @return [String] region the feature belongs to
23
- def region
24
- @region || AIXM.config.region&.upcase
25
- end
26
-
27
- def region=(value)
28
- fail(ArgumentError, "invalid region") unless value.nil? || value.is_a?(String)
29
- @region = value&.upcase
30
- end
31
-
32
20
  # @return [Boolean]
33
21
  def ==(other)
34
22
  other.is_a?(self.class) && self.to_uid == other.to_uid
@@ -9,9 +9,8 @@ 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 (falls back to AIXM.config.region)
13
12
  # organisation: AIXM.organisation
14
- # code: String
13
+ # id: String
15
14
  # name: String
16
15
  # xy: AIXM.xy
17
16
  # )
@@ -30,7 +29,7 @@ module AIXM
30
29
  class Airport < Feature
31
30
  public_class_method :new
32
31
 
33
- CODE_PATTERN = /^[A-Z]{2}([A-Z]{1,2}|\d{4})$/.freeze
32
+ ID_PATTERN = /^([A-Z]{3,4}|[A-Z]{2}[A-Z\d]{4,})$/.freeze
34
33
 
35
34
  TYPES = {
36
35
  AD: :aerodrome,
@@ -42,14 +41,16 @@ module AIXM
42
41
  # @return [AIXM::Feature::Organisation] superior organisation
43
42
  attr_reader :organisation
44
43
 
45
- # ICAO indicator, IATA indicator or ICAO serial number
44
+ # ICAO indicator, IATA indicator or generated indicator
46
45
  #
47
46
  # * four letter ICAO indicator (e.g. "LFMV")
48
47
  # * three letter IATA indicator (e.g. "AVN")
49
48
  # * two letter ICAO country code + four digit number (e.g. "LF1234")
49
+ # * two letter ICAO country code + at least four letters/digits (e.g.
50
+ # "LFFOOBAR123" or "LF" + GPS code)
50
51
  #
51
- # @return [String] airport indicator code
52
- attr_reader :code
52
+ # @return [String] airport indicator
53
+ attr_reader :id
53
54
 
54
55
  # @return [String] full name
55
56
  attr_reader :name
@@ -89,15 +90,15 @@ module AIXM
89
90
  # @return [Array<AIXM::Feature::Airport::UsageLimitation>] usage limitations
90
91
  attr_accessor :usage_limitations
91
92
 
92
- def initialize(source: nil, region: nil, organisation:, code:, name:, xy:)
93
- super(source: source, region: region)
94
- self.organisation, self.code, self.name, self.xy = organisation, code, name, xy
93
+ def initialize(source: nil, organisation:, id:, name:, xy:)
94
+ super(source: source)
95
+ self.organisation, self.id, self.name, self.xy = organisation, id, name, xy
95
96
  @runways, @helipads, @usage_limitations = [], [], []
96
97
  end
97
98
 
98
99
  # @return [String]
99
100
  def inspect
100
- %Q(#<#{self.class} code=#{code.inspect}>)
101
+ %Q(#<#{self.class} id=#{id.inspect}>)
101
102
  end
102
103
 
103
104
  def organisation=(value)
@@ -105,9 +106,9 @@ module AIXM
105
106
  @organisation = value
106
107
  end
107
108
 
108
- def code=(value)
109
- fail(ArgumentError, "invalid code `#{code}'") unless value&.upcase&.match? CODE_PATTERN
110
- @code = value.upcase
109
+ def id=(value)
110
+ fail(ArgumentError, "invalid id `#{id}'") unless value&.upcase&.match? ID_PATTERN
111
+ @id = value.upcase
111
112
  end
112
113
 
113
114
  def name=(value)
@@ -230,21 +231,21 @@ module AIXM
230
231
  # @return [String] UID markup
231
232
  def to_uid
232
233
  builder = Builder::XmlMarkup.new(indent: 2)
233
- builder.AhpUid({ region: (region if AIXM.ofmx?) }.compact) do |ahp_uid|
234
- ahp_uid.codeId(code)
234
+ builder.AhpUid do |ahp_uid|
235
+ ahp_uid.codeId(id)
235
236
  end
236
237
  end
237
238
 
238
239
  # @return [String] AIXM or OFMX markup
239
240
  def to_xml
240
241
  builder = Builder::XmlMarkup.new(indent: 2)
241
- builder.comment! "Airport: #{code} #{name}"
242
+ builder.comment! "Airport: #{id} #{name}"
242
243
  builder.Ahp({ source: (source if AIXM.ofmx?) }.compact) do |ahp|
243
244
  ahp << to_uid.indent(2)
244
245
  ahp << organisation.to_uid.indent(2)
245
246
  ahp.txtName(name)
246
- ahp.codeIcao(code) if code.length == 4
247
- ahp.codeIata(code) if code.length == 3
247
+ ahp.codeIcao(id) if id.length == 4
248
+ ahp.codeIata(id) if id.length == 3
248
249
  ahp.codeGps(gps) if AIXM.ofmx? && gps
249
250
  ahp.codeType(TYPES.key(type).to_s) if type
250
251
  ahp.geoLat(xy.lat(AIXM.schema))
@@ -8,11 +8,10 @@ module AIXM
8
8
  # ===Cheat Sheet in Pseudo Code:
9
9
  # airspace = AIXM.airspace(
10
10
  # source: String or nil
11
- # region: String or nil (falls back to AIXM.config.region)
12
11
  # id: String
13
12
  # type: String or Symbol
13
+ # local_type: String or nil
14
14
  # name: String or nil
15
- # short_name: String or nil
16
15
  # )
17
16
  # airspace.geometry << AIXM.point or AIXM.arc or AIXM.border or AIXM.circle
18
17
  # airspace.layers << AIXM.layer
@@ -65,28 +64,28 @@ module AIXM
65
64
  }.freeze
66
65
 
67
66
  # @note When assigning +nil+, a 4 byte hex derived from {#type}, {#name}
68
- # and {#short_name} is written instead.
67
+ # and {#local_type} is written instead.
69
68
  # @return [String] published identifier (e.g. "LFP81")
70
69
  attr_reader :id
71
70
 
72
71
  # @return [Symbol] type of airspace (see {TYPES})
73
72
  attr_reader :type
74
73
 
74
+ # @return [String, nil] short name (e.g. "LF P 81")
75
+ attr_reader :local_type
76
+
75
77
  # @return [String, nil] full name (e.g. "LF P 81 CHERBOURG")
76
78
  attr_reader :name
77
79
 
78
- # @return [String, nil] short name (e.g. "LF P 81")
79
- attr_reader :short_name
80
-
81
80
  # @return [AIXM::Component::Geometry] horizontal geometrical shape
82
81
  attr_accessor :geometry
83
82
 
84
83
  # @return [Array<AIXM::Compoment::Layer>] vertical layers
85
84
  attr_accessor :layers
86
85
 
87
- def initialize(source: nil, region: nil, id: nil, type:, name: nil, short_name: nil)
88
- super(source: source, region: region)
89
- self.type, self.name, self.short_name = type, name, short_name
86
+ def initialize(source: nil, id: nil, type:, local_type: nil, name: nil)
87
+ super(source: source)
88
+ self.type, self.local_type, self.name = type, local_type, name
90
89
  self.id = id
91
90
  @geometry = AIXM.geometry
92
91
  @layers = []
@@ -99,27 +98,27 @@ module AIXM
99
98
 
100
99
  def id=(value)
101
100
  fail(ArgumentError, "invalid id") unless value.nil? || value.is_a?(String)
102
- @id = value&.uptrans || [type, name, short_name].to_digest.upcase
101
+ @id = value&.uptrans || [type, local_type, name].to_digest.upcase
103
102
  end
104
103
 
105
104
  def type=(value)
106
105
  @type = TYPES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid type")
107
106
  end
108
107
 
108
+ def local_type=(value)
109
+ fail(ArgumentError, "invalid short name") unless value.nil? || value.is_a?(String)
110
+ @local_type = value&.uptrans
111
+ end
112
+
109
113
  def name=(value)
110
114
  fail(ArgumentError, "invalid name") unless value.nil? || value.is_a?(String)
111
115
  @name = value&.uptrans
112
116
  end
113
117
 
114
- def short_name=(value)
115
- fail(ArgumentError, "invalid short name") unless value.nil? || value.is_a?(String)
116
- @short_name = value&.uptrans
117
- end
118
-
119
118
  # @return [String] UID markup
120
119
  def to_uid(as: :AseUid)
121
120
  builder = Builder::XmlMarkup.new(indent: 2)
122
- builder.tag!(as, { region: (region if AIXM.ofmx?) }.compact) do |tag|
121
+ builder.tag!(as) do |tag|
123
122
  tag.codeType(TYPES.key(type).to_s)
124
123
  tag.codeId(id)
125
124
  end
@@ -129,15 +128,14 @@ module AIXM
129
128
  # @raise [AIXM::LayerError] if no layers are defined
130
129
  # @return [String] AIXM or OFMX markup
131
130
  def to_xml
132
- fail(LayerError, "no layers defined") unless layers.any?
131
+ fail(LayerError.new("no layers defined", self)) unless layers.any?
133
132
  builder = Builder::XmlMarkup.new(indent: 2)
134
133
  builder.comment! "Airspace: [#{TYPES.key(type)}] #{name || :UNNAMED}"
135
134
  builder.Ase({
136
- source: (source if AIXM.ofmx?),
137
- classLayers: (layers.count if AIXM.ofmx? && layered?)
135
+ source: (source if AIXM.ofmx?)
138
136
  }.compact) do |ase|
139
137
  ase << to_uid.indent(2)
140
- ase.txtLocalType(short_name) if short_name && short_name != name
138
+ ase.txtLocalType(local_type) if local_type && local_type != name
141
139
  ase.txtName(name) if name
142
140
  unless layered?
143
141
  ase << layers.first.to_xml.indent(2)
@@ -151,7 +149,7 @@ module AIXM
151
149
  end
152
150
  if layered?
153
151
  layers.each.with_index do |layer, index|
154
- layer_airspace = AIXM.airspace(region: region, type: 'CLASS', name: "#{name} LAYER #{index + 1}")
152
+ layer_airspace = AIXM.airspace(type: 'CLASS', name: "#{name} LAYER #{index + 1}")
155
153
  builder.Ase do |ase|
156
154
  ase << layer_airspace.to_uid.indent(2)
157
155
  ase.txtName(layer_airspace.name)
@@ -28,8 +28,8 @@ module AIXM
28
28
  # @return [String, nil] free text remarks
29
29
  attr_reader :remarks
30
30
 
31
- def initialize(source: nil, region: nil, organisation:, id:, name: nil, xy:, z: nil)
32
- super(source: source, region: region)
31
+ def initialize(source: nil, organisation:, id:, name: nil, xy:, z: nil)
32
+ super(source: source)
33
33
  self.organisation, self.id, self.name, self.xy, self.z = organisation, id, name, xy, z
34
34
  end
35
35
 
@@ -10,7 +10,6 @@ module AIXM
10
10
  # ===Cheat Sheet in Pseudo Code:
11
11
  # designated_point = AIXM.designated_point(
12
12
  # source: String or nil
13
- # region: String or nil (falls back to AIXM.config.region)
14
13
  # id: String
15
14
  # name: String or nil
16
15
  # xy: AIXM.xy
@@ -46,7 +45,7 @@ module AIXM
46
45
  # @return [String] UID markup
47
46
  def to_uid
48
47
  builder = Builder::XmlMarkup.new(indent: 2)
49
- builder.DpnUid({ region: (region if AIXM.ofmx?) }.compact) do |dpn_uid|
48
+ builder.DpnUid do |dpn_uid|
50
49
  dpn_uid.codeId(id)
51
50
  dpn_uid.geoLat(xy.lat(AIXM.schema))
52
51
  dpn_uid.geoLong(xy.long(AIXM.schema))
@@ -12,7 +12,6 @@ module AIXM
12
12
  # ===Cheat Sheet in Pseudo Code:
13
13
  # dme = AIXM.dme(
14
14
  # source: String or nil
15
- # region: String or nil (falls back to AIXM.config.region)
16
15
  # organisation: AIXM.organisation
17
16
  # id: String
18
17
  # name: String
@@ -27,6 +26,8 @@ module AIXM
27
26
  class DME < NavigationalAid
28
27
  public_class_method :new
29
28
 
29
+ CHANNEL_PATTERN = /\A([1-9]|[1-9]\d|1[0-1]\d|12[0-6])[XY]\z/.freeze
30
+
30
31
  # @return [String] radio channel
31
32
  attr_reader :channel
32
33
 
@@ -39,8 +40,24 @@ module AIXM
39
40
  end
40
41
 
41
42
  def channel=(value)
42
- fail(ArgumentError, "invalid channel") unless value.is_a? String
43
- @channel = value.upcase
43
+ fail(ArgumentError, "invalid channel") unless value.is_a?(String) && value.match?(CHANNEL_PATTERN)
44
+ @channel = value
45
+ end
46
+
47
+ # @return [AIXM::F] ghost frequency matching the channel
48
+ def ghost_f
49
+ if channel
50
+ number, letter = channel.split(/(?=[XY])/)
51
+ integer = case number.to_i
52
+ when (1..16) then 13430
53
+ when (17..59) then 10630
54
+ when (60..69) then 12730
55
+ when (70..126) then 10530
56
+ end
57
+ integer += number.to_i * 10
58
+ integer += 5 if letter == 'Y'
59
+ AIXM.f(integer.to_f / 100, :mhz)
60
+ end
44
61
  end
45
62
 
46
63
  def vor=(value)
@@ -52,7 +69,7 @@ module AIXM
52
69
  # @return [String] UID markup
53
70
  def to_uid
54
71
  builder = Builder::XmlMarkup.new(indent: 2)
55
- builder.DmeUid({ region: (region if AIXM.ofmx?) }.compact) do |dme_uid|
72
+ builder.DmeUid do |dme_uid|
56
73
  dme_uid.codeId(id)
57
74
  dme_uid.geoLat(xy.lat(AIXM.schema))
58
75
  dme_uid.geoLong(xy.long(AIXM.schema))
@@ -68,6 +85,10 @@ module AIXM
68
85
  dme << vor.to_uid.indent(2) if vor
69
86
  dme.txtName(name) if name
70
87
  dme.codeChannel(channel)
88
+ unless vor
89
+ dme.valGhostFreq(ghost_f.freq.trim)
90
+ dme.uomGhostFreq('MHZ')
91
+ end
71
92
  dme.codeDatum('WGE')
72
93
  if z
73
94
  dme.valElev(z.alt)
@@ -11,7 +11,6 @@ module AIXM
11
11
  # ===Cheat Sheet in Pseudo Code:
12
12
  # marker = AIXM.marker(
13
13
  # source: String or nil
14
- # region: String or nil (falls back to AIXM.config.region)
15
14
  # organisation: AIXM.organisation
16
15
  # id: String
17
16
  # name: String
@@ -54,7 +53,7 @@ module AIXM
54
53
  # @return [String] UID markup
55
54
  def to_uid
56
55
  builder = Builder::XmlMarkup.new(indent: 2)
57
- builder.MkrUid({ region: (region if AIXM.ofmx?) }.compact) do |mkr_uid|
56
+ builder.MkrUid do |mkr_uid|
58
57
  mkr_uid.codeId(id)
59
58
  mkr_uid.geoLat(xy.lat(AIXM.schema))
60
59
  mkr_uid.geoLong(xy.long(AIXM.schema))
@@ -10,7 +10,6 @@ module AIXM
10
10
  # ===Cheat Sheet in Pseudo Code:
11
11
  # ndb = AIXM.ndb(
12
12
  # source: String or nil
13
- # region: String or nil (falls back to AIXM.config.region)
14
13
  # organisation: AIXM.organisation
15
14
  # id: String
16
15
  # name: String
@@ -56,7 +55,7 @@ module AIXM
56
55
  # @return [String] UID markup
57
56
  def to_uid
58
57
  builder = Builder::XmlMarkup.new(indent: 2)
59
- builder.NdbUid({ region: (region if AIXM.ofmx?) }.compact) do |ndb_uid|
58
+ builder.NdbUid do |ndb_uid|
60
59
  ndb_uid.codeId(id)
61
60
  ndb_uid.geoLat(xy.lat(AIXM.schema))
62
61
  ndb_uid.geoLong(xy.long(AIXM.schema))
@@ -11,7 +11,6 @@ module AIXM
11
11
  # ===Cheat Sheet in Pseudo Code:
12
12
  # tacan = AIXM.tacan(
13
13
  # source: String or nil
14
- # region: String or nil (to use +AIXM.config.region+)
15
14
  # organisation: AIXM.organisation
16
15
  # id: String
17
16
  # name: String
@@ -29,7 +28,7 @@ module AIXM
29
28
  # @return [String] UID markup
30
29
  def to_uid
31
30
  builder = Builder::XmlMarkup.new(indent: 2)
32
- builder.TcnUid({ region: (region if AIXM.ofmx?) }.compact) do |tcn_uid|
31
+ builder.TcnUid do |tcn_uid|
33
32
  tcn_uid.codeId(id)
34
33
  tcn_uid.geoLat(xy.lat(AIXM.schema))
35
34
  tcn_uid.geoLong(xy.long(AIXM.schema))
@@ -45,6 +44,10 @@ module AIXM
45
44
  tcn << vor.to_uid.indent(2) if vor
46
45
  tcn.txtName(name) if name
47
46
  tcn.codeChannel(channel)
47
+ if !vor && AIXM.ofmx?
48
+ tcn.valGhostFreq(ghost_f.freq.trim)
49
+ tcn.uomGhostFreq('MHZ')
50
+ end
48
51
  tcn.codeDatum('WGE')
49
52
  if z
50
53
  tcn.valElev(z.alt)
@@ -11,7 +11,6 @@ module AIXM
11
11
  # ===Cheat Sheet in Pseudo Code:
12
12
  # vor = AIXM.vor(
13
13
  # source: String or nil
14
- # region: String or nil (falls back to AIXM.config.region)
15
14
  # organisation: AIXM.organisation
16
15
  # id: String
17
16
  # name: String
@@ -79,21 +78,21 @@ module AIXM
79
78
  # Associate a DME which turns the VOR into a VOR/DME
80
79
  def associate_dme(channel:)
81
80
  @dme = AIXM.dme(organisation: organisation, id: id, name: name, xy: xy, z: z, channel: channel)
82
- @dme.region, @dme.timetable, @dme.remarks = region, timetable, remarks
81
+ @dme.timetable, @dme.remarks = timetable, remarks
83
82
  @dme.send(:vor=, self)
84
83
  end
85
84
 
86
85
  # Associate a TACAN which turns the VOR into a VORTAC
87
86
  def associate_tacan(channel:)
88
87
  @tacan = AIXM.tacan(organisation: organisation, id: id, name: name, xy: xy, z: z, channel: channel)
89
- @tacan.region, @tacan.timetable, @tacan.remarks = region, timetable, remarks
88
+ @tacan.timetable, @tacan.remarks = timetable, remarks
90
89
  @tacan.send(:vor=, self)
91
90
  end
92
91
 
93
92
  # @return [String] UID markup
94
93
  def to_uid
95
94
  builder = Builder::XmlMarkup.new(indent: 2)
96
- builder.VorUid({ region: (region if AIXM.ofmx?) }.compact) do |vor_uid|
95
+ builder.VorUid do |vor_uid|
97
96
  vor_uid.codeId(id)
98
97
  vor_uid.geoLat(xy.lat(AIXM.schema))
99
98
  vor_uid.geoLong(xy.long(AIXM.schema))
@@ -9,7 +9,6 @@ module AIXM
9
9
  # ===Cheat Sheet in Pseudo Code:
10
10
  # organisation = AIXM.organisation(
11
11
  # source: String or nil
12
- # region: String or nil (falls back to +AIXM.config.region+)
13
12
  # name: String
14
13
  # type: TYPES
15
14
  # )
@@ -44,8 +43,8 @@ module AIXM
44
43
  # @return [String, nil] free text remarks
45
44
  attr_reader :remarks
46
45
 
47
- def initialize(source: nil, region: nil, name:, type:)
48
- super(source: source, region: region)
46
+ def initialize(source: nil, name:, type:)
47
+ super(source: source)
49
48
  self.name, self.type = name, type
50
49
  end
51
50
 
@@ -75,7 +74,7 @@ module AIXM
75
74
  # @return [String] UID markup
76
75
  def to_uid
77
76
  builder = Builder::XmlMarkup.new(indent: 2)
78
- builder.OrgUid({ region: (region if AIXM.ofmx?) }.compact) do |org_uid|
77
+ builder.OrgUid do |org_uid|
79
78
  org_uid.txtName(name)
80
79
  end
81
80
  end