aixm 0.1.3 → 0.2.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/CHANGELOG.md +14 -0
- data/README.md +91 -11
- data/lib/aixm.rb +10 -0
- data/lib/aixm/base.rb +10 -0
- data/lib/aixm/component/base.rb +1 -1
- data/lib/aixm/component/class_layer.rb +0 -3
- data/lib/aixm/component/geometry.rb +0 -3
- data/lib/aixm/component/geometry/arc.rb +4 -8
- data/lib/aixm/component/geometry/base.rb +8 -0
- data/lib/aixm/component/geometry/border.rb +3 -8
- data/lib/aixm/component/geometry/circle.rb +5 -9
- data/lib/aixm/component/geometry/point.rb +6 -13
- data/lib/aixm/component/schedule.rb +8 -10
- data/lib/aixm/component/vertical_limits.rb +1 -4
- data/lib/aixm/document.rb +9 -6
- data/lib/aixm/f.rb +41 -0
- data/lib/aixm/feature/airspace.rb +5 -9
- data/lib/aixm/feature/base.rb +6 -0
- data/lib/aixm/feature/navigational_aid/base.rb +46 -0
- data/lib/aixm/feature/navigational_aid/designated_point.rb +69 -0
- data/lib/aixm/feature/navigational_aid/dme.rb +54 -0
- data/lib/aixm/feature/navigational_aid/marker.rb +41 -0
- data/lib/aixm/feature/navigational_aid/ndb.rb +56 -0
- data/lib/aixm/feature/navigational_aid/tacan.rb +54 -0
- data/lib/aixm/feature/navigational_aid/vor.rb +92 -0
- data/lib/aixm/refinements.rb +23 -2
- data/lib/aixm/shortcuts.rb +12 -5
- data/lib/aixm/version.rb +1 -1
- data/lib/aixm/xy.rb +9 -6
- data/lib/aixm/z.rb +22 -11
- data/spec/factory.rb +87 -4
- data/spec/lib/aixm/component/class_layer_spec.rb +6 -6
- data/spec/lib/aixm/component/geometry/arc_spec.rb +16 -16
- data/spec/lib/aixm/component/geometry/border_spec.rb +4 -4
- data/spec/lib/aixm/component/geometry/circle_spec.rb +10 -10
- data/spec/lib/aixm/component/geometry/point_spec.rb +4 -4
- data/spec/lib/aixm/component/geometry_spec.rb +21 -21
- data/spec/lib/aixm/component/schedule_spec.rb +6 -6
- data/spec/lib/aixm/component/vertical_limits_spec.rb +18 -18
- data/spec/lib/aixm/document_spec.rb +220 -30
- data/spec/lib/aixm/f_spec.rb +58 -0
- data/spec/lib/aixm/feature/airspace_spec.rb +5 -5
- data/spec/lib/aixm/feature/navigational_aid/base_spec.rb +37 -0
- data/spec/lib/aixm/feature/navigational_aid/designated_point_spec.rb +43 -0
- data/spec/lib/aixm/feature/navigational_aid/dme_spec.rb +43 -0
- data/spec/lib/aixm/feature/navigational_aid/marker_spec.rb +44 -0
- data/spec/lib/aixm/feature/navigational_aid/ndb_spec.rb +54 -0
- data/spec/lib/aixm/feature/navigational_aid/tacan_spec.rb +43 -0
- data/spec/lib/aixm/feature/navigational_aid/vor_spec.rb +58 -0
- data/spec/lib/aixm/refinements_spec.rb +27 -0
- data/spec/lib/aixm/xy_spec.rb +29 -23
- data/spec/lib/aixm/z_spec.rb +33 -17
- metadata +29 -2
data/lib/aixm/document.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
module AIXM
|
2
|
-
class Document
|
3
|
-
|
2
|
+
class Document < Base
|
4
3
|
using AIXM::Refinements
|
5
4
|
|
5
|
+
IGNORE_ERROR_PATTERN = %r(OrgUid)
|
6
|
+
|
6
7
|
attr_reader :created_at, :effective_at
|
7
8
|
attr_accessor :features
|
8
9
|
|
@@ -20,7 +21,7 @@ module AIXM
|
|
20
21
|
##
|
21
22
|
# Check whether the document is complete (extensions excluded)
|
22
23
|
def complete?
|
23
|
-
features.any? && features.none? { |f| !f.complete? }
|
24
|
+
features.any? && features.none? { |f| f.respond_to?(:complete?) && !f.complete? }
|
24
25
|
end
|
25
26
|
|
26
27
|
##
|
@@ -33,14 +34,16 @@ module AIXM
|
|
33
34
|
# Validate against the XSD and return an array of errors
|
34
35
|
def errors
|
35
36
|
xsd = Nokogiri::XML::Schema(File.open(AIXM::SCHEMA))
|
36
|
-
xsd.validate(Nokogiri::XML(to_xml))
|
37
|
+
xsd.validate(Nokogiri::XML(to_xml)).reject do |error|
|
38
|
+
error.message =~ IGNORE_ERROR_PATTERN
|
39
|
+
end
|
37
40
|
end
|
38
41
|
|
39
42
|
##
|
40
43
|
# Render AIXM
|
41
44
|
#
|
42
45
|
# Extensions:
|
43
|
-
# * +:
|
46
|
+
# * +:ofm+ - Open Flightmaps
|
44
47
|
def to_xml(*extensions)
|
45
48
|
now = Time.now.xmlschema
|
46
49
|
meta = {
|
@@ -50,7 +53,7 @@ module AIXM
|
|
50
53
|
created: @created_at&.xmlschema || now,
|
51
54
|
effective: @effective_at&.xmlschema || now
|
52
55
|
}
|
53
|
-
meta[:version] += ' + OFM extensions of version 0.1' if extensions >> :
|
56
|
+
meta[:version] += ' + OFM extensions of version 0.1' if extensions >> :ofm
|
54
57
|
builder = Builder::XmlMarkup.new(indent: 2)
|
55
58
|
builder.instruct!
|
56
59
|
builder.tag!('AIXM-Snapshot', meta) do |aixm_snapshot|
|
data/lib/aixm/f.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module AIXM
|
2
|
+
|
3
|
+
##
|
4
|
+
# Frequency
|
5
|
+
#
|
6
|
+
# The following units are recognized:
|
7
|
+
# * +:mhz+ - megahertz
|
8
|
+
# * +:khz+ - kilohertz
|
9
|
+
class F < Base
|
10
|
+
using AIXM::Refinements
|
11
|
+
|
12
|
+
UNITS = %i(mhz khz).freeze
|
13
|
+
|
14
|
+
attr_reader :freq, :unit
|
15
|
+
|
16
|
+
def initialize(freq, unit)
|
17
|
+
@freq, @unit = freq.to_f, unit&.to_sym&.downcase
|
18
|
+
fail(ArgumentError, "unrecognized unit `#{@unit}'") unless UNITS.include? @unit
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Digest to identify the payload
|
23
|
+
def to_digest
|
24
|
+
[freq, unit].to_digest
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Check whether two frequencies are identical
|
29
|
+
def ==(other)
|
30
|
+
other.is_a?(F) && freq == other.freq && unit == other.unit
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Check whether this frequency is part of a frequency band
|
35
|
+
def between?(lower_freq, upper_freq, unit)
|
36
|
+
freq.between?(lower_freq, upper_freq) && self.unit == unit
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module AIXM
|
2
2
|
module Feature
|
3
|
-
class Airspace
|
4
|
-
|
3
|
+
class Airspace < Base
|
5
4
|
using AIXM::Refinements
|
6
5
|
|
7
6
|
attr_reader :name, :short_name, :type
|
@@ -45,15 +44,12 @@ module AIXM
|
|
45
44
|
|
46
45
|
##
|
47
46
|
# Render AIXM
|
48
|
-
#
|
49
|
-
# Extensions:
|
50
|
-
# * +:OFM+ - Open Flightmaps
|
51
47
|
def to_xml(*extensions)
|
52
48
|
mid = to_digest
|
53
49
|
builder = Builder::XmlMarkup.new(indent: 2)
|
54
50
|
builder.comment! "Airspace: [#{type}] #{name}"
|
55
|
-
builder.Ase({ xt_classLayersAvail: ((class_layers.count > 1) if extensions >> :
|
56
|
-
ase.AseUid({ mid: mid, newEntity: (true if extensions >> :
|
51
|
+
builder.Ase({ xt_classLayersAvail: ((class_layers.count > 1) if extensions >> :ofm) }.compact) do |ase|
|
52
|
+
ase.AseUid({ mid: mid, newEntity: (true if extensions >> :ofm) }.compact) do |aseuid|
|
57
53
|
aseuid.codeType(type.to_s)
|
58
54
|
aseuid.codeId(mid)
|
59
55
|
end
|
@@ -66,11 +62,11 @@ module AIXM
|
|
66
62
|
end
|
67
63
|
end
|
68
64
|
ase.txtRmk(remarks.to_s) if remarks
|
69
|
-
ase.xt_selAvail(false) if extensions >> :
|
65
|
+
ase.xt_selAvail(false) if extensions >> :ofm
|
70
66
|
end
|
71
67
|
builder.Abd do |abd|
|
72
68
|
abd.AbdUid do |abduid|
|
73
|
-
abduid.AseUid({ mid: mid, newEntity: (true if extensions >> :
|
69
|
+
abduid.AseUid({ mid: mid, newEntity: (true if extensions >> :ofm) }.compact) do |aseuid|
|
74
70
|
aseuid.codeType(type.to_s)
|
75
71
|
aseuid.codeId(mid)
|
76
72
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module AIXM
|
2
|
+
module Feature
|
3
|
+
module NavigationalAid
|
4
|
+
|
5
|
+
##
|
6
|
+
# Implements common attributes of all navigational aids
|
7
|
+
#
|
8
|
+
# Please note that the optional elevation +z+ must be in +:qnh+.
|
9
|
+
class Base < AIXM::Feature::Base
|
10
|
+
using AIXM::Refinements
|
11
|
+
|
12
|
+
attr_reader :id, :name, :xy, :z
|
13
|
+
attr_accessor :remarks
|
14
|
+
|
15
|
+
private_class_method :new
|
16
|
+
|
17
|
+
def initialize(id:, name:, xy:, z: nil)
|
18
|
+
@id, @name, @xy, @z = id&.upcase, name&.upcase, xy, z
|
19
|
+
fail(ArgumentError, "invalid xy") unless xy.is_a? AIXM::XY
|
20
|
+
fail(ArgumentError, "invalid z") unless z.nil? || (z.is_a?(AIXM::Z) && z.qnh?)
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Return either the +type_key+ or +class+
|
25
|
+
def kind
|
26
|
+
respond_to?(:type_key) ? type_key : self.class.name.split('::').last.to_sym
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Digest to identify the payload
|
31
|
+
def to_digest
|
32
|
+
[kind, id, name, xy.to_digest, z&.to_digest, remarks].to_digest
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Create builder to render AIXM in subclasses
|
37
|
+
def to_builder(*extensions)
|
38
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
39
|
+
builder.comment! "Navigational aid: [#{kind}] #{name}"
|
40
|
+
builder
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module AIXM
|
2
|
+
module Feature
|
3
|
+
module NavigationalAid
|
4
|
+
|
5
|
+
##
|
6
|
+
# Designated points are named map coordinates
|
7
|
+
#
|
8
|
+
# Types:
|
9
|
+
# * +:icao+ (+:ICAO+) - ICAO 5 letter name code designator
|
10
|
+
# * +:adhp+ (+:ADHP+) - aerodrome/heliport related name code designator
|
11
|
+
# * +:coordinates+ (+:COORD+) - point with identifier derived from its
|
12
|
+
# geographical coordinates
|
13
|
+
# * +:other+ (+:OTHER+) - other type
|
14
|
+
class DesignatedPoint < Base
|
15
|
+
using AIXM::Refinements
|
16
|
+
|
17
|
+
TYPES = {
|
18
|
+
ICAO: :icao,
|
19
|
+
ADHP: :adhp,
|
20
|
+
COORD: :coordinates,
|
21
|
+
OTHER: :other
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
attr_reader :type
|
25
|
+
|
26
|
+
public_class_method :new
|
27
|
+
|
28
|
+
def initialize(id:, name:, xy:, z: nil, type:)
|
29
|
+
super(id: id, name: name, xy: xy, z: z)
|
30
|
+
@type = TYPES.lookup(type&.to_sym, nil) || fail(ArgumentError, "invalid type")
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Digest to identify the payload
|
35
|
+
def to_digest
|
36
|
+
[super, type].to_digest
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Render AIXM
|
41
|
+
def to_xml(*extensions)
|
42
|
+
builder = to_builder(*extensions)
|
43
|
+
builder.Dpn do |dpn|
|
44
|
+
dpn.DpnUid({ newEntity: (true if extensions >> :ofm) }.compact) do |dpnuid|
|
45
|
+
dpnuid.codeId(id)
|
46
|
+
dpnuid.geoLat(xy.lat(format_for(*extensions)))
|
47
|
+
dpnuid.geoLong(xy.long(format_for(*extensions)))
|
48
|
+
end
|
49
|
+
dpn.OrgUid
|
50
|
+
dpn.txtName(name)
|
51
|
+
dpn.codeDatum('WGE')
|
52
|
+
dpn.codeType(type_key.to_s)
|
53
|
+
if z
|
54
|
+
dpn.valElev(z.alt)
|
55
|
+
dpn.uomDistVer(z.unit.to_s)
|
56
|
+
end
|
57
|
+
dpn.txtRmk(remarks) if remarks
|
58
|
+
dpn.target! # see https://github.com/jimweirich/builder/issues/42
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def type_key
|
63
|
+
TYPES.key(type)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module AIXM
|
2
|
+
module Feature
|
3
|
+
module NavigationalAid
|
4
|
+
|
5
|
+
##
|
6
|
+
# DME (distance measuring equipment) operate in the frequency band
|
7
|
+
# between 962 MHz and 1213 MHz.
|
8
|
+
#
|
9
|
+
# https://en.wikipedia.org/wiki/Distance_measuring_equipment
|
10
|
+
class DME < Base
|
11
|
+
using AIXM::Refinements
|
12
|
+
|
13
|
+
attr_reader :channel
|
14
|
+
|
15
|
+
public_class_method :new
|
16
|
+
|
17
|
+
def initialize(id:, name:, xy:, z: nil, channel:)
|
18
|
+
super(id: id, name: name, xy: xy, z: z)
|
19
|
+
@channel = channel&.upcase
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Digest to identify the payload
|
24
|
+
def to_digest
|
25
|
+
[super, channel].to_digest
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Render AIXM
|
30
|
+
def to_xml(*extensions)
|
31
|
+
builder = to_builder(*extensions)
|
32
|
+
builder.Dme do |dme|
|
33
|
+
dme.DmeUid({ newEntity: (true if extensions >> :ofm) }.compact) do |dmeuid|
|
34
|
+
dmeuid.codeId(id)
|
35
|
+
dmeuid.geoLat(xy.lat(format_for(*extensions)))
|
36
|
+
dmeuid.geoLong(xy.long(format_for(*extensions)))
|
37
|
+
end
|
38
|
+
dme.OrgUid
|
39
|
+
dme.txtName(name)
|
40
|
+
dme.codeChannel(channel)
|
41
|
+
dme.codeDatum('WGE')
|
42
|
+
if z
|
43
|
+
dme.valElev(z.alt)
|
44
|
+
dme.uomDistVer(z.unit.to_s)
|
45
|
+
end
|
46
|
+
dme.txtRmk(remarks) if remarks
|
47
|
+
dme.target! # see https://github.com/jimweirich/builder/issues/42
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module AIXM
|
2
|
+
module Feature
|
3
|
+
module NavigationalAid
|
4
|
+
|
5
|
+
##
|
6
|
+
# Marker (marker beacons) operate on 75 MHz.
|
7
|
+
#
|
8
|
+
# https://en.wikipedia.org/wiki/Marker_beacon
|
9
|
+
class Marker < Base
|
10
|
+
using AIXM::Refinements
|
11
|
+
|
12
|
+
public_class_method :new
|
13
|
+
|
14
|
+
##
|
15
|
+
# Render AIXM
|
16
|
+
def to_xml(*extensions)
|
17
|
+
builder = to_builder(*extensions)
|
18
|
+
builder.Mkr do |mkr|
|
19
|
+
mkr.MkrUid({ newEntity: (true if extensions >> :ofm) }.compact) do |mkruid|
|
20
|
+
mkruid.codeId(id)
|
21
|
+
mkruid.geoLat(xy.lat(format_for(*extensions)))
|
22
|
+
mkruid.geoLong(xy.long(format_for(*extensions)))
|
23
|
+
end
|
24
|
+
mkr.OrgUid
|
25
|
+
mkr.valFreq(75)
|
26
|
+
mkr.uomFreq('MHZ')
|
27
|
+
mkr.txtName(name)
|
28
|
+
mkr.codeDatum('WGE')
|
29
|
+
if z
|
30
|
+
mkr.valElev(z.alt)
|
31
|
+
mkr.uomDistVer(z.unit.to_s)
|
32
|
+
end
|
33
|
+
mkr.txtRmk(remarks) if remarks
|
34
|
+
mkr.target! # see https://github.com/jimweirich/builder/issues/42
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module AIXM
|
2
|
+
module Feature
|
3
|
+
module NavigationalAid
|
4
|
+
|
5
|
+
##
|
6
|
+
# NDB (non-directional beacon) operate in the frequency band between
|
7
|
+
# 190 kHz and 1750 kHz.
|
8
|
+
#
|
9
|
+
# https://en.wikipedia.org/wiki/Non-directional_beacon
|
10
|
+
class NDB < Base
|
11
|
+
using AIXM::Refinements
|
12
|
+
|
13
|
+
attr_reader :f
|
14
|
+
|
15
|
+
public_class_method :new
|
16
|
+
|
17
|
+
def initialize(id:, name:, xy:, z: nil, f:)
|
18
|
+
super(id: id, name: name, xy: xy, z: z)
|
19
|
+
@f = f
|
20
|
+
fail(ArgumentError, "invalid frequency") unless f.is_a?(F) && f.between?(190, 1750, :khz)
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Digest to identify the payload
|
25
|
+
def to_digest
|
26
|
+
[super, f.to_digest].to_digest
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Render AIXM
|
31
|
+
def to_xml(*extensions)
|
32
|
+
builder = to_builder(*extensions)
|
33
|
+
builder.Ndb do |ndb|
|
34
|
+
ndb.NdbUid({ newEntity: (true if extensions >> :ofm) }.compact) do |ndbuid|
|
35
|
+
ndbuid.codeId(id)
|
36
|
+
ndbuid.geoLat(xy.lat(format_for(*extensions)))
|
37
|
+
ndbuid.geoLong(xy.long(format_for(*extensions)))
|
38
|
+
end
|
39
|
+
ndb.OrgUid
|
40
|
+
ndb.txtName(name)
|
41
|
+
ndb.valFreq(f.freq.trim)
|
42
|
+
ndb.uomFreq(f.unit.upcase.to_s)
|
43
|
+
ndb.codeDatum('WGE')
|
44
|
+
if z
|
45
|
+
ndb.valElev(z.alt)
|
46
|
+
ndb.uomDistVer(z.unit.to_s)
|
47
|
+
end
|
48
|
+
ndb.txtRmk(remarks) if remarks
|
49
|
+
ndb.target! # see https://github.com/jimweirich/builder/issues/42
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module AIXM
|
2
|
+
module Feature
|
3
|
+
module NavigationalAid
|
4
|
+
|
5
|
+
##
|
6
|
+
# TACAN (tactical air navigation system) operate in the frequency band
|
7
|
+
# between 960 MHz and 1215 MHz.
|
8
|
+
#
|
9
|
+
# https://en.wikipedia.org/wiki/Tactical_air_navigation_system
|
10
|
+
class TACAN < Base
|
11
|
+
using AIXM::Refinements
|
12
|
+
|
13
|
+
attr_reader :channel
|
14
|
+
|
15
|
+
public_class_method :new
|
16
|
+
|
17
|
+
def initialize(id:, name:, xy:, z: nil, channel:)
|
18
|
+
super(id: id, name: name, xy: xy, z: z)
|
19
|
+
@channel = channel&.upcase
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Digest to identify the payload
|
24
|
+
def to_digest
|
25
|
+
[super, channel].to_digest
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Render AIXM
|
30
|
+
def to_xml(*extensions)
|
31
|
+
builder = to_builder(*extensions)
|
32
|
+
builder.Tcn do |tcn|
|
33
|
+
tcn.TcnUid({ newEntity: (true if extensions >> :ofm) }.compact) do |tcnuid|
|
34
|
+
tcnuid.codeId(id)
|
35
|
+
tcnuid.geoLat(xy.lat(format_for(*extensions)))
|
36
|
+
tcnuid.geoLong(xy.long(format_for(*extensions)))
|
37
|
+
end
|
38
|
+
tcn.OrgUid
|
39
|
+
tcn.txtName(name)
|
40
|
+
tcn.codeChannel(channel)
|
41
|
+
tcn.codeDatum('WGE')
|
42
|
+
if z
|
43
|
+
tcn.valElev(z.alt)
|
44
|
+
tcn.uomDistVer(z.unit.to_s)
|
45
|
+
end
|
46
|
+
tcn.txtRmk(remarks) if remarks
|
47
|
+
tcn.target! # see https://github.com/jimweirich/builder/issues/42
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|