aixm 0.1.0 → 0.1.3
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 +29 -1
- data/Guardfile +1 -1
- data/README.md +146 -17
- data/aixm.gemspec +3 -1
- data/lib/aixm.rb +12 -10
- data/lib/aixm/component/base.rb +6 -0
- data/lib/aixm/component/class_layer.rb +49 -0
- data/lib/aixm/component/geometry.rb +73 -0
- data/lib/aixm/component/geometry/arc.rb +53 -0
- data/lib/aixm/component/geometry/border.rb +49 -0
- data/lib/aixm/component/geometry/circle.rb +56 -0
- data/lib/aixm/component/geometry/point.rb +42 -0
- data/lib/aixm/component/schedule.rb +45 -0
- data/lib/aixm/{vertical/limits.rb → component/vertical_limits.rb} +9 -14
- data/lib/aixm/document.rb +30 -19
- data/lib/aixm/feature/airspace.rb +60 -29
- data/lib/aixm/refinements.rb +49 -2
- data/lib/aixm/shortcuts.rb +30 -0
- data/lib/aixm/version.rb +1 -1
- data/spec/factory.rb +42 -25
- data/spec/lib/aixm/component/class_layer_spec.rb +74 -0
- data/spec/lib/aixm/{horizontal → component/geometry}/arc_spec.rb +11 -11
- data/spec/lib/aixm/component/geometry/border_spec.rb +30 -0
- data/spec/lib/aixm/{horizontal → component/geometry}/circle_spec.rb +8 -8
- data/spec/lib/aixm/{horizontal → component/geometry}/point_spec.rb +7 -7
- data/spec/lib/aixm/{geometry_spec.rb → component/geometry_spec.rb} +39 -40
- data/spec/lib/aixm/component/schedule_spec.rb +33 -0
- data/spec/lib/aixm/{vertical/limits_spec.rb → component/vertical_limits_spec.rb} +10 -10
- data/spec/lib/aixm/document_spec.rb +97 -36
- data/spec/lib/aixm/feature/airspace_spec.rb +230 -71
- data/spec/lib/aixm/refinements_spec.rb +52 -12
- metadata +30 -23
- data/lib/aixm/constants.rb +0 -6
- data/lib/aixm/geometry.rb +0 -71
- data/lib/aixm/horizontal/arc.rb +0 -50
- data/lib/aixm/horizontal/border.rb +0 -45
- data/lib/aixm/horizontal/circle.rb +0 -53
- data/lib/aixm/horizontal/point.rb +0 -39
- data/spec/lib/aixm/horizontal/border_spec.rb +0 -47
@@ -0,0 +1,53 @@
|
|
1
|
+
module AIXM
|
2
|
+
module Component
|
3
|
+
class Geometry
|
4
|
+
|
5
|
+
##
|
6
|
+
# Arcs are +clockwise+ (true/false) circle sectors around +center_xy+ and
|
7
|
+
# starting at +xy+.
|
8
|
+
class Arc < Point
|
9
|
+
using AIXM::Refinements
|
10
|
+
|
11
|
+
attr_reader :center_xy
|
12
|
+
|
13
|
+
def initialize(xy:, center_xy:, clockwise:)
|
14
|
+
super(xy: xy)
|
15
|
+
fail(ArgumentError, "invalid center xy") unless center_xy.is_a? AIXM::XY
|
16
|
+
fail(ArgumentError, "clockwise must be true or false") unless [true, false].include? clockwise
|
17
|
+
@center_xy, @clockwise = center_xy, clockwise
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Whether the arc is going clockwise (true) or not (false)
|
22
|
+
def clockwise?
|
23
|
+
@clockwise
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Digest to identify the payload
|
28
|
+
def to_digest
|
29
|
+
[xy.lat, xy.long, center_xy.lat, center_xy.long, clockwise?].to_digest
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Render AIXM
|
34
|
+
#
|
35
|
+
# Extensions:
|
36
|
+
# * +:OFM+ - Open Flightmaps
|
37
|
+
def to_xml(*extensions)
|
38
|
+
format = extensions >> :OFM ? :OFM : :AIXM
|
39
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
40
|
+
builder.Avx do |avx|
|
41
|
+
avx.codeType(clockwise? ? 'CWA' : 'CCA')
|
42
|
+
avx.geoLat(xy.lat(format))
|
43
|
+
avx.geoLong(xy.long(format))
|
44
|
+
avx.codeDatum('WGE')
|
45
|
+
avx.geoLatArc(center_xy.lat(format))
|
46
|
+
avx.geoLongArc(center_xy.long(format))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module AIXM
|
2
|
+
module Component
|
3
|
+
class Geometry
|
4
|
+
|
5
|
+
##
|
6
|
+
# Borders are following natural or artifical border lines referenced by
|
7
|
+
# +name+ and starting at +xy+.
|
8
|
+
class Border < Point
|
9
|
+
using AIXM::Refinements
|
10
|
+
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
def initialize(xy:, name:)
|
14
|
+
super(xy: xy)
|
15
|
+
@name = name
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Digest to identify the payload
|
20
|
+
def to_digest
|
21
|
+
[xy.lat, xy.long, name].to_digest
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Render AIXM
|
26
|
+
#
|
27
|
+
# Extensions:
|
28
|
+
# * +:OFM+ - Open Flightmaps
|
29
|
+
def to_xml(*extensions)
|
30
|
+
format = extensions >> :OFM ? :OFM : :AIXM
|
31
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
32
|
+
builder.Avx do |avx|
|
33
|
+
avx.codeType('FNT')
|
34
|
+
avx.geoLat(xy.lat(format))
|
35
|
+
avx.geoLong(xy.long(format))
|
36
|
+
avx.codeDatum('WGE')
|
37
|
+
# TODO: Find examples how to do this with vanilla AIXM
|
38
|
+
if extensions >> :OFM
|
39
|
+
avx.GbrUid do |gbruid|
|
40
|
+
gbruid.txtName(name.to_s)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module AIXM
|
2
|
+
module Component
|
3
|
+
class Geometry
|
4
|
+
|
5
|
+
##
|
6
|
+
# Circles are defined by a +center_xy+ and a +radius+ in kilometers.
|
7
|
+
class Circle
|
8
|
+
using AIXM::Refinements
|
9
|
+
|
10
|
+
attr_reader :center_xy, :radius
|
11
|
+
|
12
|
+
def initialize(center_xy:, radius:)
|
13
|
+
fail(ArgumentError, "invalid center xy") unless center_xy.is_a? AIXM::XY
|
14
|
+
@center_xy, @radius = center_xy, radius
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Digest to identify the payload
|
19
|
+
def to_digest
|
20
|
+
[center_xy.lat, center_xy.long, radius].to_digest
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Render AIXM
|
25
|
+
#
|
26
|
+
# Extensions:
|
27
|
+
# * +:OFM+ - Open Flightmaps
|
28
|
+
def to_xml(*extensions)
|
29
|
+
format = extensions >> :OFM ? :OFM : :AIXM
|
30
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
31
|
+
builder.Avx do |avx|
|
32
|
+
avx.codeType('CWA')
|
33
|
+
avx.geoLat(north_xy.lat(format))
|
34
|
+
avx.geoLong(north_xy.long(format))
|
35
|
+
avx.codeDatum('WGE')
|
36
|
+
avx.geoLatArc(center_xy.lat(format))
|
37
|
+
avx.geoLongArc(center_xy.long(format))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
##
|
44
|
+
# Coordinates of the point which is both strictly north of the center
|
45
|
+
# and on the circumference of the circle
|
46
|
+
def north_xy
|
47
|
+
AIXM.xy(
|
48
|
+
lat: center_xy.lat + radius.to_f / 6371 * 180 / Math::PI,
|
49
|
+
long: center_xy.long
|
50
|
+
)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module AIXM
|
2
|
+
module Component
|
3
|
+
class Geometry
|
4
|
+
|
5
|
+
##
|
6
|
+
# Points are defined by +xy+ coordinates.
|
7
|
+
class Point
|
8
|
+
using AIXM::Refinements
|
9
|
+
|
10
|
+
attr_reader :xy
|
11
|
+
|
12
|
+
def initialize(xy:)
|
13
|
+
fail(ArgumentError, "invalid xy") unless xy.is_a? AIXM::XY
|
14
|
+
@xy = xy
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Digest to identify the payload
|
19
|
+
def to_digest
|
20
|
+
[xy.lat, xy.long].to_digest
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Render AIXM
|
25
|
+
#
|
26
|
+
# Extensions:
|
27
|
+
# * +:OFM+ - Open Flightmaps
|
28
|
+
def to_xml(*extensions)
|
29
|
+
format = extensions >> :OFM ? :OFM : :AIXM
|
30
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
31
|
+
builder.Avx do |avx|
|
32
|
+
avx.codeType('GRC')
|
33
|
+
avx.geoLat(xy.lat(format))
|
34
|
+
avx.geoLong(xy.long(format))
|
35
|
+
avx.codeDatum('WGE')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module AIXM
|
2
|
+
module Component
|
3
|
+
|
4
|
+
##
|
5
|
+
# Schedules define activity time windows. As of now, only predefined
|
6
|
+
# schedules are imlemented either by use of explicit (e.g. +:continuous+)
|
7
|
+
# or short codes (e.g. +:H24+) as listed by the +CODES+ constant.
|
8
|
+
#
|
9
|
+
# Shortcuts:
|
10
|
+
# * +AIXM::H24+ - continuous 24/7
|
11
|
+
class Schedule < Base
|
12
|
+
using AIXM::Refinements
|
13
|
+
|
14
|
+
CODES = {
|
15
|
+
continuous: :H24,
|
16
|
+
sunrise_to_sunset: :HJ,
|
17
|
+
sunset_to_sunrise: :HN,
|
18
|
+
unspecified: :HX,
|
19
|
+
operational_request: :HO,
|
20
|
+
notam: :NOTAM
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
attr_reader :code
|
24
|
+
|
25
|
+
def initialize(code:)
|
26
|
+
@code = code&.to_sym
|
27
|
+
@code = CODES[code] unless CODES.has_value? code
|
28
|
+
fail(ArgumentError, "code `#{code}' not recognized") unless @code
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Digest to identify the payload
|
33
|
+
def to_digest
|
34
|
+
[code].to_digest
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Render AIXM
|
39
|
+
def to_xml(*extensions)
|
40
|
+
Builder::XmlMarkup.new(indent: 2).codeWorkHr(code.to_s)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -1,18 +1,18 @@
|
|
1
1
|
module AIXM
|
2
|
-
module
|
2
|
+
module Component
|
3
3
|
|
4
4
|
##
|
5
|
-
# Vertical limits
|
6
|
-
#
|
7
|
-
# Normally noted as:
|
5
|
+
# Vertical limits define a 3D airspace vertically. They are normally noted
|
6
|
+
# as follows:
|
8
7
|
#
|
9
8
|
# +upper z+ (or +max_z+ whichever is higher)
|
10
9
|
# ---------
|
11
10
|
# +lower_z+ (or +min_z+ whichever is lower)
|
12
11
|
#
|
13
|
-
#
|
14
|
-
|
15
|
-
|
12
|
+
# Shortcuts:
|
13
|
+
# * +AIXM::GROUND+ - surface (aka: 0ft QFE)
|
14
|
+
# * +AIXM::UNLIMITED+ - no upper limit (aka: FL 999)
|
15
|
+
class VerticalLimits < Base
|
16
16
|
using AIXM::Refinements
|
17
17
|
|
18
18
|
TAGS = { upper: :Upper, lower: :Lower, max: :Max, min: :Mnm }.freeze
|
@@ -20,13 +20,7 @@ module AIXM
|
|
20
20
|
|
21
21
|
attr_reader :upper_z, :lower_z, :max_z, :min_z
|
22
22
|
|
23
|
-
|
24
|
-
# Defines vertical limits +upper_z+ and +lower_z+
|
25
|
-
#
|
26
|
-
# Options:
|
27
|
-
# * +max_z+ - alternative upper limit "whichever is higher"
|
28
|
-
# * +min_z+ - alternative lower limit "whichever is lower"
|
29
|
-
def initialize(upper_z:, lower_z:, max_z: nil, min_z: nil)
|
23
|
+
def initialize(max_z: nil, upper_z:, lower_z:, min_z: nil)
|
30
24
|
fail(ArgumentError, "invalid upper_z") unless upper_z.is_a? AIXM::Z
|
31
25
|
fail(ArgumentError, "invalid lower_z") unless lower_z.is_a? AIXM::Z
|
32
26
|
fail(ArgumentError, "invalid max_z") unless max_z.nil? || max_z.is_a?(AIXM::Z)
|
@@ -55,5 +49,6 @@ module AIXM
|
|
55
49
|
end.target! # see https://github.com/jimweirich/builder/issues/42
|
56
50
|
end
|
57
51
|
end
|
52
|
+
|
58
53
|
end
|
59
54
|
end
|
data/lib/aixm/document.rb
CHANGED
@@ -1,29 +1,32 @@
|
|
1
1
|
module AIXM
|
2
2
|
class Document
|
3
3
|
|
4
|
-
include Enumerable
|
5
|
-
extend Forwardable
|
6
4
|
using AIXM::Refinements
|
7
5
|
|
8
|
-
def_delegators :@result_array, :each, :<<
|
9
|
-
|
10
6
|
attr_reader :created_at, :effective_at
|
7
|
+
attr_accessor :features
|
11
8
|
|
12
9
|
##
|
13
10
|
# Define a AIXM-Snapshot document
|
14
11
|
#
|
15
12
|
# Options:
|
16
|
-
# * +created_at+ - creation date (default: now)
|
17
|
-
# * +effective_at+ - snapshot effective after date (default: now)
|
13
|
+
# * +created_at+ - creation date and time (default: now)
|
14
|
+
# * +effective_at+ - snapshot effective after date and time (default: now)
|
18
15
|
def initialize(created_at: nil, effective_at: nil)
|
19
|
-
@created_at, @effective_at = created_at, effective_at
|
20
|
-
@
|
16
|
+
@created_at, @effective_at = parse_time(created_at), parse_time(effective_at)
|
17
|
+
@features = []
|
21
18
|
end
|
22
19
|
|
23
20
|
##
|
24
|
-
#
|
25
|
-
def
|
26
|
-
|
21
|
+
# Check whether the document is complete (extensions excluded)
|
22
|
+
def complete?
|
23
|
+
features.any? && features.none? { |f| !f.complete? }
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Validate atainst the XSD and return +true+ if no errors were found
|
28
|
+
def valid?
|
29
|
+
errors.none?
|
27
30
|
end
|
28
31
|
|
29
32
|
##
|
@@ -33,12 +36,6 @@ module AIXM
|
|
33
36
|
xsd.validate(Nokogiri::XML(to_xml))
|
34
37
|
end
|
35
38
|
|
36
|
-
##
|
37
|
-
# Check whether the document is valid (extensions excluded)
|
38
|
-
def valid?
|
39
|
-
any? && reduce(true) { |b, f| b && f.valid? } && errors.none?
|
40
|
-
end
|
41
|
-
|
42
39
|
##
|
43
40
|
# Render AIXM
|
44
41
|
#
|
@@ -53,12 +50,26 @@ module AIXM
|
|
53
50
|
created: @created_at&.xmlschema || now,
|
54
51
|
effective: @effective_at&.xmlschema || now
|
55
52
|
}
|
56
|
-
meta[:version] += ' + OFM extensions of version 0.1' if extensions
|
53
|
+
meta[:version] += ' + OFM extensions of version 0.1' if extensions >> :OFM
|
57
54
|
builder = Builder::XmlMarkup.new(indent: 2)
|
58
55
|
builder.instruct!
|
59
56
|
builder.tag!('AIXM-Snapshot', meta) do |aixm_snapshot|
|
60
|
-
aixm_snapshot <<
|
57
|
+
aixm_snapshot << features.map { |f| f.to_xml(*extensions) }.join.indent(2)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def parse_time(value)
|
64
|
+
case value
|
65
|
+
when String then Time.parse(value)
|
66
|
+
when Date then value.to_time
|
67
|
+
when Time then value
|
68
|
+
when nil then nil
|
69
|
+
else fail ArgumentError
|
61
70
|
end
|
71
|
+
rescue ArgumentError
|
72
|
+
raise(ArgumentError, "`#{value}' is not a valid date and time")
|
62
73
|
end
|
63
74
|
|
64
75
|
end
|
@@ -4,38 +4,43 @@ module AIXM
|
|
4
4
|
|
5
5
|
using AIXM::Refinements
|
6
6
|
|
7
|
-
attr_reader :name, :type
|
8
|
-
attr_reader :
|
9
|
-
attr_accessor :geometry, :remarks
|
7
|
+
attr_reader :name, :short_name, :type
|
8
|
+
attr_reader :schedule
|
9
|
+
attr_accessor :geometry, :class_layers, :remarks
|
10
10
|
|
11
11
|
##
|
12
12
|
# Airspace feature
|
13
13
|
#
|
14
14
|
# Options:
|
15
|
-
# * +name+ - name of the airspace (will be converted to uppercase
|
15
|
+
# * +name+ - full name of the airspace (will be converted to uppercase,
|
16
|
+
# e.g. +LF P 81+)
|
17
|
+
# * +short_name+ - short name of the airspace (will be converted to
|
18
|
+
# uppercase, e.g. +LF P 81 CHERBOURG+)
|
16
19
|
# * +type+ - airspace type (e.g. +TMA+ or +P+)
|
17
|
-
def initialize(name:, type:)
|
18
|
-
@
|
19
|
-
@
|
20
|
+
def initialize(name:, short_name: nil, type:)
|
21
|
+
@name, @short_name, @type = name.uptrans, short_name&.uptrans, type
|
22
|
+
@schedule = nil
|
23
|
+
@geometry = AIXM.geometry
|
24
|
+
@class_layers = []
|
20
25
|
end
|
21
26
|
|
22
27
|
##
|
23
|
-
# Assign a +
|
24
|
-
def
|
25
|
-
fail(ArgumentError, "invalid
|
26
|
-
@
|
28
|
+
# Assign a +Schedule+ object or +nil+
|
29
|
+
def schedule=(value)
|
30
|
+
fail(ArgumentError, "invalid schedule") unless value.nil? || value.is_a?(AIXM::Component::Schedule)
|
31
|
+
@schedule = value
|
27
32
|
end
|
28
33
|
|
29
34
|
##
|
30
|
-
# Check whether the airspace is
|
31
|
-
def
|
32
|
-
name && type &&
|
35
|
+
# Check whether the airspace is complete
|
36
|
+
def complete?
|
37
|
+
!!name && !!type && class_layers.any? && geometry.complete?
|
33
38
|
end
|
34
39
|
|
35
40
|
##
|
36
41
|
# Digest to identify the payload
|
37
42
|
def to_digest
|
38
|
-
[name, type,
|
43
|
+
[name, short_name, type, schedule&.to_digest, class_layers.map(&:to_digest), geometry.to_digest, remarks].to_digest
|
39
44
|
end
|
40
45
|
|
41
46
|
##
|
@@ -46,28 +51,54 @@ module AIXM
|
|
46
51
|
def to_xml(*extensions)
|
47
52
|
mid = to_digest
|
48
53
|
builder = Builder::XmlMarkup.new(indent: 2)
|
49
|
-
builder.
|
50
|
-
|
51
|
-
|
52
|
-
aseuid.
|
54
|
+
builder.comment! "Airspace: [#{type}] #{name}"
|
55
|
+
builder.Ase({ xt_classLayersAvail: ((class_layers.count > 1) if extensions >> :OFM) }.compact) do |ase|
|
56
|
+
ase.AseUid({ mid: mid, newEntity: (true if extensions >> :OFM) }.compact) do |aseuid|
|
57
|
+
aseuid.codeType(type.to_s)
|
58
|
+
aseuid.codeId(mid)
|
53
59
|
end
|
54
|
-
ase.
|
55
|
-
ase
|
56
|
-
ase.
|
57
|
-
if
|
58
|
-
ase.
|
59
|
-
|
60
|
+
ase.txtLocalType(short_name.to_s) if short_name && short_name != name
|
61
|
+
ase.txtName(name.to_s)
|
62
|
+
ase << class_layers.first.to_xml(*extensions).indent(2)
|
63
|
+
if schedule
|
64
|
+
ase.Att do |att|
|
65
|
+
att << schedule.to_xml(*extensions).indent(4)
|
66
|
+
end
|
60
67
|
end
|
68
|
+
ase.txtRmk(remarks.to_s) if remarks
|
69
|
+
ase.xt_selAvail(false) if extensions >> :OFM
|
61
70
|
end
|
62
71
|
builder.Abd do |abd|
|
63
72
|
abd.AbdUid do |abduid|
|
64
|
-
abduid.AseUid(
|
65
|
-
aseuid.codeType(type)
|
66
|
-
aseuid.codeId(mid)
|
73
|
+
abduid.AseUid({ mid: mid, newEntity: (true if extensions >> :OFM) }.compact) do |aseuid|
|
74
|
+
aseuid.codeType(type.to_s)
|
75
|
+
aseuid.codeId(mid)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
abd << geometry.to_xml(*extensions).indent(2)
|
79
|
+
end
|
80
|
+
if class_layers.count > 1
|
81
|
+
builder.Adg do |adg|
|
82
|
+
class_layers.each.with_index do |class_layer, index|
|
83
|
+
adg.AdgUid do |adguid|
|
84
|
+
adguid.AseUid(mid: "#{mid}.#{index + 1}") do |aseuid|
|
85
|
+
aseuid.codeType("CLASS")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
adg.AseUidSameExtent(mid: mid)
|
90
|
+
end
|
91
|
+
class_layers.each.with_index do |class_layer, index|
|
92
|
+
builder.Ase do |ase|
|
93
|
+
ase.AseUid(mid: "#{mid}.#{index + 1}") do |aseuid|
|
94
|
+
aseuid.codeType("CLASS")
|
95
|
+
end
|
96
|
+
ase.txtName(name.to_s)
|
97
|
+
ase << class_layers[index].to_xml(*extensions).indent(2)
|
67
98
|
end
|
68
99
|
end
|
69
|
-
abd << geometry.to_xml(extensions).indent(2)
|
70
100
|
end
|
101
|
+
builder.target! # see https://github.com/jimweirich/builder/issues/42
|
71
102
|
end
|
72
103
|
end
|
73
104
|
end
|