aixm 1.3.4 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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'