aixm 1.0.0 → 1.1.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +43 -13
  4. data/README.md +31 -20
  5. data/lib/aixm/a.rb +89 -71
  6. data/lib/aixm/association.rb +37 -27
  7. data/lib/aixm/classes.rb +5 -2
  8. data/lib/aixm/{feature → component}/address.rb +12 -9
  9. data/lib/aixm/component/approach_lighting.rb +136 -0
  10. data/lib/aixm/component/fato.rb +58 -42
  11. data/lib/aixm/component/frequency.rb +2 -2
  12. data/lib/aixm/component/geometry/arc.rb +1 -1
  13. data/lib/aixm/component/geometry/border.rb +1 -1
  14. data/lib/aixm/component/geometry/circle.rb +3 -3
  15. data/lib/aixm/component/geometry/point.rb +1 -1
  16. data/lib/aixm/component/geometry/rhumb_line.rb +1 -1
  17. data/lib/aixm/component/geometry.rb +3 -2
  18. data/lib/aixm/component/helipad.rb +26 -36
  19. data/lib/aixm/component/layer.rb +5 -3
  20. data/lib/aixm/component/lighting.rb +5 -5
  21. data/lib/aixm/component/runway.rb +81 -52
  22. data/lib/aixm/component/service.rb +12 -3
  23. data/lib/aixm/component/surface.rb +12 -12
  24. data/lib/aixm/component/timetable.rb +2 -2
  25. data/lib/aixm/component/vasis.rb +105 -0
  26. data/lib/aixm/component/vertical_limit.rb +3 -3
  27. data/lib/aixm/component.rb +10 -0
  28. data/lib/aixm/config.rb +2 -0
  29. data/lib/aixm/d.rb +16 -15
  30. data/lib/aixm/document.rb +10 -1
  31. data/lib/aixm/f.rb +1 -1
  32. data/lib/aixm/feature/airport.rb +34 -10
  33. data/lib/aixm/feature/airspace.rb +3 -0
  34. data/lib/aixm/feature/navigational_aid/dme.rb +29 -10
  35. data/lib/aixm/feature/navigational_aid/marker.rb +2 -2
  36. data/lib/aixm/feature/navigational_aid/tacan.rb +3 -2
  37. data/lib/aixm/feature/navigational_aid/vor.rb +32 -13
  38. data/lib/aixm/feature/navigational_aid.rb +1 -1
  39. data/lib/aixm/feature/obstacle.rb +6 -6
  40. data/lib/aixm/feature/obstacle_group.rb +6 -2
  41. data/lib/aixm/feature/organisation.rb +1 -0
  42. data/lib/aixm/feature/unit.rb +2 -1
  43. data/lib/aixm/feature.rb +3 -0
  44. data/lib/aixm/memoize.rb +27 -11
  45. data/lib/aixm/p.rb +3 -2
  46. data/lib/aixm/payload_hash.rb +1 -1
  47. data/lib/aixm/r.rb +62 -0
  48. data/lib/aixm/refinements.rb +4 -4
  49. data/lib/aixm/version.rb +1 -1
  50. data/lib/aixm/w.rb +2 -1
  51. data/lib/aixm/xy.rb +1 -1
  52. data/lib/aixm/z.rb +5 -4
  53. data/lib/aixm.rb +10 -5
  54. data/schemas/ofmx/0.1/OFMX-DataTypes.xsd +6 -0
  55. data/schemas/ofmx/0.1/OFMX-Snapshot.xsd +5 -0
  56. data.tar.gz.sig +0 -0
  57. metadata +8 -4
  58. metadata.gz.sig +0 -0
data/lib/aixm/document.rb CHANGED
@@ -22,6 +22,7 @@ module AIXM
22
22
  # @!method features
23
23
  # @return [Array<AIXM::Feature>] features (e.g. airport or airspace) present
24
24
  # in this document
25
+ #
25
26
  # @!method add_feature(feature)
26
27
  # @param feature [AIXM::Feature]
27
28
  # @return [self]
@@ -58,6 +59,13 @@ module AIXM
58
59
  @effective_at = value&.to_time || created_at || Time.now
59
60
  end
60
61
 
62
+ # Regions used throughout this document.
63
+ #
64
+ # @return [Array<String>] white space separated list of region codes
65
+ def regions
66
+ features.map(&:region).uniq.sort
67
+ end
68
+
61
69
  # Compare all ungrouped obstacles and create new obstacle groups whose
62
70
  # members are located within +max_distance+ pairwise.
63
71
  #
@@ -110,6 +118,7 @@ module AIXM
110
118
  version: AIXM.schema(:version),
111
119
  origin: "rubygem aixm-#{AIXM::VERSION}",
112
120
  namespace: (namespace if AIXM.ofmx?),
121
+ regions: (regions.join(' '.freeze) if AIXM.ofmx?),
113
122
  created: @created_at.xmlschema,
114
123
  effective: @effective_at.xmlschema
115
124
  }.compact
@@ -117,7 +126,7 @@ module AIXM
117
126
  builder.instruct!
118
127
  builder.tag!(AIXM.schema(:root), meta) do |root|
119
128
  AIXM::Memoize.method :to_uid do
120
- root << features.map { _1.to_xml }.join.indent(2)
129
+ root << features.map(&:to_xml).join.indent(2)
121
130
  end
122
131
  end
123
132
  if AIXM.ofmx? && AIXM.config.mid
data/lib/aixm/f.rb CHANGED
@@ -32,7 +32,7 @@ module AIXM
32
32
 
33
33
  # @return [String] human readable representation (e.g. "123.35 mhz")
34
34
  def to_s
35
- [freq, unit].join(' ')
35
+ [freq, unit].join(' '.freeze)
36
36
  end
37
37
 
38
38
  def freq=(value)
@@ -16,7 +16,7 @@ module AIXM
16
16
  # xy: AIXM.xy
17
17
  # )
18
18
  # airport.gps = String or nil
19
- # airport.type = TYPES
19
+ # airport.type = TYPES (other than AD, HP and AH only)
20
20
  # airport.z = AIXM.z or nil
21
21
  # airport.declination = Float or nil
22
22
  # airport.transition_z = AIXM.z or nil
@@ -27,6 +27,8 @@ module AIXM
27
27
  # airport.add_fato(AIXM.fato)
28
28
  # airport.add_helipad(AIXM.helipad)
29
29
  # airport.add_usage_limitation(UsageLimitation::TYPES)
30
+ # airport.add_unit(AIXM.unit)
31
+ # airport.add_service(AIXM.service)
30
32
  # airport.add_address(AIXM.address)
31
33
  #
32
34
  # For airports without an +id+, you may assign the two character region
@@ -48,33 +50,30 @@ module AIXM
48
50
  LS: :landing_site
49
51
  }.freeze
50
52
 
51
- # @!method addresses
52
- # @return [Array<AIXM::Feature::Address>] postal address, url, A/A or A/G frequency etc
53
- # @!method add_address(address)
54
- # @param address [AIXM::Feature::Address]
55
- # @return [self]
56
- has_many :addresses, as: :addressable
57
-
58
53
  # @!method fatos
59
54
  # @return [Array<AIXM::Component::FATO>] FATOs present at this airport
55
+ #
60
56
  # @!method add_fato(fato)
61
57
  # @param fato [AIXM::Component::FATO]
62
58
  has_many :fatos
63
59
 
64
60
  # @!method helipads
65
61
  # @return [Array<AIXM::Component::Helipad>] helipads present at this airport
62
+ #
66
63
  # @!method add_helipad(helipad)
67
64
  # @param helipad [AIXM::Component::Helipad]
68
65
  has_many :helipads
69
66
 
70
67
  # @!method runways
71
68
  # @return [Array<AIXM::Component::Runway>] runways present at this airport
69
+ #
72
70
  # @!method add_runway(runway)
73
71
  # @param runway [AIXM::Component::Runway]
74
72
  has_many :runways
75
73
 
76
74
  # @!method usage_limitations
77
75
  # @return [Array<AIXM::Feature::Airport::UsageLimitation>] usage limitations
76
+ #
78
77
  # @!method add_usage_limitation
79
78
  # @yield [AIXM::Feature::Airport::UsageLimitation]
80
79
  # @return [self]
@@ -82,16 +81,33 @@ module AIXM
82
81
 
83
82
  # @!method designated_points
84
83
  # @return [Array<AIXM::Feature::NavigationalAid::DesignatedPoint>] designated points
84
+ #
85
85
  # @!method add_designated_point(designated_point)
86
86
  # @param designated_point [AIXM::Feature::NavigationalAid::DesignatedPoint]
87
87
  has_many :designated_points
88
88
 
89
89
  # @!method units
90
90
  # @return [Array<AIXM::Feature::Unit>] units
91
+ #
91
92
  # @!method add_unit(unit)
92
93
  # @param unit [AIXM::Feature::Unit]
93
94
  has_many :units
94
95
 
96
+ # @!method services
97
+ # @return [Array<AIXM::Component::Service>] services
98
+ #
99
+ # @!method add_service(service)
100
+ # @param service [AIXM::Component::Service]
101
+ has_many :services
102
+
103
+ # @!method addresses
104
+ # @return [Array<AIXM::Feature::Address>] postal address, url, A/A or A/G frequency etc
105
+ #
106
+ # @!method add_address(address)
107
+ # @param address [AIXM::Feature::Address]
108
+ # @return [self]
109
+ has_many :addresses, as: :addressable
110
+
95
111
  # @!method organisation
96
112
  # @return [AIXM::Feature::Organisation] superior organisation
97
113
  belongs_to :organisation, as: :member
@@ -144,8 +160,7 @@ module AIXM
144
160
 
145
161
  def initialize(source: nil, region: nil, organisation:, id: nil, name:, xy:)
146
162
  super(source: source, region: region)
147
- self.organisation, self.name, self.xy = organisation, name, xy
148
- self.id = id # name must already be set
163
+ self.organisation, self.name, self.id, self.xy = organisation, name, id, xy # name must be set before id
149
164
  end
150
165
 
151
166
  # @return [String]
@@ -293,6 +308,14 @@ module AIXM
293
308
  sequences[address.type] = (sequences[address.type] || 0) + 1
294
309
  builder << address.to_xml(as: :Aha, sequence: sequences[address.type])
295
310
  end
311
+ services.each do |service|
312
+ builder.Sah do |sah|
313
+ sah.SahUid do |sah_uid|
314
+ sah_uid << to_uid.indent(4)
315
+ sah_uid << service.to_uid.indent(4)
316
+ end
317
+ end
318
+ end
296
319
  builder.target!
297
320
  end
298
321
 
@@ -337,6 +360,7 @@ module AIXM
337
360
 
338
361
  # @!method conditions
339
362
  # @return [Array<AIXM::Feature::Airport::UsageLimitation::Condition>] conditions for this limitation to apply
363
+ #
340
364
  # @!method add_condition
341
365
  # @yield [AIXM::Feature::Airport::UsageLimitation::Condition]
342
366
  # @return [self]
@@ -80,18 +80,21 @@ module AIXM
80
80
 
81
81
  # @!method geometry
82
82
  # @return [AIXM::Component::Geometry] horizontal geometry shape
83
+ #
83
84
  # @!method geometry=(geometry)
84
85
  # @param geometry [AIXM::Component::Geometry]
85
86
  has_one :geometry
86
87
 
87
88
  # @!method layers
88
89
  # @return [Array<AIXM::Compoment::Layer>] vertical layers
90
+ #
89
91
  # @!method add_layer(layer)
90
92
  # @param layer [AIXM::Compoment::Layer]
91
93
  has_many :layers
92
94
 
93
95
  # @note When assigning +nil+, a 4 byte hex derived from {#type}, {#name}
94
96
  # and {#local_type} is written instead.
97
+ #
95
98
  # @return [String] published identifier (e.g. "LFP81")
96
99
  attr_reader :id
97
100
 
@@ -18,7 +18,8 @@ module AIXM
18
18
  # name: String
19
19
  # xy: AIXM.xy
20
20
  # z: AIXM.z or nil
21
- # channel: String
21
+ # channel: String # either set channel directly
22
+ # ghost_f: AIXM.f # or set channel via VOR ghost frequency
22
23
  # )
23
24
  # dme.timetable = AIXM.timetable or nil
24
25
  # dme.remarks = String or nil
@@ -31,6 +32,13 @@ module AIXM
31
32
 
32
33
  CHANNEL_RE = /\A([1-9]|[1-9]\d|1[0-1]\d|12[0-6])[XY]\z/.freeze
33
34
 
35
+ GHOST_MAP = {
36
+ 108_00 => (17..59),
37
+ 112_30 => (70..126),
38
+ 133_30 => (60..69),
39
+ 134_40 => (1..16)
40
+ }.freeze
41
+
34
42
  # @!method vor
35
43
  # @return [AIXM::Feature::NavigationalAid::VOR, nil] associated VOR
36
44
  belongs_to :vor, readonly: true
@@ -38,9 +46,13 @@ module AIXM
38
46
  # @return [String] radio channel
39
47
  attr_reader :channel
40
48
 
41
- def initialize(channel:, **arguments)
49
+ def initialize(channel: nil, ghost_f: nil, **arguments)
42
50
  super(**arguments)
43
- self.channel = channel
51
+ case
52
+ when channel then self.channel = channel
53
+ when ghost_f then self.ghost_f = ghost_f
54
+ else fail(ArgumentError, "either channel or ghost_f must be set")
55
+ end
44
56
  end
45
57
 
46
58
  def channel=(value)
@@ -48,17 +60,24 @@ module AIXM
48
60
  @channel = value
49
61
  end
50
62
 
63
+ def ghost_f=(value)
64
+ fail(ArgumentError, "invalid ghost_f") unless value.is_a?(AIXM::F) && value.unit == :mhz
65
+ integer, letter = (value.freq * 100).round, 'X'
66
+ unless (integer % 10).zero?
67
+ integer -= 5
68
+ letter = 'Y'
69
+ end
70
+ base = GHOST_MAP.keys.reverse.bsearch { _1 <= integer }
71
+ number = ((integer - base) / 10) + GHOST_MAP[base].min
72
+ self.channel = "#{number}#{letter}"
73
+ end
74
+
51
75
  # @return [AIXM::F] ghost frequency matching the channel
52
76
  def ghost_f
53
77
  if channel
54
78
  number, letter = channel.split(/(?=[XY])/)
55
- integer = case number.to_i
56
- when (1..16) then 13430
57
- when (17..59) then 10630
58
- when (60..69) then 12730
59
- when (70..126) then 10530
60
- end
61
- integer += number.to_i * 10
79
+ integer = GHOST_MAP.find { _2.include?(number.to_i) }.first
80
+ integer += (number.to_i - GHOST_MAP[integer].min) * 10
62
81
  integer += 5 if letter == 'Y'
63
82
  AIXM.f(integer.to_f / 100, :mhz)
64
83
  end
@@ -28,7 +28,7 @@ module AIXM
28
28
  # @see https://gitlab.com/openflightmaps/ofmx/wikis/Navigational-aid#mkr-marker-beacon
29
29
  class Marker < NavigationalAid
30
30
  include AIXM::Memoize
31
-
31
+
32
32
  public_class_method :new
33
33
 
34
34
  TYPES = {
@@ -46,7 +46,7 @@ module AIXM
46
46
  def initialize(type:, **arguments)
47
47
  super(**arguments)
48
48
  self.type = type
49
- warn("WARNING: Maker is not fully implemented yet due to the lack of ILS")
49
+ warn("WARNING: Marker is not fully implemented yet due to the lack of ILS")
50
50
  end
51
51
 
52
52
  def type=(value)
@@ -17,7 +17,8 @@ module AIXM
17
17
  # name: String
18
18
  # xy: AIXM.xy
19
19
  # z: AIXM.z or nil
20
- # channel: String
20
+ # channel: String # either set channel directly
21
+ # ghost_f: AIXM.f # or set channel via VOR ghost frequency
21
22
  # )
22
23
  # tacan.timetable = AIXM.timetable or nil
23
24
  # tacan.remarks = String or nil
@@ -25,7 +26,7 @@ module AIXM
25
26
  # @see https://gitlab.com/openflightmaps/ofmx/wikis/Navigational-aid#tcn-tacan
26
27
  class TACAN < DME
27
28
  include AIXM::Memoize
28
-
29
+
29
30
  public_class_method :new
30
31
 
31
32
  # @return [String] UID markup
@@ -23,13 +23,13 @@ module AIXM
23
23
  # )
24
24
  # vor.timetable = AIXM.timetable or nil
25
25
  # vor.remarks = String or nil
26
- # vor.associate_dme(channel: String) # turns the VOR into a VOR/DME
27
- # vor.associate_tacan(channel: String) # turns the VOR into a VORTAC
26
+ # vor.associate_dme # turns the VOR into a VOR/DME
27
+ # vor.associate_tacan # turns the VOR into a VORTAC
28
28
  #
29
29
  # @see https://gitlab.com/openflightmaps/ofmx/wikis/Navigational-aid#vor-vor
30
30
  class VOR < NavigationalAid
31
31
  include AIXM::Memoize
32
-
32
+
33
33
  public_class_method :new
34
34
 
35
35
  TYPES = {
@@ -47,12 +47,14 @@ module AIXM
47
47
 
48
48
  # @!method dme
49
49
  # @return [AIXM::Feature::NavigationalAid::DME, nil] associated DME
50
+ #
50
51
  # @!method dme=(dme)
51
52
  # @param dme [AIXM::Feature::NavigationalAid::DME, nil]
52
53
  has_one :dme, allow_nil: true
53
54
 
54
55
  # @!method tacan
55
56
  # @return [AIXM::Feature::NavigationalAid::TACAN, nil] associated TACAN
57
+ #
56
58
  # @!method tacan=(tacan)
57
59
  # @param tacan [AIXM::Feature::NavigationalAid::TACAN, nil]
58
60
  has_one :tacan, allow_nil: true
@@ -84,16 +86,33 @@ module AIXM
84
86
  @north = NORTHS.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid north")
85
87
  end
86
88
 
87
- # Associate a DME which turns the VOR into a VOR/DME
88
- def associate_dme(channel:)
89
- self.dme = AIXM.dme(region: region, organisation: organisation, id: id, name: name, xy: xy, z: z, channel: channel)
90
- dme.timetable, @dme.remarks = timetable, remarks
91
- end
92
-
93
- # Associate a TACAN which turns the VOR into a VORTAC
94
- def associate_tacan(channel:)
95
- self.tacan = AIXM.tacan(region: region, organisation: organisation, id: id, name: name, xy: xy, z: z, channel: channel)
96
- tacan.timetable, @tacan.remarks = timetable, remarks
89
+ # @!method associate_dme
90
+ # Build a DME associated to this VOR (which turns it into a VOR/DME)
91
+ #
92
+ # @return [AIXM::Feature::NavigationalAid::DME] associated DME
93
+ #
94
+ # @!method dassociate_tacan
95
+ # Build a TACAN associated to this VOR (which turns it into a VORTAC)
96
+ #
97
+ # @return [AIXM::Feature::NavigationalAid::TACAN] associated TACAN
98
+ %i(dme tacan).each do |secondary|
99
+ define_method("associate_#{secondary}") do
100
+ send("#{secondary}=",
101
+ AIXM.send(secondary,
102
+ region: region,
103
+ source: source,
104
+ organisation: organisation,
105
+ id: id,
106
+ name: name,
107
+ xy: xy,
108
+ z: z,
109
+ ghost_f: f
110
+ ).tap do |navigational_aid|
111
+ navigational_aid.timetable = timetable
112
+ navigational_aid.remarks = remarks
113
+ end
114
+ )
115
+ end
97
116
  end
98
117
 
99
118
  # @return [String] UID markup
@@ -72,7 +72,7 @@ module AIXM
72
72
 
73
73
  # @return [String] fully descriptive combination of {#class} and {#type} key
74
74
  def kind
75
- [self.class.name.split('::').last, type_key].compact.join(':')
75
+ [self.class.name.split('::').last, type_key].compact.join(':'.freeze)
76
76
  end
77
77
 
78
78
  private
@@ -155,7 +155,7 @@ module AIXM
155
155
  end
156
156
 
157
157
  def radius=(value)
158
- fail(ArgumentError, "invalid radius") unless value.nil? || (value.is_a?(AIXM::D) && value.dist > 0)
158
+ fail(ArgumentError, "invalid radius") unless value.nil? || (value.is_a?(AIXM::D) && value.dim > 0)
159
159
  @radius = value
160
160
  end
161
161
 
@@ -178,7 +178,7 @@ module AIXM
178
178
  end
179
179
 
180
180
  def height=(value)
181
- fail(ArgumentError, "invalid height") unless value.nil? || (value.is_a?(AIXM::D) && value.dist > 0)
181
+ fail(ArgumentError, "invalid height") unless value.nil? || (value.is_a?(AIXM::D) && value.dim > 0)
182
182
  @height = value
183
183
  end
184
184
 
@@ -267,21 +267,21 @@ module AIXM
267
267
  obs.txtDescrMarking(marking_remarks) if marking_remarks
268
268
  obs.codeDatum('WGE')
269
269
  if AIXM.aixm? && obstacle_group.xy_accuracy
270
- obs.valGeoAccuracy(obstacle_group.xy_accuracy.dist.trim)
270
+ obs.valGeoAccuracy(obstacle_group.xy_accuracy.dim.trim)
271
271
  obs.uomGeoAccuracy(obstacle_group.xy_accuracy.unit.upcase.to_s)
272
272
  end
273
273
  obs.valElev(z.alt)
274
274
  if AIXM.aixm? && obstacle_group.z_accuracy
275
- obs.valElevAccuracy(obstacle_group.z_accuracy.to_ft.dist.round)
275
+ obs.valElevAccuracy(obstacle_group.z_accuracy.to_ft.dim.round)
276
276
  end
277
- obs.valHgt(height.to_ft.dist.round) if height
277
+ obs.valHgt(height.to_ft.dim.round) if height
278
278
  obs.uomDistVer('FT')
279
279
  if AIXM.ofmx? && !height_accurate.nil?
280
280
  obs.codeHgtAccuracy(height_accurate ? 'Y' : 'N')
281
281
  end
282
282
  if AIXM.ofmx?
283
283
  if radius
284
- obs.valRadius(radius.dist.trim)
284
+ obs.valRadius(radius.dim.trim)
285
285
  obs.uomRadius(radius.unit.upcase.to_s)
286
286
  end
287
287
  if grouped? && linked?
@@ -45,6 +45,7 @@ module AIXM
45
45
 
46
46
  # @!method obstacles
47
47
  # @return [Array<AIXM::Feature::Obstacle>] obstacles in this obstacle group
48
+ #
48
49
  # @!method add_obstacle(obstacle, linked_to: nil, link_type: nil)
49
50
  # @param obstacle [AIXM::Feature::Obstacle] obstacle instance
50
51
  # @param linked_to [Symbol, AIXM::Feature::Obstacle, nil] Either:
@@ -62,10 +63,13 @@ module AIXM
62
63
 
63
64
  # @!method source
64
65
  # @return [String] reference to source of the feature data
66
+ #
65
67
  # @!method name
66
68
  # @return [String] obstacle group name
69
+ #
67
70
  # @!method xy_accuracy
68
71
  # @return [AIXM::D, nil] margin of error for circular base center point
72
+ #
69
73
  # @!method z_accuracy
70
74
  # @return [AIXM::D, nil] margin of error for top point
71
75
  %i(source name xy_accuracy z_accuracy).each do |method|
@@ -126,11 +130,11 @@ module AIXM
126
130
  ogr << to_uid.indent(2)
127
131
  ogr.codeDatum('WGE')
128
132
  if xy_accuracy
129
- ogr.valGeoAccuracy(xy_accuracy.dist.trim)
133
+ ogr.valGeoAccuracy(xy_accuracy.dim.trim)
130
134
  ogr.uomGeoAccuracy(xy_accuracy.unit.upcase.to_s)
131
135
  end
132
136
  if z_accuracy
133
- ogr.valElevAccuracy(z_accuracy.to_ft.dist.round)
137
+ ogr.valElevAccuracy(z_accuracy.to_ft.dim.round)
134
138
  ogr.uomElevAccuracy('FT')
135
139
  end
136
140
  ogr.txtRmk(remarks) if remarks
@@ -39,6 +39,7 @@ module AIXM
39
39
  # @return [Array<AIXM::Feature::Airport,
40
40
  # AIXM::Feature::Unit,
41
41
  # AIXM::Feature::NavigationalAid>] aiports, units or navigational aids
42
+ #
42
43
  # @!method add_member(member)
43
44
  # @param member [AIXM::Feature::Airport,
44
45
  # AIXM::Feature::Unit,
@@ -82,6 +82,7 @@ module AIXM
82
82
 
83
83
  # @!method services
84
84
  # @return [Array<AIXM::Component::Service>] services provided by this unit
85
+ #
85
86
  # @!method add_service(service)
86
87
  # @param service [AIXM::Component::Service]
87
88
  has_many :services
@@ -169,7 +170,7 @@ module AIXM
169
170
  private
170
171
 
171
172
  def name_with_type
172
- [name, TYPES.key(type)].join(' ')
173
+ [name, TYPES.key(type)].join(' '.freeze)
173
174
  end
174
175
  end
175
176
 
data/lib/aixm/feature.rb CHANGED
@@ -6,6 +6,9 @@ module AIXM
6
6
 
7
7
  private_class_method :new
8
8
 
9
+ # @return [Object] freely usable e.g. to find_by foreign keys
10
+ attr_accessor :meta
11
+
9
12
  # @return [String] reference to source of the feature data
10
13
  attr_reader :source
11
14
 
data/lib/aixm/memoize.rb CHANGED
@@ -8,6 +8,9 @@ module AIXM
8
8
  # independently. On the other hand, when calling the method with a block,
9
9
  # no memoization is performed at all.
10
10
  #
11
+ # Nested memoization of the same method is allowed and won't reset the
12
+ # memoization cache.
13
+ #
11
14
  # @example
12
15
  # class Either
13
16
  # include AIXM::Memoize
@@ -53,14 +56,15 @@ module AIXM
53
56
  unmemoized_method = :"unmemoized_#{method}"
54
57
  alias_method unmemoized_method, method
55
58
  define_method method do |*args, **kargs, &block|
56
- if block || !AIXM::Memoize.cache
59
+ if block || !AIXM::Memoize.cache.has_key?(method)
57
60
  send(unmemoized_method, *args, **kargs, &block)
58
61
  else
62
+ cache = AIXM::Memoize.cache[method]
59
63
  id = object_id.hash ^ args.hash ^ kargs.hash
60
- if AIXM::Memoize.cache.has_key?(id)
61
- AIXM::Memoize.cache[id]
64
+ if cache.has_key?(id)
65
+ cache[id]
62
66
  else
63
- AIXM::Memoize.cache[id] = send(unmemoized_method, *args, **kargs, &block)
67
+ cache[id] = send(unmemoized_method, *args, **kargs)
64
68
  end
65
69
  end
66
70
  end
@@ -68,22 +72,34 @@ module AIXM
68
72
  end
69
73
 
70
74
  class << self
75
+ attr_reader :cache
76
+
71
77
  def included(base)
72
78
  base.extend(ClassMethods)
73
79
  @cache = {}
74
80
  end
75
81
 
76
- def method(method)
77
- @method = method
78
- @cache[@method] = {}
82
+ def method(method, &)
83
+ send(:"call_with#{:out if cached?(method)}_cache", method, &)
84
+ end
85
+
86
+ private
87
+
88
+ def cached?(method)
89
+ cache.has_key?(method)
90
+ end
91
+
92
+ def call_without_cache(method)
79
93
  yield
80
- ensure
81
- @method = nil
82
94
  end
83
95
 
84
- def cache
85
- (@cache[@method] ||= {}) if @method
96
+ def call_with_cache(method)
97
+ cache[method] = {}
98
+ yield
99
+ ensure
100
+ cache.delete(method)
86
101
  end
102
+
87
103
  end
88
104
  end
89
105
  end
data/lib/aixm/p.rb CHANGED
@@ -2,7 +2,7 @@ using AIXM::Refinements
2
2
 
3
3
  module AIXM
4
4
 
5
- # pressure
5
+ # Pressure
6
6
  #
7
7
  # @example
8
8
  # AIXM.d(14, :bar)
@@ -39,7 +39,7 @@ module AIXM
39
39
 
40
40
  # @return [String] human readable representation (e.g. "14 bar")
41
41
  def to_s
42
- [pres, unit].join(' ')
42
+ [pres, unit].join(' '.freeze)
43
43
  end
44
44
 
45
45
  def pres=(value)
@@ -58,6 +58,7 @@ module AIXM
58
58
  # @!method to_psi
59
59
  # @!method to_bar
60
60
  # @!method to_torr
61
+ #
61
62
  # @return [AIXM::P] convert pressure
62
63
  UNITS.each_key do |target_unit|
63
64
  define_method "to_#{target_unit}" do
@@ -44,7 +44,7 @@ module AIXM
44
44
  end
45
45
 
46
46
  def uuid_for(array)
47
- ::Digest::MD5.hexdigest(array.flatten.map(&:to_s).join('|')).unpack("a8a4a4a4a12").join("-")
47
+ ::Digest::MD5.hexdigest(array.flatten.map(&:to_s).join('|'.freeze)).unpack("a8a4a4a4a12").join('-'.freeze)
48
48
  end
49
49
 
50
50
  # Insert OFMX-compliant payload hashes as mid attributes into an XML
data/lib/aixm/r.rb ADDED
@@ -0,0 +1,62 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+
5
+ # Rectangle shape composed of two lengths
6
+ #
7
+ # @examples
8
+ # AIXM.r(AIXM.d(25, :m), AIXM.d(20, :m)) # rectangle
9
+ # AIXM.r(AIXM.d(25, :m)) # square
10
+ class R
11
+
12
+ # @return [AIXM::D] rectangle length
13
+ attr_reader :length
14
+
15
+ # @return [AIXM::D] rectangle width
16
+ attr_reader :width
17
+
18
+ def initialize(length, width=nil)
19
+ self.length, self.width = length, (width || length)
20
+ end
21
+
22
+ # @return [String]
23
+ def inspect
24
+ %Q(#<#{self.class} #{to_s}>)
25
+ end
26
+
27
+ # @return [String] human readable representation (e.g. "25.0 m x 20.0 m")
28
+ def to_s
29
+ [length, width].join(' x ')
30
+ end
31
+
32
+ %i(length width).each do |dimension|
33
+ define_method("#{dimension}=") do |value|
34
+ fail(ArgumentError, "invalid dimension") unless value.is_a? AIXM::D
35
+ instance_variable_set(:"@#{dimension}", value)
36
+ @length, @width = @width, @length if @length && @width && @length < @width
37
+ end
38
+ end
39
+
40
+ # Calculate the surface in square meters
41
+ #
42
+ # @return [Float]
43
+ def surface
44
+ length.to_m.dim * width.to_m.dim
45
+ end
46
+
47
+ # @see Object#==
48
+ # @return [Boolean]
49
+ def ==(other)
50
+ self.class === other && length == other.length && width == other.width
51
+ end
52
+ alias_method :eql?, :==
53
+
54
+ # @see Object#hash
55
+ # @return [Integer]
56
+ def hash
57
+ to_s.hash
58
+ end
59
+
60
+ end
61
+
62
+ end