aixm 0.1.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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +5 -0
  5. data/CHANGELOG.md +14 -0
  6. data/Gemfile +3 -0
  7. data/Guardfile +7 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +100 -0
  10. data/Rakefile +12 -0
  11. data/aixm.gemspec +32 -0
  12. data/lib/aixm.rb +25 -0
  13. data/lib/aixm/constants.rb +6 -0
  14. data/lib/aixm/document.rb +65 -0
  15. data/lib/aixm/feature/airspace.rb +74 -0
  16. data/lib/aixm/geometry.rb +71 -0
  17. data/lib/aixm/horizontal/arc.rb +50 -0
  18. data/lib/aixm/horizontal/border.rb +45 -0
  19. data/lib/aixm/horizontal/circle.rb +53 -0
  20. data/lib/aixm/horizontal/point.rb +39 -0
  21. data/lib/aixm/refinements.rb +61 -0
  22. data/lib/aixm/schemas/4.5/AIXM-DataTypes.xsd +5231 -0
  23. data/lib/aixm/schemas/4.5/AIXM-Features.xsd +10066 -0
  24. data/lib/aixm/schemas/4.5/AIXM-Snapshot.xsd +352 -0
  25. data/lib/aixm/version.rb +3 -0
  26. data/lib/aixm/vertical/limits.rb +59 -0
  27. data/lib/aixm/xy.rb +52 -0
  28. data/lib/aixm/z.rb +39 -0
  29. data/spec/factory.rb +66 -0
  30. data/spec/lib/aixm/document_spec.rb +211 -0
  31. data/spec/lib/aixm/feature/airspace_spec.rb +96 -0
  32. data/spec/lib/aixm/geometry_spec.rb +218 -0
  33. data/spec/lib/aixm/horizontal/arc_spec.rb +69 -0
  34. data/spec/lib/aixm/horizontal/border_spec.rb +47 -0
  35. data/spec/lib/aixm/horizontal/circle_spec.rb +65 -0
  36. data/spec/lib/aixm/horizontal/point_spec.rb +42 -0
  37. data/spec/lib/aixm/refinements_spec.rb +180 -0
  38. data/spec/lib/aixm/version_spec.rb +7 -0
  39. data/spec/lib/aixm/vertical/limits_spec.rb +78 -0
  40. data/spec/lib/aixm/xy_spec.rb +131 -0
  41. data/spec/lib/aixm/z_spec.rb +59 -0
  42. data/spec/sounds/failure.mp3 +0 -0
  43. data/spec/sounds/success.mp3 +0 -0
  44. data/spec/spec_helper.rb +28 -0
  45. metadata +243 -0
data/lib/aixm/z.rb ADDED
@@ -0,0 +1,39 @@
1
+ module AIXM
2
+
3
+ ##
4
+ # Elevation or altitude
5
+ #
6
+ # The following Q codes are recognized:
7
+ # * QFE - height in feet
8
+ # * QNH - altitude in feet
9
+ # * QNE - altitude as flight level
10
+ class Z
11
+
12
+ CODES = %i(QFE QNH QNE).freeze
13
+
14
+ attr_reader :alt, :code
15
+
16
+ def initialize(alt:, code:)
17
+ fail(ArgumentError, "unrecognized Q code `#{code}'") unless CODES.include? code
18
+ @alt, @code = alt, code
19
+ end
20
+
21
+ def ==(other)
22
+ other.is_a?(Z) && alt == other.alt && code == other.code
23
+ end
24
+
25
+ def ground?
26
+ @code == :QFE && @alt == 0
27
+ end
28
+
29
+ def base
30
+ @code == :QFE ? :ASFC : :AMSL
31
+ end
32
+
33
+ def unit
34
+ @code == :QNE ? :FL : :FT
35
+ end
36
+
37
+ end
38
+
39
+ end
data/spec/factory.rb ADDED
@@ -0,0 +1,66 @@
1
+ module AIXM
2
+ class Factory
3
+ class << self
4
+
5
+ def vertical_limits
6
+ AIXM::Vertical::Limits.new(
7
+ upper_z: AIXM::Z.new(alt: 65, code: :QNE),
8
+ lower_z: AIXM::Z.new(alt: 45, code: :QNE),
9
+ max_z: AIXM::Z.new(alt: 6000, code: :QNH),
10
+ min_z: AIXM::Z.new(alt: 3000, code: :QFE)
11
+ )
12
+ end
13
+
14
+ def polygon_geometry
15
+ AIXM::Geometry.new.tap do |geometry|
16
+ geometry << AIXM::Horizontal::Arc.new(
17
+ xy: AIXM::XY.new(lat: %q(47°51'33"N), long: %q(007°33'36"E)),
18
+ center_xy: AIXM::XY.new(lat: %q(47°54'15"N), long: %q(007°33'48"E)),
19
+ clockwise: true
20
+ )
21
+ geometry << AIXM::Horizontal::Border.new(
22
+ xy: AIXM::XY.new(lat: %q(47°56'37"N), long: %q(007°35'45"E)),
23
+ name: 'FRANCE_GERMANY'
24
+ )
25
+ geometry << AIXM::Horizontal::Point.new(
26
+ xy: AIXM::XY.new(lat: %q(47°51'33"N), long: %q(007°33'36"E))
27
+ )
28
+ end
29
+ end
30
+
31
+ def circle_geometry
32
+ AIXM::Geometry.new.tap do |geometry|
33
+ geometry << AIXM::Horizontal::Circle.new(
34
+ center_xy: AIXM::XY.new(lat: %q(47°35'00"N), long: %q(004°53'00"E)),
35
+ radius: 10
36
+ )
37
+ end
38
+ end
39
+
40
+ def polygon_airspace
41
+ AIXM::Feature::Airspace.new(name: 'POLYGON AIRSPACE', type: 'D').tap do |airspace|
42
+ airspace.vertical_limits = vertical_limits
43
+ airspace.geometry = polygon_geometry
44
+ airspace.remarks = 'polygon airspace'
45
+ end
46
+ end
47
+
48
+ def circle_airspace
49
+ AIXM::Feature::Airspace.new(name: 'CIRCLE AIRSPACE', type: 'D').tap do |airspace|
50
+ airspace.vertical_limits = vertical_limits
51
+ airspace.geometry = circle_geometry
52
+ airspace.remarks = 'circle airspace'
53
+ end
54
+ end
55
+
56
+ def document
57
+ time = Time.parse('2018-01-18 12:00:00 +0100')
58
+ AIXM::Document.new(created_at: time, effective_at: time).tap do |document|
59
+ document << AIXM::Factory.polygon_airspace
60
+ document << AIXM::Factory.circle_airspace
61
+ end
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,211 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe AIXM::Document do
4
+ context "incomplete" do
5
+ subject do
6
+ AIXM::Document.new
7
+ end
8
+
9
+ it "must fail validation" do
10
+ subject.wont_be :valid?
11
+ end
12
+ end
13
+
14
+ context "complete" do
15
+ subject do
16
+ AIXM::Factory.document
17
+ end
18
+
19
+ it "won't have errors" do
20
+ subject.errors.must_equal []
21
+ end
22
+
23
+ it "must pass validation" do
24
+ subject.must_be :valid?
25
+ end
26
+
27
+ it "must build correct XML without extensions" do
28
+ subject.to_xml.must_equal <<~END
29
+ <?xml version="1.0" encoding="UTF-8"?>
30
+ <AIXM-Snapshot xmlns:xsi="http://www.aixm.aero/schema/4.5/AIXM-Snapshot.xsd" version="4.5" origin="AIXM 0.1.0 Ruby gem" created="2018-01-18T12:00:00+01:00" effective="2018-01-18T12:00:00+01:00">
31
+ <Ase>
32
+ <AseUid mid="7F466CA0">
33
+ <codeType>D</codeType>
34
+ <codeId>7F466CA0</codeId>
35
+ </AseUid>
36
+ <txtName>POLYGON AIRSPACE</txtName>
37
+ <codeDistVerUpper>STD</codeDistVerUpper>
38
+ <valDistVerUpper>65</valDistVerUpper>
39
+ <uomDistVerUpper>FL</uomDistVerUpper>
40
+ <codeDistVerLower>STD</codeDistVerLower>
41
+ <valDistVerLower>45</valDistVerLower>
42
+ <uomDistVerLower>FL</uomDistVerLower>
43
+ <codeDistVerMax>ALT</codeDistVerMax>
44
+ <valDistVerMax>6000</valDistVerMax>
45
+ <uomDistVerMax>FT</uomDistVerMax>
46
+ <codeDistVerMnm>HEI</codeDistVerMnm>
47
+ <valDistVerMnm>3000</valDistVerMnm>
48
+ <uomDistVerMnm>FT</uomDistVerMnm>
49
+ <txtRmk>polygon airspace</txtRmk>
50
+ </Ase>
51
+ <Abd>
52
+ <AbdUid>
53
+ <AseUid mid="7F466CA0">
54
+ <codeType>D</codeType>
55
+ <codeId>7F466CA0</codeId>
56
+ </AseUid>
57
+ </AbdUid>
58
+ <Avx>
59
+ <codeType>CWA</codeType>
60
+ <geoLat>475133.00N</geoLat>
61
+ <geoLong>0073336.00E</geoLong>
62
+ <codeDatum>WGE</codeDatum>
63
+ <geoLatArc>475415.00N</geoLatArc>
64
+ <geoLongArc>0073348.00E</geoLongArc>
65
+ </Avx>
66
+ <Avx>
67
+ <codeType>FNT</codeType>
68
+ <geoLat>475637.00N</geoLat>
69
+ <geoLong>0073545.00E</geoLong>
70
+ <codeDatum>WGE</codeDatum>
71
+ </Avx>
72
+ <Avx>
73
+ <codeType>GRC</codeType>
74
+ <geoLat>475133.00N</geoLat>
75
+ <geoLong>0073336.00E</geoLong>
76
+ <codeDatum>WGE</codeDatum>
77
+ </Avx>
78
+ </Abd>
79
+ <Ase>
80
+ <AseUid mid="E1115E8A">
81
+ <codeType>D</codeType>
82
+ <codeId>E1115E8A</codeId>
83
+ </AseUid>
84
+ <txtName>CIRCLE AIRSPACE</txtName>
85
+ <codeDistVerUpper>STD</codeDistVerUpper>
86
+ <valDistVerUpper>65</valDistVerUpper>
87
+ <uomDistVerUpper>FL</uomDistVerUpper>
88
+ <codeDistVerLower>STD</codeDistVerLower>
89
+ <valDistVerLower>45</valDistVerLower>
90
+ <uomDistVerLower>FL</uomDistVerLower>
91
+ <codeDistVerMax>ALT</codeDistVerMax>
92
+ <valDistVerMax>6000</valDistVerMax>
93
+ <uomDistVerMax>FT</uomDistVerMax>
94
+ <codeDistVerMnm>HEI</codeDistVerMnm>
95
+ <valDistVerMnm>3000</valDistVerMnm>
96
+ <uomDistVerMnm>FT</uomDistVerMnm>
97
+ <txtRmk>circle airspace</txtRmk>
98
+ </Ase>
99
+ <Abd>
100
+ <AbdUid>
101
+ <AseUid mid="E1115E8A">
102
+ <codeType>D</codeType>
103
+ <codeId>E1115E8A</codeId>
104
+ </AseUid>
105
+ </AbdUid>
106
+ <Avx>
107
+ <codeType>CWA</codeType>
108
+ <geoLat>474023.76N</geoLat>
109
+ <geoLong>0045300.00E</geoLong>
110
+ <codeDatum>WGE</codeDatum>
111
+ <geoLatArc>473500.00N</geoLatArc>
112
+ <geoLongArc>0045300.00E</geoLongArc>
113
+ </Avx>
114
+ </Abd>
115
+ </AIXM-Snapshot>
116
+ END
117
+ end
118
+
119
+ it "must build correct XML with OFM extensions" do
120
+ subject.to_xml(:OFM).must_equal <<~END
121
+ <?xml version="1.0" encoding="UTF-8"?>
122
+ <AIXM-Snapshot xmlns:xsi="http://www.aixm.aero/schema/4.5/AIXM-Snapshot.xsd" version="4.5 + OFM extensions of version 0.1" origin="AIXM 0.1.0 Ruby gem" created="2018-01-18T12:00:00+01:00" effective="2018-01-18T12:00:00+01:00">
123
+ <Ase>
124
+ <AseUid mid="7F466CA0">
125
+ <codeType>D</codeType>
126
+ <codeId>7F466CA0</codeId>
127
+ </AseUid>
128
+ <txtName>POLYGON AIRSPACE</txtName>
129
+ <codeDistVerUpper>STD</codeDistVerUpper>
130
+ <valDistVerUpper>65</valDistVerUpper>
131
+ <uomDistVerUpper>FL</uomDistVerUpper>
132
+ <codeDistVerLower>STD</codeDistVerLower>
133
+ <valDistVerLower>45</valDistVerLower>
134
+ <uomDistVerLower>FL</uomDistVerLower>
135
+ <codeDistVerMax>ALT</codeDistVerMax>
136
+ <valDistVerMax>6000</valDistVerMax>
137
+ <uomDistVerMax>FT</uomDistVerMax>
138
+ <codeDistVerMnm>HEI</codeDistVerMnm>
139
+ <valDistVerMnm>3000</valDistVerMnm>
140
+ <uomDistVerMnm>FT</uomDistVerMnm>
141
+ <txtRmk>polygon airspace</txtRmk>
142
+ </Ase>
143
+ <Abd>
144
+ <AbdUid>
145
+ <AseUid mid="7F466CA0">
146
+ <codeType>D</codeType>
147
+ <codeId>7F466CA0</codeId>
148
+ </AseUid>
149
+ </AbdUid>
150
+ <Avx>
151
+ <codeType>CWA</codeType>
152
+ <geoLat>475133.00N</geoLat>
153
+ <geoLong>0073336.00E</geoLong>
154
+ <codeDatum>WGE</codeDatum>
155
+ <geoLatArc>475415.00N</geoLatArc>
156
+ <geoLongArc>0073348.00E</geoLongArc>
157
+ </Avx>
158
+ <Avx>
159
+ <codeType>FNT</codeType>
160
+ <geoLat>475637.00N</geoLat>
161
+ <geoLong>0073545.00E</geoLong>
162
+ <codeDatum>WGE</codeDatum>
163
+ </Avx>
164
+ <Avx>
165
+ <codeType>GRC</codeType>
166
+ <geoLat>475133.00N</geoLat>
167
+ <geoLong>0073336.00E</geoLong>
168
+ <codeDatum>WGE</codeDatum>
169
+ </Avx>
170
+ </Abd>
171
+ <Ase>
172
+ <AseUid mid="E1115E8A">
173
+ <codeType>D</codeType>
174
+ <codeId>E1115E8A</codeId>
175
+ </AseUid>
176
+ <txtName>CIRCLE AIRSPACE</txtName>
177
+ <codeDistVerUpper>STD</codeDistVerUpper>
178
+ <valDistVerUpper>65</valDistVerUpper>
179
+ <uomDistVerUpper>FL</uomDistVerUpper>
180
+ <codeDistVerLower>STD</codeDistVerLower>
181
+ <valDistVerLower>45</valDistVerLower>
182
+ <uomDistVerLower>FL</uomDistVerLower>
183
+ <codeDistVerMax>ALT</codeDistVerMax>
184
+ <valDistVerMax>6000</valDistVerMax>
185
+ <uomDistVerMax>FT</uomDistVerMax>
186
+ <codeDistVerMnm>HEI</codeDistVerMnm>
187
+ <valDistVerMnm>3000</valDistVerMnm>
188
+ <uomDistVerMnm>FT</uomDistVerMnm>
189
+ <txtRmk>circle airspace</txtRmk>
190
+ </Ase>
191
+ <Abd>
192
+ <AbdUid>
193
+ <AseUid mid="E1115E8A">
194
+ <codeType>D</codeType>
195
+ <codeId>E1115E8A</codeId>
196
+ </AseUid>
197
+ </AbdUid>
198
+ <Avx>
199
+ <codeType>CWA</codeType>
200
+ <geoLat>474023.76N</geoLat>
201
+ <geoLong>0045300.00E</geoLong>
202
+ <codeDatum>WGE</codeDatum>
203
+ <geoLatArc>473500.00N</geoLatArc>
204
+ <geoLongArc>0045300.00E</geoLongArc>
205
+ </Avx>
206
+ </Abd>
207
+ </AIXM-Snapshot>
208
+ END
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,96 @@
1
+ require_relative '../../../spec_helper'
2
+
3
+ describe AIXM::Feature::Airspace do
4
+ context "incomplete" do
5
+ subject do
6
+ AIXM::Feature::Airspace.new(name: 'foobar', type: 'D')
7
+ end
8
+
9
+ describe :valid? do
10
+ it "must fail validation" do
11
+ subject.wont_be :valid?
12
+ end
13
+ end
14
+
15
+ describe :vertical_limits= do
16
+ it "won't accept invalid vertical limits" do
17
+ -> { subject.vertical_limits=0 }.must_raise ArgumentError
18
+ end
19
+ end
20
+ end
21
+
22
+ context "complete" do
23
+ subject do
24
+ AIXM::Factory.polygon_airspace
25
+ end
26
+
27
+ describe :valid? do
28
+ it "must pass validation" do
29
+ subject.must_be :valid?
30
+ end
31
+ end
32
+
33
+ describe :to_digest do
34
+ it "must return digest of payload" do
35
+ subject.to_digest.must_equal '7F466CA0'
36
+ end
37
+ end
38
+
39
+ describe :to_xml do
40
+ it "must build correct XML with OFM extensions" do
41
+ subject.to_xml(:OFM).must_equal <<~END
42
+ <Ase xt_classLayersAvail="false">
43
+ <AseUid mid="7F466CA0" newEntity="true">
44
+ <codeType>D</codeType>
45
+ <codeId>7F466CA0</codeId>
46
+ </AseUid>
47
+ <txtName>POLYGON AIRSPACE</txtName>
48
+ <codeDistVerUpper>STD</codeDistVerUpper>
49
+ <valDistVerUpper>65</valDistVerUpper>
50
+ <uomDistVerUpper>FL</uomDistVerUpper>
51
+ <codeDistVerLower>STD</codeDistVerLower>
52
+ <valDistVerLower>45</valDistVerLower>
53
+ <uomDistVerLower>FL</uomDistVerLower>
54
+ <codeDistVerMax>ALT</codeDistVerMax>
55
+ <valDistVerMax>6000</valDistVerMax>
56
+ <uomDistVerMax>FT</uomDistVerMax>
57
+ <codeDistVerMnm>HEI</codeDistVerMnm>
58
+ <valDistVerMnm>3000</valDistVerMnm>
59
+ <uomDistVerMnm>FT</uomDistVerMnm>
60
+ <txtRmk>polygon airspace</txtRmk>
61
+ <xt_txtRmk>polygon airspace</xt_txtRmk>
62
+ <xt_selAvail>false</xt_selAvail>
63
+ </Ase>
64
+ <Abd>
65
+ <AbdUid>
66
+ <AseUid mid="7F466CA0" newEntity="true">
67
+ <codeType>D</codeType>
68
+ <codeId>7F466CA0</codeId>
69
+ </AseUid>
70
+ </AbdUid>
71
+ <Avx>
72
+ <codeType>CWA</codeType>
73
+ <geoLat>475133.00N</geoLat>
74
+ <geoLong>0073336.00E</geoLong>
75
+ <codeDatum>WGE</codeDatum>
76
+ <geoLatArc>475415.00N</geoLatArc>
77
+ <geoLongArc>0073348.00E</geoLongArc>
78
+ </Avx>
79
+ <Avx>
80
+ <codeType>FNT</codeType>
81
+ <geoLat>475637.00N</geoLat>
82
+ <geoLong>0073545.00E</geoLong>
83
+ <codeDatum>WGE</codeDatum>
84
+ </Avx>
85
+ <Avx>
86
+ <codeType>GRC</codeType>
87
+ <geoLat>475133.00N</geoLat>
88
+ <geoLong>0073336.00E</geoLong>
89
+ <codeDatum>WGE</codeDatum>
90
+ </Avx>
91
+ </Abd>
92
+ END
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,218 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe AIXM::Geometry do
4
+
5
+ context "singularity" do
6
+ subject do
7
+ AIXM::Geometry.new
8
+ end
9
+
10
+ it "must fail validation" do
11
+ subject.wont_be :circle?
12
+ subject.wont_be :closed_shape?
13
+ subject.wont_be :valid?
14
+ end
15
+ end
16
+
17
+ context "point" do
18
+ subject do
19
+ AIXM::Geometry.new.tap do |geometry|
20
+ geometry << AIXM::Horizontal::Point.new(xy: AIXM::XY.new(lat: 11, long: 22))
21
+ end
22
+ end
23
+
24
+ it "must fail validation" do
25
+ subject.wont_be :circle?
26
+ subject.wont_be :closed_shape?
27
+ subject.wont_be :valid?
28
+ end
29
+ end
30
+
31
+ context "line" do
32
+ subject do
33
+ AIXM::Geometry.new.tap do |geometry|
34
+ geometry << AIXM::Horizontal::Point.new(xy: AIXM::XY.new(lat: 11, long: 22))
35
+ geometry << AIXM::Horizontal::Point.new(xy: AIXM::XY.new(lat: 22, long: 33))
36
+ end
37
+ end
38
+
39
+ it "must fail validation" do
40
+ subject.wont_be :circle?
41
+ subject.wont_be :closed_shape?
42
+ subject.wont_be :valid?
43
+ end
44
+ end
45
+
46
+ context "polygon" do
47
+ subject do
48
+ AIXM::Geometry.new.tap do |geometry|
49
+ geometry << AIXM::Horizontal::Point.new(xy: AIXM::XY.new(lat: 11, long: 22))
50
+ geometry << AIXM::Horizontal::Point.new(xy: AIXM::XY.new(lat: 22, long: 33))
51
+ end
52
+ end
53
+
54
+ it "must recognize unclosed" do
55
+ subject.wont_be :circle?
56
+ subject.wont_be :closed_shape?
57
+ subject.wont_be :valid?
58
+ end
59
+
60
+ it "must recognize closed" do
61
+ subject << AIXM::Horizontal::Point.new(xy: AIXM::XY.new(lat: 11, long: 22))
62
+ subject.wont_be :circle?
63
+ subject.must_be :closed_shape?
64
+ subject.must_be :valid?
65
+ end
66
+
67
+ it "must return elements" do
68
+ subject.horizontals.count.must_equal 2
69
+ end
70
+
71
+ it "must return digest of payload" do
72
+ subject.to_digest.must_equal '281EE1FA'
73
+ end
74
+
75
+ it "must build valid XML" do
76
+ subject.to_xml.must_equal <<~END
77
+ <Avx>
78
+ <codeType>GRC</codeType>
79
+ <geoLat>110000.00N</geoLat>
80
+ <geoLong>0220000.00E</geoLong>
81
+ <codeDatum>WGE</codeDatum>
82
+ </Avx>
83
+ <Avx>
84
+ <codeType>GRC</codeType>
85
+ <geoLat>220000.00N</geoLat>
86
+ <geoLong>0330000.00E</geoLong>
87
+ <codeDatum>WGE</codeDatum>
88
+ </Avx>
89
+ END
90
+ end
91
+ end
92
+
93
+ context "arc" do
94
+ subject do
95
+ AIXM::Geometry.new.tap do |geometry|
96
+ geometry << AIXM::Horizontal::Arc.new(xy: AIXM::XY.new(lat: 11, long: 22), center_xy: AIXM::XY.new(lat: 10, long: 20), clockwise: true)
97
+ geometry << AIXM::Horizontal::Point.new(xy: AIXM::XY.new(lat: 22, long: 33))
98
+ end
99
+ end
100
+
101
+ it "must recognize unclosed" do
102
+ subject.wont_be :circle?
103
+ subject.wont_be :closed_shape?
104
+ subject.wont_be :valid?
105
+ end
106
+
107
+ it "must recognize closed" do
108
+ subject << AIXM::Horizontal::Point.new(xy: AIXM::XY.new(lat: 11, long: 22))
109
+ subject.wont_be :circle?
110
+ subject.must_be :closed_shape?
111
+ subject.must_be :valid?
112
+ end
113
+
114
+ it "must build valid XML" do
115
+ subject.to_xml.must_equal <<~END
116
+ <Avx>
117
+ <codeType>CWA</codeType>
118
+ <geoLat>110000.00N</geoLat>
119
+ <geoLong>0220000.00E</geoLong>
120
+ <codeDatum>WGE</codeDatum>
121
+ <geoLatArc>100000.00N</geoLatArc>
122
+ <geoLongArc>0200000.00E</geoLongArc>
123
+ </Avx>
124
+ <Avx>
125
+ <codeType>GRC</codeType>
126
+ <geoLat>220000.00N</geoLat>
127
+ <geoLong>0330000.00E</geoLong>
128
+ <codeDatum>WGE</codeDatum>
129
+ </Avx>
130
+ END
131
+ end
132
+
133
+ it "must return digest of payload" do
134
+ subject.to_digest.must_equal '8DC81708'
135
+ end
136
+ end
137
+
138
+ context "border" do
139
+ subject do
140
+ AIXM::Geometry.new.tap do |geometry|
141
+ geometry << AIXM::Horizontal::Border.new(xy: AIXM::XY.new(lat: 11, long: 22), name: 'foobar')
142
+ geometry << AIXM::Horizontal::Point.new(xy: AIXM::XY.new(lat: 22, long: 33))
143
+ end
144
+ end
145
+
146
+ it "must recognize unclosed" do
147
+ subject.wont_be :circle?
148
+ subject.wont_be :closed_shape?
149
+ subject.wont_be :valid?
150
+ end
151
+
152
+ it "must recognize closed" do
153
+ subject << AIXM::Horizontal::Point.new(xy: AIXM::XY.new(lat: 11, long: 22))
154
+ subject.wont_be :circle?
155
+ subject.must_be :closed_shape?
156
+ subject.must_be :valid?
157
+ end
158
+
159
+ it "must build valid XML" do
160
+ subject.to_xml.must_equal <<~END
161
+ <Avx>
162
+ <codeType>FNT</codeType>
163
+ <geoLat>110000.00N</geoLat>
164
+ <geoLong>0220000.00E</geoLong>
165
+ <codeDatum>WGE</codeDatum>
166
+ </Avx>
167
+ <Avx>
168
+ <codeType>GRC</codeType>
169
+ <geoLat>220000.00N</geoLat>
170
+ <geoLong>0330000.00E</geoLong>
171
+ <codeDatum>WGE</codeDatum>
172
+ </Avx>
173
+ END
174
+ end
175
+
176
+ it "must return digest of payload" do
177
+ subject.to_digest.must_equal 'F208007C'
178
+ end
179
+ end
180
+
181
+ context "circle" do
182
+ subject do
183
+ AIXM::Geometry.new.tap do |geometry|
184
+ geometry << AIXM::Horizontal::Circle.new(center_xy: AIXM::XY.new(lat: 11, long: 22), radius: 10)
185
+ end
186
+ end
187
+
188
+ it "must pass validation" do
189
+ subject.must_be :circle?
190
+ subject.wont_be :closed_shape?
191
+ subject.must_be :valid?
192
+ end
193
+
194
+ it "must fail validation when additional elements are present" do
195
+ subject << AIXM::Horizontal::Point.new(xy: AIXM::XY.new(lat: 11, long: 22))
196
+ subject.wont_be :circle?
197
+ subject.wont_be :closed_shape?
198
+ subject.wont_be :valid?
199
+ end
200
+
201
+ it "must build valid XML" do
202
+ subject.to_xml.must_equal <<~END
203
+ <Avx>
204
+ <codeType>CWA</codeType>
205
+ <geoLat>110523.76N</geoLat>
206
+ <geoLong>0220000.00E</geoLong>
207
+ <codeDatum>WGE</codeDatum>
208
+ <geoLatArc>110000.00N</geoLatArc>
209
+ <geoLongArc>0220000.00E</geoLongArc>
210
+ </Avx>
211
+ END
212
+ end
213
+
214
+ it "must return digest of payload" do
215
+ subject.to_digest.must_equal 'CCBB63FD'
216
+ end
217
+ end
218
+ end