aixm 0.3.1 → 0.3.2
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 +18 -6
- data/README.md +2 -0
- data/lib/aixm.rb +3 -0
- data/lib/aixm/component/geometry/circle.rb +6 -6
- data/lib/aixm/component/helipad.rb +19 -12
- data/lib/aixm/component/runway.rb +34 -22
- data/lib/aixm/d.rb +66 -0
- data/lib/aixm/document.rb +2 -14
- data/lib/aixm/feature/airport.rb +1 -0
- data/lib/aixm/feature/obstacle.rb +288 -0
- data/lib/aixm/feature/obstacle_group.rb +104 -0
- data/lib/aixm/refinements.rb +105 -102
- data/lib/aixm/shortcuts.rb +3 -0
- data/lib/aixm/version.rb +1 -1
- data/lib/aixm/xy.rb +5 -4
- data/lib/aixm/z.rb +3 -3
- data/spec/factory.rb +108 -5
- data/spec/lib/aixm/component/geometry/circle_spec.rb +4 -8
- data/spec/lib/aixm/component/geometry_spec.rb +2 -2
- data/spec/lib/aixm/component/helipad_spec.rb +2 -10
- data/spec/lib/aixm/component/runway_spec.rb +4 -16
- data/spec/lib/aixm/d_spec.rb +130 -0
- data/spec/lib/aixm/document_spec.rb +215 -2
- data/spec/lib/aixm/feature/obstacle_group_spec.rb +284 -0
- data/spec/lib/aixm/feature/obstacle_spec.rb +285 -0
- data/spec/lib/aixm/refinements_spec.rb +100 -76
- data/spec/lib/aixm/xy_spec.rb +3 -3
- metadata +11 -2
@@ -0,0 +1,104 @@
|
|
1
|
+
using AIXM::Refinements
|
2
|
+
|
3
|
+
module AIXM
|
4
|
+
class Feature
|
5
|
+
|
6
|
+
# Groups of obstacles which consist of either linked (e.g. power line
|
7
|
+
# towers) or unlinked (e.g. wind turbines) members.
|
8
|
+
#
|
9
|
+
# ===Cheat Sheet in Pseudo Code:
|
10
|
+
# obstacle_group = AIXM.obstacle_group(
|
11
|
+
# source: String or nil # see remarks below
|
12
|
+
# name: String or nil
|
13
|
+
# )
|
14
|
+
# obstacle_group.add_obstacle( # add an obstacle to the group
|
15
|
+
# AIXM.obstacle
|
16
|
+
# )
|
17
|
+
# obstacle_group.add_obstacle( # add an obstacle to the group and link
|
18
|
+
# AIXM.obstacle, # it to the obstacle last added to the group
|
19
|
+
# linked_to: :previous,
|
20
|
+
# link_type: LINK_TYPES
|
21
|
+
# )
|
22
|
+
# obstacle_group.add_obstacle( # add an obstacle to the group and link
|
23
|
+
# AIXM.obstacle, # it to any obstacle already in the group
|
24
|
+
# linked_to: AIXM.obstacle,
|
25
|
+
# link_type: LINK_TYPES
|
26
|
+
# )
|
27
|
+
# obstacle_group.id # UUID v3 calculated from the group payload
|
28
|
+
#
|
29
|
+
# As soon as an obstacle is added to a group, it's extended with new the
|
30
|
+
# following attributes:
|
31
|
+
# * group - the group this object belongs to
|
32
|
+
# * linked_to - obstacle this one is linked to (if any)
|
33
|
+
# * link_type - type of link between the two obstacles (if any)
|
34
|
+
#
|
35
|
+
# The source set on the group is handed down to each of it's obstacles and
|
36
|
+
# will be used there unless the individual obstacle overrides it with a
|
37
|
+
# different source of it's own.
|
38
|
+
#
|
39
|
+
# @see https://github.com/openflightmaps/ofmx/wiki/Obstacle
|
40
|
+
class ObstacleGroup < Feature
|
41
|
+
public_class_method :new
|
42
|
+
|
43
|
+
LINK_TYPES = {
|
44
|
+
CABLE: :cable,
|
45
|
+
SOLID: :solid,
|
46
|
+
OTHER: :other
|
47
|
+
}.freeze
|
48
|
+
|
49
|
+
# @return [String] group name
|
50
|
+
attr_reader :name
|
51
|
+
|
52
|
+
# @return [Array<AIXM::Feature::Obstacle>] obstacles in this group
|
53
|
+
attr_reader :obstacles
|
54
|
+
|
55
|
+
def initialize(source: nil, name: nil)
|
56
|
+
super(source: source)
|
57
|
+
self.name = name
|
58
|
+
@obstacles = []
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [String]
|
62
|
+
def inspect
|
63
|
+
%Q(#<#{self.class} #{@obstacles.count} obstacle(s)>)
|
64
|
+
end
|
65
|
+
|
66
|
+
def name=(value)
|
67
|
+
fail(ArgumentError, "invalid name") unless value.nil? || value.is_a?(String)
|
68
|
+
@name = value&.uptrans
|
69
|
+
end
|
70
|
+
|
71
|
+
# Add an obstacle to the group and optionally link it to another obstacle
|
72
|
+
# from the group.
|
73
|
+
#
|
74
|
+
# @param obstacle [AIXM::Feature::Obstacle] obstacle instance
|
75
|
+
# @param linked_to [Symbol, AIXM::Feature::Obstacle, nil] Either:
|
76
|
+
# * :previous - link to the obstacle last added to the group
|
77
|
+
# * AIXM::Feature::Obstacle - link to this specific obstacle
|
78
|
+
# @param link_type [Symbol, nil] type of link (see {LINK_TYPES})
|
79
|
+
# @return [self]
|
80
|
+
def add_obstacle(obstacle, linked_to: nil, link_type: :other)
|
81
|
+
obstacle.extend AIXM::Feature::Obstacle::Grouped
|
82
|
+
obstacle.send(:group=, self)
|
83
|
+
if linked_to && link_type
|
84
|
+
obstacle.send(:linked_to=, linked_to == :previous ? @obstacles.last : linked_to)
|
85
|
+
obstacle.send(:link_type=, link_type)
|
86
|
+
end
|
87
|
+
@obstacles << obstacle
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
# @return [String] UUID version 3 group identifier
|
92
|
+
def id
|
93
|
+
([name] + @obstacles.map { |o| o.xy.to_s }).to_uuid
|
94
|
+
end
|
95
|
+
alias_method :to_uid, :id # features need "to_uid" for "==" to work
|
96
|
+
|
97
|
+
# @return [String] AIXM or OFMX markup
|
98
|
+
def to_xml
|
99
|
+
@obstacles.map { |o| o.to_xml }.join
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
data/lib/aixm/refinements.rb
CHANGED
@@ -13,13 +13,6 @@ module AIXM
|
|
13
13
|
"Ø" => "Oe"
|
14
14
|
}.freeze
|
15
15
|
|
16
|
-
KM_FACTORS = {
|
17
|
-
km: 1,
|
18
|
-
m: 0.001,
|
19
|
-
nm: 1.852,
|
20
|
-
ft: 0.0003048
|
21
|
-
}.freeze
|
22
|
-
|
23
16
|
# @!method to_digest
|
24
17
|
# Builds a 4 byte hex digest from the Array payload.
|
25
18
|
#
|
@@ -35,6 +28,83 @@ module AIXM
|
|
35
28
|
end
|
36
29
|
end
|
37
30
|
|
31
|
+
# @!method to_uuid
|
32
|
+
# Builds a UUID version 3 digest from the Array payload.
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# ['foo', :bar, nil, [123]].to_uuid
|
36
|
+
# # => "f3920098"
|
37
|
+
#
|
38
|
+
# @note This is a refinement for +Array+
|
39
|
+
# @return [String] UUID version 3
|
40
|
+
refine Array do
|
41
|
+
def to_uuid
|
42
|
+
::Digest::MD5.hexdigest(flatten.map(&:to_s).join('|')).unpack("a8a4a4a4a12").join("-")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
# @!method to_dms(padding=3)
|
48
|
+
# Convert DD angle to DMS with the degrees zero padded to +padding+
|
49
|
+
# length.
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# 43.22164444444445.to_dms(2)
|
53
|
+
# # => "43°12'77.92\""
|
54
|
+
# 43.22164444444445.to_dms
|
55
|
+
# # => "043°12'77.92\""
|
56
|
+
#
|
57
|
+
# @note This is a refinement for +Float+
|
58
|
+
# @param padding [Integer] number of digits for the degree part
|
59
|
+
# @return [String] angle in DMS notation +{-}D°MM'SS.SS"+
|
60
|
+
refine Float do
|
61
|
+
def to_dms(padding=3)
|
62
|
+
degrees = self.abs.floor
|
63
|
+
minutes = ((self.abs - degrees) * 60).floor
|
64
|
+
seconds = (self.abs - degrees - minutes.to_f / 60) * 3600
|
65
|
+
minutes, seconds = minutes + 1, 0 if seconds.round(2) == 60
|
66
|
+
degrees, minutes = degrees + 1, 0 if minutes == 60
|
67
|
+
%Q(%s%0#{padding}d°%02d'%05.2f") % [
|
68
|
+
('-' if self.negative?),
|
69
|
+
self.abs.truncate,
|
70
|
+
minutes.abs.truncate,
|
71
|
+
seconds.abs
|
72
|
+
]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @!method to_rad
|
77
|
+
# Convert an angle from degree to radian.
|
78
|
+
#
|
79
|
+
# @example
|
80
|
+
# 45.to_rad
|
81
|
+
# # => 0.7853981633974483
|
82
|
+
#
|
83
|
+
# @note This is a refinement for +Float+
|
84
|
+
# @return [Float] radian angle
|
85
|
+
refine Float do
|
86
|
+
def to_rad
|
87
|
+
self * Math::PI / 180
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# @!method trim
|
92
|
+
# Convert whole numbers to Integer and leave all other untouched.
|
93
|
+
#
|
94
|
+
# @example
|
95
|
+
# 3.0.trim
|
96
|
+
# # => 3
|
97
|
+
# 3.3.trim
|
98
|
+
# # => 3.3
|
99
|
+
#
|
100
|
+
# @note This is a refinement for +Float+
|
101
|
+
# @return [Integer, Float] converted Float
|
102
|
+
refine Float do
|
103
|
+
def trim
|
104
|
+
(self % 1).zero? ? self.to_i : self
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
38
108
|
# @!method lookup(key_or_value, fallback=omitted=true)
|
39
109
|
# Fetch a value from the hash, but unlike +Hash#fetch+, if +key_or_value+
|
40
110
|
# is no hash key, check whether +key_or_value+ is a hash value and if so
|
@@ -79,32 +149,6 @@ module AIXM
|
|
79
149
|
end
|
80
150
|
end
|
81
151
|
|
82
|
-
# @!method uptrans
|
83
|
-
# Upcase and transliterate to match the reduced character set for
|
84
|
-
# AIXM names and titles.
|
85
|
-
#
|
86
|
-
# See {UPTRANS_MAP} for supported diacryts and {UPTRANS_FILTER} for the
|
87
|
-
# list of allowed characters in the returned value.
|
88
|
-
#
|
89
|
-
# @example
|
90
|
-
# "Nîmes-Alès".uptrans
|
91
|
-
# # => "NIMES-ALES"
|
92
|
-
# "Zürich".uptrans
|
93
|
-
# # => "ZUERICH"
|
94
|
-
#
|
95
|
-
# @note This is a refinement for +String+
|
96
|
-
# @return [String] upcased and transliterated String
|
97
|
-
refine String do
|
98
|
-
def uptrans
|
99
|
-
self.dup.tap do |string|
|
100
|
-
string.upcase!
|
101
|
-
string.gsub!(/(#{UPTRANS_MAP.keys.join('|')})/, UPTRANS_MAP)
|
102
|
-
string.unicode_normalize!(:nfd)
|
103
|
-
string.gsub!(UPTRANS_FILTER, '')
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
152
|
# @!method to_dd
|
109
153
|
# Convert DMS angle to DD or +nil+ if the notation is not recognized.
|
110
154
|
#
|
@@ -129,85 +173,44 @@ module AIXM
|
|
129
173
|
end
|
130
174
|
end
|
131
175
|
|
132
|
-
# @!method
|
133
|
-
#
|
134
|
-
#
|
135
|
-
# @example
|
136
|
-
# 3.0.trim
|
137
|
-
# # => 3
|
138
|
-
# 3.3.trim
|
139
|
-
# # => 3.3
|
140
|
-
#
|
141
|
-
# @note This is a refinement for +Float+
|
142
|
-
# @return [Integer, Float] converted Float
|
143
|
-
refine Float do
|
144
|
-
def trim
|
145
|
-
(self % 1).zero? ? self.to_i : self
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
# @!method to_rad
|
150
|
-
# Convert an angle from degree to radian.
|
176
|
+
# @!method to_time
|
177
|
+
# Parse string to date and time.
|
151
178
|
#
|
152
179
|
# @example
|
153
|
-
#
|
154
|
-
# # =>
|
180
|
+
# '2018-01-01 15:00'.to_time
|
181
|
+
# # => 2018-01-01 15:00:00 +0100
|
155
182
|
#
|
156
|
-
# @note This is a refinement for +
|
157
|
-
# @return [
|
158
|
-
refine
|
159
|
-
def
|
160
|
-
self
|
183
|
+
# @note This is a refinement for +String+
|
184
|
+
# @return [Time] date and time
|
185
|
+
refine String do
|
186
|
+
def to_time
|
187
|
+
Time.parse(self)
|
161
188
|
end
|
162
189
|
end
|
163
190
|
|
164
|
-
# @!method
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
# @example
|
169
|
-
# 43.22164444444445.to_dms(2)
|
170
|
-
# # => "43°12'77.92\""
|
171
|
-
# 43.22164444444445.to_dms
|
172
|
-
# # => "043°12'77.92\""
|
191
|
+
# @!method uptrans
|
192
|
+
# Upcase and transliterate to match the reduced character set for
|
193
|
+
# AIXM names and titles.
|
173
194
|
#
|
174
|
-
#
|
175
|
-
#
|
176
|
-
# @return [String] angle in DMS notation +{-}D°MM'SS.SS"+
|
177
|
-
refine Float do
|
178
|
-
def to_dms(padding=3)
|
179
|
-
degrees = self.abs.floor
|
180
|
-
minutes = ((self.abs - degrees) * 60).floor
|
181
|
-
seconds = (self.abs - degrees - minutes.to_f / 60) * 3600
|
182
|
-
minutes, seconds = minutes + 1, 0 if seconds.round(2) == 60
|
183
|
-
degrees, minutes = degrees + 1, 0 if minutes == 60
|
184
|
-
%Q(%s%0#{padding}d°%02d'%05.2f") % [
|
185
|
-
('-' if self.negative?),
|
186
|
-
self.abs.truncate,
|
187
|
-
minutes.abs.truncate,
|
188
|
-
seconds.abs
|
189
|
-
]
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
# @!method to_km(from:)
|
194
|
-
# Convert a distance from the source unit +from+ to kilometers.
|
195
|
+
# See {UPTRANS_MAP} for supported diacryts and {UPTRANS_FILTER} for the
|
196
|
+
# list of allowed characters in the returned value.
|
195
197
|
#
|
196
198
|
# @example
|
197
|
-
#
|
198
|
-
# # =>
|
199
|
-
#
|
200
|
-
# # =>
|
199
|
+
# "Nîmes-Alès".uptrans
|
200
|
+
# # => "NIMES-ALES"
|
201
|
+
# "Zürich".uptrans
|
202
|
+
# # => "ZUERICH"
|
201
203
|
#
|
202
|
-
# @note This is a refinement for +
|
203
|
-
# @
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
204
|
+
# @note This is a refinement for +String+
|
205
|
+
# @return [String] upcased and transliterated String
|
206
|
+
refine String do
|
207
|
+
def uptrans
|
208
|
+
self.dup.tap do |string|
|
209
|
+
string.upcase!
|
210
|
+
string.gsub!(/(#{UPTRANS_MAP.keys.join('|')})/, UPTRANS_MAP)
|
211
|
+
string.unicode_normalize!(:nfd)
|
212
|
+
string.gsub!(UPTRANS_FILTER, '')
|
213
|
+
end
|
211
214
|
end
|
212
215
|
end
|
213
216
|
end
|
data/lib/aixm/shortcuts.rb
CHANGED
@@ -4,6 +4,7 @@ module AIXM
|
|
4
4
|
document: Document,
|
5
5
|
xy: XY,
|
6
6
|
z: Z,
|
7
|
+
d: D,
|
7
8
|
f: F,
|
8
9
|
organisation: Feature::Organisation,
|
9
10
|
unit: Feature::Unit,
|
@@ -26,6 +27,8 @@ module AIXM
|
|
26
27
|
tacan: Feature::NavigationalAid::TACAN,
|
27
28
|
ndb: Feature::NavigationalAid::NDB,
|
28
29
|
vor: Feature::NavigationalAid::VOR,
|
30
|
+
obstacle: Feature::Obstacle,
|
31
|
+
obstacle_group: Feature::ObstacleGroup,
|
29
32
|
timetable: Component::Timetable
|
30
33
|
}.freeze
|
31
34
|
|
data/lib/aixm/version.rb
CHANGED
data/lib/aixm/xy.rb
CHANGED
@@ -68,19 +68,20 @@ module AIXM
|
|
68
68
|
other.is_a?(self.class) && lat == other.lat && long == other.long
|
69
69
|
end
|
70
70
|
|
71
|
-
# @return [
|
71
|
+
# @return [AIXM::D] distance as calculated by use of the Haversine formula
|
72
72
|
def distance(other)
|
73
73
|
if self == other
|
74
|
-
0
|
74
|
+
AIXM.d(0, :m)
|
75
75
|
else
|
76
|
-
2 * EARTH_RADIUS * Math.asin(
|
76
|
+
value = 2 * EARTH_RADIUS * Math.asin(
|
77
77
|
Math.sqrt(
|
78
78
|
Math.sin((other.lat.to_rad - lat.to_rad) / 2) ** 2 +
|
79
79
|
Math.cos(lat.to_rad) * Math.cos(other.lat.to_rad) *
|
80
80
|
Math.sin((other.long.to_rad - long.to_rad) / 2) ** 2
|
81
81
|
)
|
82
82
|
)
|
83
|
-
|
83
|
+
AIXM.d(value.round, :m)
|
84
|
+
end
|
84
85
|
end
|
85
86
|
|
86
87
|
private
|
data/lib/aixm/z.rb
CHANGED
@@ -5,8 +5,8 @@ module AIXM
|
|
5
5
|
# Height, elevation or altitude
|
6
6
|
#
|
7
7
|
# @example
|
8
|
-
# AIXM.z(1000, :qfe) # height: 1000 ft above ground
|
9
|
-
# AIXM.z(2000, :qnh) # elevation or altitude: 2000 ft above mean sea level
|
8
|
+
# AIXM.z(1000, :qfe) # height (ft): 1000 ft above ground
|
9
|
+
# AIXM.z(2000, :qnh) # elevation or altitude (ft): 2000 ft above mean sea level
|
10
10
|
# AIXM.z(45, :qne) # altitude: flight level 45
|
11
11
|
#
|
12
12
|
# ===Shortcuts:
|
@@ -15,7 +15,7 @@ module AIXM
|
|
15
15
|
class Z
|
16
16
|
CODES = %i(qfe qnh qne).freeze
|
17
17
|
|
18
|
-
# @return [Integer]
|
18
|
+
# @return [Integer] altitude or elevation value
|
19
19
|
attr_reader :alt
|
20
20
|
|
21
21
|
# @return [Symbol] Q code - either +:qfe+ (height in feet), +:qnh+ (altitude in feet or +:qne+ (altitude as flight level)
|
data/spec/factory.rb
CHANGED
@@ -12,6 +12,10 @@ module AIXM
|
|
12
12
|
AIXM.z(1000, :qnh)
|
13
13
|
end
|
14
14
|
|
15
|
+
def d
|
16
|
+
AIXM.d(123, :m)
|
17
|
+
end
|
18
|
+
|
15
19
|
def f
|
16
20
|
AIXM.f(123.35, :mhz)
|
17
21
|
end
|
@@ -69,7 +73,7 @@ module AIXM
|
|
69
73
|
AIXM.geometry.tap do |geometry|
|
70
74
|
geometry << AIXM.circle(
|
71
75
|
center_xy: AIXM.xy(lat: %q(47°35'00"N), long: %q(004°53'00"E)),
|
72
|
-
radius: 10
|
76
|
+
radius: AIXM.d(10, :km)
|
73
77
|
)
|
74
78
|
end
|
75
79
|
end
|
@@ -313,8 +317,8 @@ module AIXM
|
|
313
317
|
|
314
318
|
def runway
|
315
319
|
AIXM.runway(name: '16L/34R').tap do |runway|
|
316
|
-
runway.length = 650
|
317
|
-
runway.width = 80
|
320
|
+
runway.length = AIXM.d(650, :m)
|
321
|
+
runway.width = AIXM.d(80, :m)
|
318
322
|
runway.composition = :graded_earth
|
319
323
|
runway.status = :closed
|
320
324
|
runway.remarks = "Markings eroded"
|
@@ -335,14 +339,110 @@ module AIXM
|
|
335
339
|
AIXM.helipad(name: 'H1').tap do |helipad|
|
336
340
|
helipad.xy = AIXM.xy(lat: %q(43°59'56.94"N), long: %q(004°45'05.56"E))
|
337
341
|
helipad.z = AIXM.z(141, :qnh)
|
338
|
-
helipad.length = 20
|
339
|
-
helipad.width = 20
|
342
|
+
helipad.length = AIXM.d(20, :m)
|
343
|
+
helipad.width = AIXM.d(20, :m)
|
340
344
|
helipad.composition = :grass
|
341
345
|
helipad.status = :other
|
342
346
|
helipad.remarks = "Authorizaton by AD operator required"
|
343
347
|
end
|
344
348
|
end
|
345
349
|
|
350
|
+
# Obstacle
|
351
|
+
|
352
|
+
def obstacle
|
353
|
+
AIXM.obstacle(
|
354
|
+
name: "Eiffel Tower",
|
355
|
+
type: :tower,
|
356
|
+
xy: AIXM.xy(lat: %q(48°51'29.7"N), long: %q(002°17'40.52"E)),
|
357
|
+
radius: AIXM.d(88, :m),
|
358
|
+
z: AIXM.z(1187 , :qnh)
|
359
|
+
).tap do |obstacle|
|
360
|
+
obstacle.lighting = true
|
361
|
+
obstacle.lighting_remarks = "red strobes"
|
362
|
+
obstacle.marking = nil
|
363
|
+
obstacle.marking_remarks = nil
|
364
|
+
obstacle.height = AIXM.d(324, :m)
|
365
|
+
obstacle.xy_accuracy = AIXM.d(2, :m)
|
366
|
+
obstacle.z_accuracy = AIXM.d(1, :m)
|
367
|
+
obstacle.height_accurate = true
|
368
|
+
obstacle.valid_from = Time.parse('2018-01-01 12:00:00 +0100')
|
369
|
+
obstacle.valid_until = Time.parse('2019-01-01 12:00:00 +0100')
|
370
|
+
obstacle.remarks = "Temporary light installations (white strobes, gyro light etc)"
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
def unlinked_obstacle_group
|
375
|
+
AIXM.obstacle_group(
|
376
|
+
name: "Mirmande éoliennes"
|
377
|
+
).tap do |obstacle_group|
|
378
|
+
obstacle_group.add_obstacle(
|
379
|
+
AIXM.obstacle(
|
380
|
+
name: "La Teissonière 1",
|
381
|
+
type: :wind_turbine,
|
382
|
+
xy: AIXM.xy(lat: %q(44°40'30.05"N), long: %q(004°52'21.24"E)),
|
383
|
+
radius: AIXM.d(80, :m),
|
384
|
+
z: AIXM.z(1764, :qnh)
|
385
|
+
).tap do |obstacle|
|
386
|
+
obstacle.height = AIXM.d(80, :m)
|
387
|
+
obstacle.xy_accuracy = AIXM.d(50, :m)
|
388
|
+
obstacle.z_accuracy = AIXM.d(10, :m)
|
389
|
+
obstacle.height_accurate = false
|
390
|
+
end
|
391
|
+
)
|
392
|
+
obstacle_group.add_obstacle(
|
393
|
+
AIXM.obstacle(
|
394
|
+
name: "La Teissonière 2",
|
395
|
+
type: :wind_turbine,
|
396
|
+
xy: AIXM.xy(lat: %q(44°40'46.08"N), long: %q(004°52'25.72"E)),
|
397
|
+
radius: AIXM.d(80, :m),
|
398
|
+
z: AIXM.z(1738 , :qnh)
|
399
|
+
).tap do |obstacle|
|
400
|
+
obstacle.height = AIXM.d(80, :m)
|
401
|
+
obstacle.xy_accuracy = AIXM.d(50, :m)
|
402
|
+
obstacle.z_accuracy = AIXM.d(10, :m)
|
403
|
+
obstacle.height_accurate = false
|
404
|
+
end
|
405
|
+
)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def linked_obstacle_group
|
410
|
+
AIXM.obstacle_group(
|
411
|
+
name: "Droitwich longwave antenna"
|
412
|
+
).tap do |obstacle_group|
|
413
|
+
obstacle_group.add_obstacle(
|
414
|
+
AIXM.obstacle(
|
415
|
+
name: "Droitwich LW north",
|
416
|
+
type: :mast,
|
417
|
+
xy: AIXM.xy(lat: %q(52°17'47.03"N), long: %q(002°06'24.31"W)),
|
418
|
+
radius: AIXM.d(200, :m),
|
419
|
+
z: AIXM.z(848 , :qnh)
|
420
|
+
).tap do |obstacle|
|
421
|
+
obstacle.height = AIXM.d(700, :ft)
|
422
|
+
obstacle.xy_accuracy = AIXM.d(0, :m)
|
423
|
+
obstacle.z_accuracy = AIXM.d(0, :ft)
|
424
|
+
obstacle.height_accurate = true
|
425
|
+
end
|
426
|
+
)
|
427
|
+
obstacle_group.add_obstacle(
|
428
|
+
AIXM.obstacle(
|
429
|
+
name: "Droitwich LW north",
|
430
|
+
type: :mast,
|
431
|
+
xy: AIXM.xy(lat: %q(52°17'40.48"N), long: %q(002°06'20.47"W)),
|
432
|
+
radius: AIXM.d(200, :m),
|
433
|
+
z: AIXM.z(848 , :qnh)
|
434
|
+
).tap do |obstacle|
|
435
|
+
obstacle.height = AIXM.d(700, :ft)
|
436
|
+
obstacle.xy_accuracy = AIXM.d(0, :m)
|
437
|
+
obstacle.z_accuracy = AIXM.d(0, :ft)
|
438
|
+
obstacle.height_accurate = true
|
439
|
+
end,
|
440
|
+
linked_to: :previous,
|
441
|
+
link_type: :cable
|
442
|
+
)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
346
446
|
# Document
|
347
447
|
|
348
448
|
def document
|
@@ -366,6 +466,9 @@ module AIXM
|
|
366
466
|
document.features << vor
|
367
467
|
document.features << vordme
|
368
468
|
document.features << vortac
|
469
|
+
document.features << obstacle
|
470
|
+
document.features << unlinked_obstacle_group
|
471
|
+
document.features << linked_obstacle_group
|
369
472
|
end
|
370
473
|
end
|
371
474
|
|