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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +24 -0
- data/lib/aixm/classes.rb +1 -0
- data/lib/aixm/component/helipad.rb +11 -0
- data/lib/aixm/component/runway.rb +84 -23
- data/lib/aixm/document.rb +3 -6
- data/lib/aixm/feature/airspace.rb +15 -0
- data/lib/aixm/feature/obstacle.rb +2 -2
- data/lib/aixm/l.rb +61 -0
- data/lib/aixm/refinements.rb +18 -3
- data/lib/aixm/version.rb +1 -1
- data/lib/aixm/xy.rb +34 -2
- data/lib/aixm.rb +1 -0
- data/schemas/ofmx/0.1/OFMX-DataTypes.xsd +12 -195
- data/schemas/ofmx/0.1/OFMX-Features.xsd +79 -721
- data/schemas/ofmx/0.1/OFMX-Snapshot.xsd +7 -43
- data.tar.gz.sig +0 -0
- metadata +4 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 893b141929be3f77025e38496a060f415c2262e48d0ef102ad4da84e601e4ff4
|
4
|
+
data.tar.gz: fb65d43a1d2493b1ceadffdf9c8a0671cb8bb46dc5be026c538d9f86435620da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
@@ -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 #
|
28
|
-
# runway.forth.
|
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
|
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
|
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]
|
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
|
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::
|
229
|
-
|
230
|
-
|
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
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
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
|
-
|
281
|
-
|
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
|
-
|
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(
|
316
|
-
rdn.geoLong(
|
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
|
320
|
-
rdn.valElevTdz(
|
321
|
-
rdn.uomElevTdz(
|
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
|
data/lib/aixm/refinements.rb
CHANGED
@@ -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
|
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
|
81
|
-
refine
|
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
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
|