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