aixm 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.ruby-version +1 -1
  4. data/.yardopts +3 -0
  5. data/CHANGELOG.md +34 -14
  6. data/Guardfile +1 -0
  7. data/README.md +64 -257
  8. data/lib/aixm.rb +16 -7
  9. data/lib/aixm/component.rb +6 -0
  10. data/lib/aixm/component/frequency.rb +135 -0
  11. data/lib/aixm/component/geometry.rb +34 -23
  12. data/lib/aixm/component/geometry/arc.rb +37 -22
  13. data/lib/aixm/component/geometry/border.rb +29 -20
  14. data/lib/aixm/component/geometry/circle.rb +39 -22
  15. data/lib/aixm/component/geometry/point.rb +29 -13
  16. data/lib/aixm/component/helipad.rb +154 -0
  17. data/lib/aixm/component/layer.rb +91 -0
  18. data/lib/aixm/component/runway.rb +294 -0
  19. data/lib/aixm/component/service.rb +170 -0
  20. data/lib/aixm/component/timetable.rb +65 -0
  21. data/lib/aixm/component/vertical_limits.rb +65 -29
  22. data/lib/aixm/config.rb +87 -0
  23. data/lib/aixm/document.rb +66 -42
  24. data/lib/aixm/errors.rb +11 -0
  25. data/lib/aixm/f.rb +34 -20
  26. data/lib/aixm/feature.rb +38 -0
  27. data/lib/aixm/feature/airport.rb +473 -0
  28. data/lib/aixm/feature/airspace.rb +145 -92
  29. data/lib/aixm/feature/navigational_aid.rb +94 -0
  30. data/lib/aixm/feature/navigational_aid/designated_point.rb +50 -54
  31. data/lib/aixm/feature/navigational_aid/dme.rb +48 -40
  32. data/lib/aixm/feature/navigational_aid/marker.rb +55 -45
  33. data/lib/aixm/feature/navigational_aid/ndb.rb +54 -50
  34. data/lib/aixm/feature/navigational_aid/tacan.rb +38 -31
  35. data/lib/aixm/feature/navigational_aid/vor.rb +84 -76
  36. data/lib/aixm/feature/organisation.rb +97 -0
  37. data/lib/aixm/feature/unit.rb +152 -0
  38. data/lib/aixm/refinements.rb +132 -47
  39. data/lib/aixm/shortcuts.rb +11 -6
  40. data/lib/aixm/version.rb +1 -1
  41. data/lib/aixm/xy.rb +64 -20
  42. data/lib/aixm/z.rb +51 -22
  43. data/{lib/aixm/schemas → schemas/aixm}/4.5/AIXM-DataTypes.xsd +0 -0
  44. data/{lib/aixm/schemas → schemas/aixm}/4.5/AIXM-Features.xsd +0 -0
  45. data/{lib/aixm/schemas → schemas/aixm}/4.5/AIXM-Snapshot.xsd +0 -0
  46. data/schemas/ofmx/0/OFMX-DataTypes.xsd +5077 -0
  47. data/schemas/ofmx/0/OFMX-Features.xsd +9955 -0
  48. data/schemas/ofmx/0/OFMX-Snapshot.xsd +217 -0
  49. data/spec/factory.rb +209 -33
  50. data/spec/lib/aixm/component/frequency_spec.rb +75 -0
  51. data/spec/lib/aixm/component/geometry/arc_spec.rb +28 -22
  52. data/spec/lib/aixm/component/geometry/border_spec.rb +23 -20
  53. data/spec/lib/aixm/component/geometry/circle_spec.rb +31 -22
  54. data/spec/lib/aixm/component/geometry/point_spec.rb +11 -14
  55. data/spec/lib/aixm/component/geometry_spec.rb +150 -69
  56. data/spec/lib/aixm/component/helipad_spec.rb +136 -0
  57. data/spec/lib/aixm/component/layer_spec.rb +110 -0
  58. data/spec/lib/aixm/component/runway_spec.rb +402 -0
  59. data/spec/lib/aixm/component/service_spec.rb +61 -0
  60. data/spec/lib/aixm/component/timetable_spec.rb +49 -0
  61. data/spec/lib/aixm/component/vertical_limits_spec.rb +39 -20
  62. data/spec/lib/aixm/config_spec.rb +41 -0
  63. data/spec/lib/aixm/document_spec.rb +637 -147
  64. data/spec/lib/aixm/errors_spec.rb +14 -0
  65. data/spec/lib/aixm/f_spec.rb +17 -10
  66. data/spec/lib/aixm/feature/airport_spec.rb +546 -0
  67. data/spec/lib/aixm/feature/airspace_spec.rb +349 -226
  68. data/spec/lib/aixm/feature/navigational_aid/designated_point_spec.rb +47 -36
  69. data/spec/lib/aixm/feature/navigational_aid/dme_spec.rb +61 -36
  70. data/spec/lib/aixm/feature/navigational_aid/marker_spec.rb +61 -113
  71. data/spec/lib/aixm/feature/navigational_aid/ndb_spec.rb +65 -79
  72. data/spec/lib/aixm/feature/navigational_aid/tacan_spec.rb +57 -36
  73. data/spec/lib/aixm/feature/navigational_aid/vor_spec.rb +86 -112
  74. data/spec/lib/aixm/feature/navigational_aid_spec.rb +52 -0
  75. data/spec/lib/aixm/feature/organisation_spec.rb +77 -0
  76. data/spec/lib/aixm/feature/unit_spec.rb +227 -0
  77. data/spec/lib/aixm/feature_spec.rb +58 -0
  78. data/spec/lib/aixm/refinements_spec.rb +187 -178
  79. data/spec/lib/aixm/xy_spec.rb +45 -34
  80. data/spec/lib/aixm/z_spec.rb +19 -21
  81. data/spec/macros/organisation.rb +11 -0
  82. data/spec/macros/remarks.rb +12 -0
  83. data/spec/macros/timetable.rb +11 -0
  84. data/spec/macros/xy.rb +11 -0
  85. data/spec/macros/z_qnh.rb +11 -0
  86. data/spec/spec_helper.rb +26 -0
  87. metadata +60 -19
  88. data/lib/aixm/base.rb +0 -10
  89. data/lib/aixm/component/base.rb +0 -6
  90. data/lib/aixm/component/class_layer.rb +0 -46
  91. data/lib/aixm/component/geometry/base.rb +0 -8
  92. data/lib/aixm/component/schedule.rb +0 -43
  93. data/lib/aixm/feature/base.rb +0 -6
  94. data/lib/aixm/feature/navigational_aid/base.rb +0 -79
  95. data/spec/lib/aixm/component/class_layer_spec.rb +0 -74
  96. data/spec/lib/aixm/component/schedule_spec.rb +0 -33
  97. data/spec/lib/aixm/feature/navigational_aid/base_spec.rb +0 -41
@@ -0,0 +1,170 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+ class Component
5
+
6
+ # Service provided by a unit.
7
+ #
8
+ # ===Cheat Sheet in Pseudo Code:
9
+ # service = AIXM.service(
10
+ # name: String
11
+ # type: TYPES
12
+ # )
13
+ # service.timetable = AIXM.timetable or nil
14
+ # service.remarks = String or nil
15
+ # service.add_frequency(AIXM.frequency)
16
+ #
17
+ # @see https://github.com/openflightmaps/ofmx/wiki/Organisation#ser-service
18
+ class Service
19
+ TYPES = {
20
+ ACS: :area_control_service,
21
+ ADS: :automatic_dependent_surveillance_service,
22
+ ADVS: :advisory_service,
23
+ AFIS: :aerodrome_flight_information_service,
24
+ AFS: :aeronautical_fixed_service,
25
+ AIS: :aeronautical_information_service,
26
+ ALRS: :alerting_service,
27
+ AMS: :aeronautical_mobile_service,
28
+ AMSS: :aeronautical_mobile_satellite_service,
29
+ APP: :approach_control_service,
30
+ 'APP-ARR': :approach_control_service_for_arrival,
31
+ 'APP-DEP': :approach_control_service_for_departure,
32
+ ARTCC: :air_route_traffic_control_centre_service,
33
+ ATC: :air_traffic_control_service,
34
+ ATFM: :air_traffic_flow_management_service,
35
+ ATIS: :automated_terminal_information_service,
36
+ 'ATIS-ARR': :automated_terminal_information_service_for_arrival,
37
+ 'ATIS-DEP': :automated_terminal_information_service_for_departure,
38
+ ATM: :air_traffic_management_service,
39
+ ATS: :air_traffic_service,
40
+ BOF: :briefing_service,
41
+ BS: :commercial_broadcasting_service,
42
+ COM: :communications_service,
43
+ CTAF: :common_traffic_advisory_frequency_service,
44
+ DVDF: :doppler_vdf_service,
45
+ EFAS: :en_route_flight_advisory_service,
46
+ ENTRY: :entry_clearance_service,
47
+ EXIT: :exit_clearance_service,
48
+ FCST: :forecasting_service,
49
+ FIS: :flight_information_service,
50
+ FISA: :automated_flight_information_service,
51
+ FSS: :flight_service_station_service,
52
+ GCA: :ground_controlled_approach_service,
53
+ INFO: :information_provision_service,
54
+ MET: :meteorological_service,
55
+ NOF: :international_notam_service,
56
+ OAC: :oceanic_area_control_service,
57
+ OVERFLT: :overflight_clearance_service,
58
+ PAR: :precision_approach_radar_service,
59
+ RAC: :rules_of_the_air_and_air_traffic_services,
60
+ RADAR: :radar_service,
61
+ RAF: :regional_area_forecasting_service,
62
+ RCC: :rescue_coordination_service,
63
+ SAR: :search_and_rescue_service,
64
+ SIGMET: :sigmet_service,
65
+ SMC: :surface_movement_control_service,
66
+ SMR: :surface_movement_radar_service,
67
+ SRA: :surveillance_radar_approach_service,
68
+ SSR: :secondary_surveillance_radar_service,
69
+ TAR: :terminal_area_radar_service,
70
+ TWEB: :transcribed_weather_broadcast_service,
71
+ TWR: :aerodrome_control_tower_service,
72
+ UAC: :upper_area_control_service,
73
+ UDF: :uhf_direction_finding_service,
74
+ VDF: :vhf_direction_finding_service,
75
+ VOLMET: :volmet_service,
76
+ VOT: :vor_test_facility,
77
+ OTHER: :other # specify in remarks
78
+ }.freeze
79
+
80
+ # @return [AIXM::Feature::Unit] unit providing this service
81
+ attr_reader :unit
82
+
83
+ # @return [String] full name
84
+ attr_reader :name
85
+
86
+ # @return [Symbol] type of service (see {TYPES})
87
+ attr_reader :type
88
+
89
+ # @return [AIXM::Component::Timetable, nil] operating hours
90
+ attr_reader :timetable
91
+
92
+ # @return [String, nil] free text remarks
93
+ attr_reader :remarks
94
+
95
+ # @return [Array<AIXM::Component::Frequency>] frequencies used by this service
96
+ attr_reader :frequencies
97
+
98
+ def initialize(name:, type:)
99
+ self.name, self.type = name, type
100
+ @frequencies = []
101
+ end
102
+
103
+ # @return [String]
104
+ def inspect
105
+ %Q(#<#{self.class} type=#{type.inspect}>)
106
+ end
107
+
108
+ def unit=(value)
109
+ fail(ArgumentError, "invalid unit") unless value.is_a? AIXM::Feature::Unit
110
+ @unit = value
111
+ end
112
+ private :unit=
113
+
114
+ def name=(value)
115
+ fail(ArgumentError, "invalid name") unless value.is_a? String
116
+ @name = value.uptrans
117
+ end
118
+
119
+ def type=(value)
120
+ @type = TYPES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid type")
121
+ end
122
+
123
+ def timetable=(value)
124
+ fail(ArgumentError, "invalid timetable") unless value.nil? || value.is_a?(AIXM::Component::Timetable)
125
+ @timetable = value
126
+ end
127
+
128
+ def remarks=(value)
129
+ @remarks = value&.to_s
130
+ end
131
+
132
+ # Add a frequency used by this service.
133
+ #
134
+ # @param frequency [AIXM::Component::Frequency] frequency instance
135
+ # @return [self]
136
+ def add_frequency(frequency)
137
+ fail(ArgumentError, "invalid frequency") unless frequency.is_a? AIXM::Component::Frequency
138
+ frequency.send(:service=, self)
139
+ @frequencies << frequency
140
+ self
141
+ end
142
+
143
+ # @return [String] UID markup
144
+ def to_uid
145
+ builder = Builder::XmlMarkup.new(indent: 2)
146
+ builder.SerUid do |ser_uid|
147
+ ser_uid << unit.to_uid.indent(2)
148
+ ser_uid.codeType(TYPES.key(type).to_s)
149
+ ser_uid.noSeq(@sequence)
150
+ end
151
+ end
152
+
153
+ # @return [String] AIXM or OFMX markup
154
+ def to_xml(sequence=1)
155
+ @sequence = sequence
156
+ builder = Builder::XmlMarkup.new(indent: 2)
157
+ builder.Ser do |ser|
158
+ ser << to_uid.indent(2)
159
+ ser << timetable.to_xml(as: :Stt).indent(2) if timetable
160
+ ser.txtRmk(remarks) if remarks
161
+ end
162
+ frequencies.each do |frequency|
163
+ builder << frequency.to_xml
164
+ end
165
+ builder.target!
166
+ end
167
+ end
168
+
169
+ end
170
+ end
@@ -0,0 +1,65 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+ class Component
5
+
6
+ # Timetables define activity time windows.
7
+ #
8
+ # @note As of now, only predefined timetables (see {CODES}) are imlemented.
9
+ #
10
+ # ===Cheat Sheat in Pseudo Code:
11
+ # timetable = AIXM.timetable(
12
+ # code: String or Symbol
13
+ # )
14
+ # timetable.remarks = String or nil
15
+ #
16
+ # ===Shortcuts:
17
+ # * +AIXM::H24+ - continuous, all day and all night
18
+ #
19
+ # @see https://github.com/openflightmaps/ofmx/wiki/Timetable#predefined-timetable
20
+ class Timetable
21
+ CODES = {
22
+ H24: :continuous, # all day and all night
23
+ HJ: :sunrise_to_sunset, # all day
24
+ HN: :sunset_to_sunrise, # all night
25
+ HX: :unspecified,
26
+ HO: :operational_request, # on request only
27
+ NOTAM: :notam, # see NOTAM
28
+ OTHER: :other # specify in remarks
29
+ }.freeze
30
+
31
+ # @return [Symbol] predefined timetable code (see {CODES})
32
+ attr_reader :code
33
+
34
+ # @return [String, nil] free text remarks
35
+ attr_reader :remarks
36
+
37
+ def initialize(code:)
38
+ self.code = code
39
+ end
40
+
41
+ # @return [String]
42
+ def inspect
43
+ %Q(#<#{self.class} code=#{code.inspect}>)
44
+ end
45
+
46
+ def code=(value)
47
+ @code = CODES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid code")
48
+ end
49
+
50
+ def remarks=(value)
51
+ @remarks = value&.to_s
52
+ end
53
+
54
+ # @return [String] AIXM or OFMX markup
55
+ def to_xml(as: :Timetable)
56
+ builder = Builder::XmlMarkup.new(indent: 2)
57
+ builder.tag!(as) do |tag|
58
+ tag.codeWorkHr(CODES.key(code).to_s)
59
+ tag.txtRmkWorkHr(remarks) if remarks
60
+ end
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -1,49 +1,85 @@
1
+ using AIXM::Refinements
2
+
1
3
  module AIXM
2
- module Component
4
+ class Component
3
5
 
4
- ##
5
- # Vertical limits define a 3D airspace vertically. They are normally noted
6
- # as follows:
6
+ # Vertical limits define a 3D airspace vertically. They are often noted in
7
+ # AIP as follows:
7
8
  #
8
- # +upper z+ (or +max_z+ whichever is higher)
9
- # ---------
10
- # +lower_z+ (or +min_z+ whichever is lower)
9
+ # upper_z (or max_z whichever is higher)
10
+ # -------
11
+ # lower_z (or min_z whichever is lower)
11
12
  #
12
- # Shortcuts:
13
- # * +AIXM::GROUND+ - surface (aka: 0ft QFE)
14
- # * +AIXM::UNLIMITED+ - no upper limit (aka: FL 999)
15
- class VerticalLimits < Base
16
- using AIXM::Refinements
17
-
13
+ # ===Cheat Sheet in Pseudo Code:
14
+ # vertical_limits = AIXM.vertical_limits(
15
+ # upper_z: AIXM.z
16
+ # lower_z: AIXM.z
17
+ # max_z: AIXM.z or nil
18
+ # min_z: AIXM.z or nil
19
+ # )
20
+ #
21
+ # ===Shortcuts:
22
+ # * +AIXM::GROUND+ - surface expressed as "0 ft QFE"
23
+ # * +AIXM::UNLIMITED+ - no upper limit expressed as "FL 999"
24
+ #
25
+ # @see https://github.com/openflightmaps/ofmx/wiki/Airspace#ase-airspace
26
+ class VerticalLimits
27
+ # @api private
18
28
  TAGS = { upper: :Upper, lower: :Lower, max: :Max, min: :Mnm }.freeze
29
+
30
+ # @api private
19
31
  CODES = { qfe: :HEI, qnh: :ALT, qne: :STD }.freeze
20
32
 
21
- attr_reader :upper_z, :lower_z, :max_z, :min_z
33
+ # @return [AIXM::Z] upper limit
34
+ attr_reader :upper_z
35
+
36
+ # @return [AIXM::Z] lower limit
37
+ attr_reader :lower_z
38
+
39
+ # @return [AIXM::Z] alternative upper limit ("whichever is higher")
40
+ attr_reader :max_z
41
+
42
+ # @return [AIXM::Z] alternative lower limit ("whichever is lower")
43
+ attr_reader :min_z
44
+
45
+ def initialize(upper_z:, lower_z:, max_z: nil, min_z: nil)
46
+ self.upper_z, self.lower_z, self.max_z, self.min_z = upper_z, lower_z, max_z, min_z
47
+ end
48
+
49
+ # @return [String]
50
+ def inspect
51
+ %Q(#<#{self.class} upper_z="#{upper_z.to_s}" lower_z="#{lower_z.to_s}">)
52
+ end
53
+
54
+ def upper_z=(value)
55
+ fail(ArgumentError, "invalid upper_z") unless value.is_a? AIXM::Z
56
+ @upper_z = value
57
+ end
58
+
59
+ def lower_z=(value)
60
+ fail(ArgumentError, "invalid lower_z") unless value.is_a? AIXM::Z
61
+ @lower_z = value
62
+ end
22
63
 
23
- def initialize(max_z: nil, upper_z:, lower_z:, min_z: nil)
24
- fail(ArgumentError, "invalid upper_z") unless upper_z.is_a? AIXM::Z
25
- fail(ArgumentError, "invalid lower_z") unless lower_z.is_a? AIXM::Z
26
- fail(ArgumentError, "invalid max_z") unless max_z.nil? || max_z.is_a?(AIXM::Z)
27
- fail(ArgumentError, "invalid min_z") unless min_z.nil? || min_z.is_a?(AIXM::Z)
28
- @upper_z, @lower_z, @max_z, @min_z = upper_z, lower_z, max_z, min_z
64
+ def max_z=(value)
65
+ fail(ArgumentError, "invalid max_z") unless value.nil? || value.is_a?(AIXM::Z)
66
+ @max_z = value
29
67
  end
30
68
 
31
- ##
32
- # Digest to identify the payload
33
- def to_digest
34
- [upper_z.alt, upper_z.code, lower_z.alt, lower_z.code, max_z&.alt, max_z&.code, min_z&.alt, min_z&.code].to_digest
69
+ def min_z=(value)
70
+ fail(ArgumentError, "invalid min_z") unless value.nil? || value.is_a?(AIXM::Z)
71
+ @min_z = value
35
72
  end
36
73
 
37
- ##
38
- # Render AIXM markup
39
- def to_aixm(*extensions)
74
+ # @return [String] AIXM or OFMX markup
75
+ def to_xml
40
76
  %i(upper lower max min).each_with_object(Builder::XmlMarkup.new(indent: 2)) do |limit, builder|
41
77
  if z = send(:"#{limit}_z")
42
78
  builder.tag!(:"codeDistVer#{TAGS[limit]}", CODES[z.code].to_s)
43
79
  builder.tag!(:"valDistVer#{TAGS[limit]}", z.alt.to_s)
44
- builder.tag!(:"uomDistVer#{TAGS[limit]}", z.unit.to_s)
80
+ builder.tag!(:"uomDistVer#{TAGS[limit]}", z.unit.upcase.to_s)
45
81
  end
46
- end.target! # see https://github.com/jimweirich/builder/issues/42
82
+ end.target!
47
83
  end
48
84
  end
49
85
 
@@ -0,0 +1,87 @@
1
+ module AIXM
2
+
3
+ SCHEMAS = {
4
+ aixm: {
5
+ version: '4.5',
6
+ namespace: 'http://www.aixm.aero/schema/4.5/AIXM-Snapshot.xsd',
7
+ xsd: Pathname(__dir__).join('..', '..', 'schemas', 'aixm', '4.5', 'AIXM-Snapshot.xsd'),
8
+ root: 'AIXM-Snapshot'
9
+ },
10
+ ofmx: {
11
+ version: '0',
12
+ namespace: 'http://openflightmaps.org/schema/0/OFMX-Snapshot.xsd',
13
+ xsd: Pathname(__dir__).join('..', '..', 'schemas', 'ofmx', '0', 'OFMX-Snapshot.xsd'),
14
+ root: 'OFMX-Snapshot'
15
+ }
16
+ }.freeze
17
+
18
+ class << self
19
+
20
+ # Access the configuration (e.g. +AIXM.config.schema+)
21
+ # @return [OpenStruct] configuration struct
22
+ def config
23
+ @@config
24
+ end
25
+
26
+ # Currently active schema
27
+ #
28
+ # @example Get the schema identifyer
29
+ # AIXM.schema # => :aixm
30
+ #
31
+ # @example Get schema details
32
+ # AIXM.schema(:version) # => '4.5'
33
+ # AIXM.schema(:root) # => 'AIXM-Snapshot'
34
+ #
35
+ # @param key [Symbol, nil] schema detail key (see {SCHEMAS})
36
+ # @return [Object] schema detail value
37
+ def schema(key = nil)
38
+ key ? SCHEMAS.dig(@@config.schema, key) : @@config.schema
39
+ end
40
+
41
+ # Shortcuts to set the schema.
42
+ #
43
+ # @example
44
+ # AIXM.aixm! # => :aixm
45
+ # AIXM.ofmx? # => false
46
+ # AIXM.ofmx! # => :ofmx
47
+ # AIXM.ofmx? # => true
48
+ #
49
+ # @!method aixm!
50
+ # @!method ofmx!
51
+ # @return [Symbol] schema key
52
+ SCHEMAS.each_key do |schema|
53
+ define_method("#{schema}!") { @@config.schema = schema }
54
+ end
55
+
56
+ # Shortcuts to query the schema.
57
+ #
58
+ # @example
59
+ # AIXM.aixm! # => :aixm
60
+ # AIXM.ofmx? # => false
61
+ # AIXM.ofmx! # => :ofmx
62
+ # AIXM.ofmx? # => true
63
+ #
64
+ # @!method aixm?
65
+ # @!method ofmx?
66
+ # @return [Boolean]
67
+ SCHEMAS.each_key do |schema|
68
+ define_method("#{schema}?") { @@config.schema == schema }
69
+ end
70
+
71
+ private
72
+
73
+ # Configuration defaults (view source for more).
74
+ #
75
+ # @!visibility public
76
+ # @api private
77
+ # @return [OpenStruct]
78
+ def initialize_config
79
+ @@config = OpenStruct.new(
80
+ schema: :aixm
81
+ )
82
+ end
83
+
84
+ end
85
+ end
86
+
87
+ AIXM.send(:initialize_config)
@@ -1,63 +1,89 @@
1
+ using AIXM::Refinements
2
+
1
3
  module AIXM
2
- class Document < Base
3
- using AIXM::Refinements
4
4
 
5
- IGNORE_ERROR_PATTERN = %r(OrgUid)
5
+ # The AIXM-Snapshot or OFMX-Snapshot document is the root container for
6
+ # aeronautical information such as airports or airspaces.
7
+ #
8
+ # ===Cheat Sheet in Pseudo Code:
9
+ # document = AIXM.document(
10
+ # namespace: String (UUID)
11
+ # created_at: Time or Date or String
12
+ # effective_at: Time or Date or String
13
+ # )
14
+ # document.features << AIXM::Feature
15
+ #
16
+ # @see https://github.com/openflightmaps/ofmx/wiki/Snapshot
17
+ class Document
18
+ NAMESPACE_PATTERN = /\A[a-f\d]{8}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{12}\z/.freeze
19
+
20
+ # @return [String] UUID to namespace the data contained in this document
21
+ attr_reader :namespace
22
+
23
+ # @return [Time] creation date and time (default: {#effective_at} or now)
24
+ attr_reader :created_at
25
+
26
+ # @return [Time] effective after date and time (default: {#created_at} or now)
27
+ attr_reader :effective_at
6
28
 
7
- attr_reader :created_at, :effective_at
29
+ # @return [Array<AIXM::Feature>] airspaces, airports and other features
8
30
  attr_accessor :features
9
31
 
10
- ##
11
- # Define a AIXM-Snapshot document
12
- #
13
- # Options:
14
- # * +created_at+ - creation date and time (default: now)
15
- # * +effective_at+ - snapshot effective after date and time (default: now)
16
- def initialize(created_at: nil, effective_at: nil)
17
- @created_at, @effective_at = parse_time(created_at), parse_time(effective_at)
32
+ def initialize(namespace: nil, created_at: nil, effective_at: nil)
33
+ self.namespace, self.created_at, self.effective_at = namespace, created_at, effective_at
18
34
  @features = []
19
35
  end
20
36
 
21
- ##
22
- # Check whether the document is complete (extensions excluded)
23
- def complete?
24
- features.any? && features.none? { |f| f.respond_to?(:complete?) && !f.complete? }
37
+ # @return [String]
38
+ def inspect
39
+ %Q(#<#{self.class} created_at=#{created_at.inspect}>)
40
+ end
41
+
42
+ def namespace=(value)
43
+ fail(ArgumentError, "invalid namespace") unless value.nil? || value.match?(NAMESPACE_PATTERN)
44
+ @namespace = value || SecureRandom.uuid
25
45
  end
26
46
 
27
- ##
28
- # Validate atainst the XSD and return +true+ if no errors were found
47
+ def created_at=(value)
48
+ @created_at = parse_time(value) || effective_at || Time.now
49
+ end
50
+
51
+ def effective_at=(value)
52
+ @effective_at = parse_time(value) || created_at || Time.now
53
+ end
54
+
55
+ # Validate the generated AIXM or OFMX atainst it's XSD.
56
+ #
57
+ # @return [Boolean] whether valid or not
29
58
  def valid?
30
59
  errors.none?
31
60
  end
32
61
 
33
- ##
34
- # Validate against the XSD and return an array of errors
62
+ # Validate the generated AIXM or OFMX atainst it's XSD and return the
63
+ # errors found.
64
+ #
65
+ # @return [Array<String>] validation errors
35
66
  def errors
36
- xsd = Nokogiri::XML::Schema(File.open(AIXM::SCHEMA))
37
- xsd.validate(Nokogiri::XML(to_aixm)).reject do |error|
38
- error.message =~ IGNORE_ERROR_PATTERN
67
+ xsd = Nokogiri::XML::Schema(File.open(AIXM.schema(:xsd)))
68
+ xsd.validate(Nokogiri::XML(to_xml)).reject do |error|
69
+ AIXM.config.ignored_errors && error.message.match?(AIXM.config.ignored_errors)
39
70
  end
40
71
  end
41
72
 
42
- ##
43
- # Render AIXM markup
44
- #
45
- # Extensions:
46
- # * +:ofm+ - Open Flightmaps
47
- def to_aixm(*extensions)
48
- now = Time.now.xmlschema
73
+ # @return [String] AIXM or OFMX markup
74
+ def to_xml
49
75
  meta = {
50
- 'xmlns:xsi': 'http://www.aixm.aero/schema/4.5/AIXM-Snapshot.xsd',
51
- version: '4.5',
52
- origin: "AIXM #{AIXM::VERSION} Ruby gem",
53
- created: @created_at&.xmlschema || now,
54
- effective: @effective_at&.xmlschema || now
55
- }
56
- meta[:version] += ' + OFM extensions of version 0.1' if extensions >> :ofm
76
+ 'xmlns:xsi': AIXM.schema(:namespace),
77
+ version: AIXM.schema(:version),
78
+ origin: "rubygem aixm-#{AIXM::VERSION}",
79
+ namespace: (namespace if AIXM.ofmx?),
80
+ created: @created_at.xmlschema,
81
+ effective: @effective_at.xmlschema
82
+ }.compact
57
83
  builder = Builder::XmlMarkup.new(indent: 2)
58
84
  builder.instruct!
59
- builder.tag!('AIXM-Snapshot', meta) do |aixm_snapshot|
60
- aixm_snapshot << features.map { |f| f.to_aixm(*extensions) }.join.indent(2)
85
+ builder.tag!(AIXM.schema(:root), meta) do |root|
86
+ root << features.map { |f| f.to_xml }.join.indent(2)
61
87
  end
62
88
  end
63
89
 
@@ -69,10 +95,8 @@ module AIXM
69
95
  when Date then value.to_time
70
96
  when Time then value
71
97
  when nil then nil
72
- else fail ArgumentError
98
+ else fail(ArgumentError, "invalid date or time")
73
99
  end
74
- rescue ArgumentError
75
- raise(ArgumentError, "`#{value}' is not a valid date and time")
76
100
  end
77
101
 
78
102
  end