aipp 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +1 -2
- data/CHANGELOG.md +15 -0
- data/README.md +122 -37
- data/TODO.md +4 -0
- data/aipp.gemspec +8 -3
- data/lib/aipp.rb +14 -2
- data/lib/aipp/aip.rb +44 -29
- data/lib/aipp/downloader.rb +115 -0
- data/lib/aipp/executable.rb +6 -6
- data/lib/aipp/parser.rb +23 -23
- data/lib/aipp/patcher.rb +47 -0
- data/lib/aipp/pdf.rb +123 -0
- data/lib/aipp/regions/LF/AD-1.3.rb +162 -0
- data/lib/aipp/regions/LF/AD-1.3.yml +511 -0
- data/lib/aipp/regions/LF/AD-1.6.rb +31 -0
- data/lib/aipp/regions/LF/AD-2.rb +316 -0
- data/lib/aipp/regions/LF/AD-2.yml +185 -0
- data/lib/aipp/regions/LF/AD-3.1.rb-NEW +11 -0
- data/lib/aipp/regions/LF/ENR-2.1.rb +25 -24
- data/lib/aipp/regions/LF/ENR-4.1.rb +24 -23
- data/lib/aipp/regions/LF/ENR-4.3.rb +8 -6
- data/lib/aipp/regions/LF/ENR-5.1.rb +32 -22
- data/lib/aipp/regions/LF/ENR-5.5.rb-NEW +11 -0
- data/lib/aipp/regions/LF/helpers/AD_radio.rb +90 -0
- data/lib/aipp/regions/LF/helpers/URL.rb +26 -0
- data/lib/aipp/regions/LF/helpers/common.rb +186 -0
- data/lib/aipp/version.rb +1 -1
- data/lib/core_ext/enumerable.rb +52 -0
- data/lib/core_ext/nil_class.rb +10 -0
- data/lib/core_ext/object.rb +42 -0
- data/lib/core_ext/string.rb +105 -0
- data/spec/fixtures/archive.zip +0 -0
- data/spec/fixtures/document.pdf +0 -0
- data/spec/fixtures/document.pdf.json +1 -0
- data/spec/fixtures/new.html +6 -0
- data/spec/fixtures/new.pdf +0 -0
- data/spec/fixtures/new.txt +1 -0
- data/spec/lib/aipp/downloader_spec.rb +81 -0
- data/spec/lib/aipp/patcher_spec.rb +46 -0
- data/spec/lib/aipp/pdf_spec.rb +124 -0
- data/spec/lib/core_ext/enumberable_spec.rb +76 -0
- data/spec/lib/core_ext/nil_class_spec.rb +11 -0
- data/spec/lib/core_ext/string_spec.rb +88 -0
- data/spec/spec_helper.rb +1 -0
- metadata +123 -23
- data/lib/aipp/progress.rb +0 -40
- data/lib/aipp/refinements.rb +0 -114
- data/lib/aipp/regions/LF/helper.rb +0 -177
- data/spec/lib/aipp/refinements_spec.rb +0 -123
@@ -0,0 +1,31 @@
|
|
1
|
+
module AIPP
|
2
|
+
module LF
|
3
|
+
|
4
|
+
# Aerodromes radiocommunication facilities (VFR only)
|
5
|
+
class AD16 < AIP
|
6
|
+
|
7
|
+
include AIPP::LF::Helpers::Common
|
8
|
+
include AIPP::LF::Helpers::ADRadio
|
9
|
+
|
10
|
+
DEPENDS = %w(AD-1.3)
|
11
|
+
|
12
|
+
ID_FIXES = {
|
13
|
+
'LF04' => 'LF9004', # illegal ID as per AIXM
|
14
|
+
'LFPY' => nil # decommissioned - see https://fr.wikipedia.org/wiki/Base_a%C3%A9rienne_217_Br%C3%A9tigny-sur-Orge
|
15
|
+
}
|
16
|
+
|
17
|
+
def parse
|
18
|
+
prepare(html: read).css('tbody').first do |tbody|
|
19
|
+
tbody.css('tr').group_by_chunks { |e| e.attr(:id).match?(/-TXT_NAME-/) }.each do |tr, trs|
|
20
|
+
id = tr.css('span[id*="CODE_ICAO"]').text.cleanup
|
21
|
+
next unless id = ID_FIXES.fetch(id, id)
|
22
|
+
@airport = select(:airport, id: id).first
|
23
|
+
addresses_from(trs).each { |a| @airport.add_address(a) }
|
24
|
+
units_from(trs).each(&method(:write))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,316 @@
|
|
1
|
+
module AIPP
|
2
|
+
module LF
|
3
|
+
|
4
|
+
# Airports (IFR capable) and their CTR, AD navigational aids etc
|
5
|
+
class AD2 < AIP
|
6
|
+
|
7
|
+
include AIPP::LF::Helpers::Common
|
8
|
+
include AIPP::LF::Helpers::ADRadio
|
9
|
+
using AIXM::Refinements
|
10
|
+
|
11
|
+
# Map source types to type and optional local type
|
12
|
+
SOURCE_TYPES = {
|
13
|
+
'CTR' => { type: 'CTR' },
|
14
|
+
'RMZ' => { type: 'RAS', local_type: 'RMZ' },
|
15
|
+
'TMZ' => { type: 'RAS', local_type: 'TMZ' },
|
16
|
+
'RMZ-TMZ' => { type: 'RAS', local_type: 'RMZ-TMZ' }
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
# Airports without VAC (e.g. military installations)
|
20
|
+
NO_VAC = %w(LFOA LFBC LFQE LFOE LFSX LFBM LFSO LFMO LFQP LFSI LFKS LFPV).freeze
|
21
|
+
|
22
|
+
# Airports without VFR reporting points
|
23
|
+
# TODO: designated points on map but no list (LFLD LFSN LFBS) or no AD info (LFRL)
|
24
|
+
NO_DESIGNATED_POINTS = %w(LFAB LFAC LFAV LFAY LFBK LFBN LFBX LFCC LFCI LFCK LFCY LFDH LFDJ LFDN LFEC LFEY LFGA LFHP LFHV LFHY LFJR LFJY LFLA LFLH LFLO LFLV LFLW LFMQ LFMQ LFNB LFOH LFOQ LFOU LFOV LFOZ LFPO LFQA LFQB LFQG LFQM LFRC LFRI LFRM LFRT LFRU LFSD LFSG LFSM LFLD LFSN LFBS LFRL).freeze
|
25
|
+
|
26
|
+
# Map synonyms for +correlate+
|
27
|
+
SYNONYMS = [
|
28
|
+
'nord', 'north',
|
29
|
+
'est', 'east',
|
30
|
+
'sud', 'south',
|
31
|
+
'ouest', 'west',
|
32
|
+
'inst', 'instruction',
|
33
|
+
'junction', 'intersection',
|
34
|
+
'harbour', 'port',
|
35
|
+
'mouth', 'embouchure',
|
36
|
+
'tower', 'chateau'
|
37
|
+
].freeze
|
38
|
+
|
39
|
+
def parse
|
40
|
+
index_html = prepare(html: read("AD-0.6")) # index for AD-2.xxxx files
|
41
|
+
index_html.css('#AD-0\.6\.eAIP > .toc-block:nth-of-type(3) .toc-block a').each do |a|
|
42
|
+
@id = a.attribute('href').value[-4,4]
|
43
|
+
begin
|
44
|
+
aip_file = "AD-2.#{@id}"
|
45
|
+
html = prepare(html: read(aip_file))
|
46
|
+
# Airport
|
47
|
+
@remarks = []
|
48
|
+
@airport = AIXM.airport(
|
49
|
+
source: source(position: html.css('tr[id*="CODE_ICAO"]').first.line, aip_file: aip_file),
|
50
|
+
organisation: organisation_lf, # TODO: not yet implemented
|
51
|
+
id: @id,
|
52
|
+
name: html.css('tr[id*="CODE_ICAO"] td span:nth-of-type(2)').text.uptrans,
|
53
|
+
xy: xy_from(html.css('#AD-2\.2-Position_Geo_Arp td:nth-of-type(3)').text)
|
54
|
+
).tap do |airport|
|
55
|
+
airport.z = elevation_from(html.css('#AD-2\.2-Altitude_Reference td:nth-of-type(3)').text)
|
56
|
+
airport.declination = declination_from(html.css('#AD-2\.2-Declinaison_Magnetique td:nth-of-type(3)').text)
|
57
|
+
airport.transition_z = AIXM.z(5000, :qnh) # TODO: default - exceptions may exist
|
58
|
+
airport.timetable = timetable_from(html.css('#AD-2\.3-Gestionnaire_AD td:nth-of-type(3)').text)
|
59
|
+
end
|
60
|
+
runways_from(html.css('div[id*="-AD-2\.12"] tbody')).each { |r| @airport.add_runway(r) if r }
|
61
|
+
helipads_from(html.css('div[id*="-AD-2\.16"] tbody')).each { |h| @airport.add_helipad(h) if h }
|
62
|
+
text = html.css('#AD-2\.2-Observations td:nth-of-type(3)').text
|
63
|
+
@airport.remarks = ([remarks_from(text)] + @remarks).compact.join("\n\n").blank_to_nil
|
64
|
+
write @airport
|
65
|
+
# Airspaces
|
66
|
+
airspaces_from(html.css('div[id*="-AD-2\.17"] tbody')).each(&method(:write))
|
67
|
+
# Radio
|
68
|
+
trs = html.css('div[id*="-AD-2\.18"] tbody tr')
|
69
|
+
addresses_from(trs).each { |a| @airport.add_address(a) }
|
70
|
+
units_from(trs).each(&method(:write))
|
71
|
+
# Landing aids
|
72
|
+
# TODO: LOC/GP/DME as of section 2.19
|
73
|
+
# Designated points
|
74
|
+
unless NO_VAC.include?(@id) || NO_DESIGNATED_POINTS.include?(@id)
|
75
|
+
pdf = read("VAC-#{@id}")
|
76
|
+
designated_points_from(pdf).tap do |designated_points|
|
77
|
+
fix_designated_point_remarks(designated_points)
|
78
|
+
# debug(designated_points)
|
79
|
+
designated_points.each(&method(:write))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
rescue => error
|
83
|
+
warn("error parsing airport #{@id}: #{error.message}", pry: error)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def elevation_from(text)
|
91
|
+
value, unit = text.strip.split
|
92
|
+
AIXM.z(AIXM.d(value.to_i, unit).to_ft.dist, :qnh)
|
93
|
+
end
|
94
|
+
|
95
|
+
def declination_from(text)
|
96
|
+
value, direction = text.strip.split('°')
|
97
|
+
value = value.to_f * (direction == 'W' ? -1 : 1)
|
98
|
+
end
|
99
|
+
|
100
|
+
def remarks_from(text)
|
101
|
+
text.sub(/NIL|\(\*\)\s+/, '').strip.gsub(/(\s)\s+/, '\1').blank_to_nil
|
102
|
+
end
|
103
|
+
|
104
|
+
def runways_from(tbody)
|
105
|
+
directions_map = tbody.css('tr[id*="TXT_DESIG"]').map do |tr|
|
106
|
+
[AIXM.a(tr.css('td:first-of-type').text.strip), tr]
|
107
|
+
end.to_h
|
108
|
+
remarks_map = tbody.css('tr[id*="TXT_RMK_NAT"]').map do |tr|
|
109
|
+
[tr.text.strip[/\A\((\d+)\)/, 1].to_i, tr.css('span')]
|
110
|
+
end.to_h
|
111
|
+
directions = directions_map.keys
|
112
|
+
grouped_directions = directions.map do |direction|
|
113
|
+
inverted_direction = direction.invert
|
114
|
+
if directions.include? inverted_direction
|
115
|
+
[direction, inverted_direction].map(&:to_s).sort.join('/')
|
116
|
+
else
|
117
|
+
direction.to_s
|
118
|
+
end
|
119
|
+
end.uniq
|
120
|
+
grouped_directions.map do |runway_name|
|
121
|
+
AIXM.runway(name: runway_name).tap do |runway|
|
122
|
+
%i(forth back).each do |direction_attr|
|
123
|
+
if direction = runway.send(direction_attr)
|
124
|
+
tr = directions_map[direction.name]
|
125
|
+
if direction_attr == :forth
|
126
|
+
length, width = tr.css('td:nth-of-type(3)').text.strip.split('x')
|
127
|
+
runway.length = AIXM.d(length.strip.to_i, :m)
|
128
|
+
runway.width = AIXM.d(width.strip.to_i, :m)
|
129
|
+
text = tr.css('td:nth-of-type(5)').text.strip.split(%r<\W+/\W+>).first
|
130
|
+
runway.surface.composition = COMPOSITIONS.fetch(text)[:composition]
|
131
|
+
runway.surface.preparation = COMPOSITIONS.fetch(text)[:preparation]
|
132
|
+
if (text = tr.css('td:nth-of-type(4)').text).match?(AIXM::PCN_RE)
|
133
|
+
runway.surface.pcn = text
|
134
|
+
end
|
135
|
+
end
|
136
|
+
text = tr.css('td:nth-of-type(6)').text.strip
|
137
|
+
direction.xy = (xy_from(text) unless text.match?(/\A(\(.*)?\z/m))
|
138
|
+
if (text = tr.css('td:nth-of-type(7)').text.strip[/thr:\s+(\d+\s+\w+)/i, 1]).present?
|
139
|
+
direction.z = elevation_from(text)
|
140
|
+
end
|
141
|
+
if (text = tr.css('td:nth-of-type(2)').text.strip.sub(/\A(\d+).*$/m, '\1')).present?
|
142
|
+
direction.geographic_orientation = AIXM.a(text.to_i)
|
143
|
+
end
|
144
|
+
if (text = tr.css('td:nth-of-type(6)').text[/\((.+)\)/m, 1]).present?
|
145
|
+
direction.displaced_threshold = xy_from(text)
|
146
|
+
end
|
147
|
+
if (text = tr.css('td:nth-of-type(10)').text.strip[/\A\((\d+)\)/, 1]).present?
|
148
|
+
direction.remarks = remarks_from(remarks_map.fetch(text.to_i).text)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def helipads_from(tbody)
|
157
|
+
text_fr = tbody.css('td:nth-of-type(3)').text.compact
|
158
|
+
text_en = tbody.css('td:nth-of-type(4)').text.compact
|
159
|
+
case text_fr
|
160
|
+
when /NIL/, /\A\W*\z/
|
161
|
+
[]
|
162
|
+
when /instructions?\s+twr/i
|
163
|
+
@remarks << "HELICOPTER:\nSur instructions TWR.\nOn TWR clearance."
|
164
|
+
[]
|
165
|
+
when AIXM::DMS_RE
|
166
|
+
text_fr.scan(AIXM::DMS_RE).each_slice(2).with_index(1).map do |(lat, long), index|
|
167
|
+
AIXM.helipad(name: "H#{index}").tap do |helipad|
|
168
|
+
helipad.xy = AIXM.xy(lat: lat.first, long: long.first)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
else
|
172
|
+
@remarks << ['HELICOPTER:', text_fr.blank_to_nil, text_en.blank_to_nil].compact.join("\n")
|
173
|
+
[]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def airspaces_from(tbody)
|
178
|
+
return [] if tbody.text.blank?
|
179
|
+
airspace = nil
|
180
|
+
tbody.css('tr').to_enum.with_object([]) do |tr, array|
|
181
|
+
if tr.attr(:class) =~ /keep-with-next-row/
|
182
|
+
airspace = airspace_from tr
|
183
|
+
else
|
184
|
+
tds = tr.css('td')
|
185
|
+
airspace.geometry = geometry_from tds[0].text
|
186
|
+
fail("geometry is not closed") unless airspace.geometry.closed?
|
187
|
+
airspace.layers << layer_from(tds[2].text, tds[1].text.strip)
|
188
|
+
airspace.layers.first.timetable = timetable_from tds[4].text
|
189
|
+
airspace.layers.first.remarks = remarks_from(tds[4].text)
|
190
|
+
array << airspace
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def airspace_from(tr)
|
196
|
+
spans = tr.css(:span)
|
197
|
+
source_type = spans[1].text.blank_to_nil
|
198
|
+
fail "unknown type `#{source_type}'" unless SOURCE_TYPES.has_key? source_type
|
199
|
+
AIXM.airspace(
|
200
|
+
name: [spans[2].text, anglicise(name: spans[3]&.text)].compact.join(' '),
|
201
|
+
type: SOURCE_TYPES.dig(source_type, :type),
|
202
|
+
local_type: SOURCE_TYPES.dig(source_type, :local_type)
|
203
|
+
).tap do |airspace|
|
204
|
+
airspace.source = source(position: tr.line)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def designated_points_from(pdf, recursive=false)
|
209
|
+
from = (pdf.text =~ /^(.*?coordinates.*?names?)/i)
|
210
|
+
return [] if recursive && !from
|
211
|
+
warn("no designated points section begin found for #{@id}", pry: binding) unless from
|
212
|
+
from += $1.length
|
213
|
+
to = from + (pdf.text.from(from) =~ /\n\s*\n\s*\n|^.*(?:ifr|vfr|ad\s*equipment|special\s*activities|training\s*flights|mto\s*minima)/i)
|
214
|
+
warn("no designated points section end found for #{@id}", pry: binding) unless to
|
215
|
+
from, to = from + pdf.range.min, to + pdf.range.min # offset when recursive
|
216
|
+
buffer = {}
|
217
|
+
pdf.from(from).to(to).each_line.with_object([]) do |(line, page, last), designated_points|
|
218
|
+
line.remove!(/\u2190/) # remove arrow symbols
|
219
|
+
has_id = $1 if line.sub!(/^\s{,20}([A-Z][A-Z\d ]{1,3})(?=\W)/, '')
|
220
|
+
has_xy = line.match?(AIXM::DMS_RE)
|
221
|
+
designated_points << designated_point_from(buffer, pdf) if has_id || has_xy
|
222
|
+
if has_xy
|
223
|
+
2.times { (buffer[:xy] ||= []) << $1 if line.sub!(AIXM::DMS_RE, '') }
|
224
|
+
buffer[:xy]&.compact!
|
225
|
+
line.remove!(/\d{3,4}\D.+?MTG/) # remove extra columns (e.g. LFML)
|
226
|
+
line.remove!(/[\s#{AIXM::MIN}#{AIXM::SEC}]*[-\u2013]/) # remove dash between coordinates
|
227
|
+
end
|
228
|
+
buffer[:page] = page
|
229
|
+
buffer[:id] = has_id if has_id
|
230
|
+
buffer[:remarks] = [buffer[:remarks], line].join("\n")
|
231
|
+
designated_points << designated_point_from(buffer, pdf) if last
|
232
|
+
end.compact + designated_points_from(pdf.from(to).to(:end), true)
|
233
|
+
end
|
234
|
+
|
235
|
+
def designated_point_from(buffer, pdf)
|
236
|
+
if buffer[:id] && buffer[:xy]&.size == 2
|
237
|
+
buffer[:remarks].gsub!(/ {20}/, "\n") # recognize empty column space
|
238
|
+
buffer[:remarks].remove!(/\(\d+\)/) # remove footnotes
|
239
|
+
buffer[:remarks] = buffer[:remarks].unglue # separate glued words
|
240
|
+
AIXM.designated_point(
|
241
|
+
source: source(position: buffer[:page], aip_file: pdf.file.basename('.*').to_s),
|
242
|
+
type: :vfr_mandatory_reporting_point,
|
243
|
+
id: buffer[:id].remove(/\W/),
|
244
|
+
xy: AIXM.xy(lat: buffer[:xy].first, long: buffer[:xy].last)
|
245
|
+
).tap do |designated_point|
|
246
|
+
designated_point.airport = @airport
|
247
|
+
designated_point.remarks = buffer[:remarks].compact.blank_to_nil
|
248
|
+
buffer.clear
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# Assign scattered similar remarks to one and the same designated point
|
254
|
+
def fix_designated_point_remarks(designated_points)
|
255
|
+
one = nil
|
256
|
+
designated_points.map do |two|
|
257
|
+
if one
|
258
|
+
one_lines, two_lines = one.remarks&.lines, two.remarks&.lines
|
259
|
+
if one_lines && two_lines
|
260
|
+
if one_lines.count > 1 && (line = one_lines.last) !~ %r(\s/\s)
|
261
|
+
# Move up
|
262
|
+
if line.correlate(remainder = one_lines[0..-2].join, SYNONYMS) < line.correlate(two.remarks)
|
263
|
+
two.remarks = [line, two.remarks].join("\n").compact
|
264
|
+
one.remarks = remainder.compact
|
265
|
+
end
|
266
|
+
elsif two_lines.count > 1 && (line = two_lines.first) !~ %r(\s/\s)
|
267
|
+
# Move down
|
268
|
+
line = two_lines.first
|
269
|
+
if line.correlate(remainder = two_lines[1..-1].join, SYNONYMS) < line.correlate(one.remarks)
|
270
|
+
one.remarks = [one.remarks, line].join("\n").compact
|
271
|
+
two.remarks = remainder.compact
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
one = two
|
277
|
+
end.map do |designated_point|
|
278
|
+
designated_point.remarks = designated_point.remarks&.cleanup.blank_to_nil
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# def debug(dp)
|
283
|
+
# f = "/Users/sschwyn/Desktop/okay/#{@id}.txt"
|
284
|
+
# result = "\n--- #{@id} ---\n\n".red
|
285
|
+
# dp.each do |d|
|
286
|
+
# result += d.id.red + "\t#{d.xy.lat} - #{d.xy.long}\n"
|
287
|
+
# result += "#{d.remarks}\n\n".blue
|
288
|
+
# end
|
289
|
+
# result += "#{dp.count} point(s) for #{@id}".red
|
290
|
+
# unless File.exist?(f) && result == File.read(f)
|
291
|
+
# puts result
|
292
|
+
# gets
|
293
|
+
# puts "\e[H\e[2J"
|
294
|
+
# end
|
295
|
+
# File.write(f, result)
|
296
|
+
# end
|
297
|
+
|
298
|
+
patch AIXM::Component::Runway::Direction, :xy do |parser, object, value|
|
299
|
+
throw :abort unless value.nil?
|
300
|
+
@fixtures ||= YAML.load_file(Pathname(__FILE__).dirname.join('AD-2.yml'))
|
301
|
+
airport_id = parser.instance_variable_get(:@airport).id
|
302
|
+
direction_name = object.name.to_s
|
303
|
+
throw :abort if (xy = @fixtures.dig('runways', airport_id, direction_name, 'xy')).nil?
|
304
|
+
lat, long = xy.split(/\s+/)
|
305
|
+
AIXM.xy(lat: lat, long: long)
|
306
|
+
end
|
307
|
+
|
308
|
+
patch AIXM::Feature::NavigationalAid, :remarks do |parser, object, value|
|
309
|
+
@fixtures ||= YAML.load_file(Pathname(__FILE__).dirname.join('AD-2.yml'))
|
310
|
+
airport_id, designated_point_id = object.airport.id, object.id
|
311
|
+
@fixtures.dig('designated_points', airport_id, designated_point_id, 'remarks') || throw(:abort)
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# On Google Maps, click in the middle of the runway beginning, then click on
|
2
|
+
# the coordinates in the little window displayed at the bottom. The DMS
|
3
|
+
# coordinates will then appear in the left column ready for copy/paste.
|
4
|
+
---
|
5
|
+
runways:
|
6
|
+
LFLP:
|
7
|
+
"04R":
|
8
|
+
xy: 45°55'32.1"N 6°05'49.2"E
|
9
|
+
"22L":
|
10
|
+
xy: 45°55'53.4"N 6°06'13.4"E
|
11
|
+
LFOQ:
|
12
|
+
"02L":
|
13
|
+
xy: 47°40'11.2"N 1°12'14.1"E
|
14
|
+
"20R":
|
15
|
+
xy: 47°40'39.6"N 1°12'30.7"E
|
16
|
+
"02R":
|
17
|
+
xy: 47°40'10.4"N 1°12'17.4"E
|
18
|
+
"20L":
|
19
|
+
xy: 47°40'38.3"N 1°12'33.8"E
|
20
|
+
"12L":
|
21
|
+
xy: 47°40'49.9"N 1°12'02.3"E
|
22
|
+
"30R":
|
23
|
+
xy: 47°40'38.9"N 1°12'36.2"E
|
24
|
+
"12R":
|
25
|
+
xy: 47°40'49.9"N 1°12'02.3"E
|
26
|
+
"30L":
|
27
|
+
xy: 47°40'39.1"N 1°12'28.3"E
|
28
|
+
LFMK:
|
29
|
+
"10L":
|
30
|
+
xy: 43°13'01.3"N 2°18'19.7"E
|
31
|
+
"28R":
|
32
|
+
xy: 43°12'58.5"N 2°18'54.6"E
|
33
|
+
LFLH:
|
34
|
+
"17L":
|
35
|
+
xy: 46°49'57.5"N 4°49'14.4"E
|
36
|
+
"35R":
|
37
|
+
xy: 46°49'29.8"N 4°49'21.2"E
|
38
|
+
LFLB:
|
39
|
+
"18L":
|
40
|
+
xy: 45°38'16.9"N 5°52'52.2"E
|
41
|
+
"36R":
|
42
|
+
xy: 45°37'54.4"N 5°52'54.0"E
|
43
|
+
LFBG:
|
44
|
+
"05R":
|
45
|
+
xy: 45°38'56.8"N 0°19'07.6"W
|
46
|
+
"23L":
|
47
|
+
xy: 45°39'15.7"N 0°18'36.3"W
|
48
|
+
LFAB:
|
49
|
+
"13L":
|
50
|
+
xy: 49°53'12.1"N 1°04'40.1"E
|
51
|
+
"31R":
|
52
|
+
xy: 49°52'59.8"N 1°05'06.2"E
|
53
|
+
LFRL:
|
54
|
+
"13":
|
55
|
+
xy: 48°17'01.8"N 4°26'46.5"W
|
56
|
+
"31":
|
57
|
+
xy: 48°16'48.7"N 4°26'21.5"W
|
58
|
+
LFRM:
|
59
|
+
"02R":
|
60
|
+
xy: 47°56'40.1"N 0°12'01.6"E
|
61
|
+
"20L":
|
62
|
+
xy: 47°57'09.6"N 0°12'18.0"E
|
63
|
+
LFSM:
|
64
|
+
"08L":
|
65
|
+
xy: 47°29'10.8"N 6°47'08.0"E
|
66
|
+
"26R":
|
67
|
+
xy: 47°29'15.9"N 6°47'48.2"E
|
68
|
+
LFBR:
|
69
|
+
"12L":
|
70
|
+
xy: 43°27'08.9"N 1°15'45.3"E
|
71
|
+
"30R":
|
72
|
+
xy: 43°27'04.4"N 1°15'57.6"E
|
73
|
+
"12R":
|
74
|
+
xy: 43°27'02.1"N 1°15'31.3"E
|
75
|
+
"30L":
|
76
|
+
xy: 43°26'50.1"N 1°16'04.0"E
|
77
|
+
LFSN:
|
78
|
+
"03R":
|
79
|
+
xy: 48°41'15.5"N 6°13'44.5"E
|
80
|
+
"21L":
|
81
|
+
xy: 48°41'31.1"N 6°13'57.3"E
|
82
|
+
LFQG:
|
83
|
+
"12L":
|
84
|
+
xy: 47°00'19.6"N 3°06'30.2"E
|
85
|
+
"30R":
|
86
|
+
xy: 47°00'03.5"N 3°07'07.8"E
|
87
|
+
LFOJ:
|
88
|
+
"04":
|
89
|
+
xy: 47°59'24.5"N 1°45'29.9"E
|
90
|
+
"22":
|
91
|
+
xy: 47°59'49.3"N 1°45'58.2"E
|
92
|
+
LFBX:
|
93
|
+
"11L":
|
94
|
+
xy: 45°11'59.1"N 0°48'34.6"E
|
95
|
+
"29R":
|
96
|
+
xy: 45°11'48.2"N 0°49'10.1"E
|
97
|
+
LFQA:
|
98
|
+
"07R":
|
99
|
+
xy: 49°12'23.4"N 4°09'03.1"E
|
100
|
+
"25L":
|
101
|
+
xy: 49°12'36.9"N 4°09'50.2"E
|
102
|
+
LFRN:
|
103
|
+
"14L":
|
104
|
+
xy: 48°04'09.4"N 1°44'18.7"W
|
105
|
+
"32R":
|
106
|
+
xy: 48°03'55.4"N 1°44'02.4"W
|
107
|
+
LFOP:
|
108
|
+
"05":
|
109
|
+
xy: 49°23'05.4"N 1°11'11.4"E
|
110
|
+
"23":
|
111
|
+
xy: 49°23'26.9"N 1°11'44.1"E
|
112
|
+
LFSI:
|
113
|
+
"11L":
|
114
|
+
xy: 48°38'11.9"N 4°54'06.0"E
|
115
|
+
"29R":
|
116
|
+
xy: 48°37'57.2"N 4°54'58.1"E
|
117
|
+
LFMY:
|
118
|
+
"09":
|
119
|
+
xy: 43°36'32.8"N 5°06'33.4"E
|
120
|
+
"27":
|
121
|
+
xy: 43°36'32.3"N 5°07'08.8"E
|
122
|
+
"16L":
|
123
|
+
xy: 43°36'53.1"N 5°06'57.6"E
|
124
|
+
"34R":
|
125
|
+
xy: 43°36'22.9"N 5°07'09.5"E
|
126
|
+
"16R":
|
127
|
+
xy: 43°36'46.4"N 5°06'52.0"E
|
128
|
+
"34L":
|
129
|
+
xy: 43°36'12.1"N 5°07'05.4"E
|
130
|
+
LFQB:
|
131
|
+
"05":
|
132
|
+
xy: 48°19'01.1"N 4°00'28.2"E
|
133
|
+
"23":
|
134
|
+
xy: 48°19'15.8"N 4°00'56.2"E
|
135
|
+
"17R":
|
136
|
+
xy: 48°19'32.1"N 4°00'44.3"E
|
137
|
+
"35L":
|
138
|
+
xy: 48°19'03.1"N 4°00'49.1"E
|
139
|
+
LFLU:
|
140
|
+
"01L":
|
141
|
+
xy: 44°54'56.1"N 4°58'10.9"E
|
142
|
+
"19R":
|
143
|
+
xy: 44°55'34.4"N 4°58'18.1"E
|
144
|
+
"01R":
|
145
|
+
xy: 44°55'09.4"N 4°58'17.0"E
|
146
|
+
"19L":
|
147
|
+
xy: 44°55'22.3"N 4°58'19.4"E
|
148
|
+
LFAV:
|
149
|
+
"06":
|
150
|
+
xy: 50°19'40.0"N 3°27'19.8"E
|
151
|
+
"24":
|
152
|
+
xy: 50°19'49.6"N 3°27'47.5"E
|
153
|
+
LFRV:
|
154
|
+
"08":
|
155
|
+
xy: 47°43'26.6"N 2°43'51.6"W
|
156
|
+
"26":
|
157
|
+
xy: 47°43'31.0"N 2°43'04.4"W
|
158
|
+
designated_points:
|
159
|
+
LFQQ:
|
160
|
+
SA:
|
161
|
+
remarks: "Rond-point à l'Est de PONT A MARCQ, et au Nord du golf.\nTraffic circle East of PONT A MARCQ and North of the golf.\nPont de l’autoroute A1 et pont ligne TGV sur le canal de la\nDEULE."
|
162
|
+
SW:
|
163
|
+
remarks: "Bridge of A1 motorway and bridge of the high speed train\n(TGV) line over the DEULE canal."
|
164
|
+
LFML:
|
165
|
+
LP:
|
166
|
+
remarks: "St Cannat La Pile"
|
167
|
+
LFPG:
|
168
|
+
RH1:
|
169
|
+
remarks: "Péage autoroute A1.\nMotorway Toll A1."
|
170
|
+
RH2:
|
171
|
+
remarks: "Est du bourg (radial 181 PGS).\nEast Town (PGS 181 radial)."
|
172
|
+
LFLN:
|
173
|
+
S:
|
174
|
+
remarks: "Iguerande"
|
175
|
+
SA:
|
176
|
+
remarks: "ABM W ville d’Avrilly, ABM E château d’eau d’Avrilly\nABM W of town of Avrilly, ABM E water tower of Avrilly"
|
177
|
+
W:
|
178
|
+
remarks: "Le Donjon"
|
179
|
+
WA:
|
180
|
+
remarks: "Le Pin, château d’eau au SE de la localité\nLe Pin, water tower SE of the town"
|
181
|
+
LFBO:
|
182
|
+
WF:
|
183
|
+
remarks: "Lac de la Bordette.Forêt de Bouconne / La Bordette lake.\nBouconne forest"
|
184
|
+
WD:
|
185
|
+
remarks: "Echangeur N124.Zone commerciale En Jacca.A côté de\nLeroy Merlin.\nInterchange N124.Commercial zone En Jacca.Near Leroy\nMerlin."
|