aixm 0.3.8 → 0.3.10
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
- checksums.yaml.gz.sig +1 -0
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +33 -3
- data/README.md +166 -56
- data/exe/ckmid +14 -0
- data/exe/mkmid +14 -0
- data/lib/aixm.rb +16 -6
- data/lib/aixm/association.rb +369 -0
- data/lib/aixm/classes.rb +43 -0
- data/lib/aixm/component/fato.rb +45 -53
- data/lib/aixm/component/frequency.rb +11 -12
- data/lib/aixm/component/geometry.rb +36 -38
- data/lib/aixm/component/geometry/arc.rb +2 -2
- data/lib/aixm/component/geometry/border.rb +6 -3
- data/lib/aixm/component/geometry/circle.rb +8 -2
- data/lib/aixm/component/geometry/point.rb +8 -2
- data/lib/aixm/component/helipad.rb +30 -38
- data/lib/aixm/component/layer.rb +28 -19
- data/lib/aixm/component/lighting.rb +12 -13
- data/lib/aixm/component/runway.rb +44 -48
- data/lib/aixm/{feature → component}/service.rb +37 -36
- data/lib/aixm/component/surface.rb +3 -3
- data/lib/aixm/component/timetable.rb +2 -2
- data/lib/aixm/component/{vertical_limits.rb → vertical_limit.rb} +12 -6
- data/lib/aixm/config.rb +2 -1
- data/lib/aixm/document.rb +27 -50
- data/lib/aixm/executables.rb +85 -0
- data/lib/aixm/feature.rb +13 -3
- data/lib/aixm/feature/address.rb +12 -13
- data/lib/aixm/feature/airport.rb +103 -128
- data/lib/aixm/feature/airspace.rb +44 -17
- data/lib/aixm/feature/navigational_aid.rb +7 -9
- data/lib/aixm/feature/navigational_aid/designated_point.rb +13 -15
- data/lib/aixm/feature/navigational_aid/dme.rb +11 -12
- data/lib/aixm/feature/navigational_aid/marker.rb +7 -3
- data/lib/aixm/feature/navigational_aid/ndb.rb +7 -3
- data/lib/aixm/feature/navigational_aid/tacan.rb +7 -3
- data/lib/aixm/feature/navigational_aid/vor.rb +23 -15
- data/lib/aixm/feature/obstacle.rb +29 -43
- data/lib/aixm/feature/obstacle_group.rb +37 -34
- data/lib/aixm/feature/organisation.rb +21 -5
- data/lib/aixm/feature/unit.rb +36 -46
- data/lib/aixm/memoize.rb +89 -0
- data/lib/aixm/object.rb +9 -0
- data/lib/aixm/payload_hash.rb +114 -0
- data/lib/aixm/refinements.rb +29 -76
- data/lib/aixm/shortcuts.rb +5 -42
- data/lib/aixm/version.rb +1 -1
- data/lib/aixm/xy.rb +1 -1
- data/schemas/ofmx/0/OFMX-Features.xsd +152 -20
- data/schemas/ofmx/0/OFMX-Snapshot.xsd +0 -5
- metadata +107 -156
- metadata.gz.sig +2 -0
- data/.github/workflows/test.yml +0 -26
- data/.gitignore +0 -6
- data/.ruby-version +0 -1
- data/.yardopts +0 -3
- data/Guardfile +0 -8
- data/aixm.gemspec +0 -35
- data/gems.rb +0 -3
- data/lib/aixm/component.rb +0 -6
- data/rakefile.rb +0 -36
- data/spec/factory.rb +0 -559
- data/spec/lib/aixm/a_spec.rb +0 -203
- data/spec/lib/aixm/component/fato_spec.rb +0 -267
- data/spec/lib/aixm/component/frequency_spec.rb +0 -74
- data/spec/lib/aixm/component/geometry/arc_spec.rb +0 -73
- data/spec/lib/aixm/component/geometry/border_spec.rb +0 -38
- data/spec/lib/aixm/component/geometry/circle_spec.rb +0 -68
- data/spec/lib/aixm/component/geometry/point_spec.rb +0 -37
- data/spec/lib/aixm/component/geometry_spec.rb +0 -316
- data/spec/lib/aixm/component/helipad_spec.rb +0 -193
- data/spec/lib/aixm/component/layer_spec.rb +0 -135
- data/spec/lib/aixm/component/lighting_spec.rb +0 -94
- data/spec/lib/aixm/component/runway_spec.rb +0 -479
- data/spec/lib/aixm/component/surface_spec.rb +0 -124
- data/spec/lib/aixm/component/timetable_spec.rb +0 -47
- data/spec/lib/aixm/component/vertical_limits_spec.rb +0 -94
- data/spec/lib/aixm/config_spec.rb +0 -41
- data/spec/lib/aixm/d_spec.rb +0 -150
- data/spec/lib/aixm/document_spec.rb +0 -1884
- data/spec/lib/aixm/errors_spec.rb +0 -14
- data/spec/lib/aixm/f_spec.rb +0 -85
- data/spec/lib/aixm/feature/address_spec.rb +0 -60
- data/spec/lib/aixm/feature/airport_spec.rb +0 -776
- data/spec/lib/aixm/feature/airspace_spec.rb +0 -394
- data/spec/lib/aixm/feature/navigational_aid/designated_point_spec.rb +0 -103
- data/spec/lib/aixm/feature/navigational_aid/dme_spec.rb +0 -98
- data/spec/lib/aixm/feature/navigational_aid/marker_spec.rb +0 -85
- data/spec/lib/aixm/feature/navigational_aid/ndb_spec.rb +0 -95
- data/spec/lib/aixm/feature/navigational_aid/tacan_spec.rb +0 -94
- data/spec/lib/aixm/feature/navigational_aid/vor_spec.rb +0 -251
- data/spec/lib/aixm/feature/navigational_aid_spec.rb +0 -52
- data/spec/lib/aixm/feature/obstacle_group_spec.rb +0 -330
- data/spec/lib/aixm/feature/obstacle_spec.rb +0 -284
- data/spec/lib/aixm/feature/organisation_spec.rb +0 -83
- data/spec/lib/aixm/feature/service_spec.rb +0 -59
- data/spec/lib/aixm/feature/unit_spec.rb +0 -238
- data/spec/lib/aixm/feature_spec.rb +0 -38
- data/spec/lib/aixm/p_spec.rb +0 -189
- data/spec/lib/aixm/refinements_spec.rb +0 -430
- data/spec/lib/aixm/version_spec.rb +0 -7
- data/spec/lib/aixm/w_spec.rb +0 -150
- data/spec/lib/aixm/xy_spec.rb +0 -180
- data/spec/lib/aixm/z_spec.rb +0 -94
- data/spec/macros/marking.rb +0 -12
- data/spec/macros/organisation.rb +0 -11
- data/spec/macros/remarks.rb +0 -12
- data/spec/macros/timetable.rb +0 -11
- data/spec/macros/xy.rb +0 -11
- data/spec/macros/z_qnh.rb +0 -11
- data/spec/sounds/failure.mp3 +0 -0
- data/spec/sounds/success.mp3 +0 -0
- data/spec/spec_helper.rb +0 -62
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
using AIXM::Refinements
|
|
2
|
+
|
|
3
|
+
module AIXM
|
|
4
|
+
|
|
5
|
+
# Associate features and components with a minimalistic implementation of
|
|
6
|
+
# +has_many+, +has_one+ and +belongs_to+ associations.
|
|
7
|
+
#
|
|
8
|
+
# When adding or assigning an object on the associator (where the +has_many+
|
|
9
|
+
# or +has_one+ declaration is made), the object is verified and must be an
|
|
10
|
+
# instance of the declared class or a superclass thereof.
|
|
11
|
+
#
|
|
12
|
+
# When assigning an object on the associated (where the +belongs_to+
|
|
13
|
+
# declaration is made), the object is not verified. However, since the actual
|
|
14
|
+
# assignment is always delegated to the associator, unacceptable objects will
|
|
15
|
+
# raise errors.
|
|
16
|
+
#
|
|
17
|
+
# @example Simple +has_many+ association
|
|
18
|
+
# class Blog
|
|
19
|
+
# has_many :posts # :post has to be a key in AIXM::CLASSES
|
|
20
|
+
# end
|
|
21
|
+
# class Post
|
|
22
|
+
# belongs_to :blog
|
|
23
|
+
# end
|
|
24
|
+
# blog, post = Blog.new, Post.new
|
|
25
|
+
# # --either--
|
|
26
|
+
# blog.add_post(post)
|
|
27
|
+
# blog.posts.count # => 1
|
|
28
|
+
# blog.posts.first == post # => true
|
|
29
|
+
# post.blog == blog # => true
|
|
30
|
+
# blog.remove_post(post)
|
|
31
|
+
# blog.posts.count # => 0
|
|
32
|
+
# # --or--
|
|
33
|
+
# post.blog = blog
|
|
34
|
+
# blog.posts.count # => 1
|
|
35
|
+
# blog.posts.first == post # => true
|
|
36
|
+
# post.blog == blog # => true
|
|
37
|
+
# post.blog = nil
|
|
38
|
+
# blog.posts.count # => 0
|
|
39
|
+
# # --or--
|
|
40
|
+
# post_2 = Post.new
|
|
41
|
+
# blog.add_posts([post, post_2])
|
|
42
|
+
# blog.posts.count # => 2
|
|
43
|
+
# blog.posts == [post, post_2] # => true
|
|
44
|
+
# blog.remove_posts([post_2, post])
|
|
45
|
+
# blog.posts.count # => 0
|
|
46
|
+
#
|
|
47
|
+
# @example Simple +has_one+ association
|
|
48
|
+
# class Blog
|
|
49
|
+
# has_one :posts # :post has to be a key in AIXM::CLASSES
|
|
50
|
+
# end
|
|
51
|
+
# class Post
|
|
52
|
+
# belongs_to :blog
|
|
53
|
+
# end
|
|
54
|
+
# blog, post = Blog.new, Post.new
|
|
55
|
+
# # --either--
|
|
56
|
+
# blog.post = post
|
|
57
|
+
# blog.post == post # => true
|
|
58
|
+
# post.blog == blog # => true
|
|
59
|
+
# blog.post = nil
|
|
60
|
+
# blog.post # => nil
|
|
61
|
+
# post.blog # => nil
|
|
62
|
+
# # --or--
|
|
63
|
+
# post.blog = blog
|
|
64
|
+
# post.blog == blog # => true
|
|
65
|
+
# blog.post == post # => true
|
|
66
|
+
# post.blog = nil
|
|
67
|
+
# post.blog # => nil
|
|
68
|
+
# blog.post # => nil
|
|
69
|
+
#
|
|
70
|
+
# @example Association with readonly +belongs_to+ (idem for +has_one+)
|
|
71
|
+
# class Blog
|
|
72
|
+
# has_many :posts # :post has to be a key in AIXM::CLASSES
|
|
73
|
+
# end
|
|
74
|
+
# class Post
|
|
75
|
+
# belongs_to :blog, readonly: true
|
|
76
|
+
# end
|
|
77
|
+
# blog, post = Blog.new, Post.new
|
|
78
|
+
# post.blog = blog # => NoMethodError
|
|
79
|
+
#
|
|
80
|
+
# @example Association with explicit class (idem for +has_one+)
|
|
81
|
+
# class Blog
|
|
82
|
+
# include AIXM::Association
|
|
83
|
+
# has_many :posts, accept: 'Picture'
|
|
84
|
+
# end
|
|
85
|
+
# class Picture
|
|
86
|
+
# include AIXM::Association
|
|
87
|
+
# belongs_to :blog
|
|
88
|
+
# end
|
|
89
|
+
# blog, picture = Blog.new, Picture.new
|
|
90
|
+
# blog.add_post(picture)
|
|
91
|
+
# blog.posts.first == picture # => true
|
|
92
|
+
#
|
|
93
|
+
# @example Polymorphic associator (idem for +has_one+)
|
|
94
|
+
# class Blog
|
|
95
|
+
# has_many :posts, as: :postable
|
|
96
|
+
# end
|
|
97
|
+
# class Feed
|
|
98
|
+
# has_many :posts, as: :postable
|
|
99
|
+
# end
|
|
100
|
+
# class Post
|
|
101
|
+
# belongs_to :postable
|
|
102
|
+
# end
|
|
103
|
+
# blog, feed, post_1, post_2, post_3 = Blog.new, Feed.new, Post.new, Post.new, Post.new
|
|
104
|
+
# blog.add_post(post_1)
|
|
105
|
+
# post_1.postable == blog # => true
|
|
106
|
+
# feed.add_post(post_2)
|
|
107
|
+
# post_2.postable == feed # => true
|
|
108
|
+
# post_3.postable = blog # => NoMethodError
|
|
109
|
+
#
|
|
110
|
+
# @example Polymorphic associated (idem for +has_one+)
|
|
111
|
+
# class Blog
|
|
112
|
+
# include AIXM::Association
|
|
113
|
+
# has_many :items, accept: ['Post', :picture]
|
|
114
|
+
# end
|
|
115
|
+
# class Post
|
|
116
|
+
# include AIXM::Association
|
|
117
|
+
# belongs_to :blog, as: :item
|
|
118
|
+
# end
|
|
119
|
+
# class Picture
|
|
120
|
+
# include AIXM::Association
|
|
121
|
+
# belongs_to :blog, as: :item
|
|
122
|
+
# end
|
|
123
|
+
# blog, post, picture = Blog.new, Post.new, Picture.new
|
|
124
|
+
# blog.add_item(post)
|
|
125
|
+
# blog.add_item(picture)
|
|
126
|
+
# blog.items.count # => 2
|
|
127
|
+
# blog.items.first == post # => true
|
|
128
|
+
# blog.items.last == picture # => true
|
|
129
|
+
# post.blog == blog # => true
|
|
130
|
+
# picture.blog == blog # => true
|
|
131
|
+
#
|
|
132
|
+
# @example Add method which enriches passed associated object (+has_many+ only)
|
|
133
|
+
# class Blog
|
|
134
|
+
# has_many :posts do |post, related_to: nil| # this defines the signature of add_post
|
|
135
|
+
# post.related_to = related_to || @posts.last # executes in the context of the current blog
|
|
136
|
+
# end
|
|
137
|
+
# end
|
|
138
|
+
# class Post
|
|
139
|
+
# belongs_to :blog
|
|
140
|
+
# attr_accessor :related_to
|
|
141
|
+
# end
|
|
142
|
+
# blog, post_1, post_2, post_3 = Blog.new, Post.new, Post.new, Post.new
|
|
143
|
+
# blog.add_post(post_1)
|
|
144
|
+
# post_1.related_to # => nil
|
|
145
|
+
# blog.add_post(post_2)
|
|
146
|
+
# post_2.related_to == post_1 # => true
|
|
147
|
+
# blog.add_post(post_3, related_to: post_1)
|
|
148
|
+
# post_3.related_to == post_1 # => true
|
|
149
|
+
#
|
|
150
|
+
# @example Add method which builds and yields new associated object (+has_many+ only)
|
|
151
|
+
# class Blog
|
|
152
|
+
# include AIXM::Association
|
|
153
|
+
# has_many :posts do |post, title:| end
|
|
154
|
+
# end
|
|
155
|
+
# class Post
|
|
156
|
+
# include AIXM::Association
|
|
157
|
+
# belongs_to :blog
|
|
158
|
+
# attr_accessor :title, :text
|
|
159
|
+
# def initialize(title:) # same signature as "has_many" block above
|
|
160
|
+
# @title = title
|
|
161
|
+
# end
|
|
162
|
+
# end
|
|
163
|
+
# blog = Blog.new
|
|
164
|
+
# blog.add_post(title: "title") do |post| # note that no post instance is passed
|
|
165
|
+
# post.text = "text"
|
|
166
|
+
# end
|
|
167
|
+
# blog.posts.first.title # => "title"
|
|
168
|
+
# blog.posts.first.text # => "text"
|
|
169
|
+
module Association
|
|
170
|
+
module ClassMethods
|
|
171
|
+
attr_reader :has_many_attributes, :has_one_attributes, :belongs_to_attributes
|
|
172
|
+
|
|
173
|
+
def has_many(attribute, as: nil, accept: nil, &association_block)
|
|
174
|
+
association = attribute.to_s.inflect(:singularize)
|
|
175
|
+
inversion = as || self.to_s.inflect(:demodulize, :tableize, :singularize)
|
|
176
|
+
class_names = [accept || association].flatten.map { AIXM::CLASSES[_1.to_sym] || _1 }
|
|
177
|
+
(@has_many_attributes ||= []) << attribute
|
|
178
|
+
# features
|
|
179
|
+
define_method(attribute) do
|
|
180
|
+
instance_eval("@#{attribute} ||= AIXM::Association::Array.new")
|
|
181
|
+
end
|
|
182
|
+
# add_feature
|
|
183
|
+
define_method(:"add_#{association}") do |object=nil, **options, &add_block|
|
|
184
|
+
unless object
|
|
185
|
+
fail(ArgumentError, "must pass object to add") if class_names.count > 1
|
|
186
|
+
object = class_names.first.to_class.new(**options)
|
|
187
|
+
add_block.call(object) if add_block
|
|
188
|
+
end
|
|
189
|
+
instance_exec(object, **options, &association_block) if association_block
|
|
190
|
+
fail(ArgumentError, "#{object.__class__} not allowed") unless class_names.reduce(false){ |m, c| m || object.is_a?(c.to_class) }
|
|
191
|
+
send(attribute).send(:push, object)
|
|
192
|
+
object.instance_variable_set(:"@#{inversion}", self)
|
|
193
|
+
self
|
|
194
|
+
end
|
|
195
|
+
# add_features
|
|
196
|
+
define_method(:"add_#{attribute}") do |objects=[], **options, &add_block|
|
|
197
|
+
objects.each { send(:"add_#{association}", _1, **options, &add_block) }
|
|
198
|
+
self
|
|
199
|
+
end
|
|
200
|
+
# remove_feature
|
|
201
|
+
define_method(:"remove_#{association}") do |object|
|
|
202
|
+
send(attribute).send(:delete, object)
|
|
203
|
+
object.instance_variable_set(:"@#{inversion}", nil)
|
|
204
|
+
self
|
|
205
|
+
end
|
|
206
|
+
# remove_features
|
|
207
|
+
define_method(:"remove_#{attribute}") do |objects=[]|
|
|
208
|
+
objects.each { send(:"remove_#{association}", _1) }
|
|
209
|
+
self
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def has_one(attribute, as: nil, accept: nil, allow_nil: false)
|
|
214
|
+
association = attribute.to_s
|
|
215
|
+
inversion = (as || self.to_s.inflect(:demodulize, :tableize, :singularize)).to_s
|
|
216
|
+
class_names = [accept || association].flatten.map { AIXM::CLASSES[_1.to_sym] || _1 }
|
|
217
|
+
class_names << 'NilClass' if allow_nil
|
|
218
|
+
(@has_one_attributes ||= []) << attribute
|
|
219
|
+
# feature
|
|
220
|
+
attr_reader attribute
|
|
221
|
+
# feature= / add_feature
|
|
222
|
+
define_method(:"#{association}=") do |object|
|
|
223
|
+
fail(ArgumentError, "#{object.__class__} not allowed") unless class_names.reduce(false){ |m, c| m || object.is_a?(c.to_class) }
|
|
224
|
+
instance_variable_get(:"@#{attribute}")&.instance_variable_set(:"@#{inversion}", nil)
|
|
225
|
+
instance_variable_set(:"@#{attribute}", object)
|
|
226
|
+
object&.instance_variable_set(:"@#{inversion}", self)
|
|
227
|
+
end
|
|
228
|
+
alias_method(:"add_#{association}", :"#{association}=")
|
|
229
|
+
# remove_feature
|
|
230
|
+
define_method(:"remove_#{association}") do |_|
|
|
231
|
+
send(:"#{association}=", nil)
|
|
232
|
+
self
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def belongs_to(attribute, as: nil, readonly: false)
|
|
237
|
+
association = self.to_s.inflect(:demodulize, :tableize, :singularize)
|
|
238
|
+
inversion = (as || association).to_s
|
|
239
|
+
(@belongs_to_attributes ||= []) << attribute
|
|
240
|
+
# feature
|
|
241
|
+
attr_reader attribute
|
|
242
|
+
# feature=
|
|
243
|
+
unless readonly
|
|
244
|
+
define_method(:"#{attribute}=") do |object|
|
|
245
|
+
instance_variable_get(:"@#{attribute}")&.send(:"remove_#{inversion}", self)
|
|
246
|
+
object&.send(:"add_#{inversion}", self)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def self.included(base)
|
|
253
|
+
base.extend(ClassMethods)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
class Array < ::Array
|
|
257
|
+
private :<<, :push, :append, :unshift, :prepend
|
|
258
|
+
private :delete, :pop, :shift
|
|
259
|
+
|
|
260
|
+
# Find objects of the given class and optionally with the given
|
|
261
|
+
# attribute values on a has_many association.
|
|
262
|
+
#
|
|
263
|
+
# The class can either be declared by passing the class itself or by
|
|
264
|
+
# passing a shortcut symbol as listed in +AIXM::CLASSES+.
|
|
265
|
+
#
|
|
266
|
+
# @example
|
|
267
|
+
# class Blog
|
|
268
|
+
# include AIXM::Association
|
|
269
|
+
# has_many :items, accept: %i(post picture)
|
|
270
|
+
# end
|
|
271
|
+
# class Post
|
|
272
|
+
# include AIXM::Association
|
|
273
|
+
# belongs_to :blog, as: :item
|
|
274
|
+
# attr_accessor :title
|
|
275
|
+
# end
|
|
276
|
+
# class Picture
|
|
277
|
+
# include AIXM::Association
|
|
278
|
+
# belongs_to :blog, as: :item
|
|
279
|
+
# end
|
|
280
|
+
# blog, post, picture = Blog.new, Post.new, Picture.new
|
|
281
|
+
# post.title = "title"
|
|
282
|
+
# blog.add_item(post)
|
|
283
|
+
# blog.add_item(picture)
|
|
284
|
+
# blog.items.find_by(:post) == [post] # => true
|
|
285
|
+
# blog.items.find_by(Post) == [post] # => true
|
|
286
|
+
# blog.items.find_by(:post, title: "title") == [post] # => true
|
|
287
|
+
# blog.items.find_by(Object) == [post, picture] # => true
|
|
288
|
+
#
|
|
289
|
+
# @param klass [Class, Symbol] class (e.g. AIXM::Feature::Airport,
|
|
290
|
+
# AIXM::Feature::NavigationalAid::VOR) or shortcut symbol (e.g.
|
|
291
|
+
# :airport or :vor) as listed in AIXM::CLASSES
|
|
292
|
+
# @param attributes [Hash] search attributes by their values
|
|
293
|
+
# @return [AIXM::Association::Array]
|
|
294
|
+
def find_by(klass, attributes={})
|
|
295
|
+
if klass.is_a? Symbol
|
|
296
|
+
klass = AIXM::CLASSES[klass]&.to_class || fail(ArgumentError, "unknown class shortcut `#{klass}'")
|
|
297
|
+
end
|
|
298
|
+
self.class.new(
|
|
299
|
+
select do |element|
|
|
300
|
+
if element.kind_of? klass
|
|
301
|
+
attributes.reduce(true) do |memo, (attribute, value)|
|
|
302
|
+
memo && element.send(attribute) == value
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Find equal objects on a has_many association.
|
|
310
|
+
#
|
|
311
|
+
# This may seem redundant at first, but keep in mind that two instances
|
|
312
|
+
# of +AIXM::CLASSES+ which implement `#to_uid` are considered equal if
|
|
313
|
+
# they are instances of the same class and both their UIDs as calculated
|
|
314
|
+
# by `#to_uid` are equal. Attributes which are not part of the `#to_uid`
|
|
315
|
+
# calculation are irrelevant!
|
|
316
|
+
#
|
|
317
|
+
# @example
|
|
318
|
+
# class Blog
|
|
319
|
+
# include AIXM::Association
|
|
320
|
+
# has_many :items, accept: %i(post picture)
|
|
321
|
+
# end
|
|
322
|
+
# class Post
|
|
323
|
+
# include AIXM::Association
|
|
324
|
+
# belongs_to :blog, as: :item
|
|
325
|
+
# attr_accessor :title
|
|
326
|
+
# end
|
|
327
|
+
# blog, post = Blog.new, Post.new
|
|
328
|
+
# blog.add_item(post)
|
|
329
|
+
# blog.items.find(post) == [post] # => true
|
|
330
|
+
#
|
|
331
|
+
# @param object [Object] instance of class listed in AIXM::CLASSES
|
|
332
|
+
# @return [AIXM::Association::Array]
|
|
333
|
+
def find(object)
|
|
334
|
+
klass = object.__class__
|
|
335
|
+
self.class.new(
|
|
336
|
+
select do |element|
|
|
337
|
+
element.kind_of?(klass) && element == object
|
|
338
|
+
end
|
|
339
|
+
)
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Find equal or identical duplicates on a has_many association.
|
|
343
|
+
#
|
|
344
|
+
# @example
|
|
345
|
+
# class Blog
|
|
346
|
+
# include AIXM::Association
|
|
347
|
+
# has_many :posts
|
|
348
|
+
# end
|
|
349
|
+
# class Post
|
|
350
|
+
# include AIXM::Association
|
|
351
|
+
# belongs_to :blog
|
|
352
|
+
# end
|
|
353
|
+
# blog, post = Blog.new, Post.new
|
|
354
|
+
# blog.add_posts([post, post])
|
|
355
|
+
# blog.posts.duplicates # => [post]
|
|
356
|
+
#
|
|
357
|
+
# @return [AIXM::Association::Array]
|
|
358
|
+
def duplicates
|
|
359
|
+
AIXM::Memoize.method :to_uid do
|
|
360
|
+
self.class.new(
|
|
361
|
+
select.with_index do |element, index|
|
|
362
|
+
index != self.index(element)
|
|
363
|
+
end
|
|
364
|
+
)
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
end
|
data/lib/aixm/classes.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module AIXM
|
|
2
|
+
|
|
3
|
+
# Manifest of shorthand names and their corresponding AIXM class names
|
|
4
|
+
CLASSES = {
|
|
5
|
+
document: 'AIXM::Document',
|
|
6
|
+
xy: 'AIXM::XY',
|
|
7
|
+
z: 'AIXM::Z',
|
|
8
|
+
d: 'AIXM::D',
|
|
9
|
+
f: 'AIXM::F',
|
|
10
|
+
a: 'AIXM::A',
|
|
11
|
+
w: 'AIXM::W',
|
|
12
|
+
p: 'AIXM::P',
|
|
13
|
+
address: 'AIXM::Feature::Address',
|
|
14
|
+
organisation: 'AIXM::Feature::Organisation',
|
|
15
|
+
unit: 'AIXM::Feature::Unit',
|
|
16
|
+
service: 'AIXM::Component::Service',
|
|
17
|
+
frequency: 'AIXM::Component::Frequency',
|
|
18
|
+
airport: 'AIXM::Feature::Airport',
|
|
19
|
+
runway: 'AIXM::Component::Runway',
|
|
20
|
+
fato: 'AIXM::Component::FATO',
|
|
21
|
+
helipad: 'AIXM::Component::Helipad',
|
|
22
|
+
surface: 'AIXM::Component::Surface',
|
|
23
|
+
lighting: 'AIXM::Component::Lighting',
|
|
24
|
+
airspace: 'AIXM::Feature::Airspace',
|
|
25
|
+
layer: 'AIXM::Component::Layer',
|
|
26
|
+
geometry: 'AIXM::Component::Geometry',
|
|
27
|
+
vertical_limit: 'AIXM::Component::VerticalLimit',
|
|
28
|
+
arc: 'AIXM::Component::Geometry::Arc',
|
|
29
|
+
border: 'AIXM::Component::Geometry::Border',
|
|
30
|
+
circle: 'AIXM::Component::Geometry::Circle',
|
|
31
|
+
point: 'AIXM::Component::Geometry::Point',
|
|
32
|
+
dme: 'AIXM::Feature::NavigationalAid::DME',
|
|
33
|
+
designated_point: 'AIXM::Feature::NavigationalAid::DesignatedPoint',
|
|
34
|
+
marker: 'AIXM::Feature::NavigationalAid::Marker',
|
|
35
|
+
tacan: 'AIXM::Feature::NavigationalAid::TACAN',
|
|
36
|
+
ndb: 'AIXM::Feature::NavigationalAid::NDB',
|
|
37
|
+
vor: 'AIXM::Feature::NavigationalAid::VOR',
|
|
38
|
+
obstacle: 'AIXM::Feature::Obstacle',
|
|
39
|
+
obstacle_group: 'AIXM::Feature::ObstacleGroup',
|
|
40
|
+
timetable: 'AIXM::Component::Timetable'
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
end
|
data/lib/aixm/component/fato.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
using AIXM::Refinements
|
|
2
2
|
|
|
3
3
|
module AIXM
|
|
4
|
-
|
|
4
|
+
module Component
|
|
5
5
|
|
|
6
6
|
# FATO (final approach and take-off area) for vertical take-off aircraft
|
|
7
7
|
# such as helicopters.
|
|
@@ -24,8 +24,11 @@ module AIXM
|
|
|
24
24
|
# direction.remarks = String or nil
|
|
25
25
|
# end
|
|
26
26
|
#
|
|
27
|
-
# @see https://
|
|
27
|
+
# @see https://gitlab.com/openflightmaps/ofmx/wikis/Airport#fto-fato
|
|
28
28
|
class FATO
|
|
29
|
+
include AIXM::Association
|
|
30
|
+
include AIXM::Memoize
|
|
31
|
+
|
|
29
32
|
STATUSES = {
|
|
30
33
|
CLSD: :closed,
|
|
31
34
|
WIP: :work_in_progress, # e.g. construction work
|
|
@@ -35,8 +38,26 @@ module AIXM
|
|
|
35
38
|
OTHER: :other # specify in remarks
|
|
36
39
|
}.freeze
|
|
37
40
|
|
|
38
|
-
#
|
|
39
|
-
|
|
41
|
+
# @!method surface
|
|
42
|
+
# @return [AIXM::Component::Surface] surface of the FATO
|
|
43
|
+
# @!method surface=(surface)
|
|
44
|
+
# @param surface [AIXM::Component::Surface]
|
|
45
|
+
has_one :surface
|
|
46
|
+
|
|
47
|
+
# @!method directions
|
|
48
|
+
# @return [Array<AIXM::Component::FATO::Direction>] maps added direction names to full FATO directions
|
|
49
|
+
# @!method add_direction(direction)
|
|
50
|
+
# @param direction [AIXM::A] name of the FATO direction (e.g. "12" or "16L")
|
|
51
|
+
# @return [self]
|
|
52
|
+
has_many :directions, accept: 'AIXM::Component::FATO::Direction' do |direction, name:| end
|
|
53
|
+
|
|
54
|
+
# @!method airport
|
|
55
|
+
# @return [AIXM::Feature::Airport] airport this FATO belongs to
|
|
56
|
+
belongs_to :airport
|
|
57
|
+
|
|
58
|
+
# @!method helipad
|
|
59
|
+
# @return [AIXM::Component::Helipad] helipad situated on this FATO
|
|
60
|
+
belongs_to :helipad
|
|
40
61
|
|
|
41
62
|
# @return [String] full name (e.g. "H1")
|
|
42
63
|
attr_reader :name
|
|
@@ -47,9 +68,6 @@ module AIXM
|
|
|
47
68
|
# @return [AIXM::D, nil] width
|
|
48
69
|
attr_reader :width
|
|
49
70
|
|
|
50
|
-
# @return [AIXM::Component::Surface] surface of the FATO
|
|
51
|
-
attr_reader :surface
|
|
52
|
-
|
|
53
71
|
# @return [String, nil] markings
|
|
54
72
|
attr_reader :marking
|
|
55
73
|
|
|
@@ -62,13 +80,9 @@ module AIXM
|
|
|
62
80
|
# @return [String, nil] free text remarks
|
|
63
81
|
attr_reader :remarks
|
|
64
82
|
|
|
65
|
-
# @return [Hash{String => AIXM::Component::FATO::Direction}] maps added direction names to full FATO directions
|
|
66
|
-
attr_reader :directions
|
|
67
|
-
|
|
68
83
|
def initialize(name:)
|
|
69
84
|
self.name = name
|
|
70
|
-
|
|
71
|
-
@directions = {}
|
|
85
|
+
self.surface = AIXM.surface
|
|
72
86
|
end
|
|
73
87
|
|
|
74
88
|
# @return [String]
|
|
@@ -76,12 +90,6 @@ module AIXM
|
|
|
76
90
|
%Q(#<#{self.class} airport=#{airport&.id.inspect} name=#{name.inspect}>)
|
|
77
91
|
end
|
|
78
92
|
|
|
79
|
-
def airport=(value)
|
|
80
|
-
fail(ArgumentError, "invalid airport") unless value.is_a? AIXM::Feature::Airport
|
|
81
|
-
@airport = value
|
|
82
|
-
end
|
|
83
|
-
private :airport=
|
|
84
|
-
|
|
85
93
|
def name=(value)
|
|
86
94
|
fail(ArgumentError, "invalid name") unless value.is_a? String
|
|
87
95
|
@name = value.uptrans
|
|
@@ -119,20 +127,15 @@ module AIXM
|
|
|
119
127
|
@remarks = value&.to_s
|
|
120
128
|
end
|
|
121
129
|
|
|
122
|
-
def add_direction(name:)
|
|
123
|
-
direction = Direction.new(fato: self, name: name)
|
|
124
|
-
yield direction
|
|
125
|
-
@directions[name] = direction
|
|
126
|
-
end
|
|
127
|
-
|
|
128
130
|
# @return [String] UID markup
|
|
129
131
|
def to_uid
|
|
130
132
|
builder = Builder::XmlMarkup.new(indent: 2)
|
|
131
133
|
builder.FtoUid do |fto_uid|
|
|
132
134
|
fto_uid << airport.to_uid.indent(2)
|
|
133
135
|
fto_uid.txtDesig(name)
|
|
134
|
-
end
|
|
136
|
+
end
|
|
135
137
|
end
|
|
138
|
+
memoize :to_uid
|
|
136
139
|
|
|
137
140
|
# @return [String] AIXM or OFMX markup
|
|
138
141
|
def to_xml
|
|
@@ -151,7 +154,7 @@ module AIXM
|
|
|
151
154
|
fto.codeSts(STATUSES.key(status).to_s) if status
|
|
152
155
|
fto.txtRmk(remarks) if remarks
|
|
153
156
|
end
|
|
154
|
-
directions.
|
|
157
|
+
directions.each do |direction|
|
|
155
158
|
builder << direction.to_xml
|
|
156
159
|
end
|
|
157
160
|
builder.target!
|
|
@@ -159,11 +162,20 @@ module AIXM
|
|
|
159
162
|
|
|
160
163
|
# FATO directions further describe each direction to and from the FATO.
|
|
161
164
|
#
|
|
162
|
-
# @see https://
|
|
165
|
+
# @see https://gitlab.com/openflightmaps/ofmx/wikis/Airport#fdn-fato-direction
|
|
163
166
|
class Direction
|
|
167
|
+
include AIXM::Association
|
|
168
|
+
include AIXM::Memoize
|
|
169
|
+
|
|
170
|
+
# @!method lightings
|
|
171
|
+
# @return [Array<AIXM::Component::Lighting>] installed lighting systems
|
|
172
|
+
# @!method add_lighting(lighting)
|
|
173
|
+
# @param lighting [AIXM::Component::Lighting]
|
|
174
|
+
has_many :lightings, as: :lightable
|
|
164
175
|
|
|
165
|
-
#
|
|
166
|
-
|
|
176
|
+
# @!method fato
|
|
177
|
+
# @return [AIXM::Component::FATO] FATO the FATO direction is further describing
|
|
178
|
+
belongs_to :fato
|
|
167
179
|
|
|
168
180
|
# @return [AIXM::A] name of the FATO direction (e.g. "12" or "16L")
|
|
169
181
|
attr_reader :name
|
|
@@ -174,12 +186,8 @@ module AIXM
|
|
|
174
186
|
# @return [String, nil] free text remarks
|
|
175
187
|
attr_reader :remarks
|
|
176
188
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
def initialize(fato:, name:)
|
|
181
|
-
self.fato, self.name = fato, name
|
|
182
|
-
@lightings = []
|
|
189
|
+
def initialize(name:)
|
|
190
|
+
self.name = name
|
|
183
191
|
end
|
|
184
192
|
|
|
185
193
|
# @return [String]
|
|
@@ -187,12 +195,6 @@ module AIXM
|
|
|
187
195
|
%Q(#<#{self.class} airport=#{fato&.airport&.id.inspect} name=#{name.inspect}>)
|
|
188
196
|
end
|
|
189
197
|
|
|
190
|
-
def fato=(value)
|
|
191
|
-
fail(ArgumentError, "invalid FATO") unless value.is_a? AIXM::Component::FATO
|
|
192
|
-
@fato = value
|
|
193
|
-
end
|
|
194
|
-
private :fato
|
|
195
|
-
|
|
196
198
|
def name=(value)
|
|
197
199
|
fail(ArgumentError, "invalid name") unless value.is_a? String
|
|
198
200
|
@name = AIXM.a(value)
|
|
@@ -208,17 +210,6 @@ module AIXM
|
|
|
208
210
|
@remarks = value&.to_s
|
|
209
211
|
end
|
|
210
212
|
|
|
211
|
-
# Add a lighting system to the FATO direction.
|
|
212
|
-
#
|
|
213
|
-
# @param lighting [AIXM::Component::Lighting] lighting instance
|
|
214
|
-
# @return [self]
|
|
215
|
-
def add_lighting(lighting)
|
|
216
|
-
fail(ArgumentError, "invalid lighting") unless lighting.is_a? AIXM::Component::Lighting
|
|
217
|
-
lighting.send(:lightable=, self)
|
|
218
|
-
@lightings << lighting
|
|
219
|
-
self
|
|
220
|
-
end
|
|
221
|
-
|
|
222
213
|
# @return [AIXM::A] magnetic orientation (magnetic bearing) in degrees
|
|
223
214
|
def magnetic_orientation
|
|
224
215
|
if geographic_orientation && fato.airport.declination
|
|
@@ -232,8 +223,9 @@ module AIXM
|
|
|
232
223
|
builder.FdnUid do |fdn_uid|
|
|
233
224
|
fdn_uid << fato.to_uid.indent(2)
|
|
234
225
|
fdn_uid.txtDesig(name)
|
|
235
|
-
end
|
|
226
|
+
end
|
|
236
227
|
end
|
|
228
|
+
memoize :to_uid
|
|
237
229
|
|
|
238
230
|
# @return [String] AIXM or OFMX markup
|
|
239
231
|
def to_xml
|