aixm 0.3.5 → 0.3.6

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.
@@ -8,7 +8,7 @@ module AIXM
8
8
  # ===Cheat Sheet in Pseudo Code:
9
9
  # airspace = AIXM.airspace(
10
10
  # source: String or nil
11
- # id: String
11
+ # id: String or nil # nil is converted to an 8 character digest
12
12
  # type: String or Symbol
13
13
  # local_type: String or nil
14
14
  # name: String or nil
@@ -16,6 +16,10 @@ module AIXM
16
16
  # airspace.geometry << AIXM.point or AIXM.arc or AIXM.border or AIXM.circle
17
17
  # airspace.layers << AIXM.layer
18
18
  #
19
+ # The +id+ is mandatory, however, you may omit it when initializing a new
20
+ # airspace or assign +nil+ to an existing airspace which will generate a 8
21
+ # character digest from +type+, +local_type+ and +name+.
22
+ #
19
23
  # Some regions define additional airspace types. In LF (France) for
20
24
  # intance, the types RMZ (radio mandatory zone) and TMZ (transponder
21
25
  # mandatory zone) exist. Such airspaces are usually specified together
@@ -105,6 +109,8 @@ module AIXM
105
109
  %Q(#<#{self.class} type=#{type.inspect} name=#{name.inspect}>)
106
110
  end
107
111
 
112
+ # The +id+ is mandatory, however, you may assign +nil+ which will generate
113
+ # an 8 character digest from +type+, +local_type+ and +name+.
108
114
  def id=(value)
109
115
  fail(ArgumentError, "invalid id") unless value.nil? || value.is_a?(String)
110
116
  @id = value&.uptrans || [type, local_type, name].to_digest.upcase
@@ -100,7 +100,7 @@ module AIXM
100
100
 
101
101
  # @return [String]
102
102
  def inspect
103
- %Q(#<#{self.class} name=#{name.inspect} type=#{type.inspect}>)
103
+ %Q(#<#{original_class} name=#{name.inspect} type=#{type.inspect}>)
104
104
  end
105
105
 
106
106
  def organisation=(value)
@@ -118,7 +118,9 @@ module AIXM
118
118
  end
119
119
 
120
120
  # @!attribute class
121
+ # @note Use +original_class+ to query the Ruby object class.
121
122
  # @return [Symbol] class of unit (see {CLASSES})
123
+ alias_method :original_class, :class
122
124
  def class
123
125
  @klass
124
126
  end
@@ -0,0 +1,88 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+
5
+ # pressure
6
+ #
7
+ # @example
8
+ # AIXM.d(14, :bar)
9
+ class P
10
+ include Comparable
11
+ extend Forwardable
12
+
13
+ UNITS = {
14
+ p: { mpa: 0.000001, psi: 0.000145037738, bar: 0.00001, torr: 0.0075006 },
15
+ mpa: { p: 1_000_000, psi: 145.037738, bar: 10, torr: 7500.6 },
16
+ psi: { p: 6894.75729, mpa: 0.00689475729, bar: 0.0689475729, torr: 51.714816529374 },
17
+ bar: { p: 100000, mpa: 0.1, psi: 14.5037738, torr: 750.06 },
18
+ torr: { p: 133.322, mpa: 0.000133322, psi: 0.019336721305636, bar: 0.00133322 }
19
+ }.freeze
20
+
21
+ # @!method zero?
22
+ # @return [Boolean] whether pressure is zero
23
+ def_delegator :@pres, :zero?
24
+
25
+ # @return [Float] pressure
26
+ attr_reader :pres
27
+
28
+ # @return [Symbol] unit (see {UNITS})
29
+ attr_reader :unit
30
+
31
+ def initialize(pres, unit)
32
+ self.pres, self.unit = pres, unit
33
+ end
34
+
35
+ # @return [String]
36
+ def inspect
37
+ %Q(#<#{self.class} #{to_s}>)
38
+ end
39
+
40
+ # @return [String] human readable representation (e.g. "14 bar")
41
+ def to_s
42
+ [pres, unit].join(' ')
43
+ end
44
+
45
+ def pres=(value)
46
+ fail(ArgumentError, "invalid pres") unless value.is_a?(Numeric) && value >= 0
47
+ @pres = value.to_f
48
+ end
49
+
50
+ def unit=(value)
51
+ fail(ArgumentError, "invalid unit") unless value.respond_to? :to_sym
52
+ @unit = value.to_sym.downcase
53
+ fail(ArgumentError, "invalid unit") unless UNITS.has_key? @unit
54
+ end
55
+
56
+ # @!method to_p
57
+ # @!method to_mpa
58
+ # @!method to_psi
59
+ # @!method to_bar
60
+ # @!method to_torr
61
+ # @return [AIXM::P] convert pressure
62
+ UNITS.each_key do |target_unit|
63
+ define_method "to_#{target_unit}" do
64
+ return self if unit == target_unit
65
+ self.class.new((pres * UNITS[unit][target_unit]).round(8), target_unit)
66
+ end
67
+ end
68
+
69
+ # @see Object#<=>
70
+ # @return [Integer]
71
+ def <=>(other)
72
+ pres <=> other.send(:"to_#{unit}").pres
73
+ end
74
+
75
+ # @see Object#==
76
+ # @return [Boolean]
77
+ def ==(other)
78
+ self.class === other && (self <=> other).zero?
79
+ end
80
+ alias_method :eql?, :==
81
+
82
+ # @see Object#hash
83
+ # @return [Integer]
84
+ def hash
85
+ to_s.hash
86
+ end
87
+ end
88
+ end
@@ -15,21 +15,6 @@ module AIXM
15
15
  "Ø" => "Oe"
16
16
  }.freeze
17
17
 
18
- # @!method then_if
19
- # Same as +Object#then+ but only applied if the condition is true.
20
- #
21
- # @example
22
- # "foobar".then_if(false) { |s| s.gsub(/o/, 'i') } # => "foobar"
23
- # "foobar".then_if(true) { |s| s.gsub(/o/, 'i') } # => "fiibar"
24
- #
25
- # @note This is a refinement for +Object+
26
- # @return [Object]
27
- refine Object do
28
- def then_if(condition, &block)
29
- condition ? self.then(&block) : self
30
- end
31
- end
32
-
33
18
  # @!method to_digest
34
19
  # Builds a 4 byte hex digest from the Array payload.
35
20
  #
@@ -149,6 +134,21 @@ module AIXM
149
134
  end
150
135
  end
151
136
 
137
+ # @!method then_if
138
+ # Same as +Object#then+ but only applied if the condition is true.
139
+ #
140
+ # @example
141
+ # "foobar".then_if(false) { |s| s.gsub(/o/, 'i') } # => "foobar"
142
+ # "foobar".then_if(true) { |s| s.gsub(/o/, 'i') } # => "fiibar"
143
+ #
144
+ # @note This is a refinement for +Object+
145
+ # @return [Object]
146
+ refine Object do
147
+ def then_if(condition, &block)
148
+ condition ? self.then(&block) : self
149
+ end
150
+ end
151
+
152
152
  # @!method decapture
153
153
  # Replace all groups with non-caputuring groups
154
154
  #
@@ -8,6 +8,8 @@ module AIXM
8
8
  d: D,
9
9
  f: F,
10
10
  a: A,
11
+ w: W,
12
+ p: P,
11
13
  address: Feature::Address,
12
14
  organisation: Feature::Organisation,
13
15
  unit: Feature::Unit,
@@ -15,8 +17,10 @@ module AIXM
15
17
  frequency: Component::Frequency,
16
18
  airport: Feature::Airport,
17
19
  runway: Component::Runway,
20
+ fato: Component::FATO,
18
21
  helipad: Component::Helipad,
19
22
  surface: Component::Surface,
23
+ lighting: Component::Lighting,
20
24
  airspace: Feature::Airspace,
21
25
  layer: Component::Layer,
22
26
  geometry: Component::Geometry,
@@ -1,3 +1,3 @@
1
1
  module AIXM
2
- VERSION = "0.3.5".freeze
2
+ VERSION = "0.3.6".freeze
3
3
  end
@@ -0,0 +1,86 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+
5
+ # Weight
6
+ #
7
+ # @example
8
+ # AIXM.w(2.9, :t)
9
+ class W
10
+ include Comparable
11
+ extend Forwardable
12
+
13
+ UNITS = {
14
+ kg: { t: 0.001, lb: 2.204622622, ton: 0.00110231131 },
15
+ t: { kg: 1000, lb: 2204.622622, ton: 1.10231131 },
16
+ lb: { kg: 0.45359237, t: 0.00045359237, ton: 0.000499999999581 },
17
+ ton: { kg: 907.18474, t: 0.90718474, lb: 2000.00000013718828 }
18
+ }.freeze
19
+
20
+ # @!method zero?
21
+ # @return [Boolean] whether weight is zero
22
+ def_delegator :@wgt, :zero?
23
+
24
+ # @return [Float] weight
25
+ attr_reader :wgt
26
+
27
+ # @return [Symbol] unit (see {UNITS})
28
+ attr_reader :unit
29
+
30
+ def initialize(wgt, unit)
31
+ self.wgt, self.unit = wgt, unit
32
+ end
33
+
34
+ # @return [String]
35
+ def inspect
36
+ %Q(#<#{self.class} #{to_s}>)
37
+ end
38
+
39
+ # @return [String] human readable representation (e.g. "123 t")
40
+ def to_s
41
+ [wgt, unit].join(' ')
42
+ end
43
+
44
+ def wgt=(value)
45
+ fail(ArgumentError, "invalid wgt") unless value.is_a?(Numeric) && value >= 0
46
+ @wgt = value.to_f
47
+ end
48
+
49
+ def unit=(value)
50
+ fail(ArgumentError, "invalid unit") unless value.respond_to? :to_sym
51
+ @unit = value.to_sym.downcase
52
+ fail(ArgumentError, "invalid unit") unless UNITS.has_key? @unit
53
+ end
54
+
55
+ # @!method to_kg
56
+ # @!method to_t
57
+ # @!method to_lb
58
+ # @!method to_ton
59
+ # @return [AIXM::W] convert weight
60
+ UNITS.each_key do |target_unit|
61
+ define_method "to_#{target_unit}" do
62
+ return self if unit == target_unit
63
+ self.class.new((wgt * UNITS[unit][target_unit]).round(8), target_unit)
64
+ end
65
+ end
66
+
67
+ # @see Object#<=>
68
+ # @return [Integer]
69
+ def <=>(other)
70
+ wgt <=> other.send(:"to_#{unit}").wgt
71
+ end
72
+
73
+ # @see Object#==
74
+ # @return [Boolean]
75
+ def ==(other)
76
+ self.class === other && (self <=> other).zero?
77
+ end
78
+ alias_method :eql?, :==
79
+
80
+ # @see Object#hash
81
+ # @return [Integer]
82
+ def hash
83
+ to_s.hash
84
+ end
85
+ end
86
+ end
@@ -69,6 +69,12 @@ module AIXM
69
69
  end
70
70
  end
71
71
 
72
+ # @return [Boolean] +false+ if both longitude and latitude have zero DMS
73
+ # seconds which may indicate rounded or estimated coordinates
74
+ def seconds?
75
+ !(long.to_dms[-6,5].to_f.zero? && lat.to_dms[-6,5].to_f.zero?)
76
+ end
77
+
72
78
  # @return [AIXM::Component::Geometry::Point] convert to point
73
79
  def to_point
74
80
  AIXM.point(xy: self)
@@ -9,4 +9,14 @@ Rake::TestTask.new do |t|
9
9
  t.warning = true
10
10
  end
11
11
 
12
+ desc "Run local YARD documentation server"
13
+ task :yard do
14
+ `rm -rf ./.yardoc`
15
+ Thread.new do
16
+ sleep 2
17
+ `open http://localhost:8808`
18
+ end
19
+ `yard server -r`
20
+ end
21
+
12
22
  task default: :test
@@ -24,6 +24,14 @@ module AIXM
24
24
  AIXM.a('34L')
25
25
  end
26
26
 
27
+ def w
28
+ AIXM.w(1.5, :t)
29
+ end
30
+
31
+ def p
32
+ AIXM.p(0.5, :mpa)
33
+ end
34
+
27
35
  # Components
28
36
 
29
37
  def address
@@ -36,11 +44,22 @@ module AIXM
36
44
  end
37
45
  end
38
46
 
47
+ def lighting
48
+ AIXM.lighting(
49
+ position: :aiming_point
50
+ ).tap do |lighting|
51
+ lighting.description = "omnidirectional"
52
+ lighting.intensity = :medium
53
+ lighting.color = :green
54
+ lighting.remarks = "lighting remarks"
55
+ end
56
+ end
57
+
39
58
  def timetable
40
59
  AIXM.timetable(
41
60
  code: :sunrise_to_sunset
42
61
  ).tap do |timetable|
43
- timetable.remarks = "timetable remarks"
62
+ timetable.remarks = "timetable remarks"
44
63
  end
45
64
  end
46
65
 
@@ -317,9 +336,12 @@ module AIXM
317
336
  airport.z = AIXM.z(146, :qnh)
318
337
  airport.declination = 1.08
319
338
  airport.transition_z = AIXM.z(10_000, :qnh)
339
+ airport.operator = "Municipality of Pujaut"
320
340
  airport.remarks = "Restricted access"
321
341
  airport.add_runway(runway)
342
+ airport.add_fato(fato)
322
343
  airport.add_helipad(helipad)
344
+ airport.helipads.first.fato = airport.fatos.first # necessary when using factories only
323
345
  airport.add_usage_limitation :permitted
324
346
  airport.add_usage_limitation(:reservation_required) do |reservation_required|
325
347
  reservation_required.add_condition { |c| c.aircraft = :glider }
@@ -339,6 +361,9 @@ module AIXM
339
361
  runway.surface.preparation = :paved
340
362
  runway.surface.condition = :good
341
363
  runway.surface.pcn = "59/F/A/W/T"
364
+ runway.surface.siwl_weight = AIXM.w(1500, :kg)
365
+ runway.surface.siwl_tire_pressure = AIXM.p(0.5, :mpa)
366
+ runway.surface.auw_weight = AIXM.w(30, :t)
342
367
  runway.surface.remarks = "Paved shoulder on 2.5m on each side of the RWY."
343
368
  runway.status = :closed
344
369
  runway.remarks = "Markings eroded"
@@ -348,18 +373,46 @@ module AIXM
348
373
  runway.forth.geographic_orientation = AIXM.a(165)
349
374
  runway.forth.vfr_pattern = :left_or_right
350
375
  runway.forth.remarks = "forth remarks"
376
+ runway.forth.add_lighting(lighting)
351
377
  runway.back.xy = AIXM.xy(lat: %q(43°59'25.31"N), long: %q(004°45'23.24"E))
352
378
  runway.back.z = AIXM.z(147, :qnh)
353
379
  runway.back.displaced_threshold = AIXM.xy(lat: %q(43°59'31.84"N), long: %q(004°45'20.85"E))
354
380
  runway.back.geographic_orientation = AIXM.a(345)
355
381
  runway.back.vfr_pattern = :left
356
382
  runway.back.remarks = "back remarks"
383
+ runway.back.add_lighting(lighting)
384
+ end
385
+ end
386
+
387
+ def fato
388
+ AIXM.fato(name: 'H1').tap do |fato|
389
+ fato.length = AIXM.d(35, :m)
390
+ fato.width = AIXM.d(35, :m)
391
+ fato.surface.composition = :concrete
392
+ fato.surface.preparation = :paved
393
+ fato.surface.condition = :fair
394
+ fato.surface.pcn = "30/F/A/W/U"
395
+ fato.surface.siwl_weight = AIXM.w(1500, :kg)
396
+ fato.surface.siwl_tire_pressure = AIXM.p(0.5, :mpa)
397
+ fato.surface.auw_weight = AIXM.w(8, :t)
398
+ fato.surface.remarks = "Cracks near the center"
399
+ fato.profile = "Northwest from RWY 12/30"
400
+ fato.marking = "Dashed white lines"
401
+ fato.status = :other
402
+ fato.remarks = "Authorizaton by AD operator required"
403
+ fato.add_direction(name: '35') do |direction|
404
+ direction.geographic_orientation = AIXM.a(355)
405
+ direction.remarks = "Avoid flight over residental area"
406
+ direction.add_lighting(lighting)
407
+ end
357
408
  end
358
409
  end
359
410
 
360
411
  def helipad
361
- AIXM.helipad(name: 'H1').tap do |helipad|
362
- helipad.xy = AIXM.xy(lat: %q(43°59'56.94"N), long: %q(004°45'05.56"E))
412
+ AIXM.helipad(
413
+ name: 'H1',
414
+ xy: AIXM.xy(lat: %q(43°59'56.94"N), long: %q(004°45'05.56"E))
415
+ ).tap do |helipad|
363
416
  helipad.z = AIXM.z(141, :qnh)
364
417
  helipad.length = AIXM.d(20, :m)
365
418
  helipad.width = AIXM.d(20, :m)
@@ -367,9 +420,15 @@ module AIXM
367
420
  helipad.surface.preparation = :paved
368
421
  helipad.surface.condition = :fair
369
422
  helipad.surface.pcn = "30/F/A/W/U"
370
- helipad.surface.remarks = "Cracks near the center."
423
+ helipad.surface.siwl_weight = AIXM.w(1500, :kg)
424
+ helipad.surface.siwl_tire_pressure = AIXM.p(0.5, :mpa)
425
+ helipad.surface.auw_weight = AIXM.w(8, :t)
426
+ helipad.surface.remarks = "Cracks near the center"
427
+ helipad.marking = "Continuous white lines"
428
+ helipad.helicopter_class = 1
371
429
  helipad.status = :other
372
430
  helipad.remarks = "Authorizaton by AD operator required"
431
+ helipad.add_lighting(lighting)
373
432
  end
374
433
  end
375
434
 
@@ -470,11 +529,10 @@ module AIXM
470
529
  # Document
471
530
 
472
531
  def document
473
- time = Time.parse('2018-01-01 12:00:00 +0100')
474
532
  AIXM.document(
475
533
  region: 'LF',
476
534
  namespace: '00000000-0000-0000-0000-000000000000',
477
- created_at: time,
535
+ created_at: (time = Time.parse('2018-01-01 12:00:00 +0100')),
478
536
  effective_at: time
479
537
  ).tap do |document|
480
538
  document.features << organisation