AIPP
¶ ↑
Parser for Aeronautical Information Publication (AIP) available online.
This gem incluces two executables to download and parse aeronautical data as HTML or PDF, then build and export is as AIXM or OFMX.
-
Author: Sven Schwyn - Bitcetera
Table of Contents¶ ↑
{Install}[#install]
{Usage}[#usage]
{Storage}[#storage]
{Regions}[#regions]
{AIP Parsers}[#aip-parsers]
{References}[#references]
{AIRAC Date Calculations}[#airac-date-calculations]
Development
Install¶ ↑
Security¶ ↑
This gem is cryptographically signed in order to assure it hasn't been tampered with. Unless already done, please add the author's public key as a trusted certificate now:
gem cert --add <(curl -Ls https://raw.github.com/svoop/aipp/master/certs/svoop.pem)
Standalone¶ ↑
Make sure to have the latest version of Ruby and then install this gem:
gem install aipp --trust-policy MediumSecurity
Bundler¶ ↑
If you're familiar with Bundler powered Ruby projects, you might prefer to add the following to your Gemfile
or gems.rb
:
gem aipp
And then install the bundle:
bundle install --trust-policy MediumSecurity
Usage¶ ↑
See the built-in help for all options:
aip2aixm --help aip2ofmx --help
Say, you with to build the complete OFMX file for the current AIRAC cycle of the region LF:
aip2ofmx -r LF
You'll find the OFMX file in the current directory if the binary exits successfully.
Storage¶ ↑
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.
You'll find a directory for each region which contains the following items:
-
sources/
This directory contains one ZIP archive per AIRAC cycle which incrementially caches all source files used to build the AIXM/OFMX file. Therefore, to make sure it contains all source files for a region, you have to build at least one complete AIXM/OFMX file for that region. -
builds/
This directory contains one ZIP archive per AIRAC cycle which is overwritten on every run. Therefore, to make sure it contains the complete build for a region, you have to make sure that your last run builds the complete AIXM/OFMX for that region. This archive contains: -
the built AIXM/OFMX file
-
build.yaml
– context of the build process -
manifest.csv
– diffable manifest (see below) -
config.yml
This file contains configuration which will be read on subsequent runs, most notably the namespace UUID used to identify the creator of OFMX files.
The manifest is a CSV which lists every feature on a separate line along with its hashes, AIP and comment. You can diff
or git diff
two manifests:
$ git diff -U0 2019-09-12/manifest.csv 2019-10-10/manifest.csv --- a/2019-09-12/manifest.csv +++ b/2019-10-10/manifest.csv @@ -204 +204 @@ AD-1.3,Ahp,9e9f031e,d6f22057,Airport: LFLJ COURCHEVEL -AD-1.3,Ahp,9f1eed18,37ddbbde,Airport: LFQD ARRAS ROCLINCOURT +AD-1.3,Ahp,9f1eed18,f0e60105,Airport: LFQD ARRAS ROCLINCOURT @@ -312 +312 @@ AD-2,Aha,4250c9ee,04d49dc7,Address: RADIO for LFHV -AD-2,Aha,6b381b32,fb947716,Address: RADIO for LFPO +AD-2,Aha,6b381b32,b9723b7e,Address: RADIO for LFPO @@ -664 +663,0 @@ AD-2,Ser,3920a7fd,4545c5eb,Service: AFIS by LFGA TWR -AD-2,Ser,39215774,1f13f2cf,Service: APP by LFCR APP @@ -878 +876,0 @@ AD-2,Ser,bb5228d7,7cfb4572,Service: TWR by LFMH TWR -AD-2,Ser,bc72caf2,0a15b39c,Service: FIS by LFCR FIC (...)
The advantage of git diff
is it's ability to hightlight exactly which part of a line has changed. Check out this post to learn how.
Regions¶ ↑
The reference implementation is region “LF” (France).
To implement a region, you have to create a new directory lib/aipp/regions/{REGION}/
and place the following files there:
AIP Parsers¶ ↑
Say, you want to parse ENR-4.3, you have to create the file ENR-4.3.rb
which defines the class AIPP::LF::ENR43
as follows:
module AIPP module LF class ENR43 < AIP DEPENDS = %w(ENR-2.1 ENR-2.2) # declare dependencies to other AIPs def parse html = read # read the Nokogiri::HTML5 document feature = (...) # build the feature add(feature: feature) # add the feature to AIXM::Document end end end end
Some AIP may be split over several files which require a little more code to load the individual HTML source files:
module AIPP module LF class AD2 < AIP def parse %i(one two three).each do |part| html = read(aip_file: "#{aip}.#{part}") # read with a non-standard name support_html = read(aip_file: 'AD-0.6') # maybe read necessary support documents (...) end end end end end
Inside the parse
method, you have access to the following methods:
-
{
read
} – download and read an AIP file -
{
find_by
} – find previously written {AIXM::Feature
s} by class and attribute values -
some core extensions from ActiveSupport – {
Object#blank
} and {String
} -
core extensions from this gem – {
Object
}, {NilClass
}, {Integer
}, {String
}, {Hash
} and {Enumerable
}
As well as the following methods:
-
{
options
} – arguments read fromaip2aixm
oraip2ofmx
respectively -
{
config
} – configuration read fromconfig.yml
-
{
borders
} – borders defined as GeoJSON read from the region (see below) -
{
cache
} – virginOStruct
instance to make objects available across AIPs
Borders¶ ↑
AIXM knows named borders for country boundaries. However, you might need additional borders which don't exist as named boarders.
To define additional borders, create simple GeoJSON files in the lib/aipp/regions/{REGION}/borders/
directory, for example this custom_border.geojson
:
{ "type": "GeometryCollection", "geometries": [ { "type": "LineString", "coordinates": [ [6.009531650000042, 45.12013319700009], [6.015747738000073, 45.12006702600007] ] } ] }
⚠️ The GeoJSON file must consist of exactly one GeometryCollection
which may contain any number of LineString
geometries. Only LineString
geometries are recognized! To define a closed polygon, the first coordinates of a LineString
must be identical to the last coordinates.
The {borders
} method gives you access to a map from the border name (upcased file name) to the corresponding {AIPP::Border
} object:
borders # => { "CUSTOM_BORDER" => #<AIPP::Border file=custom_border.geojson> }
The border object implements simple nearest point and segment calculations to create arrays of {AIXM::XY
} which can be used with {AIXM::Component::Geometry
}.
See {AIPP::Border
} for more on this.
Helpers¶ ↑
Helpers are modules defined in the lib/aipp/regions/{REGION}/helpers/
directory. All helper modules are required automatically in alphabetic order.
There is one mandatory helper called URL.rb
which must define the following method to build URLs from which to download AIPs:
module AIPP module LF module Helpers module URL def url_for(aip_file) # build and return the download URL for the aip file end end end end end
Feel free to add more helpers to DRY code which is shared by multiple AIP parsers. Say you want to extract methods which are used by all AIP parsers:
module AIPP module LF module Helpers module Base def source(position:, aip_file: nil) (...) end end end end end
To use this source
method, simply include the helper module in the AIP parser:
module AIPP module LF class AD2 < AIP include AIPP::LF::Helpers::Base end end end
Fixtures and Patches¶ ↑
Fixtures is static data defined as YAML in the lib/aipp/regions/{REGION}/fixtures/
directory. All fixtures are read automatically. Please note that the name of the AIP parser (e.g. AD-1.3.rb
) must match the name of the corresponding fixture (e.g. fixtures/AD-1.3.yml
).
When parsed data is faulty or missing, you may fall back to such static data instead. This is where patches come in. You can patch any AIXM attribute setter by defining a patch block inside the AIP parser and accessing the static data via parser.fixture
:
module AIPP module LF class AD2 < AIP patch AIXM::Component::Runway::Direction, :xy do |parser, object, value| throw :abort unless value.nil? @fixtures ||= YAML.load_file(Pathname(__FILE__).dirname.join('AD-1.3.yml')) airport_id = parser.instance_variable_get(:@airport).id direction_name = object.name.to_s throw :abort if (xy = parser.fixture.dig('runways', airport_id, direction_name, 'xy')).nil? lat, long = xy.split(/\s+/) AIXM.xy(lat: lat, long: long) end end end end
The patch block receives the object and the current value. If this value is okay, throw :abort
to leave the patch block without touching anything. Otherwise, have the patch block return a new value which will be used instead.
Source File Line Numbers¶ ↑
In order to reference the source of an AIXM/OFMX feature, it's necessary to know the line number where a particular node occurs in the HTML source file. You can ask any HTML element as follows:
tr.line
⚠️ Make sure you have build Nokogumbo --with-libxml2
. Otherwise, all elements will report line number 0
and therefore render OFMX documents invalid. See the Nokogumbo README for more on this.
Errors¶ ↑
You should fail
on fatal problems. The -e
command line argument will open a Pry session when such an error occurs. Issue errors as usual:
fail "my message"
Warnings¶ ↑
You should warn
on non-fatal problems. The -w ID
command line argument will open a Pry session when then warning with the given ID occurs. To issue a warning:
warn("my message", pry: binding) # open Pry attached to the binding warn("my message", pry: error) # open Pry attached to the error
Messages¶ ↑
info¶ ↑
Use info
for essential info messages:
info("my message") # displays "my message" in black info("my message", color: :green) # displays "my message" in green
verbose info¶ ↑
Use verbose_info
for in-depth info messages which are only shown if the --verbose
mode is set:
verbose_info("my message") # displays "my message" in blue
Pry¶ ↑
Pry is loaded with stack explorer support. Type help
in the Pry console to see all available commands. The most useful command in the context of this gem is up
which beams you one frame up in the caller stack.
Note: It's not currently possible to use pry-byebug at this time since it interferes with pry-rescue.
AIRAC Date Calculations¶ ↑
The AIPP::AIRAC
class is used to calculate AIRAC cycles:
airac = AIPP::AIRAC.new(Date.parse('2017-12-24')) airac.date # => 2018-12-07 airac.id # => 1713 airac.next_date # => 2018-01-04 airac.next_id # => 1801
References¶ ↑
-
LF - France
Development¶ ↑
To install the development dependencies and then run the test suite:
bundle install bundle exec rake # run tests once bundle exec guard # run tests whenever files are modified
Please submit issues on:
To contribute code, fork the project on Github, add your code and submit a pull request:
help.github.com/articles/fork-a-repo
License¶ ↑
The gem is available as open source under the terms of the MIT License.