aixm 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +1 -2
  4. data/CHANGELOG.md +20 -0
  5. data/README.md +7 -2
  6. data/aixm.gemspec +2 -3
  7. data/lib/aixm.rb +6 -2
  8. data/lib/aixm/a.rb +151 -0
  9. data/lib/aixm/component/frequency.rb +2 -2
  10. data/lib/aixm/component/geometry.rb +4 -0
  11. data/lib/aixm/component/geometry/point.rb +0 -4
  12. data/lib/aixm/component/helipad.rb +8 -22
  13. data/lib/aixm/component/runway.rb +36 -36
  14. data/lib/aixm/component/surface.rb +121 -0
  15. data/lib/aixm/component/timetable.rb +1 -0
  16. data/lib/aixm/constants.rb +40 -0
  17. data/lib/aixm/d.rb +5 -0
  18. data/lib/aixm/document.rb +2 -2
  19. data/lib/aixm/f.rb +6 -0
  20. data/lib/aixm/feature/address.rb +100 -0
  21. data/lib/aixm/feature/airport.rb +26 -7
  22. data/lib/aixm/feature/airspace.rb +10 -1
  23. data/lib/aixm/feature/navigational_aid.rb +1 -1
  24. data/lib/aixm/feature/navigational_aid/designated_point.rb +20 -5
  25. data/lib/aixm/feature/navigational_aid/dme.rb +2 -2
  26. data/lib/aixm/{component → feature}/service.rb +67 -16
  27. data/lib/aixm/feature/unit.rb +40 -6
  28. data/lib/aixm/refinements.rb +63 -6
  29. data/lib/aixm/shortcuts.rb +12 -4
  30. data/lib/aixm/version.rb +1 -1
  31. data/lib/aixm/xy.rb +6 -1
  32. data/lib/aixm/z.rb +6 -0
  33. data/schemas/ofmx/0/OFMX-DataTypes.xsd +5 -2
  34. data/schemas/ofmx/0/OFMX-Features.xsd +2 -0
  35. data/spec/factory.rb +32 -10
  36. data/spec/lib/aixm/a_spec.rb +203 -0
  37. data/spec/lib/aixm/component/helipad_spec.rb +11 -17
  38. data/spec/lib/aixm/component/runway_spec.rb +46 -32
  39. data/spec/lib/aixm/component/surface_spec.rb +88 -0
  40. data/spec/lib/aixm/d_spec.rb +10 -0
  41. data/spec/lib/aixm/document_spec.rb +104 -32
  42. data/spec/lib/aixm/f_spec.rb +10 -0
  43. data/spec/lib/aixm/feature/address_spec.rb +55 -0
  44. data/spec/lib/aixm/feature/airport_spec.rb +73 -3
  45. data/spec/lib/aixm/feature/navigational_aid/designated_point_spec.rb +43 -6
  46. data/spec/lib/aixm/feature/navigational_aid/dme_spec.rb +2 -2
  47. data/spec/lib/aixm/feature/navigational_aid/marker_spec.rb +2 -2
  48. data/spec/lib/aixm/feature/navigational_aid/ndb_spec.rb +2 -2
  49. data/spec/lib/aixm/feature/navigational_aid/tacan_spec.rb +2 -2
  50. data/spec/lib/aixm/feature/navigational_aid/vor_spec.rb +6 -6
  51. data/spec/lib/aixm/{component → feature}/service_spec.rb +12 -14
  52. data/spec/lib/aixm/feature/unit_spec.rb +7 -4
  53. data/spec/lib/aixm/refinements_spec.rb +100 -15
  54. data/spec/lib/aixm/z_spec.rb +10 -0
  55. metadata +17 -25
  56. data/lib/aixm/h.rb +0 -87
  57. data/spec/lib/aixm/h_spec.rb +0 -113
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4e0b32c04cba8526128ce56c60abf4343b7ac8f2e8a3e8252e1046ce3ccbfe3
4
- data.tar.gz: c88875a5a1921f90a1d4055302469c1608ae610bce5b513c9a3e2d50ed32f842
3
+ metadata.gz: cbcadda83460463ad9ab68569e04738a5811b10e3534f3f91fde0ed1f4104874
4
+ data.tar.gz: aa14c72ecf04553fde4243ff74b68e5cfe2b1d3f19efbb4ef1f473a6dce79a38
5
5
  SHA512:
6
- metadata.gz: e0f11994c12259bee9a013b92162ed14f763206855adfb5f0d25fc45807a885da5b330010c1c7768590cd3cca19c260976c68674b0354091b2ef64288a70914e
7
- data.tar.gz: 800ef3ea1d7e298281454957f3471e06452d9a23a4333c0f978c24590cd9fc140b21948d7588a07a896995731897810bffc317ae610e39822b6e27e51554cb10
6
+ metadata.gz: 18f3c67e93a4d6527d8240f819c942d82e81cef33332902749ebcde5e62c3aa4aaf2e12ea2ddce4b642b4ba63b2913e557207161dd4400b01685ec673c436885
7
+ data.tar.gz: f35b079b357d0b00e5c7bc11a71f0e610cc787863eb678f49aa6697ab11eccc32804c33e73e9f2d4851209d9604b16a271c9f85759b2c41b7e8094a928ff5034
@@ -1 +1 @@
1
- ruby-2.5.3
1
+ ruby-2.6.3
@@ -1,5 +1,4 @@
1
1
  ---
2
2
  language: ruby
3
3
  rvm:
4
- - 2.5.3
5
- before_install: gem install bundler -v 1.17.1
4
+ - 2.6.0
@@ -1,3 +1,22 @@
1
+ ## 0.3.4
2
+
3
+ #### Additions
4
+ * Address feature
5
+ * `Runway#preparation`, `Runway#condition` and `Runway#vfr_pattern`
6
+ * `Service#guessed_unit_type`
7
+ * Surface for `Runway|Helipad#surface`
8
+ * Extracted `AIXM::MIN`, `AIXM::SEC` and `AIXM::DMS_RE` to scan for coordinates in texts
9
+ * Refinement `String#payload_hash`
10
+
11
+ #### Breaking Changes
12
+ * Require Ruby 2.6
13
+ * Renamed `AIXM::H` to `AIXM::A` (angle) and add simple arithmetics to make it more versatile
14
+ * `Runway|Helipad#composition` moved to `Runway|Helipad#surface`
15
+ * DMS notation `{-}{DD}DMMSS{.SS}[NESW]` now requires compulsory cardinal direction (N, E, S or W) at the end
16
+
17
+ #### Changes
18
+ * Service is a feature now
19
+
1
20
  ## 0.3.3
2
21
 
3
22
  #### Additions
@@ -132,6 +151,7 @@
132
151
  ## 0.1.0
133
152
 
134
153
  #### Initial Implementation
154
+ * Require Ruby 2.5
135
155
  * `AIXM::XY` (coordinates)
136
156
  * `AIXM::Z` (altitude or elevation)
137
157
  * AIXM-Snapshot 4.5 Document
data/README.md CHANGED
@@ -37,13 +37,15 @@ document.aixm! # not really necessary since AIXM is the default schema
37
37
  document.to_xml
38
38
  ```
39
39
 
40
- You can initialize all elements either traditionally or by use of shorter AIXM class methods. The following two statements are identical:
40
+ You can initialize all elements either traditionally or by use of the corresponding shorthand AIXM class method. The following two statements are identical:
41
41
 
42
42
  ```ruby
43
43
  AIXM::Feature::NavigationalAid::DesignatedPoint.new(...)
44
44
  AIXM.designated_point(...)
45
45
  ```
46
46
 
47
+ See `AIXM::CLASSES` for the complete list of shorthand names.
48
+
47
49
  ## Configuration
48
50
 
49
51
  The following configuration options are available for setting and getting:
@@ -76,10 +78,13 @@ AIXM.schema(:version) # => 0
76
78
  * [Z (height, elevation or altitude)](http://www.rubydoc.info/gems/aixm/AIXM/Z.html)
77
79
  * [D (distance or length)](http://www.rubydoc.info/gems/aixm/AIXM/D.html)
78
80
  * [F (frequency)](http://www.rubydoc.info/gems/aixm/AIXM/F.html)
81
+ * [A (angle)](http://www.rubydoc.info/gems/aixm/AIXM/A.html)
79
82
 
80
83
  ### Features
84
+ * [Address](http://www.rubydoc.info/gems/aixm/AIXM/Feature/Address.html)
81
85
  * [Organisation](http://www.rubydoc.info/gems/aixm/AIXM/Feature/Organisation.html)
82
86
  * [Unit](http://www.rubydoc.info/gems/aixm/AIXM/Feature/Unit.html)
87
+ * [Service](http://www.rubydoc.info/gems/aixm/AIXM/Component/Service.html)
83
88
  * [Airport](http://www.rubydoc.info/gems/aixm/AIXM/Feature/Airport.html)
84
89
  * [Airspace](http://www.rubydoc.info/gems/aixm/AIXM/Feature/Airspace.html)
85
90
  * [Navigational aid](http://www.rubydoc.info/gems/aixm/AIXM/NavigationalAid.html)
@@ -92,7 +97,6 @@ AIXM.schema(:version) # => 0
92
97
  * [Obstacle and obstacle group](http://www.rubydoc.info/gems/aixm/AIXM/Feature/Obstacle.html)
93
98
 
94
99
  ### Components
95
- * [Service](http://www.rubydoc.info/gems/aixm/AIXM/Component/Service.html)
96
100
  * [Frequency](http://www.rubydoc.info/gems/aixm/AIXM/Component/Frequency.html)
97
101
  * [Geometry](http://www.rubydoc.info/gems/aixm/AIXM/Component/Geometry.html)
98
102
  * [Point](http://www.rubydoc.info/gems/aixm/AIXM/Component/Point.html)
@@ -101,6 +105,7 @@ AIXM.schema(:version) # => 0
101
105
  * [Circle](http://www.rubydoc.info/gems/aixm/AIXM/Component/Circle.html)
102
106
  * [Runway](http://www.rubydoc.info/gems/aixm/AIXM/Component/Runway.html)
103
107
  * [Helipad](http://www.rubydoc.info/gems/aixm/AIXM/Component/Helipad.html)
108
+ * [Surface](http://www.rubydoc.info/gems/aixm/AIXM/Component/Surface.html)
104
109
  * [Layer](http://www.rubydoc.info/gems/aixm/AIXM/Component/Layer.html)
105
110
  * [Vertical limits](http://www.rubydoc.info/gems/aixm/AIXM/Component/VerticalLimits.html)
106
111
  * [Timetable](http://www.rubydoc.info/gems/aixm/AIXM/Component/Timetable.html)
@@ -18,9 +18,8 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.required_ruby_version = '>= 2.5'
22
-
23
- spec.add_development_dependency 'bundler'
21
+ spec.required_ruby_version = '>= 2.6'
22
+
24
23
  spec.add_development_dependency 'rake'
25
24
  spec.add_development_dependency 'minitest'
26
25
  spec.add_development_dependency 'minitest-reporters'
@@ -5,8 +5,10 @@ require 'nokogiri'
5
5
  require 'forwardable'
6
6
  require 'digest'
7
7
  require 'time'
8
+ require 'pathname'
8
9
 
9
10
  require_relative 'aixm/version'
11
+ require_relative 'aixm/constants'
10
12
  require_relative 'aixm/refinements'
11
13
  require_relative 'aixm/config'
12
14
  require_relative 'aixm/errors'
@@ -16,10 +18,9 @@ require_relative 'aixm/xy'
16
18
  require_relative 'aixm/z'
17
19
  require_relative 'aixm/d'
18
20
  require_relative 'aixm/f'
19
- require_relative 'aixm/h'
21
+ require_relative 'aixm/a'
20
22
 
21
23
  require_relative 'aixm/component'
22
- require_relative 'aixm/component/service'
23
24
  require_relative 'aixm/component/frequency'
24
25
  require_relative 'aixm/component/geometry'
25
26
  require_relative 'aixm/component/geometry/point'
@@ -31,10 +32,13 @@ require_relative 'aixm/component/vertical_limits'
31
32
  require_relative 'aixm/component/timetable'
32
33
  require_relative 'aixm/component/runway'
33
34
  require_relative 'aixm/component/helipad'
35
+ require_relative 'aixm/component/surface'
34
36
 
35
37
  require_relative 'aixm/feature'
38
+ require_relative 'aixm/feature/address'
36
39
  require_relative 'aixm/feature/organisation'
37
40
  require_relative 'aixm/feature/unit'
41
+ require_relative 'aixm/feature/service'
38
42
  require_relative 'aixm/feature/airspace'
39
43
  require_relative 'aixm/feature/airport'
40
44
  require_relative 'aixm/feature/navigational_aid'
@@ -0,0 +1,151 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+
5
+ # Angle from 0 to 359 degrees with an optional suffix used for azimuths,
6
+ # bearings, headings, courses etc.
7
+ #
8
+ # @example Initialized with Numeric
9
+ # a = AIXM.a(12) # 12 degrees, 1 degree precision, no suffix
10
+ # a.precision # => 3 (three digits = steps of 1 degree)
11
+ # a.to_s # => "012"
12
+ # a.suffix # => nil
13
+ # a.deg # => 12
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")
17
+ #
18
+ # @example Initialized with String
19
+ # a = AIXM.a('06L') # 60 degrees, 10 degree precision, suffix :L
20
+ # a.precision # => 2 (two digits = steps of 10 degrees)
21
+ # a.to_s # => "06L"
22
+ # a.suffix # => :L
23
+ # a.deg # => 60
24
+ # a.deg += 7 # => 70
25
+ # a.deg += 190 # => 0 - deg is always within (0..359)
26
+ # a.to_s # => "36L" - to_s converts to ("01".."36")
27
+ class A
28
+ SUFFIX_INVERSIONS = {
29
+ R: :L,
30
+ L: :R
31
+ }.freeze
32
+
33
+ # @return [Integer] angle
34
+ attr_reader :deg
35
+
36
+ # @return [Integer] precision: +2+ (10 degree steps) or +3+ (1 degree steps)
37
+ attr_reader :precision
38
+
39
+ # @return [Symbol, nil] suffix
40
+ attr_reader :suffix
41
+
42
+ def initialize(deg_and_suffix)
43
+ case deg_and_suffix
44
+ when Numeric
45
+ self.deg, @precision = deg_and_suffix, 3
46
+ when String
47
+ fail(ArgumentError, "invalid angle") unless deg_and_suffix.to_s =~ /\A(\d+)([A-Z]+)?\z/
48
+ self.deg, @precision, self.suffix = $1.to_i * 10, 2, $2
49
+ when Symbol # used only by private build method
50
+ fail(ArgumentError, "invalid precision") unless %i(2 3).include? deg_and_suffix
51
+ @deg, @precision = 0, deg_and_suffix.to_s.to_i
52
+ else
53
+ fail(ArgumentError, "invalid angle")
54
+ end
55
+ end
56
+
57
+ # @return [String]
58
+ def inspect
59
+ %Q(#<#{self.class}[precision=#{precision}] #{to_s}>)
60
+ end
61
+
62
+ # @return [String] human readable representation according to precision
63
+ def to_s
64
+ if precision == 2
65
+ [('%02d' % ((deg / 10 + 35) % 36 + 1)), suffix].map(&:to_s).join
66
+ else
67
+ ('%03d' % deg)
68
+ end
69
+ end
70
+
71
+ def deg=(value)
72
+ fail(ArgumentError, "invalid deg `#{value}'") unless value.is_a?(Numeric) && value.round.between?(0, 360)
73
+ @deg = (precision == 2 ? (value.to_f / 10).round * 10 : value.round) % 360
74
+ end
75
+
76
+ def suffix=(value)
77
+ fail(RuntimeError, "suffix only allowed when precision is 2") unless value.nil? || precision == 2
78
+ fail(ArgumentError, "invalid suffix") unless value.nil? || value.to_s =~ /\A[A-Z]+\z/
79
+ @suffix = value&.to_s&.to_sym
80
+ end
81
+
82
+ # Invert an angle by 180 degrees
83
+ #
84
+ # @example
85
+ # AIXM.a(120).invert # => AIXM.a(300)
86
+ # AIXM.a("34L").invert # => AIXM.a("16R")
87
+ # AIXM.a("33X").invert # => AIXM.a("33X")
88
+ #
89
+ # @return [AIXM::A] inverted angle
90
+ def invert
91
+ build(precision: precision, deg: (deg + 180) % 360, suffix: SUFFIX_INVERSIONS.fetch(suffix, suffix))
92
+ end
93
+
94
+ # Check whether +other+ angle is the inverse
95
+ #
96
+ # @example
97
+ # AIXM.a(120).inverse_of? AIXM.a(300) # => true
98
+ # AIXM.a("34L").inverse_of? AIXM.a("16R") # => true
99
+ # AIXM.a("33X").inverse_of? AIXM.a("33X") # => true
100
+ # AIXM.a("16R").inverse_of? AIXM.a("16L") # => false
101
+ #
102
+ # @return [Boolean] whether the inverted angle or not
103
+ def inverse_of?(other)
104
+ invert == other
105
+ end
106
+
107
+ # Add degrees
108
+ #
109
+ # @return [AIXM::A]
110
+ def +(numeric_or_angle)
111
+ fail ArgumentError unless numeric_or_angle.respond_to? :round
112
+ build(precision: precision, deg: (deg + numeric_or_angle.round) % 360, suffix: suffix)
113
+ end
114
+
115
+ # Subtract degrees
116
+ #
117
+ # @return [AIXM::A]
118
+ def -(numeric_or_angle)
119
+ fail ArgumentError unless numeric_or_angle.respond_to? :round
120
+ build(precision: precision, deg: (deg - numeric_or_angle.round + 360) % 360, suffix: suffix)
121
+ end
122
+
123
+ # @private
124
+ def round
125
+ deg
126
+ end
127
+
128
+ # @see Object#==
129
+ # @return [Boolean]
130
+ def ==(other)
131
+ self.class === other && deg == other.deg && precision == other.precision && suffix == other.suffix
132
+ end
133
+ alias_method :eql?, :==
134
+
135
+ # @see Object#hash
136
+ # @return [Integer]
137
+ def hash
138
+ to_s.hash
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
148
+ end
149
+ end
150
+
151
+ end
@@ -32,7 +32,7 @@ module AIXM
32
32
  OTHER: :other # specify in remarks
33
33
  }.freeze
34
34
 
35
- # @return [AIXM::Component::Service] service the frequency belongs to
35
+ # @return [AIXM::Feature::Service] service the frequency belongs to
36
36
  attr_reader :service
37
37
 
38
38
  # @return [AIXM::F] frequency for transmission (outgoing)
@@ -69,7 +69,7 @@ module AIXM
69
69
  end
70
70
 
71
71
  def service=(value)
72
- fail(ArgumentError, "invalid service") unless value.is_a? AIXM::Component::Service
72
+ fail(ArgumentError, "invalid service") unless value.is_a? AIXM::Feature::Service
73
73
  @service = value
74
74
  end
75
75
  private :service=
@@ -33,6 +33,10 @@ module AIXM
33
33
  include Enumerable
34
34
  extend Forwardable
35
35
 
36
+ # @!method each
37
+ # @return [Enumerator] see Array#each
38
+ # @!method <<
39
+ # @return [Array] see Array#<<
36
40
  def_delegators :@result_array, :each, :<<
37
41
 
38
42
  def initialize(*segments)
@@ -13,10 +13,6 @@ module AIXM
13
13
  #
14
14
  # @see https://github.com/openflightmaps/ofmx/wiki/Airspace#point
15
15
  class Point
16
- extend Forwardable
17
-
18
- def_delegators :xy
19
-
20
16
  # @return [AIXM::XY] (starting) point
21
17
  attr_reader :xy
22
18
 
@@ -13,25 +13,12 @@ module AIXM
13
13
  # helipad.z = AIXM.z or nil
14
14
  # helipad.length = AIXM.d or nil # must use same unit as width
15
15
  # helipad.width = AIXM.d or nil # must use same unit as length
16
- # helipad.composition = COMPOSITIONS or nil
16
+ # helipad.surface = AIXM.surface
17
17
  # helipad.status = STATUSES or nil
18
18
  # helipad.remarks = String or nil
19
19
  #
20
20
  # @see https://github.com/openflightmaps/ofmx/wiki/Airport#tla-helipad-tlof
21
21
  class Helipad
22
- COMPOSITIONS = {
23
- ASPH: :asphalt,
24
- BITUM: :bitumen, # dug up, bound and rolled ground
25
- CONC: :concrete,
26
- GRAVE: :gravel, # small and midsize rounded stones
27
- MACADAM: :macadam, # small rounded stones
28
- SAND: :sand,
29
- GRADE: :graded_earth, # graded or rolled earth possibly with some grass
30
- GRASS: :grass, # lawn
31
- WATER: :water,
32
- OTHER: :other # specify in remarks
33
- }.freeze
34
-
35
22
  STATUSES = {
36
23
  CLSD: :closed,
37
24
  WIP: :work_in_progress, # e.g. construction work
@@ -59,8 +46,8 @@ module AIXM
59
46
  # @return [AIXM::D, nil] width
60
47
  attr_reader :width
61
48
 
62
- # @return [Symbol, nil] composition of the surface (see {COMPOSITIONS})
63
- attr_reader :composition
49
+ # @return [AIXM::Component::Surface] surface of the helipad
50
+ attr_reader :surface
64
51
 
65
52
  # @return [Symbol, nil] status of the helipad (see {STATUSES}) or +nil+ for normal operation
66
53
  attr_reader :status
@@ -70,11 +57,12 @@ module AIXM
70
57
 
71
58
  def initialize(name:)
72
59
  self.name = name
60
+ @surface = AIXM.surface
73
61
  end
74
62
 
75
63
  # @return [String]
76
64
  def inspect
77
- %Q(#<#{self.class} name=#{name.inspect}>)
65
+ %Q(#<#{self.class} airport=#{airport&.id.inspect} name=#{name.inspect}>)
78
66
  end
79
67
 
80
68
  def airport=(value)
@@ -114,10 +102,6 @@ module AIXM
114
102
  end
115
103
  end
116
104
 
117
- def composition=(value)
118
- @composition = value.nil? ? nil : COMPOSITIONS.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid composition")
119
- end
120
-
121
105
  def status=(value)
122
106
  @status = value.nil? ? nil : (STATUSES.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid status"))
123
107
  end
@@ -151,7 +135,9 @@ module AIXM
151
135
  tla.valWid(width.dist.trim) if width
152
136
  tla.uomDim(length.unit.to_s.upcase) if length
153
137
  tla.uomDim(width.unit.to_s.upcase) if width && !length
154
- tla.codeComposition(COMPOSITIONS.key(composition).to_s) if composition
138
+ unless (xml = surface.to_xml).empty?
139
+ tla << xml.indent(2)
140
+ end
155
141
  tla.codeSts(STATUSES.key(status).to_s) if status
156
142
  tla.txtRmk(remarks) if remarks
157
143
  end
@@ -18,14 +18,15 @@ module AIXM
18
18
  # )
19
19
  # runway.length = AIXM.d or nil # must use same unit as width
20
20
  # runway.width = AIXM.d or nil # must use same unit as length
21
- # runway.composition = COMPOSITIONS or nil
21
+ # runway.surface = AIXM.surface
22
22
  # runway.status = STATUSES or nil
23
23
  # runway.remarks = String or nil
24
- # runway.forth.name = AIXM.h # preset based on the runway name
25
- # runway.forth.geographic_orientation = Integer or nil # degrees
24
+ # runway.forth.name = AIXM.a[precision=2] # preset based on the runway name
25
+ # runway.forth.geographic_orientation = AIXM.a[precision=3] or nil
26
26
  # runway.forth.xy = AIXM.xy
27
27
  # runway.forth.z = AIXM.z or nil
28
28
  # runway.forth.displaced_threshold = AIXM.xy or AIXM.d or nil
29
+ # runway.forth.vfr_pattern = VFR_PATTERNS or nil
29
30
  # runway.forth.remarks = String or nil
30
31
  #
31
32
  # @example Bidirectional runway
@@ -45,19 +46,6 @@ module AIXM
45
46
  #
46
47
  # @see https://github.com/openflightmaps/ofmx/wiki/Airport#rwy-runway
47
48
  class Runway
48
- COMPOSITIONS = {
49
- ASPH: :asphalt,
50
- BITUM: :bitumen, # dug up, bound and rolled ground
51
- CONC: :concrete,
52
- GRAVE: :gravel, # small and midsize rounded stones
53
- MACADAM: :macadam, # small rounded stones
54
- SAND: :sand,
55
- GRADE: :graded_earth, # graded or rolled earth possibly with some grass
56
- GRASS: :grass, # lawn
57
- WATER: :water,
58
- OTHER: :other # specify in remarks
59
- }
60
-
61
49
  STATUSES = {
62
50
  CLSD: :closed,
63
51
  WIP: :work_in_progress, # e.g. construction work
@@ -79,8 +67,8 @@ module AIXM
79
67
  # @return [AIXM::D, nil] width
80
68
  attr_reader :width
81
69
 
82
- # @return [Symbol, nil] composition of the surface (see {COMPOSITIONS})
83
- attr_reader :composition
70
+ # @return [AIXM::Component::Surface] surface of the runway
71
+ attr_reader :surface
84
72
 
85
73
  # @return [Symbol, nil] status of the runway (see {STATUSES}) or +nil+ for normal operation
86
74
  attr_reader :status
@@ -96,16 +84,17 @@ module AIXM
96
84
 
97
85
  def initialize(name:)
98
86
  self.name = name
99
- @name.split('/').tap do |forth, back|
100
- @forth = Direction.new(runway: self, name: AIXM.h(forth))
101
- @back = Direction.new(runway: self, name: AIXM.h(back)) if back
87
+ @name.split("/").tap do |forth, back|
88
+ @forth = Direction.new(runway: self, name: AIXM.a(forth))
89
+ @back = Direction.new(runway: self, name: AIXM.a(back)) if back
102
90
  fail(ArgumentError, "invalid name") unless !@back || @back.name.inverse_of?(@forth.name)
103
91
  end
92
+ @surface = AIXM.surface
104
93
  end
105
94
 
106
95
  # @return [String]
107
96
  def inspect
108
- %Q(#<#{self.class} name=#{name.inspect}>)
97
+ %Q(#<#{self.class} airport=#{airport&.id.inspect} name=#{name.inspect}>)
109
98
  end
110
99
 
111
100
  def airport=(value)
@@ -135,10 +124,6 @@ module AIXM
135
124
  end
136
125
  end
137
126
 
138
- def composition=(value)
139
- @composition = value.nil? ? nil : COMPOSITIONS.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid composition")
140
- end
141
-
142
127
  def status=(value)
143
128
  @status = value.nil? ? nil : (STATUSES.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid status"))
144
129
  end
@@ -165,7 +150,9 @@ module AIXM
165
150
  rwy.valWid(width.dist.trim) if width
166
151
  rwy.uomDimRwy(length.unit.to_s.upcase) if length
167
152
  rwy.uomDimRwy(width.unit.to_s.upcase) if width && !length
168
- rwy.codeComposition(COMPOSITIONS.key(composition).to_s) if composition
153
+ unless (xml = surface.to_xml).empty?
154
+ rwy << xml.indent(2)
155
+ end
169
156
  rwy.codeSts(STATUSES.key(status).to_s) if status
170
157
  rwy.txtRmk(remarks) if remarks
171
158
  end
@@ -181,13 +168,19 @@ module AIXM
181
168
  #
182
169
  # @see https://github.com/openflightmaps/ofmx/wiki/Airport#rdn-runway-direction
183
170
  class Direction
171
+ VFR_PATTERNS = {
172
+ L: :left,
173
+ R: :right,
174
+ E: :left_or_right
175
+ }
176
+
184
177
  # @return [AIXM::Component::Runway] runway the runway direction is further describing
185
178
  attr_reader :runway
186
179
 
187
- # @return [AIXM::H] partial name of runway (e.g. "12" or "16L")
180
+ # @return [AIXM::A] partial name of runway (e.g. "12" or "16L")
188
181
  attr_reader :name
189
182
 
190
- # @return [Integer, nil] geographic orientation (true bearing) in degrees
183
+ # @return [AIXM::A, nil] geographic orientation (true bearing) in degrees
191
184
  attr_reader :geographic_orientation
192
185
 
193
186
  # @return [AIXM::XY] beginning point (middle of the runway width)
@@ -201,6 +194,9 @@ module AIXM
201
194
  # point
202
195
  attr_reader :displaced_threshold
203
196
 
197
+ # @return [Symbol, nil] direction of the VFR flight pattern (see {VFR_PATTERNS})
198
+ attr_reader :vfr_pattern
199
+
204
200
  # @return [String, nil] free text remarks
205
201
  attr_reader :remarks
206
202
 
@@ -210,7 +206,7 @@ module AIXM
210
206
 
211
207
  # @return [String]
212
208
  def inspect
213
- %Q(#<#{self.class} name=#{name.inspect}>)
209
+ %Q(#<#{self.class} airport=#{runway&.airport&.id.inspect} name=#{name.inspect}>)
214
210
  end
215
211
 
216
212
  def runway=(value)
@@ -220,15 +216,14 @@ module AIXM
220
216
  private :runway
221
217
 
222
218
  def name=(value)
223
- fail(ArgumentError, "invalid name") unless value.is_a? AIXM::H
219
+ fail(ArgumentError, "invalid name") unless value.is_a? AIXM::A
224
220
  @name = value
225
221
  end
226
222
 
227
223
  def geographic_orientation=(value)
228
224
  return @geographic_orientation = nil if value.nil?
229
- fail(ArgumentError, "invalid geographic orientation") unless value.is_a? Numeric
230
- @geographic_orientation = value.to_i
231
- fail(ArgumentError, "invalid geographic orientation") unless (1..360).include? @geographic_orientation
225
+ fail(ArgumentError, "invalid geographic orientation") unless value.is_a? AIXM::A
226
+ @geographic_orientation = value
232
227
  end
233
228
 
234
229
  def xy=(value)
@@ -255,14 +250,18 @@ module AIXM
255
250
  end
256
251
  end
257
252
 
253
+ def vfr_pattern=(value)
254
+ @vfr_pattern = value.nil? ? nil : (VFR_PATTERNS.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid VFR pattern"))
255
+ end
256
+
258
257
  def remarks=(value)
259
258
  @remarks = value&.to_s
260
259
  end
261
260
 
262
- # @return [Integer] magnetic orientation (magnetic bearing) in degrees
261
+ # @return [AIXM::A] magnetic orientation (magnetic bearing) in degrees
263
262
  def magnetic_orientation
264
263
  if geographic_orientation && runway.airport.declination
265
- (geographic_orientation + runway.airport.declination).round
264
+ geographic_orientation + runway.airport.declination
266
265
  end
267
266
  end
268
267
 
@@ -282,6 +281,7 @@ module AIXM
282
281
  rdn.valElevTdz(z.alt)
283
282
  rdn.uomElevTdz(z.unit.upcase.to_s)
284
283
  end
284
+ rdn.codeVfrPattern(VFR_PATTERNS.key(vfr_pattern).to_s) if vfr_pattern
285
285
  rdn.txtRmk(remarks) if remarks
286
286
  end
287
287
  if displaced_threshold