aixm 1.3.4 → 1.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9fe9565888ad7ed4bcf509211380c7b7102551ebe9ae30a3aa18810e51eb6c30
4
- data.tar.gz: 4b2d98978eeedb786c6df9ee15d1a58965da68661e8e8fe3e74706d5f2d403bc
3
+ metadata.gz: 893b141929be3f77025e38496a060f415c2262e48d0ef102ad4da84e601e4ff4
4
+ data.tar.gz: fb65d43a1d2493b1ceadffdf9c8a0671cb8bb46dc5be026c538d9f86435620da
5
5
  SHA512:
6
- metadata.gz: 0437a116319aaa8aef2bb8107b6d86481686a735734ba172ed451cc73924389fb4a160d518d87d817ef7a79f84c3388b9d39f3d925bab8329b93c84e3b92c1bb
7
- data.tar.gz: c8fdeec76012ffe37a66096f6e57f620ea9a2c0b731ff569f8b325fec2a315cdab74080ddf53553880c5acfa7df17b24059047dd935802821c0d637955cc54ec
6
+ metadata.gz: 9104a9562a75872c26780e711f2744c7717c1349901ad89e661ea08cf444b00de5fa344c314c9cfc082b12e3e9a8247553af1c225a21f2f85a75924a1136b432
7
+ data.tar.gz: 4ce0eb72f3e26fde3aee41175771f83718d8dc2c4818fef1bcd6eaa47ae54d7f26d40d19df39ee449acdd2da0516a82ae552acb3c3ddfd90d224b4e41c49a50b
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  Nothing so far
4
4
 
5
+ ## 1.4.0
6
+
7
+ #### Additions
8
+ * Runways include the center line from edge to edge as two `Rcp` features if
9
+ the center line is known (bidirectional runway) or can be calculated
10
+ (unidirectional runway with known dimensions).
11
+ * `Airspace#alternative_name` (OFMX only)
12
+ * `Helipad#geographic_bearing` (OFMX only)
13
+ * `AIXM::L` for lines with optional elevation profile
14
+ * Refinement `Numeric#to_deg`
15
+ * `AIXM::XY#bearing` and `AIXM::XY@add_distance`
16
+
17
+ #### Breaking Changes
18
+ * Up until now, `Rdn->geoLat` and `Rdn->geoLong` were set to the THR. This
19
+ change sets them to the DTHR if any.
20
+ * `Runway::Direction#displaced_threshold=` fails when set as distance unless
21
+ `Runway::Direction#xy` and `Runway::Direction#bearing` are known.
22
+ * `Runway::Direction#displaced_threshold` always returns coordinates.
23
+
24
+ #### Changes
25
+ * `Document#created_at` and similar accept local times and convert them to UTC
26
+ when the XML is generated.
27
+ * Moved refinements `Float#to_dms` and `Float#to_rad` to `Numeric`
28
+
5
29
  ## 1.3.4
6
30
 
7
31
  #### Additions
data/lib/aixm/classes.rb CHANGED
@@ -6,6 +6,7 @@ module AIXM
6
6
  xy: 'AIXM::XY',
7
7
  z: 'AIXM::Z',
8
8
  d: 'AIXM::D',
9
+ l: 'AIXM::L',
9
10
  r: 'AIXM::R',
10
11
  f: 'AIXM::F',
11
12
  a: 'AIXM::A',
@@ -11,6 +11,7 @@ module AIXM
11
11
  # name: String
12
12
  # xy: AIXM.xy
13
13
  # )
14
+ # helipad.geographic_bearing = AIXM.a or nil (OFMX only)
14
15
  # helipad.z = AIXM.z or nil
15
16
  # helipad.dimensions = AIXM.r or nil
16
17
  # helipad.surface = AIXM.surface
@@ -77,6 +78,9 @@ module AIXM
77
78
  # @param value [String]
78
79
  attr_reader :name
79
80
 
81
+ # @return [AIXM::A, nil] (true) geographic bearing of H-marking in degrees
82
+ attr_reader :geographic_bearing
83
+
80
84
  # Center point
81
85
  #
82
86
  # @overload center_xy
@@ -135,6 +139,12 @@ module AIXM
135
139
  @name = value.uptrans
136
140
  end
137
141
 
142
+ def geographic_bearing=(value)
143
+ return @geographic_bearing = nil if value.nil?
144
+ fail(ArgumentError, "invalid geographic bearing") unless value.is_a? AIXM::A
145
+ @geographic_bearing = value
146
+ end
147
+
138
148
  def xy=(value)
139
149
  fail(ArgumentError, "invalid xy") unless value.is_a? AIXM::XY
140
150
  @xy = value
@@ -174,6 +184,7 @@ module AIXM
174
184
  tla.geoLat(xy.lat(AIXM.schema))
175
185
  tla.geoLong(xy.long(AIXM.schema))
176
186
  tla.codeDatum('WGE')
187
+ tla.valTrueBrg(geographic_bearing.to_s(:bearing)) if AIXM.ofmx? && geographic_bearing
177
188
  if z
178
189
  tla.valElev(z.alt)
179
190
  tla.uomDistVer(z.unit.upcase)
@@ -23,9 +23,11 @@ module AIXM
23
23
  # runway.remarks = String or nil
24
24
  # runway.forth.name = AIXM.a # preset based on the runway name
25
25
  # runway.forth.geographic_bearing = AIXM.a or nil
26
- # runway.forth.xy = AIXM.xy
27
- # runway.forth.z = AIXM.z or nil # highest point of the TDZ
28
- # runway.forth.displaced_threshold = AIXM.xy or AIXM.d or nil
26
+ # runway.forth.xy = AIXM.xy # center point at beginning edge of runway
27
+ # runway.forth.z = AIXM.z or nil # center point at beginning edge of runway
28
+ # runway.forth.touch_down_zone_z = AIXM.z or nil
29
+ # runway.forth.displaced_threshold = AIXM.d or nil # sets displaced_threshold_xy as well
30
+ # runway.forth.displaced_threshold_xy = AIXM.xy or nil # sets displaced_threshold as well
29
31
  # runway.forth.vasis = AIXM.vasis or nil (default: unspecified VASIS)
30
32
  # runway.forth.add_lighting = AIXM.lighting
31
33
  # runway.forth.add_approach_lighting = AIXM.approach_lighting
@@ -143,6 +145,25 @@ module AIXM
143
145
  @status = value.nil? ? nil : (STATUSES.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid status"))
144
146
  end
145
147
 
148
+ # Center line of the runway
149
+ #
150
+ # The center line of unidirectional runwawys is calculated using the
151
+ # runway dimensions. If they are unknown, the calculation is not possible
152
+ # and this method returns +nil+.
153
+ #
154
+ # @return [AIXM::L, nil]
155
+ def center_line
156
+ if back || dimensions
157
+ AIXM.l.add_line_point(
158
+ xy: forth.xy,
159
+ z: forth.z
160
+ ).add_line_point(
161
+ xy: (back&.xy || forth.xy.add_distance(dimensions.length, forth.geographic_bearing)),
162
+ z: back&.z
163
+ )
164
+ end
165
+ end
166
+
146
167
  # @!visibility private
147
168
  def add_uid_to(builder)
148
169
  builder.RwyUid do |rwy_uid|
@@ -165,6 +186,20 @@ module AIXM
165
186
  rwy.txtMarking(marking) if marking
166
187
  rwy.txtRmk(remarks) if remarks
167
188
  end
189
+ center_line.line_points.each do |line_point|
190
+ builder.Rcp do |rcp|
191
+ rcp.RcpUid do |rcp_uid|
192
+ add_uid_to(rcp)
193
+ rcp.geoLat(line_point.xy.lat(AIXM.schema))
194
+ rcp.geoLong(line_point.xy.long(AIXM.schema))
195
+ end
196
+ rcp.codeDatum('WGE')
197
+ if line_point.z
198
+ rcp.valElev(line_point.z.alt)
199
+ rcp.uomDistVer(line_point.z.unit.upcase)
200
+ end
201
+ end
202
+ end
168
203
  %i(@forth @back).each do |direction|
169
204
  if direction = instance_variable_get(direction)
170
205
  direction.add_to(builder)
@@ -197,7 +232,8 @@ module AIXM
197
232
  has_many :lightings, as: :lightable
198
233
 
199
234
  # @!method approach_lightings
200
- # @return [Array<AIXM::Component::ApproachLighting>] installed approach lighting systems
235
+ # @return [Array<AIXM::Component::ApproachLighting>] installed approach
236
+ # lighting systems
201
237
  #
202
238
  # @!method add_approach_lighting(approach_lighting)
203
239
  # @param approach_lighting [AIXM::Component::ApproachLighting]
@@ -205,7 +241,8 @@ module AIXM
205
241
  has_many :approach_lightings, as: :approach_lightable
206
242
 
207
243
  # @!method runway
208
- # @return [AIXM::Component::Runway] runway the runway direction is further describing
244
+ # @return [AIXM::Component::Runway] runway the runway direction is
245
+ # further describing
209
246
  belongs_to :runway, readonly: true
210
247
 
211
248
  # Partial name of runway (e.g. "12" or "16L")
@@ -219,17 +256,22 @@ module AIXM
219
256
  # @return [AIXM::A, nil] (true) geographic bearing in degrees
220
257
  attr_reader :geographic_bearing
221
258
 
222
- # @return [AIXM::XY] beginning point (middle of the runway width)
259
+ # @return [AIXM::XY] center point at the beginning edge of the runway
223
260
  attr_reader :xy
224
261
 
225
- # @return [AIXM::Z, nil] elevation of the touch down zone in +qnh+
262
+ # @return [AIXM::Z, nil] elevation of the center point at the beginning
263
+ # edge of the runway in +qnh+
226
264
  attr_reader :z
227
265
 
228
- # @return [AIXM::XY, AIXM::D, nil] displaced threshold point either as
229
- # coordinates (AIXM::XY) or distance (AIXM::D) from the beginning
230
- # point
266
+ # @return [AIXM::Z, nil] elevation of the touch down zone in +qnh+
267
+ attr_reader :touch_down_zone_z
268
+
269
+ # @return [AIXM::D, nil] displaced threshold distance from edge of runway
231
270
  attr_reader :displaced_threshold
232
271
 
272
+ # @return [AIXM::XY, nil] displaced threshold point
273
+ attr_reader :displaced_threshold_xy
274
+
233
275
  # @return [AIXM::Component::VASIS, nil] visual approach slope indicator
234
276
  # system
235
277
  attr_reader :vasis
@@ -270,17 +312,31 @@ module AIXM
270
312
  @z = value
271
313
  end
272
314
 
315
+ def touch_down_zone_z=(value)
316
+ fail(ArgumentError, "invalid touch_down_zone_z") unless value.nil? || (value.is_a?(AIXM::Z) && value.qnh?)
317
+ @touch_down_zone_z = value
318
+ end
319
+
273
320
  def displaced_threshold=(value)
274
- case value
275
- when AIXM::XY
276
- @displaced_threshold = @xy.distance(value)
277
- when AIXM::D
278
- fail(ArgumentError, "invalid displaced threshold") unless value.dim > 0
321
+ if value
322
+ fail(ArgumentError, "invalid displaced threshold") unless value.is_a?(AIXM::D) && value.dim > 0
323
+ fail(RuntimeError, "xy required to calculate displaced threshold xy") unless xy
324
+ fail(RuntimeError, "geographic bearing required to calculate displaced threshold xy") unless geographic_bearing
279
325
  @displaced_threshold = value
280
- when NilClass
281
- @displaced_threshold = nil
326
+ @displaced_threshold_xy = xy.add_distance(value, geographic_bearing)
327
+ else
328
+ @displaced_threshold = @displaced_threshold_xy = nil
329
+ end
330
+ end
331
+
332
+ def displaced_threshold_xy=(value)
333
+ if value
334
+ fail(ArgumentError, "invalid displaced threshold xy") unless value.is_a? AIXM::XY
335
+ fail(RuntimeError, "xy required to calculate displaced threshold") unless xy
336
+ @displaced_threshold_xy = value
337
+ @displaced_threshold = xy.distance(value)
282
338
  else
283
- fail(ArgumentError, "invalid displaced threshold")
339
+ @displaced_threshold = @displaced_threshold_xy = nil
284
340
  end
285
341
  end
286
342
 
@@ -300,6 +356,11 @@ module AIXM
300
356
  end
301
357
  end
302
358
 
359
+ # @return [AIXM::XY] displaced threshold if any or edge of runway otherwise
360
+ def threshold_xy
361
+ displaced_threshold_xy || xy
362
+ end
363
+
303
364
  # @!visibility private
304
365
  def add_uid_to(builder)
305
366
  builder.RdnUid do |rdn_uid|
@@ -312,13 +373,13 @@ module AIXM
312
373
  def add_to(builder)
313
374
  builder.Rdn do |rdn|
314
375
  add_uid_to(rdn)
315
- rdn.geoLat(xy.lat(AIXM.schema))
316
- rdn.geoLong(xy.long(AIXM.schema))
376
+ rdn.geoLat(threshold_xy.lat(AIXM.schema))
377
+ rdn.geoLong(threshold_xy.long(AIXM.schema))
317
378
  rdn.valTrueBrg(geographic_bearing.to_s(:bearing)) if geographic_bearing
318
379
  rdn.valMagBrg(magnetic_bearing.to_s(:bearing)) if magnetic_bearing
319
- if z
320
- rdn.valElevTdz(z.alt)
321
- rdn.uomElevTdz(z.unit.upcase)
380
+ if touch_down_zone_z
381
+ rdn.valElevTdz(touch_down_zone_z.alt)
382
+ rdn.uomElevTdz(touch_down_zone_z.unit.upcase)
322
383
  end
323
384
  vasis.add_to(rdn) if vasis
324
385
  rdn.codeVfrPattern(VFR_PATTERNS.key(vfr_pattern)) if vfr_pattern
data/lib/aixm/document.rb CHANGED
@@ -80,7 +80,6 @@ module AIXM
80
80
 
81
81
  def created_at=(value)
82
82
  @created_at = if time = value&.to_time
83
- fail(ArgumentError, "must be UTC") unless time.utc_offset.zero?
84
83
  time.round
85
84
  else
86
85
  Time.now.utc.round
@@ -89,7 +88,6 @@ module AIXM
89
88
 
90
89
  def effective_at=(value)
91
90
  @effective_at = if time = value&.to_time
92
- fail(ArgumentError, "must be UTC") unless time.utc_offset.zero?
93
91
  time.round
94
92
  else
95
93
  created_at || Time.now.utc.round
@@ -99,7 +97,6 @@ module AIXM
99
97
  def expiration_at=(value)
100
98
  @expiration_at = value&.to_time
101
99
  @expiration_at = if time = value&.to_time
102
- fail(ArgumentError, "must be UTC") unless time.utc_offset.zero?
103
100
  time.round
104
101
  end
105
102
  end
@@ -164,9 +161,9 @@ module AIXM
164
161
  origin: "rubygem aixm-#{AIXM::VERSION}",
165
162
  namespace: (namespace if AIXM.ofmx?),
166
163
  regions: (regions.join(' '.freeze) if AIXM.ofmx?),
167
- created: @created_at.xmlschema,
168
- effective: @effective_at.xmlschema,
169
- expiration: (@expiration_at&.xmlschema if AIXM.ofmx?)
164
+ created: @created_at.utc.xmlschema,
165
+ effective: @effective_at.utc.xmlschema,
166
+ expiration: (@expiration_at&.utc&.xmlschema if AIXM.ofmx?)
170
167
  }.compact
171
168
  Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |builder|
172
169
  builder.send(AIXM.schema(:root), meta) do |root|
@@ -14,6 +14,7 @@ module AIXM
14
14
  # local_type: String or nil
15
15
  # name: String or nil
16
16
  # )
17
+ # airspace.alternative_name = String (OFMX only)
17
18
  # airspace.comment = Object or nil
18
19
  # airspace.add_layer(AIXM.layer)
19
20
  # airspace.geometry.add_segment(AIXM.point or AIXM.arc or AIXM.border or AIXM.circle)
@@ -132,6 +133,14 @@ module AIXM
132
133
  # @param value [String, nil]
133
134
  attr_reader :name
134
135
 
136
+ # Alternative name (e.g. "LF P 81")
137
+ #
138
+ # @overload alternative_name
139
+ # @return [String, nil]
140
+ # @overload alternative_name=(value)
141
+ # @param value [String, nil]
142
+ attr_reader :alternative_name
143
+
135
144
  # See the {cheat sheet}[AIXM::Feature::Airspace] for examples on how to
136
145
  # create instances of this class.
137
146
  def initialize(source: nil, region: nil, id: nil, type:, local_type: nil, name: nil)
@@ -167,6 +176,11 @@ module AIXM
167
176
  @name = value&.uptrans
168
177
  end
169
178
 
179
+ def alternative_name=(value)
180
+ fail(ArgumentError, "invalid alternative name") unless value.nil? || value.is_a?(String)
181
+ @alternative_name = value&.uptrans
182
+ end
183
+
170
184
  # @!visibility private
171
185
  def add_uid_to(builder, as: :AseUid)
172
186
  builder.send(as, ({ region: (region if AIXM.ofmx?) }.compact)) do |tag|
@@ -193,6 +207,7 @@ module AIXM
193
207
  add_uid_to(ase)
194
208
  ase.txtLocalType(local_type) if AIXM.aixm? && local_type && local_type != name
195
209
  ase.txtName(name) if name
210
+ ase.txtNameAlt(alternative_name) if AIXM.ofmx? && alternative_name
196
211
  layers.first.add_to(ase) unless layered?
197
212
  end
198
213
  builder.Abd do |abd|
@@ -363,8 +363,8 @@ module AIXM
363
363
  linked_to.add_uid_to(obs, as: :ObsUidLink)
364
364
  obs.codeLinkType(LINK_TYPES.key(link_type))
365
365
  end
366
- obs.datetimeValidWef(valid_from.xmlschema) if valid_from
367
- obs.datetimeValidTil(valid_until.xmlschema) if valid_until
366
+ obs.datetimeValidWef(valid_from.utc.xmlschema) if valid_from
367
+ obs.datetimeValidTil(valid_until.utc.xmlschema) if valid_until
368
368
  end
369
369
  obs.txtRmk(remarks) if remarks
370
370
  end
data/lib/aixm/l.rb ADDED
@@ -0,0 +1,61 @@
1
+ module AIXM
2
+
3
+ # Geographical line with optional profile
4
+ #
5
+ # Each line point is an OStruct which can be queried for its coordinates with
6
+ # `.xy` or its optional elevation with `.z`
7
+ #
8
+ # @example All of the below are equivalent
9
+ # line = AIXM.l # or:
10
+ # line = AIXM.l(xy: AIXM.xy(...), z: AIXM.z(...))
11
+ # line.add_line_point(xy: AIXM.xy(...), z: AIXM.z(...))
12
+ # line.line_points.first.xy # => AIXM::XY
13
+ # line.line_points.first.z # => AIXM::Z
14
+ class L
15
+
16
+ # Array of line points
17
+ #
18
+ # @return [Array<OStruct>]
19
+ attr_reader :line_points
20
+
21
+ # See the {overview}[AIXM::L] for examples.
22
+ def initialize(xy: nil, z: nil)
23
+ @line_points = []
24
+ add_line_point(xy: xy, z: z) if xy
25
+ end
26
+
27
+ # @return [String]
28
+ def inspect
29
+ %Q(#<#{self.class} #{to_s}>)
30
+ end
31
+
32
+ # @return [String] human readable representation
33
+ def to_s
34
+ line_points.map { _1.to_h.values.map(&:to_s).join(' ') }.join(', ')
35
+ end
36
+
37
+ # Add a line point to the line
38
+ #
39
+ # @param xy [AIXM::XY] coordinates
40
+ # @param z [AIXM::Z, nil] elevation
41
+ # @return [self]
42
+ def add_line_point(xy:, z: nil)
43
+ fail(ArgumentError, "invalid xy") unless xy.instance_of?(AIXM::XY)
44
+ fail(ArgumentError, "invalid z") unless !z || z.instance_of?(AIXM::Z)
45
+ line_points << OpenStruct.new(xy: xy, z: z)
46
+ self
47
+ end
48
+
49
+ # Whether there are enough line points to define a line
50
+ #
51
+ # @return [Boolean]
52
+ def line?
53
+ line_points.count >= 2
54
+ end
55
+
56
+ # @see Object#==
57
+ def ==(other)
58
+ self.class === other && to_s == other.to_s
59
+ end
60
+ end
61
+ end
@@ -53,7 +53,7 @@ module AIXM
53
53
  # @note This is a refinement for +Float+
54
54
  # @param padding [Integer] number of digits for the degree part
55
55
  # @return [String] angle in DMS notation +{-}D°MM'SS.SS"+
56
- refine Float do
56
+ refine Numeric do
57
57
  def to_dms(padding=3)
58
58
  degrees = self.abs.floor
59
59
  minutes = ((self.abs - degrees) * 60).floor
@@ -69,6 +69,21 @@ module AIXM
69
69
  end
70
70
  end
71
71
 
72
+ # @!method to_deg
73
+ # Convert an angle from radian to degrees.
74
+ #
75
+ # @example
76
+ # 0.5.to_rad
77
+ # # => 28.6478897565
78
+ #
79
+ # @note This is a refinement for +Float+
80
+ # @return [Float] angle in degrees
81
+ refine Numeric do
82
+ def to_deg
83
+ 180 * self / Math::PI
84
+ end
85
+ end
86
+
72
87
  # @!method to_rad
73
88
  # Convert an angle from degree to radian.
74
89
  #
@@ -77,8 +92,8 @@ module AIXM
77
92
  # # => 0.7853981633974483
78
93
  #
79
94
  # @note This is a refinement for +Float+
80
- # @return [Float] radian angle
81
- refine Float do
95
+ # @return [Float] angle in radian
96
+ refine Numeric do
82
97
  def to_rad
83
98
  self * Math::PI / 180
84
99
  end
data/lib/aixm/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module AIXM
2
- VERSION = "1.3.4".freeze
2
+ VERSION = "1.4.0".freeze
3
3
  end
data/lib/aixm/xy.rb CHANGED
@@ -31,7 +31,7 @@ module AIXM
31
31
  class XY
32
32
  include AIXM::Concerns::HashEquality
33
33
 
34
- EARTH_RADIUS = 6_371_008.8
34
+ EARTH_RADIUS = 6_371_008.8 # meters
35
35
 
36
36
  # See the {overview}[AIXM::XY] for examples.
37
37
  def initialize(lat:, long:)
@@ -105,7 +105,7 @@ module AIXM
105
105
  AIXM.point(xy: self)
106
106
  end
107
107
 
108
- # Distance as calculated by the Haversine formula
108
+ # Distance to another point as calculated by the Haversine formula
109
109
  #
110
110
  # @return [AIXM::D]
111
111
  def distance(other)
@@ -123,6 +123,38 @@ module AIXM
123
123
  end
124
124
  end
125
125
 
126
+ # Bearing to another point
127
+ #
128
+ # @return [AIXM::A]
129
+ def bearing(other)
130
+ fail "cannot calculate bearing to identical point" if self == other
131
+ delta_long = other.long.to_rad - long.to_rad
132
+ AIXM.a(
133
+ Math.atan2(
134
+ Math.cos(other.lat.to_rad) * Math.sin(delta_long),
135
+ Math.cos(lat.to_rad) * Math.sin(other.lat.to_rad) -
136
+ Math.sin(lat.to_rad) * Math.cos(other.lat.to_rad) *
137
+ Math.cos(delta_long)
138
+ ).to_deg
139
+ )
140
+ end
141
+
142
+ # Calculate a new point by adding the distance in the given bearing
143
+ #
144
+ # @return [AIXM::XY]
145
+ def add_distance(distance, bearing)
146
+ angular_dist = distance.to_m.dim / EARTH_RADIUS
147
+ dest_lat = Math.asin(
148
+ Math.sin(lat.to_rad) * Math.cos(angular_dist) +
149
+ Math.cos(lat.to_rad) * Math.sin(angular_dist) * Math.cos(bearing.to_f.to_rad)
150
+ )
151
+ dest_long = long.to_rad + Math.atan2(
152
+ Math.sin(bearing.to_f.to_rad) * Math.sin(angular_dist) * Math.cos(lat.to_rad),
153
+ Math.cos(angular_dist) - Math.sin(lat.to_rad) * Math.sin(dest_lat)
154
+ )
155
+ AIXM.xy(lat: dest_lat.to_deg, long: dest_long.to_deg)
156
+ end
157
+
126
158
  # @see Object#==
127
159
  def ==(other)
128
160
  self.class === other && lat == other.lat && long == other.long
data/lib/aixm.rb CHANGED
@@ -36,6 +36,7 @@ require_relative 'aixm/document'
36
36
  require_relative 'aixm/xy'
37
37
  require_relative 'aixm/z'
38
38
  require_relative 'aixm/d'
39
+ require_relative 'aixm/l'
39
40
  require_relative 'aixm/r'
40
41
  require_relative 'aixm/f'
41
42
  require_relative 'aixm/a'