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 +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
|