aixm 0.3.11 → 1.2.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 +64 -12
- data/README.md +34 -21
- data/lib/aixm/a.rb +110 -78
- data/lib/aixm/association.rb +39 -28
- data/lib/aixm/classes.rb +12 -4
- data/lib/aixm/{feature → component}/address.rb +27 -18
- data/lib/aixm/component/approach_lighting.rb +139 -0
- data/lib/aixm/component/fato.rb +93 -65
- data/lib/aixm/component/frequency.rb +34 -22
- data/lib/aixm/component/geometry/arc.rb +17 -4
- data/lib/aixm/component/geometry/border.rb +9 -2
- data/lib/aixm/component/geometry/circle.rb +18 -6
- data/lib/aixm/component/geometry/point.rb +12 -4
- data/lib/aixm/component/geometry/rhumb_line.rb +61 -0
- data/lib/aixm/component/geometry.rb +30 -17
- data/lib/aixm/component/helipad.rb +67 -56
- data/lib/aixm/component/layer.rb +36 -23
- data/lib/aixm/component/lighting.rb +26 -28
- data/lib/aixm/component/runway.rb +108 -72
- data/lib/aixm/component/service.rb +23 -20
- data/lib/aixm/component/surface.rb +60 -27
- data/lib/aixm/component/timesheet.rb +178 -0
- data/lib/aixm/component/timetable.rb +36 -15
- data/lib/aixm/component/vasis.rb +135 -0
- data/lib/aixm/component/vertical_limit.rb +29 -7
- data/lib/aixm/component.rb +13 -0
- data/lib/aixm/concerns/hash_equality.rb +21 -0
- data/lib/aixm/concerns/intensity.rb +30 -0
- data/lib/aixm/concerns/marking.rb +21 -0
- data/lib/aixm/concerns/remarks.rb +21 -0
- data/lib/aixm/concerns/timetable.rb +22 -0
- data/lib/aixm/config.rb +4 -2
- data/lib/aixm/d.rb +32 -25
- data/lib/aixm/document.rb +36 -6
- data/lib/aixm/f.rb +30 -18
- data/lib/aixm/feature/airport.rb +125 -55
- data/lib/aixm/feature/airspace.rb +33 -6
- data/lib/aixm/feature/navigational_aid/designated_point.rb +8 -1
- data/lib/aixm/feature/navigational_aid/dme.rb +41 -12
- data/lib/aixm/feature/navigational_aid/marker.rb +11 -4
- data/lib/aixm/feature/navigational_aid/ndb.rb +15 -3
- data/lib/aixm/feature/navigational_aid/tacan.rb +3 -2
- data/lib/aixm/feature/navigational_aid/vor.rb +52 -16
- data/lib/aixm/feature/navigational_aid.rb +30 -21
- data/lib/aixm/feature/obstacle.rb +138 -46
- data/lib/aixm/feature/obstacle_group.rb +15 -18
- data/lib/aixm/feature/organisation.rb +24 -14
- data/lib/aixm/feature/unit.rb +25 -12
- data/lib/aixm/feature.rb +25 -3
- data/lib/aixm/memoize.rb +27 -11
- data/lib/aixm/p.rb +22 -15
- data/lib/aixm/payload_hash.rb +6 -3
- data/lib/aixm/r.rb +65 -0
- data/lib/aixm/refinements.rb +43 -3
- data/lib/aixm/schedule/date.rb +181 -0
- data/lib/aixm/schedule/day.rb +114 -0
- data/lib/aixm/schedule/time.rb +255 -0
- data/lib/aixm/shortcuts.rb +3 -0
- data/lib/aixm/version.rb +1 -1
- data/lib/aixm/w.rb +21 -13
- data/lib/aixm/xy.rb +45 -26
- data/lib/aixm/z.rb +28 -15
- data/lib/aixm.rb +25 -6
- data/schemas/ofmx/{0 → 0.1}/OFMX-CSV-Obstacle.json +0 -0
- data/schemas/ofmx/{0 → 0.1}/OFMX-CSV.json +0 -0
- data/schemas/ofmx/{0 → 0.1}/OFMX-DataTypes.xsd +58 -2
- data/schemas/ofmx/{0 → 0.1}/OFMX-Features.xsd +119 -40
- data/schemas/ofmx/{0 → 0.1}/OFMX-Snapshot.xsd +5 -0
- data.tar.gz.sig +0 -0
- metadata +33 -19
- 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: a1dc4f9cb88ab4e481f835ca4d309142c6a17910b396f4293a6d9e7d3f946ec6
|
4
|
+
data.tar.gz: 244adc808983bc3e810fb5847051a89dbd0893df3d2c709943d2c7231acfb434
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac00f735cca36dc6b1b0ec00c671dcbcbdb1e14c0d051fc8d865737e9ac1363d5648f5df69098b0daf721688c541a79188cac3603a07e01f8719fde3c8070d91
|
7
|
+
data.tar.gz: 59d6ffb78fcb04104838bc4fb0ba50e7f3c5d526e921410e4300d08dafa08a2aa9cd1d637d374d7e5428d05c56e5f66186377a2f32fb6bff6c14ce5539044884
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,55 @@
|
|
1
|
+
## Main
|
2
|
+
|
3
|
+
Nothing so far
|
4
|
+
|
5
|
+
## 1.2.0
|
6
|
+
|
7
|
+
#### Additions
|
8
|
+
* `Timesheet` to add custom schedules to `Timetable`
|
9
|
+
* `AIXM::Schedule::(Date|Day|Time)` for custom timetables
|
10
|
+
* Interface to allow most class instances as Hash keys
|
11
|
+
|
12
|
+
#### Fixes
|
13
|
+
* Fix typo in `GUESSED_UNIT_TYPES_MAP`
|
14
|
+
|
15
|
+
## 1.1.0
|
16
|
+
|
17
|
+
#### Breaking Changes
|
18
|
+
* `AIXM::Association:Array#duplicates` now returns an array of arrays which
|
19
|
+
group all duplicates together.
|
20
|
+
* `VOR#associate_dme` and `VOR#associate_tacan` no longer take the channel
|
21
|
+
as argument but calculate it from the (ghost) frequency of the VOR.
|
22
|
+
* Replaced `#length`/`#width` with `#dimensions` on `Runway`, `Helipad` and `FATO`
|
23
|
+
* Renamed `AIXM::D#dist` to `AIXM::D#dim`
|
24
|
+
* Renamed `TLOF#helicopter_class` to `TLOF#performance_class`
|
25
|
+
* Renamed `#geographic_orientation` and `#magnetic_orientation` to more familiar
|
26
|
+
`#geographic_bearing` and `#magnetic_bearing` on `Runway` and `FATO`
|
27
|
+
* Re-implementation of `AIXM::A` without precision
|
28
|
+
* Demoted `Address` to component
|
29
|
+
* Fixed typo in `Service` type `:vdf_direction_finding_service`
|
30
|
+
|
31
|
+
#### Additions
|
32
|
+
* Associations from `Service` to `Airport` and `Airspace`
|
33
|
+
* `AIXM::R` (rectangle)
|
34
|
+
* `Runway#marking`
|
35
|
+
* `ApproachLighting` on `Runway::Direction` and `FATO::Direction`
|
36
|
+
* `VASIS` on `Runway::Direction` and `FATO::Direction`
|
37
|
+
* `#meta` on every feature and component
|
38
|
+
* `Document#regions` which is added to the root element for OFMX
|
39
|
+
|
40
|
+
#### Changes
|
41
|
+
* Nested memoization of the same method is now allowed and won't reset the
|
42
|
+
memoization cache anymore.
|
43
|
+
* Remove unit "mhz" from `Address` of type `:radio_frequency`.
|
44
|
+
|
45
|
+
## 1.0.0
|
46
|
+
|
47
|
+
#### Breaking Changes
|
48
|
+
* Move `Ase->txtLocalType` up into `AseUid` for OFMX
|
49
|
+
|
50
|
+
#### Additions
|
51
|
+
* Add rhumb line geometry
|
52
|
+
|
1
53
|
## 0.3.11
|
2
54
|
|
3
55
|
#### Breaking Changes
|
@@ -11,21 +63,21 @@
|
|
11
63
|
#### Additions
|
12
64
|
* Add `f#voice?` and `AIXM.config.voice_channel_separation` to check whether a
|
13
65
|
frequency belongs to the voice communication airband and use it to validate
|
14
|
-
`
|
66
|
+
`Frequency`
|
15
67
|
|
16
68
|
## 0.3.10
|
17
69
|
|
18
70
|
#### Additions
|
19
71
|
* Proper `has_many` and `has_one` associations
|
20
|
-
* `AIXM::Association:Array#find_by|find|
|
72
|
+
* `AIXM::Association:Array#find_by|find|duplicates` on `has_many` associations
|
21
73
|
* `AIXM.config.mid` now defines whether `mid` attributes are inserted or not
|
22
74
|
provided the selected schema is OFMX
|
23
75
|
* `AIXM::Memoize` module
|
24
76
|
* `AIXM::PayloadHash` class
|
25
77
|
* `mkmid` executable to insert `mid` attributes into valid OFMX file
|
26
78
|
* `ckmid` executable to check `mid` attributes in an OFMX file
|
27
|
-
*
|
28
|
-
* `
|
79
|
+
* Geometries respond to `#point?`, `#circle?` and `#polygon?`
|
80
|
+
* `Layer#services`
|
29
81
|
|
30
82
|
#### Breaking Changes
|
31
83
|
* Require Ruby 2.7
|
@@ -55,20 +107,20 @@
|
|
55
107
|
## 0.3.7
|
56
108
|
|
57
109
|
#### Additions
|
58
|
-
* `
|
59
|
-
* `
|
110
|
+
* `Document#select_features`
|
111
|
+
* `Document#group_obstacles!`
|
60
112
|
|
61
113
|
## 0.3.6
|
62
114
|
|
63
115
|
#### Additions
|
64
|
-
* `
|
65
|
-
* `
|
116
|
+
* `FATO`
|
117
|
+
* `Helipad#helicopter_class` and `Helipad#marking`
|
66
118
|
* `AIXM::XY#seconds?` to detect possibly rounded or estimated coordinates
|
67
|
-
* `
|
119
|
+
* `Airport#operator`
|
68
120
|
* `AIXM::W` (weight)
|
69
121
|
* `AIXM::P` (pressure)
|
70
|
-
* `
|
71
|
-
* Surface details
|
122
|
+
* `Lighting` for use with runways, helipads and FATOs
|
123
|
+
* Surface details `#siwl_weight`, `#siwl_tire_pressure` and `#auw_weight`
|
72
124
|
|
73
125
|
#### Changes
|
74
126
|
* Generate `Airport#id` from region and `Airport#name`
|
@@ -234,7 +286,7 @@
|
|
234
286
|
|
235
287
|
#### Changes
|
236
288
|
* `Document#created_at` and `#effective_at` accept Time, Date, String or *nil*
|
237
|
-
* Separate `
|
289
|
+
* Separate `Document#valid?` from `#complete?`
|
238
290
|
* Write coordinates in DD if extension `:OFM` is set
|
239
291
|
* `Array#to_digest` returns Integer which fits in signed 32bit
|
240
292
|
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@ For now, only the parts needed to automize the AIP import of [open flightmaps](h
|
|
11
11
|
|
12
12
|
* [Homepage](https://github.com/svoop/aixm)
|
13
13
|
* [API](https://www.rubydoc.info/gems/aixm)
|
14
|
-
* Author: [Sven Schwyn - Bitcetera](
|
14
|
+
* Author: [Sven Schwyn - Bitcetera](https://bitcetera.com)
|
15
15
|
|
16
16
|
## Install
|
17
17
|
|
@@ -47,7 +47,7 @@ gem install aixm --trust-policy MediumSecurity
|
|
47
47
|
|
48
48
|
## Usage
|
49
49
|
|
50
|
-
Here's how to build a document object, populate it with a simple feature and then render it as AIXM:
|
50
|
+
Here's how to build a document object, populate it with a simple feature and then render it as AIXM or OFMX:
|
51
51
|
|
52
52
|
```ruby
|
53
53
|
document = AIXM.document(
|
@@ -139,17 +139,16 @@ AIXM.config.ignored_errors = /invalid date/i
|
|
139
139
|
|
140
140
|
### Fundamentals
|
141
141
|
* [Document](https://www.rubydoc.info/gems/aixm/AIXM/Document.html)
|
142
|
+
* [A (angle)](https://www.rubydoc.info/gems/aixm/AIXM/A.html)
|
143
|
+
* [D (dimension, distance or length)](https://www.rubydoc.info/gems/aixm/AIXM/D.html)
|
144
|
+
* [F (frequency)](https://www.rubydoc.info/gems/aixm/AIXM/F.html)
|
145
|
+
* [P (pressure)](https://www.rubydoc.info/gems/aixm/AIXM/P.html)
|
146
|
+
* [R (rectangle)](https://www.rubydoc.info/gems/aixm/AIXM/R.html)
|
142
147
|
* [XY (longitude and latitude)](https://www.rubydoc.info/gems/aixm/AIXM/XY.html)
|
143
148
|
* [Z (height, elevation or altitude)](https://www.rubydoc.info/gems/aixm/AIXM/Z.html)
|
144
|
-
* [D (distance or length)](https://www.rubydoc.info/gems/aixm/AIXM/D.html)
|
145
|
-
* [F (frequency)](https://www.rubydoc.info/gems/aixm/AIXM/F.html)
|
146
|
-
* [A (angle)](https://www.rubydoc.info/gems/aixm/AIXM/A.html)
|
147
149
|
|
148
150
|
### Features
|
149
151
|
* [Address](https://www.rubydoc.info/gems/aixm/AIXM/Feature/Address.html)
|
150
|
-
* [Organisation](https://www.rubydoc.info/gems/aixm/AIXM/Feature/Organisation.html)
|
151
|
-
* [Unit](https://www.rubydoc.info/gems/aixm/AIXM/Feature/Unit.html)
|
152
|
-
* [Service](https://www.rubydoc.info/gems/aixm/AIXM/Component/Service.html)
|
153
152
|
* [Airport](https://www.rubydoc.info/gems/aixm/AIXM/Feature/Airport.html)
|
154
153
|
* [Airspace](https://www.rubydoc.info/gems/aixm/AIXM/Feature/Airspace.html)
|
155
154
|
* [Navigational aid](https://www.rubydoc.info/gems/aixm/AIXM/NavigationalAid.html)
|
@@ -159,22 +158,33 @@ AIXM.config.ignored_errors = /invalid date/i
|
|
159
158
|
* [NDB](https://www.rubydoc.info/gems/aixm/AIXM/Feature/NDB.html)
|
160
159
|
* [TACAN](https://www.rubydoc.info/gems/aixm/AIXM/Feature/TACAN.html)
|
161
160
|
* [VOR](https://www.rubydoc.info/gems/aixm/AIXM/Feature/VOR.html)
|
162
|
-
* [Obstacle
|
161
|
+
* [Obstacle](https://www.rubydoc.info/gems/aixm/AIXM/Feature/Obstacle.html)
|
162
|
+
* [Obstacle group](https://www.rubydoc.info/gems/aixm/AIXM/Feature/ObstacleGroup.html)
|
163
|
+
* [Organisation](https://www.rubydoc.info/gems/aixm/AIXM/Feature/Organisation.html)
|
164
|
+
* [Service](https://www.rubydoc.info/gems/aixm/AIXM/Component/Service.html)
|
165
|
+
* [Unit](https://www.rubydoc.info/gems/aixm/AIXM/Feature/Unit.html)
|
163
166
|
|
164
167
|
### Components
|
168
|
+
|
169
|
+
* [ApproachLighting](https://www.rubydoc.info/gems/aixm/AIXM/Component/ApproachLighting.html)
|
170
|
+
* [FATO](https://www.rubydoc.info/gems/aixm/AIXM/Component/FATO.html)
|
165
171
|
* [Frequency](https://www.rubydoc.info/gems/aixm/AIXM/Component/Frequency.html)
|
166
172
|
* [Geometry](https://www.rubydoc.info/gems/aixm/AIXM/Component/Geometry.html)
|
167
|
-
* [Point](https://www.rubydoc.info/gems/aixm/AIXM/Component/Point.html)
|
168
173
|
* [Arc](https://www.rubydoc.info/gems/aixm/AIXM/Component/Arc.html)
|
169
174
|
* [Border](https://www.rubydoc.info/gems/aixm/AIXM/Component/Border.html)
|
170
175
|
* [Circle](https://www.rubydoc.info/gems/aixm/AIXM/Component/Circle.html)
|
171
|
-
* [
|
176
|
+
* [Point](https://www.rubydoc.info/gems/aixm/AIXM/Component/Point.html)
|
177
|
+
* [RhumbLine](https://www.rubydoc.info/gems/aixm/AIXM/Component/RhumbLine.html)
|
172
178
|
* [Helipad](https://www.rubydoc.info/gems/aixm/AIXM/Component/Helipad.html)
|
173
|
-
* [FATO](https://www.rubydoc.info/gems/aixm/AIXM/Component/FATO.html)
|
174
|
-
* [Surface](https://www.rubydoc.info/gems/aixm/AIXM/Component/Surface.html)
|
175
179
|
* [Layer](https://www.rubydoc.info/gems/aixm/AIXM/Component/Layer.html)
|
176
|
-
* [
|
180
|
+
* [Lighting](https://www.rubydoc.info/gems/aixm/AIXM/Component/Lighting.html)
|
181
|
+
* [Runway](https://www.rubydoc.info/gems/aixm/AIXM/Component/Runway.html)
|
182
|
+
* [Service](https://www.rubydoc.info/gems/aixm/AIXM/Component/Service.html)
|
183
|
+
* [Surface](https://www.rubydoc.info/gems/aixm/AIXM/Component/Surface.html)
|
177
184
|
* [Timetable](https://www.rubydoc.info/gems/aixm/AIXM/Component/Timetable.html)
|
185
|
+
* [Timesheet](https://www.rubydoc.info/gems/aixm/AIXM/Component/Timesheet.html)
|
186
|
+
* [VASIS](https://www.rubydoc.info/gems/aixm/AIXM/Component/VASIS.html)
|
187
|
+
* [Vertical limit](https://www.rubydoc.info/gems/aixm/AIXM/Component/VerticalLimit.html)
|
178
188
|
|
179
189
|
## Associations
|
180
190
|
|
@@ -204,6 +214,14 @@ document.features.find(airport) # => [#<AIXM::Feature::Airport>]
|
|
204
214
|
|
205
215
|
This may seem redundant at first, but keep in mind that two instances of +AIXM::CLASSES+ which implement `#to_uid` are considered equal if they are instances of the same class and both their UIDs as calculated by `#to_uid` are equal. Attributes which are not part of the `#to_uid` calculation are irrelevant!
|
206
216
|
|
217
|
+
### meta
|
218
|
+
|
219
|
+
You can write arbitrary meta information to any feature or component. It won't be used when building the AIXM or OFMX document, in fact, it is not used by this gem at all. But you can store e.g. foreign keys and then later use them to find a feature or component like so:
|
220
|
+
|
221
|
+
```ruby
|
222
|
+
document.features.find_by(:airport, meta: 1234) # 1234 is the foreign key
|
223
|
+
```
|
224
|
+
|
207
225
|
### duplicates
|
208
226
|
|
209
227
|
Equally on `has_many` associations, use `duplicates` to find identical or equal associations:
|
@@ -257,6 +275,7 @@ ckmid --help
|
|
257
275
|
### AIXM
|
258
276
|
* [AIXM](http://aixm.aero)
|
259
277
|
* [AICM 4.5 documentation](https://openflightmaps.gitlab.io/ofmx/aixm/4.5/manual/aicm/)
|
278
|
+
* [AICM 4.5 manual](https://www.aixm.aero/sites/aixm.aero/files/imce/library/aicm_manual_4-5.pdf)
|
260
279
|
* [AIXM 4.5 specification](http://aixm.aero/document/aixm-45-specification)
|
261
280
|
|
262
281
|
### OFMX
|
@@ -282,13 +301,7 @@ bundle exec rake # run tests once
|
|
282
301
|
bundle exec guard # run tests whenever files are modified
|
283
302
|
```
|
284
303
|
|
285
|
-
|
286
|
-
|
287
|
-
https://github.com/svoop/aixm/issues
|
288
|
-
|
289
|
-
To contribute code, fork the project on Github, add your code and submit a pull request:
|
290
|
-
|
291
|
-
https://help.github.com/articles/fork-a-repo
|
304
|
+
You're welcome to [submit issues](https://github.com/svoop/aixm/issues) and contribute code by [forking the project and submitting pull requests](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
|
292
305
|
|
293
306
|
## License
|
294
307
|
|
data/lib/aixm/a.rb
CHANGED
@@ -2,53 +2,61 @@ using AIXM::Refinements
|
|
2
2
|
|
3
3
|
module AIXM
|
4
4
|
|
5
|
-
# Angle
|
6
|
-
#
|
5
|
+
# Angle in the range of -360 < angle < 360 degrees (used for azimuths or
|
6
|
+
# courses) and with an optional one-letter suffix (used for runways).
|
7
7
|
#
|
8
|
-
# @example
|
9
|
-
#
|
10
|
-
# a
|
11
|
-
# a
|
12
|
-
# a
|
13
|
-
# a
|
14
|
-
# a.deg += 7 # => 19
|
15
|
-
# a.deg += 341 # => 0 - deg is always within (0..359)
|
16
|
-
# a.to_s # => "000" - to_s is always within ("000".."359")
|
8
|
+
# @example Initialization
|
9
|
+
# AIXM.a(-36.9) # => #<AIXM::A -36.9° "32">
|
10
|
+
# AIXM.a(12) # => #<AIXM::A 12° "01">
|
11
|
+
# AIXM.a("12L") # => #<AIXM::A 120° "12L">
|
12
|
+
# AIXM.a(360) # => #<AIXM::A 0° "36">
|
13
|
+
# AIXM.a(-400) # => #<AIXM::A -40° "32">
|
17
14
|
#
|
18
|
-
# @example
|
19
|
-
# a = AIXM.a(
|
20
|
-
# a
|
21
|
-
# a.
|
22
|
-
# a.
|
23
|
-
# a.
|
24
|
-
# a.
|
25
|
-
# a.
|
26
|
-
# a.
|
15
|
+
# @example Calculations
|
16
|
+
# a = AIXM.a("02L")
|
17
|
+
# a += 5 # => #<AIXM::A 25° "03L">
|
18
|
+
# a -= AIXM.a(342.8) # => #<AIXM::A -317.8° "04L">
|
19
|
+
# a.to_s # => "-317.8°"
|
20
|
+
# a.to_s(:runway) # => "04L"
|
21
|
+
# a.to_s(:bearing) # => "042.2000"
|
22
|
+
# a.to_f # => 42.2
|
23
|
+
# a.to_i # => 42
|
24
|
+
# a.invert # => #<AIXM::A -137.8° "22R">
|
25
|
+
# a.to_s(:runway) # => "22R"
|
27
26
|
class A
|
27
|
+
include AIXM::Concerns::HashEquality
|
28
|
+
|
28
29
|
SUFFIX_INVERSIONS = {
|
29
30
|
R: :L,
|
30
31
|
L: :R
|
31
32
|
}.freeze
|
32
33
|
|
33
|
-
|
34
|
-
attr_reader :deg
|
34
|
+
RUNWAY_RE = /\A(0[1-9]|[12]\d|3[0-6])([A-Z])?\z/
|
35
35
|
|
36
|
-
#
|
37
|
-
|
36
|
+
# Angle in the range of -360 < angle < 360.
|
37
|
+
#
|
38
|
+
# @overload deg
|
39
|
+
# @return [Integer]
|
40
|
+
# @overload deg=(value)
|
41
|
+
# @param value [Integer]
|
42
|
+
attr_reader :deg
|
38
43
|
|
39
|
-
#
|
44
|
+
# One-letter suffix.
|
45
|
+
#
|
46
|
+
# @overload suffix
|
47
|
+
# @return [Symbol, nil]
|
48
|
+
# @overload suffix=(value)
|
49
|
+
# @param value [Symbol, nil]
|
40
50
|
attr_reader :suffix
|
41
51
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
self.deg, @precision = deg_and_suffix, 3
|
52
|
+
# See the {overview}[AIXM::A] for examples.
|
53
|
+
def initialize(value)
|
54
|
+
case value
|
46
55
|
when String
|
47
|
-
fail(ArgumentError, "invalid angle") unless
|
48
|
-
self.deg,
|
49
|
-
when
|
50
|
-
|
51
|
-
@deg, @precision = 0, deg_and_suffix.to_s.to_i
|
56
|
+
fail(ArgumentError, "invalid angle") unless value =~ RUNWAY_RE
|
57
|
+
self.deg, self.suffix = $1.to_i * 10, $2
|
58
|
+
when Numeric
|
59
|
+
self.deg = value
|
52
60
|
else
|
53
61
|
fail(ArgumentError, "invalid angle")
|
54
62
|
end
|
@@ -56,42 +64,69 @@ module AIXM
|
|
56
64
|
|
57
65
|
# @return [String]
|
58
66
|
def inspect
|
59
|
-
%Q(#<#{self.class}
|
67
|
+
%Q(#<#{self.class} #{to_s} #{to_s(:runway).inspect}>)
|
60
68
|
end
|
61
69
|
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
70
|
+
# Degrees in the range of 0..359
|
71
|
+
#
|
72
|
+
# @return [Integer]
|
73
|
+
def to_i
|
74
|
+
(deg.round + 360) % 360
|
75
|
+
end
|
76
|
+
|
77
|
+
# Degrees in the range of 0.0...360.0
|
78
|
+
#
|
79
|
+
# @return [Float]
|
80
|
+
def to_f
|
81
|
+
((deg + 360) % 360).to_f
|
82
|
+
end
|
83
|
+
|
84
|
+
# Degrees as formatted string
|
85
|
+
#
|
86
|
+
# Types are:
|
87
|
+
# * :human - degrees within -359.9~..359.9~ as D.D° (default)
|
88
|
+
# * :bearing - degrees within 0.0..359.9~ as DDD.DDDD
|
89
|
+
# * :runway - degrees within "01".."36" plus optional suffix
|
90
|
+
#
|
91
|
+
# @param type [Symbol, nil] either :runway, :bearing or nil
|
92
|
+
# @param unit [String] unit to postfix
|
93
|
+
# @return [String]
|
94
|
+
def to_s(type=:human)
|
95
|
+
return '' unless deg
|
96
|
+
case type
|
97
|
+
when :runway then [('%02d' % (((deg / 10).round + 35) % 36 + 1)), suffix].join
|
98
|
+
when :bearing then '%08.4f' % to_f.round(4)
|
99
|
+
when :human then [deg.to_s('F').sub(/\.0$/, ''), '°'].join
|
100
|
+
else fail ArgumentError
|
68
101
|
end
|
69
102
|
end
|
70
103
|
|
71
104
|
def deg=(value)
|
72
|
-
fail(ArgumentError, "invalid deg `#{value}'") unless value.is_a?
|
73
|
-
|
105
|
+
fail(ArgumentError, "invalid deg `#{value}'") unless value.is_a? Numeric
|
106
|
+
normalized_value = value.abs % 360
|
107
|
+
sign = '-' if value.negative? && normalized_value.nonzero?
|
108
|
+
@deg = BigDecimal("#{sign}#{normalized_value}")
|
74
109
|
end
|
75
110
|
|
76
111
|
def suffix=(value)
|
77
|
-
fail(
|
78
|
-
fail(ArgumentError, "invalid suffix") unless value.nil? || value.to_s =~ /\A[A-Z]+\z/
|
112
|
+
fail(ArgumentError, "invalid suffix") unless value.nil? || value.to_s =~ /\A[A-Z]\z/
|
79
113
|
@suffix = value&.to_s&.to_sym
|
80
114
|
end
|
81
115
|
|
82
|
-
# Invert an angle by 180 degrees
|
116
|
+
# Invert an angle by 180 degrees.
|
83
117
|
#
|
84
118
|
# @example
|
85
|
-
# AIXM.a(120).invert #
|
86
|
-
# AIXM.a("34L").invert #
|
87
|
-
# AIXM.a("33X").invert # => AIXM.a("33X")
|
119
|
+
# AIXM.a(120).invert # (300°)
|
120
|
+
# AIXM.a("34L").invert # (160° suffix "R")
|
88
121
|
#
|
89
|
-
# @return [AIXM::A]
|
122
|
+
# @return [AIXM::A]
|
90
123
|
def invert
|
91
|
-
|
124
|
+
self.class.new(deg.negative? ? deg - 180 : deg + 180).tap do |angle|
|
125
|
+
angle.suffix = SUFFIX_INVERSIONS.fetch(suffix, suffix)
|
126
|
+
end
|
92
127
|
end
|
93
128
|
|
94
|
-
#
|
129
|
+
# Whether other angle is the inverse.
|
95
130
|
#
|
96
131
|
# @example
|
97
132
|
# AIXM.a(120).inverse_of? AIXM.a(300) # => true
|
@@ -99,52 +134,49 @@ module AIXM
|
|
99
134
|
# AIXM.a("33X").inverse_of? AIXM.a("33X") # => true
|
100
135
|
# AIXM.a("16R").inverse_of? AIXM.a("16L") # => false
|
101
136
|
#
|
102
|
-
# @return [Boolean]
|
137
|
+
# @return [Boolean]
|
103
138
|
def inverse_of?(other)
|
104
139
|
invert == other
|
105
140
|
end
|
106
141
|
|
107
|
-
#
|
142
|
+
# Negate degrees.
|
108
143
|
#
|
109
144
|
# @return [AIXM::A]
|
110
|
-
def
|
111
|
-
|
112
|
-
build(precision: precision, deg: (deg + numeric_or_angle.round) % 360, suffix: suffix)
|
145
|
+
def -@
|
146
|
+
deg.zero? ? self : self.class.new(-deg).tap { _1.suffix = suffix }
|
113
147
|
end
|
114
148
|
|
115
|
-
#
|
149
|
+
# Add degrees.
|
116
150
|
#
|
151
|
+
# @param value [Numeric, AIXM::A]
|
117
152
|
# @return [AIXM::A]
|
118
|
-
def
|
119
|
-
|
120
|
-
|
153
|
+
def +(value)
|
154
|
+
case value
|
155
|
+
when Numeric
|
156
|
+
value.zero? ? self : self.class.new(deg + value).tap { _1.suffix = suffix }
|
157
|
+
when AIXM::A
|
158
|
+
value.deg.zero? ? self : self.class.new(deg + value.deg).tap { _1.suffix = suffix }
|
159
|
+
else
|
160
|
+
fail ArgumentError
|
161
|
+
end
|
121
162
|
end
|
122
163
|
|
123
|
-
#
|
124
|
-
|
125
|
-
|
164
|
+
# Subtract degrees.
|
165
|
+
#
|
166
|
+
# @param value [Numeric, AIXM::A]
|
167
|
+
# @return [AIXM::A]
|
168
|
+
def -(value)
|
169
|
+
self + -value
|
126
170
|
end
|
127
171
|
|
128
172
|
# @see Object#==
|
129
|
-
# @return [Boolean]
|
130
173
|
def ==(other)
|
131
|
-
self.class === other && deg == other.deg &&
|
174
|
+
self.class === other && deg == other.deg && suffix == other.suffix
|
132
175
|
end
|
133
|
-
alias_method :eql?, :==
|
134
176
|
|
135
177
|
# @see Object#hash
|
136
|
-
# @return [Integer]
|
137
178
|
def hash
|
138
|
-
|
139
|
-
end
|
140
|
-
|
141
|
-
private
|
142
|
-
|
143
|
-
def build(precision:, deg:, suffix: nil)
|
144
|
-
self.class.new(precision.to_s.to_sym).tap do |a|
|
145
|
-
a.deg = deg
|
146
|
-
a.suffix = suffix
|
147
|
-
end
|
179
|
+
[self.class, deg, suffix].join.hash
|
148
180
|
end
|
149
181
|
end
|
150
182
|
|
data/lib/aixm/association.rb
CHANGED
@@ -23,18 +23,18 @@ module AIXM
|
|
23
23
|
# end
|
24
24
|
# blog, post = Blog.new, Post.new
|
25
25
|
# # --either--
|
26
|
-
# blog.add_post(post)
|
26
|
+
# blog.add_post(post) # => Blog
|
27
27
|
# blog.posts.count # => 1
|
28
28
|
# blog.posts.first == post # => true
|
29
29
|
# post.blog == blog # => true
|
30
|
-
# blog.remove_post(post)
|
30
|
+
# blog.remove_post(post) # => Blog
|
31
31
|
# blog.posts.count # => 0
|
32
32
|
# # --or--
|
33
|
-
# post.blog = blog
|
33
|
+
# post.blog = blog # => Blog
|
34
34
|
# blog.posts.count # => 1
|
35
35
|
# blog.posts.first == post # => true
|
36
36
|
# post.blog == blog # => true
|
37
|
-
# post.blog = nil
|
37
|
+
# post.blog = nil # => nil
|
38
38
|
# blog.posts.count # => 0
|
39
39
|
# # --or--
|
40
40
|
# post_2 = Post.new
|
@@ -53,19 +53,21 @@ module AIXM
|
|
53
53
|
# end
|
54
54
|
# blog, post = Blog.new, Post.new
|
55
55
|
# # --either--
|
56
|
-
# blog.post = post
|
57
|
-
# blog.post
|
58
|
-
# post
|
59
|
-
# blog
|
60
|
-
# blog.post
|
61
|
-
# post
|
56
|
+
# blog.post = post # => Post (standard assignment)
|
57
|
+
# blog.add_post(post) # => Blog (alternative for chaining)
|
58
|
+
# blog.post == post # => true
|
59
|
+
# post.blog == blog # => true
|
60
|
+
# blog.post = nil # => nil
|
61
|
+
# blog.post # => nil
|
62
|
+
# post.blog # => nil
|
62
63
|
# # --or--
|
63
|
-
# post.blog = blog
|
64
|
-
# post.blog
|
65
|
-
# blog
|
66
|
-
# post
|
67
|
-
# post.blog
|
68
|
-
# blog
|
64
|
+
# post.blog = blog # => Blog (standard assignment)
|
65
|
+
# post.add_blog(blog) # => Post (alternative for chaining)
|
66
|
+
# post.blog == blog # => true
|
67
|
+
# blog.post == post # => true
|
68
|
+
# post.blog = nil # => nil
|
69
|
+
# post.blog # => nil
|
70
|
+
# blog.post # => nil
|
69
71
|
#
|
70
72
|
# @example Association with readonly +belongs_to+ (idem for +has_one+)
|
71
73
|
# class Blog
|
@@ -177,7 +179,7 @@ module AIXM
|
|
177
179
|
(@has_many_attributes ||= []) << attribute
|
178
180
|
# features
|
179
181
|
define_method(attribute) do
|
180
|
-
|
182
|
+
instance_variable_get(:"@#{attribute}") || AIXM::Association::Array.new
|
181
183
|
end
|
182
184
|
# add_feature
|
183
185
|
define_method(:"add_#{association}") do |object=nil, **options, &add_block|
|
@@ -188,6 +190,7 @@ module AIXM
|
|
188
190
|
end
|
189
191
|
instance_exec(object, **options, &association_block) if association_block
|
190
192
|
fail(ArgumentError, "#{object.__class__} not allowed") unless class_names.any? { |c| object.is_a?(c.to_class) }
|
193
|
+
instance_eval("@#{attribute} ||= AIXM::Association::Array.new")
|
191
194
|
send(attribute).send(:push, object)
|
192
195
|
object.instance_variable_set(:"@#{inversion}", self)
|
193
196
|
self
|
@@ -218,14 +221,19 @@ module AIXM
|
|
218
221
|
(@has_one_attributes ||= []) << attribute
|
219
222
|
# feature
|
220
223
|
attr_reader attribute
|
221
|
-
# feature=
|
224
|
+
# feature=
|
222
225
|
define_method(:"#{association}=") do |object|
|
223
226
|
fail(ArgumentError, "#{object.__class__} not allowed") unless class_names.any? { |c| object.is_a?(c.to_class) }
|
224
227
|
instance_variable_get(:"@#{attribute}")&.instance_variable_set(:"@#{inversion}", nil)
|
225
228
|
instance_variable_set(:"@#{attribute}", object)
|
226
229
|
object&.instance_variable_set(:"@#{inversion}", self)
|
230
|
+
object
|
231
|
+
end
|
232
|
+
# add_feature
|
233
|
+
define_method(:"add_#{association}") do |object|
|
234
|
+
send("#{association}=", object)
|
235
|
+
self
|
227
236
|
end
|
228
|
-
alias_method(:"add_#{association}", :"#{association}=")
|
229
237
|
# remove_feature
|
230
238
|
define_method(:"remove_#{association}") do |_|
|
231
239
|
send(:"#{association}=", nil)
|
@@ -239,11 +247,17 @@ module AIXM
|
|
239
247
|
(@belongs_to_attributes ||= []) << attribute
|
240
248
|
# feature
|
241
249
|
attr_reader attribute
|
242
|
-
# feature=
|
243
250
|
unless readonly
|
251
|
+
# feature=
|
244
252
|
define_method(:"#{attribute}=") do |object|
|
245
253
|
instance_variable_get(:"@#{attribute}")&.send(:"remove_#{inversion}", self)
|
246
254
|
object&.send(:"add_#{inversion}", self)
|
255
|
+
object
|
256
|
+
end
|
257
|
+
# add_feature
|
258
|
+
define_method(:"add_#{attribute}") do |object|
|
259
|
+
send("#{attribute}=", object)
|
260
|
+
self
|
247
261
|
end
|
248
262
|
end
|
249
263
|
end
|
@@ -349,17 +363,14 @@ module AIXM
|
|
349
363
|
# belongs_to :blog
|
350
364
|
# end
|
351
365
|
# blog, post = Blog.new, Post.new
|
352
|
-
#
|
353
|
-
# blog.
|
366
|
+
# duplicate_post = post.dup
|
367
|
+
# blog.add_posts([post, duplicate_post])
|
368
|
+
# blog.posts.duplicates # => [[post, duplicate_post]]
|
354
369
|
#
|
355
|
-
# @return [AIXM::
|
370
|
+
# @return [Array<Array<AIXM::Feature>>]
|
356
371
|
def duplicates
|
357
372
|
AIXM::Memoize.method :to_uid do
|
358
|
-
|
359
|
-
select.with_index do |element, index|
|
360
|
-
index != self.index(element)
|
361
|
-
end
|
362
|
-
)
|
373
|
+
group_by(&:to_uid).select { |_, a| a.count > 1 }.map(&:last)
|
363
374
|
end
|
364
375
|
end
|
365
376
|
end
|