aipp 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -2
- data/CHANGELOG.md +8 -0
- data/README.md +21 -14
- data/exe/aip2aixm +1 -1
- data/exe/aip2ofmx +1 -1
- data/lib/aipp/debugger.rb +5 -1
- data/lib/aipp/downloader/file.rb +7 -5
- data/lib/aipp/downloader/http.rb +4 -4
- data/lib/aipp/downloader.rb +9 -2
- data/lib/aipp/executable.rb +52 -31
- data/lib/aipp/regions/LF/aip/services.rb +24 -4
- data/lib/aipp/regions/LF/helpers/base.rb +4 -6
- data/lib/aipp/regions/LS/README.md +58 -2
- data/lib/aipp/regions/LS/helpers/base.rb +10 -5
- data/lib/aipp/regions/LS/notam/ENR.rb +12 -10
- data/lib/aipp/regions/LS/shoot/shooting_grounds.rb +116 -0
- data/lib/aipp/runner.rb +6 -6
- data/lib/aipp/scopes/aip/executable.rb +38 -0
- data/lib/aipp/{aip → scopes/aip}/runner.rb +7 -3
- data/lib/aipp/scopes/notam/executable.rb +28 -0
- data/lib/aipp/{notam → scopes/notam}/runner.rb +6 -2
- data/lib/aipp/scopes/shoot/README.md +10 -0
- data/lib/aipp/scopes/shoot/executable.rb +28 -0
- data/lib/aipp/scopes/shoot/parser.rb +9 -0
- data/lib/aipp/scopes/shoot/runner.rb +32 -0
- data/lib/aipp/version.rb +1 -1
- data/lib/aipp.rb +10 -8
- data.tar.gz.sig +0 -0
- metadata +16 -15
- metadata.gz.sig +0 -0
- data/exe/notam2aixm +0 -5
- data/exe/notam2ofmx +0 -5
- data/lib/aipp/aip/executable.rb +0 -40
- data/lib/aipp/notam/executable.rb +0 -27
- /data/lib/aipp/{aip → scopes/aip}/README.md +0 -0
- /data/lib/aipp/{aip → scopes/aip}/parser.rb +0 -0
- /data/lib/aipp/{notam → scopes/notam}/README.md +0 -0
- /data/lib/aipp/{notam → scopes/notam}/parser.rb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d141c73e057eda545578568c2817d648c998e56154f1d68ecd40c59589a04738
|
|
4
|
+
data.tar.gz: 70986dde2f1a7120f49061a65ee1b07c980469ff5f9ea6da4527f0160f613ed8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1e97fc5731a4356836ce60703cddd076d33d99d94cfaa667a8f1cf203f28003710d3c27263efd8e638e902f6ee9a076c46fb36881575425bf994202842b2a3d3
|
|
7
|
+
data.tar.gz: '08648422f96c2dd925873ec4153b3420754323704df5ec037042bed1f5a4611929d01ae2ee6ab02b718e6e28f9ea57ef76481eaf7ab4abe0a6dd0d89a547fa90'
|
checksums.yaml.gz.sig
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
�
|
|
1
|
+
1�ǵm��Gy
|
|
2
|
+
Z��ѳ�y@�'�i��:��x���琇�������%�\
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -55,22 +55,29 @@ bundle install --trust-policy MediumSecurity
|
|
|
55
55
|
|
|
56
56
|
## Usage
|
|
57
57
|
|
|
58
|
-
AIPP parses different kind of information sources
|
|
58
|
+
AIPP parses different kind of information sources and converts them to different output formats depending on which executable you use:
|
|
59
|
+
|
|
60
|
+
Executable | Output Format
|
|
61
|
+
-----------|--------------
|
|
62
|
+
`aip2aixm` | AIXM
|
|
63
|
+
`aip2ofmx` | OFMX
|
|
64
|
+
|
|
65
|
+
The parsers are organized in three levels:
|
|
59
66
|
|
|
60
67
|
```
|
|
61
68
|
region ⬅︎ aeronautical region such as "LF" (France)
|
|
62
|
-
└──
|
|
63
|
-
└── section ⬅︎
|
|
69
|
+
└── scope ⬅︎ scope such as "AIP" or "NOTAM"
|
|
70
|
+
└── section ⬅︎ section of the scope such as "ENR-2.1" or "aerodromes"
|
|
64
71
|
```
|
|
65
72
|
|
|
66
|
-
The following
|
|
73
|
+
The following scopes are currently available:
|
|
67
74
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
[AIP](lib/aipp/aip/README.md) | aeronautical information publication |
|
|
71
|
-
[NOTAM](lib/aipp/notam/README.md) | notice to airmen |
|
|
75
|
+
Scope | Content | Cache
|
|
76
|
+
------|---------|------
|
|
77
|
+
[AIP](lib/aipp/aip/README.md) (default) | aeronautical information publication | by AIRAC cycle
|
|
78
|
+
[NOTAM](lib/aipp/notam/README.md) | notice to airmen | by effective date and hour
|
|
72
79
|
|
|
73
|
-
To list all available regions and sections
|
|
80
|
+
To list all available scopes, regions and sections:
|
|
74
81
|
|
|
75
82
|
```
|
|
76
83
|
aip2aixm --list
|
|
@@ -92,15 +99,15 @@ You'll find the OFMX file in the current directory if the binary exits successfu
|
|
|
92
99
|
|
|
93
100
|
## Regions
|
|
94
101
|
|
|
95
|
-
To implement a region, you have to create a directory <samp>lib/aipp/regions/{REGION}/</samp> off the gem root and then subdirectories for each
|
|
102
|
+
To implement a region, you have to create a directory <samp>lib/aipp/regions/{REGION}/</samp> off the gem root and then subdirectories for each scope as well as for support files. Here's a simplified overview for the region "LF" (France):
|
|
96
103
|
|
|
97
104
|
```
|
|
98
105
|
LF/ ⬅︎ region "LF"
|
|
99
106
|
├── README.md
|
|
100
|
-
├── aip ⬅︎
|
|
107
|
+
├── aip ⬅︎ scope "AIP"
|
|
101
108
|
│ ├── AD-2.rb ⬅︎ section "AD-2"
|
|
102
109
|
│ └── ENR-4.3.rb ⬅︎ section "ENR-4.3"
|
|
103
|
-
├── notam ⬅︎
|
|
110
|
+
├── notam ⬅︎ scope "NOTAM"
|
|
104
111
|
│ ├── AD.rb ⬅︎ section "AD"
|
|
105
112
|
│ └── ENR.rb ⬅︎ section "ENR"
|
|
106
113
|
├── borders
|
|
@@ -315,7 +322,7 @@ module MyAPI
|
|
|
315
322
|
end
|
|
316
323
|
```
|
|
317
324
|
|
|
318
|
-
For performance, all downloads are cached and subsequent runs will use the cached data rather than fetching the sources anew. Each
|
|
325
|
+
For performance, all downloads are cached and subsequent runs will use the cached data rather than fetching the sources anew. Each scope defines a cache time window, see the [table of scopes above](#label-Usage). You can discard existing and rebuild caches by use of the `--clean` command line argument.
|
|
319
326
|
|
|
320
327
|
#### Optional `setup` Method
|
|
321
328
|
|
|
@@ -499,7 +506,7 @@ debugger
|
|
|
499
506
|
|
|
500
507
|
AIPP uses a storage directory for configuration, caching and in order to keep the results of previous runs. The default location is `~/.aipp`, however, you can pass a different directory with the `--storage` argument.
|
|
501
508
|
|
|
502
|
-
You'll find a directory for each region and
|
|
509
|
+
You'll find a directory for each region and scope which contains the following items:
|
|
503
510
|
|
|
504
511
|
* `sources/`<br>ZIP archives which cache all source files used to build.
|
|
505
512
|
* `builds/`<br>ZIP archives of successful builds containing:
|
data/exe/aip2aixm
CHANGED
data/exe/aip2ofmx
CHANGED
data/lib/aipp/debugger.rb
CHANGED
|
@@ -78,7 +78,11 @@ module AIPP
|
|
|
78
78
|
rescue => error
|
|
79
79
|
message = error.respond_to?(:original_message) ? error.original_message : error.message
|
|
80
80
|
puts "ERROR: #{message}".magenta
|
|
81
|
-
|
|
81
|
+
if AIPP.options.verbose
|
|
82
|
+
raise
|
|
83
|
+
else
|
|
84
|
+
exit 1
|
|
85
|
+
end
|
|
82
86
|
end
|
|
83
87
|
|
|
84
88
|
def call_without_rescue(&block)
|
data/lib/aipp/downloader/file.rb
CHANGED
|
@@ -3,6 +3,8 @@ module AIPP
|
|
|
3
3
|
|
|
4
4
|
# Local file
|
|
5
5
|
class File
|
|
6
|
+
attr_reader :file
|
|
7
|
+
|
|
6
8
|
def initialize(archive: nil, file:, type: nil)
|
|
7
9
|
@archive = Pathname(archive) if archive
|
|
8
10
|
@file, @type = Pathname(file), type&.to_s
|
|
@@ -12,10 +14,10 @@ module AIPP
|
|
|
12
14
|
path.join(fetched_file).tap do |target|
|
|
13
15
|
if @archive
|
|
14
16
|
fail NotFoundError unless @archive.exist?
|
|
15
|
-
extract(
|
|
17
|
+
extract(file, from: @archive, as: target)
|
|
16
18
|
else
|
|
17
|
-
fail NotFoundError unless
|
|
18
|
-
FileUtils.cp(
|
|
19
|
+
fail NotFoundError unless file.exist?
|
|
20
|
+
FileUtils.cp(file, target)
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
self
|
|
@@ -28,11 +30,11 @@ module AIPP
|
|
|
28
30
|
private
|
|
29
31
|
|
|
30
32
|
def name
|
|
31
|
-
|
|
33
|
+
file.basename(file.extname).to_s
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
def type
|
|
35
|
-
@type ||
|
|
37
|
+
@type || file.extname[1..] || fail("type must be declared")
|
|
36
38
|
end
|
|
37
39
|
|
|
38
40
|
def extract(file, from:, as:)
|
data/lib/aipp/downloader/http.rb
CHANGED
|
@@ -16,14 +16,14 @@ module AIPP
|
|
|
16
16
|
# @param path [Pathname] directory where to write the fetched file
|
|
17
17
|
# @return [File] fetched file
|
|
18
18
|
def fetch_to(path)
|
|
19
|
-
response = Excon.get((@archive ||
|
|
19
|
+
response = Excon.get((@archive || file).to_s, headers: @headers)
|
|
20
20
|
fail NotFoundError if response.status == 404
|
|
21
21
|
mime_type = ARCHIVE_MIME_TYPES.fetch(response.headers['Content-Type'], :dat)
|
|
22
22
|
downloaded_file = path.join([@digest, mime_type].join('.'))
|
|
23
23
|
::File.write(downloaded_file, response.body)
|
|
24
24
|
path.join(fetched_file).tap do |target|
|
|
25
25
|
if @archive
|
|
26
|
-
extract(
|
|
26
|
+
extract(file, from: downloaded_file, as: target)
|
|
27
27
|
::File.delete(downloaded_file)
|
|
28
28
|
else
|
|
29
29
|
::File.rename(downloaded_file, target)
|
|
@@ -35,12 +35,12 @@ module AIPP
|
|
|
35
35
|
private
|
|
36
36
|
|
|
37
37
|
def name
|
|
38
|
-
path = Pathname(
|
|
38
|
+
path = Pathname(file.path)
|
|
39
39
|
path.basename(path.extname).to_s.blank_to_nil || @digest
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def type
|
|
43
|
-
@type || Pathname(
|
|
43
|
+
@type || Pathname(file.path).extname[1..].blank_to_nil || fail("type must be declared")
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
data/lib/aipp/downloader.rb
CHANGED
|
@@ -165,13 +165,20 @@ module AIPP
|
|
|
165
165
|
case file.extname
|
|
166
166
|
when '.xml', '.ofmx' then Nokogiri.XML(::File.open(file), &:noblanks)
|
|
167
167
|
when '.html' then Nokogiri.HTML5(::File.open(file))
|
|
168
|
-
when '.json' then JSON.load_file(file)
|
|
168
|
+
when '.json' then JSON.load_file(file, symbolize_names: true)
|
|
169
169
|
when '.pdf' then AIPP::PDF.new(file)
|
|
170
|
-
when '.xlsx', '.ods'
|
|
170
|
+
when '.xlsx', '.ods' then Roo::Spreadsheet.open(file.to_s)
|
|
171
|
+
when '.csv' then Roo::Spreadsheet.open(file.to_s, csv_options: { col_sep: separator(file) })
|
|
171
172
|
when '.txt' then ::File.read(file)
|
|
172
173
|
else fail(ArgumentError, "unrecognized file type")
|
|
173
174
|
end
|
|
174
175
|
end
|
|
175
176
|
|
|
177
|
+
# @return [String] most likely separator character of CSV and similar files
|
|
178
|
+
def separator(file)
|
|
179
|
+
content = file.read
|
|
180
|
+
%W(, ; \t).map { [content.scan(_1).count, _1] }.sort.last.last
|
|
181
|
+
end
|
|
182
|
+
|
|
176
183
|
end
|
|
177
184
|
end
|
data/lib/aipp/executable.rb
CHANGED
|
@@ -5,7 +5,9 @@ module AIPP
|
|
|
5
5
|
include AIPP::Debugger
|
|
6
6
|
|
|
7
7
|
def initialize(exe_file)
|
|
8
|
+
require_scope
|
|
8
9
|
AIPP.options.replace(
|
|
10
|
+
scope: scope,
|
|
9
11
|
schema: exe_file.split('2').last.to_sym,
|
|
10
12
|
storage: Pathname(Dir.home).join('.aipp'),
|
|
11
13
|
clean: false,
|
|
@@ -16,13 +18,36 @@ module AIPP
|
|
|
16
18
|
debug_on_warning: false,
|
|
17
19
|
debug_on_error: false
|
|
18
20
|
)
|
|
21
|
+
options
|
|
22
|
+
OptionParser.new do |o|
|
|
23
|
+
o.on('-r', '--region STRING', String, 'region (e.g. "LF")') { AIPP.options.region = _1.upcase }
|
|
24
|
+
o.on('-s', '--section STRING', String, 'process this section only') { AIPP.options.section = _1.classify }
|
|
25
|
+
o.on('-d', '--storage DIR', String, 'storage directory (default: "~/.aipp")') { AIPP.options.storage = Pathname(_1) }
|
|
26
|
+
o.on('-o', '--output FILE', String, 'output file') { AIPP.options.output_file = _1 }
|
|
27
|
+
option_parser(o)
|
|
28
|
+
if AIPP.options.schema == :ofmx
|
|
29
|
+
o.on('-m', '--[no-]mid', 'insert mid attributes into all Uid elements (default: false)') { AIPP.options.mid = _1 }
|
|
30
|
+
end
|
|
31
|
+
o.on('-h', '--[no-]check-links', 'check all links with HEAD requests') { AIPP.options.check_links = _1 }
|
|
32
|
+
o.on('-c', '--[no-]clean', 'clean cache and download from sources anew (default: false)') { AIPP.options.clean = _1 }
|
|
33
|
+
o.on('-f', '--[no-]force', 'continue on non-fatal errors (default: false)') { AIPP.options.force = _1 }
|
|
34
|
+
o.on('-q', '--[no-]quiet', 'suppress all informational output (default: false)') { AIPP.options.quiet = _1 }
|
|
35
|
+
o.on('-v', '--[no-]verbose', 'verbose output including unsevere warnings (default: false)') { AIPP.options.verbose = _1 }
|
|
36
|
+
o.on('-w', '--debug-on-warning [ID]', Integer, 'open debug session on warning with ID (default: false)') { AIPP.options.debug_on_warning = _1 || true }
|
|
37
|
+
o.on('-e', '--[no-]debug-on-error', 'open debug session on error (default: false)') { AIPP.options.debug_on_error = _1 }
|
|
38
|
+
o.on('-A', '--about', 'show author/license information and exit') { about }
|
|
39
|
+
o.on('-R', '--readme', 'show README and exit') { readme }
|
|
40
|
+
o.on('-L', '--list', 'list implemented regions') { list }
|
|
41
|
+
o.on('-V', '--version', 'show version and exit') { version }
|
|
42
|
+
end.parse!
|
|
43
|
+
guard if respond_to? :guard
|
|
19
44
|
end
|
|
20
45
|
|
|
21
46
|
def run
|
|
22
47
|
with_debugger do
|
|
23
48
|
String.disable_colorization = !STDOUT.tty?
|
|
24
49
|
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
25
|
-
[:AIPP, AIPP.options.
|
|
50
|
+
[:AIPP, AIPP.options.scope, :Runner].constantize.new.run
|
|
26
51
|
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
27
52
|
info("finished after %s" % Time.at(ending - starting).utc.strftime("%H:%M:%S"))
|
|
28
53
|
end
|
|
@@ -30,30 +55,6 @@ module AIPP
|
|
|
30
55
|
|
|
31
56
|
private
|
|
32
57
|
|
|
33
|
-
def common_options(o)
|
|
34
|
-
o.on('-r', '--region STRING', String, 'region (e.g. "LF")') { AIPP.options.region = _1.upcase }
|
|
35
|
-
o.on('-s', '--section STRING', String, 'process this section only') { AIPP.options.section = _1.classify }
|
|
36
|
-
o.on('-d', '--storage DIR', String, 'storage directory (default: "~/.aipp")') { AIPP.options.storage = Pathname(_1) }
|
|
37
|
-
o.on('-o', '--output FILE', String, 'output file') { AIPP.options.output_file = _1 }
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def developer_options(o)
|
|
41
|
-
if AIPP.options.schema == :ofmx
|
|
42
|
-
o.on('-m', '--[no-]mid', 'insert mid attributes into all Uid elements (default: false)') { AIPP.options.mid = _1 }
|
|
43
|
-
end
|
|
44
|
-
o.on('-h', '--[no-]check-links', 'check all links with HEAD requests') { AIPP.options.check_links = _1 }
|
|
45
|
-
o.on('-c', '--[no-]clean', 'clean cache and download from sources anew (default: false)') { AIPP.options.clean = _1 }
|
|
46
|
-
o.on('-f', '--[no-]force', 'continue on non-fatal errors (default: false)') { AIPP.options.force = _1 }
|
|
47
|
-
o.on('-q', '--[no-]quiet', 'suppress all informational output (default: false)') { AIPP.options.quiet = _1 }
|
|
48
|
-
o.on('-v', '--[no-]verbose', 'verbose output including unsevere warnings (default: false)') { AIPP.options.verbose = _1 }
|
|
49
|
-
o.on('-w', '--debug-on-warning [ID]', Integer, 'open debug session on warning with ID (default: false)') { AIPP.options.debug_on_warning = _1 || true }
|
|
50
|
-
o.on('-e', '--[no-]debug-on-error', 'open debug session on error (default: false)') { AIPP.options.debug_on_error = _1 }
|
|
51
|
-
o.on('-A', '--about', 'show author/license information and exit') { about }
|
|
52
|
-
o.on('-R', '--readme', 'show README and exit') { readme }
|
|
53
|
-
o.on('-L', '--list', 'list implemented regions') { list }
|
|
54
|
-
o.on('-V', '--version', 'show version and exit') { version }
|
|
55
|
-
end
|
|
56
|
-
|
|
57
58
|
def about
|
|
58
59
|
puts 'Written by Sven Schwyn (bitcetera.com) and distributed under MIT license.'
|
|
59
60
|
exit
|
|
@@ -65,13 +66,18 @@ module AIPP
|
|
|
65
66
|
end
|
|
66
67
|
|
|
67
68
|
def list
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
puts "Available scopes -> regions -> sections:"
|
|
70
|
+
lib_dir.join('scopes').each_child do |scope_dir|
|
|
71
|
+
next unless scope_dir.directory?
|
|
72
|
+
puts "\n#{scope_dir.basename} ->".upcase
|
|
73
|
+
lib_dir.join('regions').each_child do |region_dir|
|
|
74
|
+
next unless region_dir.directory? && region_dir.join(scope_dir.basename).exist?
|
|
75
|
+
puts " #{region_dir.basename} ->"
|
|
76
|
+
region_dir.join(scope_dir.basename).glob('*.rb') do |section_file|
|
|
77
|
+
puts " #{section_file.basename('.rb')}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
73
80
|
end
|
|
74
|
-
puts hash.to_yaml.lines[1..]
|
|
75
81
|
exit
|
|
76
82
|
end
|
|
77
83
|
|
|
@@ -80,5 +86,20 @@ module AIPP
|
|
|
80
86
|
exit
|
|
81
87
|
end
|
|
82
88
|
|
|
89
|
+
def lib_dir
|
|
90
|
+
Pathname(__FILE__).dirname
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def scope
|
|
94
|
+
@scope ||= ARGV.first.match?(/^-/) ? 'AIP' : ARGV.shift.upcase
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def require_scope
|
|
98
|
+
lib_dir.join('scopes', scope.downcase).glob('*.rb').each { require _1 }
|
|
99
|
+
extend [:AIPP, scope, :Executable].constantize
|
|
100
|
+
rescue NameError
|
|
101
|
+
puts "ERROR: unknown scope `#{scope}'".magenta
|
|
102
|
+
exit 1
|
|
103
|
+
end
|
|
83
104
|
end
|
|
84
105
|
end
|
|
@@ -68,10 +68,12 @@ module AIPP::LF::AIP
|
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
|
-
# Assign
|
|
71
|
+
# Assign A/A address to all yet radioless airports
|
|
72
72
|
find_by(:airport).each do |airport|
|
|
73
73
|
unless airport.services.find_by(:service, type: :aerodrome_control_tower_service).any? || airport.addresses.any?
|
|
74
|
-
airport.add_address(
|
|
74
|
+
airport.add_address(
|
|
75
|
+
address_from_vac_for(airport) || fallback_address_for(airport)
|
|
76
|
+
)
|
|
75
77
|
end
|
|
76
78
|
end
|
|
77
79
|
end
|
|
@@ -98,14 +100,32 @@ module AIPP::LF::AIP
|
|
|
98
100
|
end.compact
|
|
99
101
|
end
|
|
100
102
|
|
|
101
|
-
|
|
103
|
+
# TODO: A/A adresses are read unreliably from VAC due to data inconsistencies
|
|
104
|
+
# in XML. Once fixed, integrate this into `addresses_from` as per:
|
|
105
|
+
# https://gitlab.com/openflightmaps/region-issues/-/issues/68
|
|
106
|
+
def address_from_vac_for(airport)
|
|
107
|
+
if aa = read("VAC-#{airport.id}").text.first_match(%r(A/A\s+\(?(\d{3}\.\d{1,3})))
|
|
108
|
+
AIXM.address(
|
|
109
|
+
type: :radio_frequency,
|
|
110
|
+
address: AIXM.f(aa.to_f, :mhz)
|
|
111
|
+
).tap do |address|
|
|
112
|
+
address.remarks = {
|
|
113
|
+
'type' => 'A/A',
|
|
114
|
+
'indicatif/callsign' => airport.name
|
|
115
|
+
}.to_remarks
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
rescue AIPP::Downloader::NotFoundError
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def fallback_address_for(airport)
|
|
102
122
|
AIXM.address(
|
|
103
123
|
type: :radio_frequency,
|
|
104
124
|
address: AIXM.f(123.5, :mhz)
|
|
105
125
|
).tap do |address|
|
|
106
126
|
address.remarks = {
|
|
107
127
|
'type' => 'A/A',
|
|
108
|
-
'indicatif/callsign' =>
|
|
128
|
+
'indicatif/callsign' => airport.name
|
|
109
129
|
}.to_remarks
|
|
110
130
|
end
|
|
111
131
|
end
|
|
@@ -28,12 +28,10 @@ module AIPP
|
|
|
28
28
|
case document
|
|
29
29
|
when /^Obstacles$/ # obstacles spreadsheet
|
|
30
30
|
AIPP::Downloader::HTTP.new(file: "#{sia_url}/FRANCE/ObstaclesDataZone1MFRANCE_#{xml_date.remove('-')}.xlsx")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# when /^[A-Z]+-/ # eAIP HTML page (e.g. ENR-5.5)
|
|
36
|
-
# AIPP::Downloader::HTTP.new(file: "#{sia_url}/FRANCE/AIRAC-#{xml_date}/html/eAIP/FR-#{document}-fr-FR.html")
|
|
31
|
+
when /^VAC\-(\w+)/ # aerodrome VAC PDF
|
|
32
|
+
AIPP::Downloader::HTTP.new(file: "#{sia_url}/Atlas-VAC/PDF_AIPparSSection/VAC/AD/AD-2.#{$1}.pdf")
|
|
33
|
+
when /^VACH\-(\w+)/ # helipad VAC PDF
|
|
34
|
+
AIPP::Downloader::HTTP.new(file: "#{sia_url}/Atlas-VAC/PDF_AIPparSSection/VACH/AD/AD-3.#{$1}.pdf")
|
|
37
35
|
else # SIA XML database dump
|
|
38
36
|
AIPP::Downloader::File.new(file: "XML_SIA_#{xml_date}.xml")
|
|
39
37
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# LS – Switzerland
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## NOTAM API
|
|
4
4
|
|
|
5
|
-
The NOTAM messages are fetched from the Neway API which is a non-public
|
|
5
|
+
The NOTAM messages are fetched from the Neway API which is a **non-public**
|
|
6
6
|
GraphQL API.
|
|
7
7
|
|
|
8
8
|
You have to set the following environment variables:
|
|
@@ -36,6 +36,62 @@ The following query object shows all parameters and columns:
|
|
|
36
36
|
}
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
## SHOOT API
|
|
40
|
+
|
|
41
|
+
Geometries and most shooting details are available on the [geoinformation portal](https://geo.admin.ch) managed by GKG/swisstopo. However, all details as well as a list of active shooting ranges is only available in the original `schiessanzeigen.csv` file compiled by the Swiss army and distributed on the portal as well:
|
|
42
|
+
|
|
43
|
+
* [geo.admin.ch JSON API](https://api.geo.admin.ch/services/sdiservices.html) [(example)](https://api3.geo.admin.ch/rest/services/api/MapServer/ch.vbs.schiessanzeigen/1201.050?sr=4326&geometryFormat=geojson)
|
|
44
|
+
* [schiessanzeigen.csv](https://data.geo.admin.ch/ch.vbs.schiessanzeigen/schiessanzeigen/schiessanzeigen.csv)
|
|
45
|
+
|
|
46
|
+
As of Feburary 2023, the structure of the CSV is as follows:
|
|
47
|
+
|
|
48
|
+
### Shooting Ground Description Records
|
|
49
|
+
|
|
50
|
+
Row | Col | Attribute | Content | Mand | Remarks
|
|
51
|
+
----|-----|------------|-----------|------|--------
|
|
52
|
+
SPL | 0 | Row | text(3) | yes | **"SPL" (aka: Schiessplatz Stammdaten)**
|
|
53
|
+
SPL | 1 | Belplan ID | text(8) | yes | **ID of the shooting ground from Belplan as "nnnn.nnn" (equal to module# and object#) e.g. "3104.010"**
|
|
54
|
+
SPL | 2 | arimmo ID | text(10) | yes | ID of the shooting ground from arimmo e.g. "04.203"
|
|
55
|
+
SPL | 3 | Name | text(50) | yes | **Name of the shooting ground, e.g. "DAMMASTOCK / SUSTENHORN"**
|
|
56
|
+
SPL | 4 | URL DE | text(100) | yes | Info URL in DE from CMS-VBS
|
|
57
|
+
SPL | 5 | URL FR | text(100) | yes | Info URL in FR from CMS-VBS
|
|
58
|
+
SPL | 6 | URL IT | text(100) | yes | Info URL in IT from CMS-VBS
|
|
59
|
+
SPL | 7 | URL EN | text(100) | yes | **Info URL in EN from CMS-VBS**
|
|
60
|
+
SPL | 8 | Info name | text(100) | yes | Name of the info point e.g. "Koordinationsstelle Terreg 3, Altdorf"
|
|
61
|
+
SPL | 9 | Info phone | text(20) | yes | **Phone of the info point**
|
|
62
|
+
SPL | 10 | Info email | text(100) | no | **Email of the info point**
|
|
63
|
+
|
|
64
|
+
### Shooting Ground Activity Records
|
|
65
|
+
|
|
66
|
+
Row | Col | Attribute | Content | Mand | Remarks
|
|
67
|
+
----|-----|----------------|----------------|------|--------
|
|
68
|
+
BSZ | 0 | Row | text(3) | yes | **"BSZ" (aka: Belegungszeiten eines SPL)**
|
|
69
|
+
BSZ | 1 | Belplan ID | text(8) | yes | **ID of the shooting ground from Belplan as "nnnn.nnn" (equal to module# and object#) e.g. "3104.010"**
|
|
70
|
+
BSZ | 2 | Act date | date(yyyymmdd) | yes | **Datum of activity**
|
|
71
|
+
BSZ | 3 | Act time from | time(hhmm) | no | **Time when activity begins**
|
|
72
|
+
BSZ | 4 | Act time until | time(hhmm) | no | **Time when activity ends**
|
|
73
|
+
BSZ | 5 | Locations | text(50) | no | Locations of shooting activity [R2, (max 50 char)]
|
|
74
|
+
BSZ | 6 | Remarks | text(100) | no | Remarks [R2]
|
|
75
|
+
BSZ | 7 | URL DE | text(200) | no | Announcement URL DE [R2]
|
|
76
|
+
BSZ | 8 | URL FR | text(200) | no | Announcement URL FR [R2]
|
|
77
|
+
BSZ | 9 | URL IT | text(200) | no | Announcement URL IT [R2]
|
|
78
|
+
BSZ | 10 | URL EN | text(200) | no | **Announcement URL EN [R2]**
|
|
79
|
+
BSZ | 11 | Unit | text(120) | no | Military unit involved [R2]
|
|
80
|
+
BSZ | 12 | Weapons | text(50) | no | Weapons and ammunition involved [R2]
|
|
81
|
+
BSZ | 13 | Positions | text(50) | no | Shooting positions [R2]
|
|
82
|
+
BSZ | 14 | Coordinates | text(25) | no | Shooting coordinates [R2]
|
|
83
|
+
BSZ | 15 | Vertex height | number | no | **Max height of activity [R2]**
|
|
84
|
+
BSZ | 16 | DABS | boolean | no | **Relevant for DABS (formerly KOSIF): 0=no, 1=yes [R2]**
|
|
85
|
+
BSZ | 17 | No shooting | boolean | no | **0=no, 1=yes [R2]**
|
|
86
|
+
|
|
87
|
+
### Total Records
|
|
88
|
+
|
|
89
|
+
Row | Col | Attribute | Content | Mand | Remarks
|
|
90
|
+
----|-----|------------|---------------------------|------|--------
|
|
91
|
+
TOT | 0 | Row | text(3) | yes | "TOT" (aka: Total)
|
|
92
|
+
TOT | 1 | Created at | datetime(yyyymmddhhmmss) | yes | Date and time of CSV creation
|
|
93
|
+
TOT | 2 | SPL count | number | yes | Number of SPL records
|
|
94
|
+
|
|
39
95
|
## Asynchronous Use
|
|
40
96
|
|
|
41
97
|
### Command Line Arguments
|
|
@@ -29,11 +29,6 @@ module AIPP
|
|
|
29
29
|
|
|
30
30
|
# Mandatory Interface
|
|
31
31
|
|
|
32
|
-
def setup
|
|
33
|
-
AIPP.cache.aip = read('AIP').css('Ase')
|
|
34
|
-
AIPP.cache.dabs = read('DABS')
|
|
35
|
-
end
|
|
36
|
-
|
|
37
32
|
def origin_for(document)
|
|
38
33
|
case document
|
|
39
34
|
when 'ENR'
|
|
@@ -63,6 +58,16 @@ module AIPP
|
|
|
63
58
|
type: :pdf
|
|
64
59
|
)
|
|
65
60
|
end
|
|
61
|
+
when 'shooting_grounds'
|
|
62
|
+
AIPP::Downloader::HTTP.new(
|
|
63
|
+
file: "https://data.geo.admin.ch/ch.vbs.schiessanzeigen/schiessanzeigen/schiessanzeigen.csv",
|
|
64
|
+
type: :csv
|
|
65
|
+
)
|
|
66
|
+
when /^shooting_grounds-(\d+\.\d+)/
|
|
67
|
+
AIPP::Downloader::HTTP.new(
|
|
68
|
+
file: "https://api3.geo.admin.ch/rest/services/api/MapServer/ch.vbs.schiessanzeigen/#{$1}?sr=4326&geometryFormat=geojson",
|
|
69
|
+
type: :json
|
|
70
|
+
)
|
|
66
71
|
else
|
|
67
72
|
fail "document not recognized"
|
|
68
73
|
end
|
|
@@ -6,30 +6,32 @@ module AIPP::LS::NOTAM
|
|
|
6
6
|
include AIPP::LS::Helpers::Base
|
|
7
7
|
|
|
8
8
|
def parse
|
|
9
|
+
AIPP.cache.aip ||= read('AIP').css('Ase')
|
|
10
|
+
AIPP.cache.dabs ||= read('DABS')
|
|
9
11
|
json = read
|
|
10
|
-
fail "malformed JSON received from API" unless json.has_key?(
|
|
12
|
+
fail "malformed JSON received from API" unless json.has_key?(:queryNOTAMs)
|
|
11
13
|
added_notam_ids = []
|
|
12
|
-
json[
|
|
13
|
-
next unless row[
|
|
14
|
+
json[:queryNOTAMs].each do |row|
|
|
15
|
+
next unless row[:notamRaw].match? /^Q\) LS/ # only parse national NOTAM
|
|
14
16
|
|
|
15
17
|
# HACK: try to add missing commas to D-item of A- and B-series NOTAM
|
|
16
|
-
if row[
|
|
17
|
-
if row[
|
|
18
|
-
row[
|
|
19
|
-
row[
|
|
18
|
+
if row[:notamRaw].match? /\A[AB]/
|
|
19
|
+
if row[:notamRaw].gsub!(/(#{NOTAM::Schedule::HOUR_RE.decapture}-#{NOTAM::Schedule::HOUR_RE.decapture})/, '\1,')
|
|
20
|
+
row[:notamRaw].gsub!(/,+/, ',')
|
|
21
|
+
row[:notamRaw].sub!(/,\n/, "\n")
|
|
20
22
|
warn("HACK: added missing commas to D item")
|
|
21
23
|
end
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
# HACK: remove braindead years from D-item of W-series NOTAM
|
|
25
|
-
if row[
|
|
27
|
+
if row[:notamRaw].match? /\AW/
|
|
26
28
|
year = Time.now.year
|
|
27
|
-
if row[
|
|
29
|
+
if row[:notamRaw].gsub!(/\s*(?:#{year}|#{year+1})\s*(#{NOTAM::Schedule::MONTH_RE})/, ' \1')
|
|
28
30
|
warn("HACK: removed braindead years from D item")
|
|
29
31
|
end
|
|
30
32
|
end
|
|
31
33
|
|
|
32
|
-
(notam = notam_for(row[
|
|
34
|
+
(notam = notam_for(row[:notamRaw])) or next
|
|
33
35
|
if respect? notam
|
|
34
36
|
next if notam.data[:five_day_schedules] == []
|
|
35
37
|
added_notam_ids << notam.data[:id]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
using AIXM::Refinements
|
|
2
|
+
|
|
3
|
+
module AIPP::LS::SHOOT
|
|
4
|
+
class ShootingGrounds < AIPP::SHOOT::Parser
|
|
5
|
+
|
|
6
|
+
include AIPP::LS::Helpers::Base
|
|
7
|
+
|
|
8
|
+
DEFAULT_Z = AIXM.z(1000, :qfe) # height 300m unless present in publication
|
|
9
|
+
|
|
10
|
+
def parse
|
|
11
|
+
effective_date = aixm.effective_at.strftime('%Y%m%d')
|
|
12
|
+
airac_date = AIRAC::Cycle.new(aixm.effective_at).to_s('%Y-%m-%d')
|
|
13
|
+
shooting_grounds = {}
|
|
14
|
+
read.each_with_index do |row, line|
|
|
15
|
+
type, id, date, no_shooting = row[0], row[1], row[2], (row[17] == "1")
|
|
16
|
+
if type == 'BSZ' && !no_shooting && date == effective_date
|
|
17
|
+
shooting_grounds[id] ||= read("shooting_grounds-#{id}")
|
|
18
|
+
.fetch(:feature)
|
|
19
|
+
.merge(
|
|
20
|
+
csv_line: line,
|
|
21
|
+
location_codes: row[5].split(/ *, */), # TODO: currently ignored - not available as separate geometries
|
|
22
|
+
details: row[6].blank_to_nil,
|
|
23
|
+
url: row[10].blank_to_nil,
|
|
24
|
+
upper_z: (AIXM.z(AIXM.d(row[15].to_i, :m).to_ft.dim.round, :qfe) if row[15]),
|
|
25
|
+
dabs: (row[16] == '1'),
|
|
26
|
+
schedules: []
|
|
27
|
+
)
|
|
28
|
+
shooting_grounds[id][:schedules] << schedule_for(row)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
shooting_grounds.each do |id, data|
|
|
32
|
+
data in csv_line:, location_codes:, details:, url:, upper_z:, schedules:, properties: { bezeichnung: name, infotelefonnr: phone, infoemail: email }
|
|
33
|
+
if schedules.compact.any?
|
|
34
|
+
geometries = geometries_for data[:geometry]
|
|
35
|
+
indexed = geometries.count > 1
|
|
36
|
+
geometries.each_with_index do |geometry, index|
|
|
37
|
+
remarks = {
|
|
38
|
+
details: details,
|
|
39
|
+
phone: phone,
|
|
40
|
+
email: email,
|
|
41
|
+
bulletin: url
|
|
42
|
+
}.to_remarks
|
|
43
|
+
add(
|
|
44
|
+
AIXM.airspace(
|
|
45
|
+
source: "LS|OTHER|schiessgebiete.csv|#{airac_date}|#{csv_line}",
|
|
46
|
+
region: 'LS',
|
|
47
|
+
type: :dangerous_activities_area,
|
|
48
|
+
name: "LS-S#{id} #{name} #{index if indexed}".strip
|
|
49
|
+
).tap do |airspace|
|
|
50
|
+
airspace.add_layer layer_for(upper_z, schedules, remarks)
|
|
51
|
+
airspace.geometry = geometry
|
|
52
|
+
airspace.comment = "DABS: marked for publication"
|
|
53
|
+
end
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
# Returns +nil+ if neither beginning nor ending time is declared which
|
|
63
|
+
# has to be treated as "no shooting".
|
|
64
|
+
def schedule_for(row)
|
|
65
|
+
from = AIXM.time("#{row[3]} #{AIPP.options.time_zone}") if row[3]
|
|
66
|
+
to = AIXM.time("#{row[4]} #{AIPP.options.time_zone}") if row[4]
|
|
67
|
+
case
|
|
68
|
+
when from && to then (from..to)
|
|
69
|
+
when from then (from..AIXM::END_OF_DAY)
|
|
70
|
+
when to then (AIXM::BEGINNING_OF_DAY..to)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def geometries_for(polygons)
|
|
75
|
+
fail "only type MultiPolygon supported" unless polygons[:type] == 'MultiPolygon'
|
|
76
|
+
fail "polygon coordinates missing" unless polygons[:coordinates]
|
|
77
|
+
polygons[:coordinates].map do |(outer_polygon, inner_polygon)|
|
|
78
|
+
warn "hole in polygon is ignored" if inner_polygon
|
|
79
|
+
AIXM.geometry(
|
|
80
|
+
*outer_polygon.map { AIXM.point(xy: AIXM.xy(long: _1.first , lat: _1.last)) }
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def layer_for(upper_z, schedules, remarks)
|
|
86
|
+
AIXM.layer(
|
|
87
|
+
vertical_limit: AIXM.vertical_limit(
|
|
88
|
+
upper_z: (upper_z || DEFAULT_Z),
|
|
89
|
+
lower_z: AIXM::GROUND
|
|
90
|
+
)
|
|
91
|
+
).tap do |layer|
|
|
92
|
+
layer.activity = :shooting_from_ground
|
|
93
|
+
layer.timetable = timetable_for(schedules)
|
|
94
|
+
layer.remarks = remarks
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def timetable_for(schedules)
|
|
99
|
+
AIXM.timetable.tap do |timetable|
|
|
100
|
+
schedules.each do |schedule|
|
|
101
|
+
timetable.add_timesheet(
|
|
102
|
+
AIXM.timesheet(
|
|
103
|
+
adjust_to_dst: true,
|
|
104
|
+
dates: (AIXM.date(aixm.effective_at)..AIXM.date(aixm.effective_at))
|
|
105
|
+
# TODO: transform to...
|
|
106
|
+
# dates: AIXM.date(aixm.effective_at)
|
|
107
|
+
).tap do |timesheet|
|
|
108
|
+
timesheet.times = schedule
|
|
109
|
+
end
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
end
|
data/lib/aipp/runner.rb
CHANGED
|
@@ -9,7 +9,7 @@ module AIPP
|
|
|
9
9
|
attr_reader :aixm
|
|
10
10
|
|
|
11
11
|
def initialize
|
|
12
|
-
AIPP.options.storage = AIPP.options.storage.join(AIPP.options.region, AIPP.options.
|
|
12
|
+
AIPP.options.storage = AIPP.options.storage.join(AIPP.options.region, AIPP.options.scope.downcase)
|
|
13
13
|
AIPP.options.storage.mkpath
|
|
14
14
|
@dependencies = THash.new
|
|
15
15
|
@aixm = AIXM.document(effective_at: effective_at, expiration_at: expiration_at)
|
|
@@ -49,7 +49,7 @@ module AIPP
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def output_file
|
|
52
|
-
"#{AIPP.options.region}_#{AIPP.options.
|
|
52
|
+
"#{AIPP.options.region}_#{AIPP.options.scope}_#{effective_at.strftime('%F_%HZ')}.#{AIPP.options.schema}"
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
# @return [Pathname] directory containing the builds
|
|
@@ -86,7 +86,7 @@ module AIPP
|
|
|
86
86
|
# Read parser files.
|
|
87
87
|
def read_parsers
|
|
88
88
|
verbose_info("reading parsers")
|
|
89
|
-
region_dir.join(AIPP.options.
|
|
89
|
+
region_dir.join(AIPP.options.scope.downcase).glob('*.rb').each do |file|
|
|
90
90
|
verbose_info "requiring #{file.basename}"
|
|
91
91
|
require file
|
|
92
92
|
section = file.basename('.*').to_s.classify
|
|
@@ -123,7 +123,7 @@ module AIPP
|
|
|
123
123
|
details = aixm.errors.map(&:message).join("\n")
|
|
124
124
|
AIPP.options.force ? warn(message) : fail([message, details].join(":\n"))
|
|
125
125
|
end
|
|
126
|
-
info("counting #{aixm.features.count}
|
|
126
|
+
info("counting #{aixm.features.count} feature(s)")
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
# Write the AIXM document.
|
|
@@ -135,7 +135,7 @@ module AIPP
|
|
|
135
135
|
|
|
136
136
|
# Write build information.
|
|
137
137
|
def write_build
|
|
138
|
-
info
|
|
138
|
+
info("skipping build")
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
# Write the configuration to config.yml.
|
|
@@ -145,7 +145,7 @@ module AIPP
|
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
def class_for(section)
|
|
148
|
-
[:AIPP, AIPP.options.region, AIPP.options.
|
|
148
|
+
[:AIPP, AIPP.options.region, AIPP.options.scope, section.classify].constantize
|
|
149
149
|
end
|
|
150
150
|
end
|
|
151
151
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module AIPP
|
|
2
|
+
module AIP
|
|
3
|
+
|
|
4
|
+
module Executable
|
|
5
|
+
|
|
6
|
+
def options
|
|
7
|
+
AIPP.options.merge(
|
|
8
|
+
scope: 'AIP',
|
|
9
|
+
airac: AIRAC::Cycle.new,
|
|
10
|
+
region_options: []
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def option_parser(o)
|
|
15
|
+
o.banner = <<~END
|
|
16
|
+
Download online AIP and convert it to #{AIPP.options.schema.upcase}.
|
|
17
|
+
Usage: #{File.basename($0)} [options]
|
|
18
|
+
END
|
|
19
|
+
o.on('-a', '--airac (DATE|INTEGER)', String, %Q[AIRAC date or delta e.g. "+1" (default: "#{AIPP.options.airac.date.xmlschema}")]) { AIPP.options.airac = airac_for(_1) }
|
|
20
|
+
if AIPP.options.schema == :ofmx
|
|
21
|
+
o.on('-g', '--[no-]grouped-obstacles', 'group obstacles (default: false)') { AIPP.options.grouped_obstacles = _1 }
|
|
22
|
+
end
|
|
23
|
+
o.on('-O', '--region-options STRING', String, %Q[comma separated region specific options]) { AIPP.options.region_options = _1.split(',') }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def airac_for(argument)
|
|
29
|
+
if argument.match?(/^[+-]\d+$/) # delta
|
|
30
|
+
AIRAC::Cycle.new + argument.to_i
|
|
31
|
+
else # date
|
|
32
|
+
AIRAC::Cycle.new(argument)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -17,9 +17,13 @@ module AIPP
|
|
|
17
17
|
read_region
|
|
18
18
|
read_parsers
|
|
19
19
|
parse_sections
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
if aixm.features.any?
|
|
21
|
+
validate_aixm
|
|
22
|
+
write_build
|
|
23
|
+
write_aixm(AIPP.options.output_file || output_file)
|
|
24
|
+
else
|
|
25
|
+
warn("no features to write")
|
|
26
|
+
end
|
|
23
27
|
write_config
|
|
24
28
|
end
|
|
25
29
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module AIPP
|
|
2
|
+
module NOTAM
|
|
3
|
+
|
|
4
|
+
module Executable
|
|
5
|
+
|
|
6
|
+
def options
|
|
7
|
+
AIPP.options.merge(
|
|
8
|
+
module: 'NOTAM',
|
|
9
|
+
effective_at: Time.now.change(min: 0, sec: 0)
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def option_parser(o)
|
|
14
|
+
o.banner = <<~END
|
|
15
|
+
Download online NOTAM and convert it to #{AIPP.options.schema.upcase}.
|
|
16
|
+
Usage: #{File.basename($0)} notam [options]
|
|
17
|
+
END
|
|
18
|
+
o.on('-t', '--effective (TIME)', String, %Q[effective at this time (default: "#{AIPP.options.effective_at}")]) { AIPP.options.effective_at = Time.parse(_1) }
|
|
19
|
+
o.on('-x', '--crossload DIR', String, 'crossload directory') { AIPP.options.crossload = Pathname(_1) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def guard
|
|
23
|
+
AIPP.options.effective_at = AIPP.options.effective_at.change(min: 0, sec: 0).utc
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -17,8 +17,12 @@ module AIPP
|
|
|
17
17
|
read_region
|
|
18
18
|
read_parsers
|
|
19
19
|
parse_sections
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
if aixm.features.any?
|
|
21
|
+
validate_aixm
|
|
22
|
+
write_aixm(AIPP.options.output_file || output_file)
|
|
23
|
+
else
|
|
24
|
+
warn("no features to write")
|
|
25
|
+
end
|
|
22
26
|
write_config
|
|
23
27
|
end
|
|
24
28
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# AIPP Shoot Module
|
|
2
|
+
|
|
3
|
+
## Cache Time Window
|
|
4
|
+
|
|
5
|
+
The default time window for SHOOT is the day. This means:
|
|
6
|
+
|
|
7
|
+
* Source data is downloaded and cached based on the day.
|
|
8
|
+
* The effective date and time is rounded down to the previous midnight.
|
|
9
|
+
|
|
10
|
+
To force a rebuild within this time window, you have to clean the cache using the `-c` command line argument.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module AIPP
|
|
2
|
+
module SHOOT
|
|
3
|
+
|
|
4
|
+
module Executable
|
|
5
|
+
|
|
6
|
+
def options
|
|
7
|
+
AIPP.options.merge(
|
|
8
|
+
module: 'Shoot',
|
|
9
|
+
effective_at: Time.now.at_midnight
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def option_parser(o)
|
|
14
|
+
o.banner = <<~END
|
|
15
|
+
Download online shooting activities and convert them to #{AIPP.options.schema.upcase}.
|
|
16
|
+
Usage: #{File.basename($0)} shoot [options]
|
|
17
|
+
END
|
|
18
|
+
o.on('-t', '--effective (DATE)', String, %Q[effective on this date (default: "#{AIPP.options.effective_at.to_date}")]) { AIPP.options.effective_at = Time.parse("#{_1} CET") }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def guard
|
|
22
|
+
AIPP.options.time_zone = AIPP.options.effective_at.at_noon.strftime('%z')
|
|
23
|
+
AIPP.options.effective_at = AIPP.options.effective_at.at_midnight.utc
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module AIPP
|
|
2
|
+
module SHOOT
|
|
3
|
+
|
|
4
|
+
class Runner < AIPP::Runner
|
|
5
|
+
|
|
6
|
+
def effective_at
|
|
7
|
+
AIPP.options.effective_at
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def expiration_at
|
|
11
|
+
effective_at.end_of_day.round - 1
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run
|
|
15
|
+
info("SHOOT effective #{effective_at}", color: :green)
|
|
16
|
+
read_config
|
|
17
|
+
read_region
|
|
18
|
+
read_parsers
|
|
19
|
+
parse_sections
|
|
20
|
+
if aixm.features.any?
|
|
21
|
+
validate_aixm
|
|
22
|
+
write_aixm(AIPP.options.output_file || output_file)
|
|
23
|
+
else
|
|
24
|
+
warn("no features to write")
|
|
25
|
+
end
|
|
26
|
+
write_config
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/aipp/version.rb
CHANGED
data/lib/aipp.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require 'debug/session'
|
|
2
2
|
require 'singleton'
|
|
3
|
-
require 'colorize'
|
|
4
3
|
require 'optparse'
|
|
5
4
|
require 'yaml'
|
|
5
|
+
require 'json'
|
|
6
6
|
require 'pathname'
|
|
7
7
|
require 'fileutils'
|
|
8
8
|
require 'tmpdir'
|
|
@@ -10,6 +10,8 @@ require 'securerandom'
|
|
|
10
10
|
require 'tsort'
|
|
11
11
|
require 'ostruct'
|
|
12
12
|
require 'date'
|
|
13
|
+
|
|
14
|
+
require 'colorize'
|
|
13
15
|
require 'excon'
|
|
14
16
|
require 'graphql/client'
|
|
15
17
|
require 'graphql/client/http'
|
|
@@ -17,7 +19,6 @@ require 'nokogiri'
|
|
|
17
19
|
require 'csv'
|
|
18
20
|
require 'roo'
|
|
19
21
|
require 'pdf-reader'
|
|
20
|
-
require 'json'
|
|
21
22
|
require 'zip'
|
|
22
23
|
require 'airac'
|
|
23
24
|
require 'aixm'
|
|
@@ -53,10 +54,11 @@ require_relative 'aipp/t_hash'
|
|
|
53
54
|
require_relative 'aipp/executable'
|
|
54
55
|
require_relative 'aipp/runner'
|
|
55
56
|
|
|
56
|
-
require_relative 'aipp/aip/executable'
|
|
57
|
-
require_relative 'aipp/aip/runner'
|
|
58
|
-
require_relative 'aipp/aip/parser'
|
|
57
|
+
require_relative 'aipp/scopes/aip/executable'
|
|
58
|
+
require_relative 'aipp/scopes/aip/runner'
|
|
59
|
+
require_relative 'aipp/scopes/aip/parser'
|
|
60
|
+
|
|
61
|
+
require_relative 'aipp/scopes/notam/executable'
|
|
62
|
+
require_relative 'aipp/scopes/notam/runner'
|
|
63
|
+
require_relative 'aipp/scopes/notam/parser'
|
|
59
64
|
|
|
60
|
-
require_relative 'aipp/notam/executable'
|
|
61
|
-
require_relative 'aipp/notam/runner'
|
|
62
|
-
require_relative 'aipp/notam/parser'
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: aipp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sven Schwyn
|
|
@@ -29,7 +29,7 @@ cert_chain:
|
|
|
29
29
|
kAyiRqgxF4dJviwtqI7mZIomWL63+kXLgjOjMe1SHxfIPo/0ji6+r1p4KYa7o41v
|
|
30
30
|
fwIwU1MKlFBdsjkd
|
|
31
31
|
-----END CERTIFICATE-----
|
|
32
|
-
date: 2023-
|
|
32
|
+
date: 2023-02-27 00:00:00.000000000 Z
|
|
33
33
|
dependencies:
|
|
34
34
|
- !ruby/object:Gem::Dependency
|
|
35
35
|
name: airac
|
|
@@ -344,8 +344,6 @@ email:
|
|
|
344
344
|
executables:
|
|
345
345
|
- aip2aixm
|
|
346
346
|
- aip2ofmx
|
|
347
|
-
- notam2aixm
|
|
348
|
-
- notam2ofmx
|
|
349
347
|
extensions: []
|
|
350
348
|
extra_rdoc_files:
|
|
351
349
|
- README.md
|
|
@@ -357,13 +355,7 @@ files:
|
|
|
357
355
|
- README.md
|
|
358
356
|
- exe/aip2aixm
|
|
359
357
|
- exe/aip2ofmx
|
|
360
|
-
- exe/notam2aixm
|
|
361
|
-
- exe/notam2ofmx
|
|
362
358
|
- lib/aipp.rb
|
|
363
|
-
- lib/aipp/aip/README.md
|
|
364
|
-
- lib/aipp/aip/executable.rb
|
|
365
|
-
- lib/aipp/aip/parser.rb
|
|
366
|
-
- lib/aipp/aip/runner.rb
|
|
367
359
|
- lib/aipp/border.rb
|
|
368
360
|
- lib/aipp/debugger.rb
|
|
369
361
|
- lib/aipp/downloader.rb
|
|
@@ -372,10 +364,6 @@ files:
|
|
|
372
364
|
- lib/aipp/downloader/http.rb
|
|
373
365
|
- lib/aipp/environment.rb
|
|
374
366
|
- lib/aipp/executable.rb
|
|
375
|
-
- lib/aipp/notam/README.md
|
|
376
|
-
- lib/aipp/notam/executable.rb
|
|
377
|
-
- lib/aipp/notam/parser.rb
|
|
378
|
-
- lib/aipp/notam/runner.rb
|
|
379
367
|
- lib/aipp/parser.rb
|
|
380
368
|
- lib/aipp/patcher.rb
|
|
381
369
|
- lib/aipp/pdf.rb
|
|
@@ -400,7 +388,20 @@ files:
|
|
|
400
388
|
- lib/aipp/regions/LS/README.md
|
|
401
389
|
- lib/aipp/regions/LS/helpers/base.rb
|
|
402
390
|
- lib/aipp/regions/LS/notam/ENR.rb
|
|
391
|
+
- lib/aipp/regions/LS/shoot/shooting_grounds.rb
|
|
403
392
|
- lib/aipp/runner.rb
|
|
393
|
+
- lib/aipp/scopes/aip/README.md
|
|
394
|
+
- lib/aipp/scopes/aip/executable.rb
|
|
395
|
+
- lib/aipp/scopes/aip/parser.rb
|
|
396
|
+
- lib/aipp/scopes/aip/runner.rb
|
|
397
|
+
- lib/aipp/scopes/notam/README.md
|
|
398
|
+
- lib/aipp/scopes/notam/executable.rb
|
|
399
|
+
- lib/aipp/scopes/notam/parser.rb
|
|
400
|
+
- lib/aipp/scopes/notam/runner.rb
|
|
401
|
+
- lib/aipp/scopes/shoot/README.md
|
|
402
|
+
- lib/aipp/scopes/shoot/executable.rb
|
|
403
|
+
- lib/aipp/scopes/shoot/parser.rb
|
|
404
|
+
- lib/aipp/scopes/shoot/runner.rb
|
|
404
405
|
- lib/aipp/t_hash.rb
|
|
405
406
|
- lib/aipp/version.rb
|
|
406
407
|
- lib/core_ext/array.rb
|
|
@@ -441,7 +442,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
441
442
|
- !ruby/object:Gem::Version
|
|
442
443
|
version: '0'
|
|
443
444
|
requirements: []
|
|
444
|
-
rubygems_version: 3.4.
|
|
445
|
+
rubygems_version: 3.4.7
|
|
445
446
|
signing_key:
|
|
446
447
|
specification_version: 4
|
|
447
448
|
summary: Parser for aeronautical information publications
|
metadata.gz.sig
CHANGED
|
Binary file
|
data/exe/notam2aixm
DELETED
data/exe/notam2ofmx
DELETED
data/lib/aipp/aip/executable.rb
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
module AIPP
|
|
2
|
-
module AIP
|
|
3
|
-
|
|
4
|
-
class Executable < AIPP::Executable
|
|
5
|
-
|
|
6
|
-
def initialize(exe_file)
|
|
7
|
-
super
|
|
8
|
-
AIPP.options.merge(
|
|
9
|
-
module: 'AIP',
|
|
10
|
-
airac: AIRAC::Cycle.new,
|
|
11
|
-
region_options: []
|
|
12
|
-
)
|
|
13
|
-
OptionParser.new do |o|
|
|
14
|
-
o.banner = <<~END
|
|
15
|
-
Download online AIP and convert it to #{AIPP.options.schema.upcase}.
|
|
16
|
-
Usage: #{File.basename($0)} [options]
|
|
17
|
-
END
|
|
18
|
-
common_options(o)
|
|
19
|
-
o.on('-a', '--airac (DATE|INTEGER)', String, %Q[AIRAC date or delta e.g. "+1" (default: "#{AIPP.options.airac.date.xmlschema}")]) { AIPP.options.airac = airac_for(_1) }
|
|
20
|
-
if AIPP.options.schema == :ofmx
|
|
21
|
-
o.on('-g', '--[no-]grouped-obstacles', 'group obstacles (default: false)') { AIPP.options.grouped_obstacles = _1 }
|
|
22
|
-
end
|
|
23
|
-
o.on('-O', '--region-options STRING', String, %Q[comma separated region specific options]) { AIPP.options.region_options = _1.split(',') }
|
|
24
|
-
developer_options(o)
|
|
25
|
-
end.parse!
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
private
|
|
29
|
-
|
|
30
|
-
def airac_for(argument)
|
|
31
|
-
if argument.match?(/^[+-]\d+$/) # delta
|
|
32
|
-
AIRAC::Cycle.new + argument.to_i
|
|
33
|
-
else # date
|
|
34
|
-
AIRAC::Cycle.new(argument)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
module AIPP
|
|
2
|
-
module NOTAM
|
|
3
|
-
|
|
4
|
-
class Executable < AIPP::Executable
|
|
5
|
-
|
|
6
|
-
def initialize(exe_file)
|
|
7
|
-
super
|
|
8
|
-
now = Time.now.utc.round
|
|
9
|
-
AIPP.options.merge(
|
|
10
|
-
module: 'NOTAM',
|
|
11
|
-
effective_at: now - now.sec - (now.min * 60) # previous full hour
|
|
12
|
-
)
|
|
13
|
-
OptionParser.new do |o|
|
|
14
|
-
o.banner = <<~END
|
|
15
|
-
Download online NOTAM and convert it to #{AIPP.options.schema.upcase}.
|
|
16
|
-
Usage: #{File.basename($0)} [options]
|
|
17
|
-
END
|
|
18
|
-
common_options(o)
|
|
19
|
-
o.on('-t', '--effective (TIME)', String, %Q[effective after this point in time (default: #{AIPP.options.effective_at})]) { AIPP.options.effective_at = Time.parse(_1) }
|
|
20
|
-
o.on('-x', '--crossload DIR', String, 'crossload directory') { AIPP.options.crossload = Pathname(_1) }
|
|
21
|
-
developer_options(o)
|
|
22
|
-
end.parse!
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|