aixm 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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