aipp 0.2.4 → 1.0.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -0
- data/CHANGELOG.md +38 -0
- data/README.md +222 -88
- data/exe/aip2aixm +2 -2
- data/exe/aip2ofmx +2 -2
- data/lib/aipp/aip.rb +113 -31
- data/lib/aipp/border.rb +77 -46
- data/lib/aipp/debugger.rb +101 -0
- data/lib/aipp/downloader.rb +39 -26
- data/lib/aipp/executable.rb +41 -22
- data/lib/aipp/parser.rb +94 -21
- data/lib/aipp/patcher.rb +5 -2
- data/lib/aipp/pdf.rb +1 -1
- data/lib/aipp/regions/LF/README.md +49 -0
- data/lib/aipp/regions/LF/aerodromes.rb +223 -0
- data/lib/aipp/regions/LF/d_p_r_airspaces.rb +56 -0
- data/lib/aipp/regions/LF/dangerous_activities.rb +49 -0
- data/lib/aipp/regions/LF/designated_points.rb +47 -0
- data/lib/aipp/regions/LF/fixtures/aerodromes.yml +608 -0
- data/lib/aipp/regions/LF/helipads.rb +122 -0
- data/lib/aipp/regions/LF/helpers/base.rb +218 -0
- data/lib/aipp/regions/LF/helpers/surface.rb +49 -0
- data/lib/aipp/regions/LF/helpers/usage_limitation.rb +20 -0
- data/lib/aipp/regions/LF/navigational_aids.rb +85 -0
- data/lib/aipp/regions/LF/obstacles.rb +153 -0
- data/lib/aipp/regions/LF/serviced_airspaces.rb +70 -0
- data/lib/aipp/regions/LF/services.rb +172 -0
- data/lib/aipp/t_hash.rb +4 -5
- data/lib/aipp/version.rb +1 -1
- data/lib/aipp.rb +11 -5
- data/lib/core_ext/enumerable.rb +9 -9
- data/lib/core_ext/hash.rb +21 -5
- data/lib/core_ext/nokogiri.rb +54 -0
- data/lib/core_ext/string.rb +38 -66
- data.tar.gz.sig +2 -0
- metadata +180 -188
- metadata.gz.sig +0 -0
- data/.gitignore +0 -8
- data/.ruby-version +0 -1
- data/.travis.yml +0 -8
- data/.yardopts +0 -3
- data/Guardfile +0 -7
- data/TODO.md +0 -6
- data/aipp.gemspec +0 -44
- data/gems.rb +0 -3
- data/lib/aipp/airac.rb +0 -55
- data/lib/aipp/regions/LF/AD-1.3.rb +0 -162
- data/lib/aipp/regions/LF/AD-1.6.rb +0 -31
- data/lib/aipp/regions/LF/AD-2.rb +0 -313
- data/lib/aipp/regions/LF/AD-3.1.rb +0 -185
- data/lib/aipp/regions/LF/ENR-2.1.rb +0 -92
- data/lib/aipp/regions/LF/ENR-4.1.rb +0 -97
- data/lib/aipp/regions/LF/ENR-4.3.rb +0 -28
- data/lib/aipp/regions/LF/ENR-5.1.rb +0 -75
- data/lib/aipp/regions/LF/ENR-5.5.rb +0 -53
- data/lib/aipp/regions/LF/fixtures/AD-1.3.yml +0 -511
- data/lib/aipp/regions/LF/fixtures/AD-2.yml +0 -185
- data/lib/aipp/regions/LF/fixtures/AD-3.1.yml +0 -10
- data/lib/aipp/regions/LF/helpers/AD_radio.rb +0 -90
- data/lib/aipp/regions/LF/helpers/URL.rb +0 -26
- data/lib/aipp/regions/LF/helpers/common.rb +0 -217
- data/lib/core_ext/object.rb +0 -43
- data/rakefile.rb +0 -12
- data/spec/fixtures/archive.zip +0 -0
- data/spec/fixtures/border.geojson +0 -201
- data/spec/fixtures/document.pdf +0 -0
- data/spec/fixtures/document.pdf.json +0 -1
- data/spec/fixtures/new.html +0 -6
- data/spec/fixtures/new.pdf +0 -0
- data/spec/fixtures/new.txt +0 -1
- data/spec/lib/aipp/airac_spec.rb +0 -98
- data/spec/lib/aipp/border_spec.rb +0 -135
- data/spec/lib/aipp/downloader_spec.rb +0 -81
- data/spec/lib/aipp/patcher_spec.rb +0 -46
- data/spec/lib/aipp/pdf_spec.rb +0 -124
- data/spec/lib/aipp/t_hash_spec.rb +0 -44
- data/spec/lib/aipp/version_spec.rb +0 -7
- data/spec/lib/core_ext/enumberable_spec.rb +0 -76
- data/spec/lib/core_ext/hash_spec.rb +0 -27
- data/spec/lib/core_ext/integer_spec.rb +0 -15
- data/spec/lib/core_ext/nil_class_spec.rb +0 -11
- data/spec/lib/core_ext/string_spec.rb +0 -112
- data/spec/sounds/failure.mp3 +0 -0
- data/spec/sounds/success.mp3 +0 -0
- data/spec/spec_helper.rb +0 -28
@@ -1,185 +0,0 @@
|
|
1
|
-
module AIPP
|
2
|
-
module LF
|
3
|
-
|
4
|
-
# Helipads
|
5
|
-
class AD31 < AIP
|
6
|
-
|
7
|
-
include AIPP::LF::Helpers::Common
|
8
|
-
using AIXM::Refinements
|
9
|
-
|
10
|
-
DEPENDS = %w(AD-2)
|
11
|
-
|
12
|
-
HOSTILITIES = {
|
13
|
-
'zone hostile habitée' => 'Zone hostile habitée / hostile populated area',
|
14
|
-
'zone hostile non habitée' => 'Zone hostile non habitée / hostile unpopulated area',
|
15
|
-
'zone non hostile' => 'Zone non hostile / non-hostile area'
|
16
|
-
}.freeze
|
17
|
-
|
18
|
-
POSITIONINGS = {
|
19
|
-
'en terrasse' => 'En terrasse / on deck',
|
20
|
-
'en surface' => 'En surface / on ground'
|
21
|
-
}.freeze
|
22
|
-
|
23
|
-
DIMENSIONS_RE = /( diam.tre\s+\d+ | (?:\d[\s\d,.m]*x\s*){1,}[\s\d,.m]+ )/ix.freeze
|
24
|
-
|
25
|
-
def parse
|
26
|
-
prepare(html: read).css('tbody').each do |tbody|
|
27
|
-
tbody.css('tr').to_enum.each_slice(3).with_index(1) do |trs, index|
|
28
|
-
name = trs[0].css('span[id*="ADHP.TXT_NAME"]').text.cleanup.remove(/[^\w' ]/)
|
29
|
-
if select(:airport, name: name).any?
|
30
|
-
verbose_info "Skipping #{name} in favor of AD-2"
|
31
|
-
next
|
32
|
-
end
|
33
|
-
# Airport
|
34
|
-
@airport = AIXM.airport(
|
35
|
-
source: source(position: trs[0].line),
|
36
|
-
organisation: organisation_lf, # TODO: not yet implemented
|
37
|
-
id: options[:region],
|
38
|
-
name: name,
|
39
|
-
xy: xy_from(trs[1].css('td:nth-of-type(1)').text.cleanup)
|
40
|
-
).tap do |airport|
|
41
|
-
airport.z = elevation_from(trs[1].css('td:nth-of-type(2)').text)
|
42
|
-
end
|
43
|
-
# Usage restrictions
|
44
|
-
if trs[0].css('span[id*="ADHP.STATUT"]').text.match?(/usage\s+restreint/i)
|
45
|
-
@airport.add_usage_limitation(:reservation_required) do |reservation_required|
|
46
|
-
reservation_required.remarks = "Usage restreint / restricted use"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
if trs[0].css('span[id*="ADHP.STATUT"]').text.match?(/r.serv.\s+aux\s+administrations/i)
|
50
|
-
@airport.add_usage_limitation(:other) do |other|
|
51
|
-
other.remarks = "Réservé aux administrations de l'État / reserved for State administrations"
|
52
|
-
end
|
53
|
-
end
|
54
|
-
# FATOs and helipads
|
55
|
-
text = trs[2].css('span[id*="ADHP.REVETEMENT"]').text.remove(/tlof\s*|\s*\(.*?\)/i).downcase
|
56
|
-
surface = text.blank? ? {} : SURFACES.metch(text)
|
57
|
-
lighting = lighting_from(trs[1].css('span[id*="ADHP.BALISAGE"]').text.cleanup)
|
58
|
-
fatos_from(trs[1].css('span[id*="ADHP.DIM_FATO"]').text).each { |f| @airport.add_fato f }
|
59
|
-
helipads_from(trs[1].css('span[id*="ADHP.DIM_TLOF"]').text).each do |helipad|
|
60
|
-
helipad.surface.composition = surface[:composition]
|
61
|
-
helipad.surface.preparation = surface[:preparation]
|
62
|
-
helipad.surface.remarks = surface[:remarks]
|
63
|
-
helipad.surface.auw_weight = auw_weight_from(trs[2].css('span[id*="ADHP.RESISTANCE"]').text)
|
64
|
-
helipad.add_lighting(lighting) if lighting
|
65
|
-
@airport.add_helipad helipad
|
66
|
-
end
|
67
|
-
# Operator and addresses
|
68
|
-
operator = trs[0].css('span[id*="ADHP.EXPLOITANT"]')
|
69
|
-
splitted = operator.text.split(/( (?<!\p{L})t[ée]l | fax | standard | [\d\s]{10,} | \.\s | \( )/ix, 2)
|
70
|
-
@airport.operator = splitted[0].full_strip.truncate(60, omission: '…').blank_to_nil
|
71
|
-
raw_addresses = splitted[1..].join.cleanup.full_strip
|
72
|
-
addresses_from(splitted[1..].join, source(position: operator.first.line)).each { |a| @airport.add_address(a) }
|
73
|
-
# Remarks
|
74
|
-
@airport.remarks = [].tap do |remarks|
|
75
|
-
hostility = trs[2].css('span[id*="ADHP.ZONE_HABITEE"]').text.cleanup.downcase.blank_to_nil
|
76
|
-
hostility = HOSTILITIES.fetch(hostility) if hostility
|
77
|
-
positioning = trs[2].css('span[id*="ADHP.EN_TERRASSE"]').text.cleanup.downcase.blank_to_nil
|
78
|
-
positioning = POSITIONINGS.fetch(positioning) if positioning
|
79
|
-
remarks << ('**SITUATION**' if hostility || positioning) << hostility << positioning << ''
|
80
|
-
remarks << trs[2].css('td:nth-of-type(5)').text.cleanup
|
81
|
-
remarks << raw_addresses unless raw_addresses.blank?
|
82
|
-
end.compact.join("\n").strip
|
83
|
-
add(@airport) if @airport.fatos.any? || @airport.helipads.any?
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
private
|
89
|
-
|
90
|
-
def fatos_from(text)
|
91
|
-
[
|
92
|
-
if text.cleanup.match DIMENSIONS_RE
|
93
|
-
AIXM.fato(name: 'FATO').tap do |fato|
|
94
|
-
fato.length, fato.width = dimensions_from($1)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
].compact
|
98
|
-
end
|
99
|
-
|
100
|
-
def helipads_from(text)
|
101
|
-
[
|
102
|
-
if text.cleanup.match DIMENSIONS_RE
|
103
|
-
AIXM.helipad(name: 'TLOF', xy: @airport.xy).tap do |helipad|
|
104
|
-
helipad.z = @airport.z
|
105
|
-
helipad.length, helipad.width = dimensions_from($1)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
].compact
|
109
|
-
end
|
110
|
-
|
111
|
-
def dimensions_from(text)
|
112
|
-
dims = text.remove(/[^x\d.,]/i).split(/x/i).map { |s| s.to_ff.floor }
|
113
|
-
case dims.size
|
114
|
-
when 1
|
115
|
-
[dim = AIXM.d(dims[0], :m), dim]
|
116
|
-
when 2
|
117
|
-
[AIXM.d(dims[0], :m), AIXM.d(dims[1], :m)]
|
118
|
-
when 4
|
119
|
-
[dim = AIXM.d(dims.min, :m), dim]
|
120
|
-
else
|
121
|
-
warn("bad dimensions for #{@airport.name}", pry: binding)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def auw_weight_from(text)
|
126
|
-
if wgt = text.match(/(\d+(?:[,.]\d+)?)\s*t/i)&.captures&.first
|
127
|
-
AIXM.w(wgt.to_ff, :t)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def lighting_from(text)
|
132
|
-
return if text.blank? || text.match?(/nil|balisage\s*:\s*non/i)
|
133
|
-
description = text.remove(/balisage\s*:|oui\.?\s*:?/i).compact.full_strip
|
134
|
-
AIXM.lighting(position: :edge).tap do |lighting|
|
135
|
-
lighting.description = description unless description.blank?
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def addresses_from(text, source)
|
140
|
-
[].tap do |addresses|
|
141
|
-
text.sub! /(?<!\p{L})t[ée]l\D*([\d\s.]{10,}(?:poste[\d\s.]{2,})?)[-\/]?/i do |m|
|
142
|
-
addresses << AIXM.address(
|
143
|
-
source: source,
|
144
|
-
type: :phone,
|
145
|
-
address: m.strip.sub(/poste/i, '-').remove(/[^\d-]|-$/)
|
146
|
-
)
|
147
|
-
end
|
148
|
-
text.sub! /fax\D*([\d\s.]{10,}(?:poste[\d\s.]{2,})?)[-\/]?/i do |m|
|
149
|
-
addresses << AIXM.address(
|
150
|
-
source: source,
|
151
|
-
type: :fax,
|
152
|
-
address: m.strip.sub(/poste/i, '-').remove(/[^\d-]|-$/)
|
153
|
-
)
|
154
|
-
end
|
155
|
-
text.sub! /e-mail\W*(\S+)[-\/]?/i do |m|
|
156
|
-
addresses << AIXM.address(
|
157
|
-
source: source,
|
158
|
-
type: :email,
|
159
|
-
address: m.strip
|
160
|
-
)
|
161
|
-
end
|
162
|
-
text.sub! /(\d[\d\s]{9,}(?:poste[\d\s.]{2,})?)[-\/]?/i do |m|
|
163
|
-
addresses << AIXM.address(
|
164
|
-
source: source,
|
165
|
-
type: :phone,
|
166
|
-
address: m.strip.sub(/poste/i, '-').remove(/[^\d-]|-$/)
|
167
|
-
)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
patch AIXM::Feature::Airport, :xy do |parser, object, value|
|
173
|
-
throw :abort if value.seconds?
|
174
|
-
if xy = parser.fixture.dig(object.name, 'xy')
|
175
|
-
lat, long = xy.split(/\s+/)
|
176
|
-
AIXM.xy(lat: lat, long: long)
|
177
|
-
else
|
178
|
-
warn("coordinates for #{object.name} appear not to be exact", pry: binding)
|
179
|
-
throw :abort
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
@@ -1,92 +0,0 @@
|
|
1
|
-
module AIPP
|
2
|
-
module LF
|
3
|
-
|
4
|
-
# FIR, TMA etc
|
5
|
-
class ENR21 < AIP
|
6
|
-
|
7
|
-
include AIPP::LF::Helpers::Common
|
8
|
-
|
9
|
-
# Map source types to type and optional local type
|
10
|
-
SOURCE_TYPES = {
|
11
|
-
'FIR' => { type: 'FIR' },
|
12
|
-
'UIR' => { type: 'UIR' },
|
13
|
-
'UTA' => { type: 'UTA' },
|
14
|
-
'CTA' => { type: 'CTA' },
|
15
|
-
'LTA' => { type: 'CTA', local_type: 'LTA' },
|
16
|
-
'TMA' => { type: 'TMA' },
|
17
|
-
'SIV' => { type: 'SECTOR', local_type: 'SIV' } # providing FIS
|
18
|
-
}.freeze
|
19
|
-
|
20
|
-
# Map airspace "<type> <name>" to location indicator
|
21
|
-
LOCATION_INDICATORS = {
|
22
|
-
'FIR BORDEAUX' => 'LFBB',
|
23
|
-
'FIR BREST' => 'LFRR',
|
24
|
-
'FIR MARSEILLE' => 'LFMM',
|
25
|
-
'FIR PARIS' => 'LFFF',
|
26
|
-
'FIR REIMS' => 'LFRR'
|
27
|
-
}.freeze
|
28
|
-
|
29
|
-
def parse
|
30
|
-
prepare(html: read).css('tbody').each do |tbody|
|
31
|
-
airspace = nil
|
32
|
-
tbody.css('tr').to_enum.with_index(1).each do |tr, index|
|
33
|
-
if tr.attr(:id).match?(/--TXT_NAME/)
|
34
|
-
add airspace if airspace
|
35
|
-
airspace = airspace_from tr.css(:td).first
|
36
|
-
verbose_info "Parsing #{airspace.type} #{airspace.name}" unless airspace.type == :terminal_control_area
|
37
|
-
next
|
38
|
-
end
|
39
|
-
begin
|
40
|
-
tds = tr.css('td')
|
41
|
-
if airspace.type == :terminal_control_area && tds[0].text.blank_to_nil
|
42
|
-
add airspace if airspace.layers.any?
|
43
|
-
airspace = airspace_from tds[0]
|
44
|
-
verbose_info "Parsing #{airspace.type} #{airspace.name}"
|
45
|
-
end
|
46
|
-
if airspace
|
47
|
-
if tds[0].text.blank_to_nil
|
48
|
-
airspace.geometry = geometry_from tds[0].text
|
49
|
-
fail("geometry is not closed") unless airspace.geometry.closed?
|
50
|
-
end
|
51
|
-
layer = layer_from(tds[-3].text)
|
52
|
-
layer.class = class_from(tds[1].text) if tds.count == 5
|
53
|
-
layer.location_indicator = LOCATION_INDICATORS.fetch("#{airspace.type} #{airspace.name}", nil)
|
54
|
-
# TODO: unit, call sign and frequency from tds[-2] and with extracted remarks such as (1), (2) etc
|
55
|
-
remarks = tds[-1].text
|
56
|
-
layer.timetable = timetable_from! remarks
|
57
|
-
layer.remarks = remarks_from remarks
|
58
|
-
airspace.layers << layer
|
59
|
-
end
|
60
|
-
rescue => error
|
61
|
-
warn("error parsing #{airspace.type} `#{airspace.name}' at ##{index}: #{error.message}", pry: error)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
add airspace if airspace
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def airspace_from(td)
|
71
|
-
spans = td.children.split { |e| e.name == 'br' }.first.css(:span).drop_while { |e| e.text.match? '\s' }
|
72
|
-
source_type = spans[0].text.blank_to_nil
|
73
|
-
fail "unknown type `#{source_type}'" unless SOURCE_TYPES.has_key? source_type
|
74
|
-
AIXM.airspace(
|
75
|
-
name: anglicise(name: spans[1..-1].join(' ')),
|
76
|
-
type: SOURCE_TYPES.dig(source_type, :type),
|
77
|
-
local_type: SOURCE_TYPES.dig(source_type, :local_type)
|
78
|
-
).tap do |airspace|
|
79
|
-
airspace.source = source(position: td.line)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def class_from(text)
|
84
|
-
text.strip
|
85
|
-
end
|
86
|
-
|
87
|
-
def remarks_from(text)
|
88
|
-
text.strip.gsub(/(\s)\s+/, '\1').blank_to_nil
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
module AIPP
|
2
|
-
module LF
|
3
|
-
|
4
|
-
# ENR Navaids
|
5
|
-
class ENR41 < AIP
|
6
|
-
|
7
|
-
include AIPP::LF::Helpers::Common
|
8
|
-
|
9
|
-
def parse
|
10
|
-
prepare(html: read).css('tbody').each do |tbody|
|
11
|
-
tbody.css('tr').to_enum.with_index(1).each do |tr, index|
|
12
|
-
tds = tr.css('td')
|
13
|
-
master, slave = tds[1].text.strip.gsub(/[^\w-]/, '').downcase.split('-')
|
14
|
-
navaid = AIXM.send(master, base_from(tds).merge(send("#{master}_from", tds)))
|
15
|
-
navaid.source = source(position: tr.line)
|
16
|
-
navaid.timetable = timetable_from! tds[4].text
|
17
|
-
navaid.remarks = remarks_from(tds[5], tds[7], tds[9])
|
18
|
-
navaid.send("associate_#{slave}", channel: channel_from(tds[3].text)) if slave
|
19
|
-
add navaid
|
20
|
-
rescue => error
|
21
|
-
warn("error parsing navigational aid at ##{index}: #{error.message}", pry: error)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def base_from(tds)
|
29
|
-
{
|
30
|
-
organisation: organisation_lf,
|
31
|
-
id: tds[2].text.strip,
|
32
|
-
name: tds[0].text.strip,
|
33
|
-
xy: xy_from(tds[5].text),
|
34
|
-
z: z_from(tds[6].text)
|
35
|
-
}
|
36
|
-
end
|
37
|
-
|
38
|
-
def vor_from(tds)
|
39
|
-
{
|
40
|
-
type: :conventional,
|
41
|
-
f: f_from(tds[3].text),
|
42
|
-
north: :magnetic,
|
43
|
-
}
|
44
|
-
end
|
45
|
-
|
46
|
-
def dme_from(tds)
|
47
|
-
{
|
48
|
-
channel: channel_from(tds[3].text)
|
49
|
-
}
|
50
|
-
end
|
51
|
-
|
52
|
-
def ndb_from(tds)
|
53
|
-
{
|
54
|
-
type: :en_route,
|
55
|
-
f: f_from(tds[3].text)
|
56
|
-
}
|
57
|
-
end
|
58
|
-
|
59
|
-
def tacan_from(tds)
|
60
|
-
{
|
61
|
-
channel: channel_from(tds[3].text)
|
62
|
-
}
|
63
|
-
end
|
64
|
-
|
65
|
-
def z_from(text)
|
66
|
-
parts = text.strip.split(/\s+/)
|
67
|
-
AIXM.z(parts[0].to_i, :qnh) if parts[1] == 'ft'
|
68
|
-
end
|
69
|
-
|
70
|
-
def f_from(text)
|
71
|
-
parts = text.strip.split(/\s+/)
|
72
|
-
AIXM.f(parts[0].to_f, parts[1]) if parts[1] =~ /hz$/i
|
73
|
-
end
|
74
|
-
|
75
|
-
def channel_from(text)
|
76
|
-
parts = text.strip.split(/\s+/)
|
77
|
-
parts.last if parts[-2].downcase == 'ch'
|
78
|
-
end
|
79
|
-
|
80
|
-
def remarks_from(*parts)
|
81
|
-
part_titles = ['RANGE', 'SITUATION', 'OBSERVATIONS']
|
82
|
-
[].tap do |remarks|
|
83
|
-
parts.each.with_index do |part, index|
|
84
|
-
text = if index == 0
|
85
|
-
part = part.text.strip.split(/\s+/)
|
86
|
-
part.shift(2)
|
87
|
-
part.join(' ').blank_to_nil
|
88
|
-
else
|
89
|
-
part.text.strip.blank_to_nil
|
90
|
-
end
|
91
|
-
remarks << "**#{part_titles[index]}**\n#{text}" if text
|
92
|
-
end
|
93
|
-
end.join("\n\n").blank_to_nil
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module AIPP
|
2
|
-
module LF
|
3
|
-
|
4
|
-
# Designated Points
|
5
|
-
class ENR43 < AIP
|
6
|
-
|
7
|
-
include AIPP::LF::Helpers::Common
|
8
|
-
|
9
|
-
def parse
|
10
|
-
prepare(html: read).css('tbody').each do |tbody|
|
11
|
-
tbody.css('tr').to_enum.with_index(1).each do |tr, index|
|
12
|
-
tds = tr.css('td')
|
13
|
-
designated_point = AIXM.designated_point(
|
14
|
-
type: :icao,
|
15
|
-
id: tds[0].text.strip,
|
16
|
-
xy: xy_from(tds[1].text)
|
17
|
-
)
|
18
|
-
designated_point.source = source(position: tr.line)
|
19
|
-
add designated_point
|
20
|
-
rescue => error
|
21
|
-
warn("error parsing designated point at ##{index}: #{error.message}", pry: error)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
module AIPP
|
2
|
-
module LF
|
3
|
-
|
4
|
-
# D/P/R Zones
|
5
|
-
class ENR51 < AIP
|
6
|
-
|
7
|
-
include AIPP::LF::Helpers::Common
|
8
|
-
|
9
|
-
# Map source types to type and optional local type
|
10
|
-
SOURCE_TYPES = {
|
11
|
-
'D' => { type: 'D' },
|
12
|
-
'P' => { type: 'P' },
|
13
|
-
'R' => { type: 'R' },
|
14
|
-
'ZIT' => { type: 'P', local_type: 'ZIT' }
|
15
|
-
}.freeze
|
16
|
-
|
17
|
-
def parse
|
18
|
-
prepare(html: read).css('tbody:has(tr[id^=mid])').each do |tbody|
|
19
|
-
airspace = nil
|
20
|
-
tbody.css('tr').to_enum.with_index(1).each do |tr, index|
|
21
|
-
tds = tr.css('td')
|
22
|
-
case
|
23
|
-
when tr.attr(:id).match?(/TXT_NAME/) # airspace
|
24
|
-
airspace = airspace_from tr
|
25
|
-
when tds.count == 1 # big comment on separate row
|
26
|
-
airspace.layers.first.remarks.
|
27
|
-
concat("\n", tds.text.cleanup).
|
28
|
-
remove!(/\((\d)\)\s*\(\1\)\W*/)
|
29
|
-
else # layer
|
30
|
-
begin
|
31
|
-
tds = tr.css('td')
|
32
|
-
airspace.geometry = geometry_from tds[0].text
|
33
|
-
fail("geometry is not closed") unless airspace.geometry.closed?
|
34
|
-
airspace.layers << layer_from(tds[1].text)
|
35
|
-
airspace.layers.first.timetable = timetable_from! tds[2].text
|
36
|
-
airspace.layers.first.remarks = remarks_from(tds[2], tds[3], tds[4])
|
37
|
-
add airspace
|
38
|
-
rescue => error
|
39
|
-
warn("error parsing airspace `#{airspace.name}' at ##{index}: #{error.message}", pry: error)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def airspace_from(tr)
|
49
|
-
spans = tr.css('span:not([class*=strong])')
|
50
|
-
source_type = spans[1].text.blank_to_nil
|
51
|
-
fail "unknown type `#{source_type}'" unless SOURCE_TYPES.has_key? source_type
|
52
|
-
AIXM.airspace(
|
53
|
-
name: spans.map { |s| s.text.strip.blank_to_nil }.compact.join(' '),
|
54
|
-
type: SOURCE_TYPES.dig(source_type, :type),
|
55
|
-
local_type: SOURCE_TYPES.dig(source_type, :local_type)
|
56
|
-
).tap do |airspace|
|
57
|
-
airspace.source = source(position: tr.line)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def remarks_from(*parts)
|
62
|
-
part_titles = ['TIMETABLE', 'RESTRICTION', 'AUTHORITY/CONDITIONS']
|
63
|
-
[].tap do |remarks|
|
64
|
-
parts.each.with_index do |part, index|
|
65
|
-
if part = part.text.gsub(/ +/, ' ').gsub(/(\n ?)+/, "\n").strip.blank_to_nil
|
66
|
-
unless index.zero? && part == 'H24'
|
67
|
-
remarks << "**#{part_titles[index]}**\n#{part}"
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end.join("\n\n").blank_to_nil
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
module AIPP
|
2
|
-
module LF
|
3
|
-
|
4
|
-
# Sporting and Recreational Activities
|
5
|
-
class ENR55 < AIP
|
6
|
-
|
7
|
-
include AIPP::LF::Helpers::Common
|
8
|
-
|
9
|
-
# Map raw activities to activity and airspace type
|
10
|
-
ACTIVITIES = {
|
11
|
-
'activité particulière' => { activity: :other, airspace_type: :dangerous_activities_area },
|
12
|
-
'aéromodélisme' => { activity: :aeromodelling, airspace_type: :dangerous_activities_area },
|
13
|
-
'parachutage' => { activity: :parachuting, airspace_type: :dangerous_activities_area },
|
14
|
-
'treuillage' => { activity: :glider_winch, airspace_type: :dangerous_activities_area },
|
15
|
-
'voltige' => { activity: :acrobatics, airspace_type: :dangerous_activities_area }
|
16
|
-
}.freeze
|
17
|
-
|
18
|
-
def parse
|
19
|
-
prepare(html: read).css('tbody').each do |tbody|
|
20
|
-
tbody.css('tr').to_enum.each_slice(2).with_index(1) do |trs, index|
|
21
|
-
begin
|
22
|
-
id, activity_and_name, upper_limits, timetable = trs.first.css('td')
|
23
|
-
activity, name = activity_and_name.css('span')
|
24
|
-
lateral_limits, lower_limits, remarks = trs.last.css('td')
|
25
|
-
lateral_limits.search('br').each { |br| br.replace("|||") }
|
26
|
-
geometry, lateral_limits = lateral_limits.text.split('|||', 2)
|
27
|
-
lateral_limits&.gsub!('|||', "\n")
|
28
|
-
remarks = [remarks&.text&.cleanup&.blank_to_nil]
|
29
|
-
s = timetable&.text&.cleanup and remarks.prepend('**SCHEDULE**', s, '')
|
30
|
-
s = lateral_limits&.cleanup and remarks.prepend('**LATERAL LIMITS**', s, '')
|
31
|
-
airspace = AIXM.airspace(
|
32
|
-
source: source(position: trs.first.line),
|
33
|
-
id: id.text.strip,
|
34
|
-
type: ACTIVITIES.fetch(activity.text.downcase).fetch(:airspace_type),
|
35
|
-
name: [id.text.strip, name.text.cleanup].join(' ')
|
36
|
-
).tap do |airspace|
|
37
|
-
airspace.geometry = geometry_from(geometry)
|
38
|
-
airspace.layers << layer_from([upper_limits.text, lower_limits.text].join('---').cleanup).tap do |layer|
|
39
|
-
layer.activity = ACTIVITIES.fetch(activity.text.downcase).fetch(:activity)
|
40
|
-
layer.remarks = remarks.compact.join("\n")
|
41
|
-
end
|
42
|
-
end
|
43
|
-
rescue => error
|
44
|
-
warn("error parsing #{airspace.type} `#{airspace.name}' at ##{index}: #{error.message}", pry: error)
|
45
|
-
end
|
46
|
-
add airspace
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|