Skip to content

Commit

Permalink
Parse AIRAC files from aixm and files from the DDR2 database (#23)
Browse files Browse the repository at this point in the history
A parser for the files that are extracted from the DDR2 database has been implemented with the help of @obbe79.

As a consequence, there are changes in the API (airac becomes aixm_airspaces and there is a new nm_airspaces with consistent interfaces). Proper warning messages are here to help implementing the transition.

Documentation has also been update accordingly.

fix #19
  • Loading branch information
xoolive committed Mar 29, 2019
1 parent 59b912f commit 26f1835
Show file tree
Hide file tree
Showing 19 changed files with 484 additions and 248 deletions.
Binary file removed docs/_static/airac_fir_uir.png
Binary file not shown.
140 changes: 37 additions & 103 deletions docs/airac_usage.rst

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions docs/data_basic.rst
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ easily added in background of a Matplotlib representation.
%matplotlib inline
import matplotlib.pyplot as plt
from traffic.data import airac
from traffic.data import nm_airspaces
from traffic.drawing import EuroPP, countries
with plt.style.context('traffic'):
Expand All @@ -380,10 +380,10 @@ easily added in background of a Matplotlib representation.
ax = plt.axes(projection=EuroPP())
ax.add_feature(countries())
ax.set_extent(airac['LFBBBDX'])
ax.set_extent(nm_airspaces['LFBBBDX'])
airac['LFBBBDX'].plot(ax, edgecolor="firebrick", lw=2)
airways.intersects(airac['LFBBBDX'].flatten(),
nm_airspaces['LFBBBDX'].plot(ax, edgecolor="firebrick", lw=2)
airways.intersects(nm_airspaces['LFBBBDX'].flatten(),
min_upper=400).plot(ax)
airways['UN859'].plot(ax)
Expand Down
4 changes: 2 additions & 2 deletions docs/paper/atc_detect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ the machine learning method).
%matplotlib inline
import matplotlib.pyplot as plt
from traffic.data import airac, airports, navaids
from traffic.data import airports, navaids, nm_airspaces
from traffic.drawing import EuroPP, PlateCarree
with plt.style.context("traffic"):
Expand All @@ -85,7 +85,7 @@ the machine learning method).
)
# TMA of Toulouse Airport
airac["LFBOTMA"].plot(
nm_airspaces["LFBOTMA"].plot(
ax, edgecolor="black", facecolor="#cccccc", alpha=.3, linewidth=2
)
Expand Down
18 changes: 9 additions & 9 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,8 @@ Area (TMA) for Toulouse.

.. code:: python
from traffic.data import airac
airac['LFBOTMA']
from traffic.data import nm_airspaces
nm_airspaces['LFBOTMA']
Expand All @@ -403,7 +403,7 @@ TMA.
t_tma = Traffic.from_flights(
flight for flight in t
if flight.intersects(airac['LFBOTMA'])
if flight.intersects(nm_airspaces['LFBOTMA'])
)
Expand Down Expand Up @@ -524,7 +524,7 @@ TMA.
ax.set_extent('Occitanie')
# Airspaces can plot their footprint
airac['LFBOTMA'].plot(ax, linewidth=2, linestyle='dashed')
nm_airspaces['LFBOTMA'].plot(ax, linewidth=2, linestyle='dashed')
t_tma.plot(ax)
Expand Down Expand Up @@ -664,7 +664,7 @@ Let's limit ourselves to aircraft trajectories coming under 2000ft.
location('Occitanie').plot(ax, linestyle='dotted')
ax.set_extent('Occitanie')
airac['LFBOTMA'].plot(ax, linewidth=2, linestyle='dashed')
nm_airspaces['LFBOTMA'].plot(ax, linewidth=2, linestyle='dashed')
t_airport.plot(ax)
Expand Down Expand Up @@ -694,7 +694,7 @@ trajectory for their plotting.
location('Occitanie').plot(ax, linestyle='dotted')
ax.set_extent('Occitanie')
airac['LFBOTMA'].plot(ax, linewidth=2, linestyle='dashed')
nm_airspaces['LFBOTMA'].plot(ax, linewidth=2, linestyle='dashed')
for flight in t_airport:
flight.plot(
Expand Down Expand Up @@ -742,8 +742,8 @@ Notice the use of:
ax.add_feature(rivers(alpha=.2))
# Limit the extent to the TMA
ax.set_extent(airac['LFBOTMA'])
airac['LFBOTMA'].plot(ax, linestyle='dashed', linewidth=2)
ax.set_extent(nm_airspaces['LFBOTMA'])
nm_airspaces['LFBOTMA'].plot(ax, linestyle='dashed', linewidth=2)
# Airports get their footprint from OpenStreetMap as well
airports['LFBO'].plot(ax, color='xkcd:baby poop')
Expand Down Expand Up @@ -806,7 +806,7 @@ landing.
layout=Layout(width="100%", max_width="800px", height="500px"),
)
map_.add_layer(airac["LFBOTMA"])
map_.add_layer(nm_airspaces["LFBOTMA"])
for flight in demo:
map_.add_layer(flight, color="#990000", weight=2)
Expand Down
18 changes: 9 additions & 9 deletions docs/so6_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,8 @@ Each flight can check whether it crosses an airspace:

.. code:: python
from traffic.data import airac
so6['HOP36PP'].intersects(airac['LFBBBDX'])
from traffic.data import nm_airspaces
so6['HOP36PP'].intersects(nm_airspaces['LFBBBDX'])
True
Expand All @@ -275,7 +275,7 @@ airspace can be projected to a 2D representation for that purpose:

.. code:: python
so6['HOP36PP'].clip(airac['LFBBBDX'].flatten())
so6['HOP36PP'].clip(nm_airspaces['LFBBBDX'].flatten())
Expand Down Expand Up @@ -484,7 +484,7 @@ with time intervals.
(so6['HOP36PP']
.between("2018/01/01 18:25", timedelta(minutes=30))
.intersects(airac['LFBBBDX']))
.intersects(nm_airspaces['LFBBBDX']))
True
Expand All @@ -494,7 +494,7 @@ with time intervals.
(so6['HOP36PP']
.between("2018/01/01 18:15", timedelta(minutes=5))
.intersects(airac['LFBBBDX']))
.intersects(nm_airspaces['LFBBBDX']))
False
Expand All @@ -510,7 +510,7 @@ wisely.
%%time
# First, filter inside the bounding box (faster than polygon check)
bdx_so6 = so6.inside_bbox(airac["LFBBBDX"])
bdx_so6 = so6.inside_bbox(nm_airspaces["LFBBBDX"])
CPU times: user 7.11 s, sys: 19.3 ms, total: 7.13 s
Wall time: 7.14 s
Expand All @@ -533,7 +533,7 @@ airspace. Note that this chaining may not be safe for smaller airspaces.
.. code:: python
%%time
bdx_flights = noon.intersects(airac['LFBBBDX'])
bdx_flights = noon.intersects(nm_airspaces['LFBBBDX'])
CPU times: user 3.9 s, sys: 0 ns, total: 3.9 s
Wall time: 3.9 s
Expand All @@ -544,8 +544,8 @@ airspace. Note that this chaining may not be safe for smaller airspaces.
%%time
bdx_flights = (
noon
.inside_bbox(airac["LFBBBDX"])
.intersects(airac["LFBBBDX"])
.inside_bbox(nm_airspaces["LFBBBDX"])
.intersects(nm_airspaces["LFBBBDX"])
)
CPU times: user 1.42 s, sys: 8.27 ms, total: 1.43 s
Expand Down
19 changes: 13 additions & 6 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,21 @@ may want to file a [PR](https://yangsu.github.io/pull-request-tutorial/) and
keep the authorship. It is also easier for me to keep track of corrections and
remember why things are the way they are.

- I want to know more about AIRAC files
- I want to know more about Eurocontrol NM files

When you import `airac` from `traffic.data`, you need to set a path to a
directory containing AIRAC files. These are XML files following the
We download those files from Eurocontrol [Network Manager DDR2 repository
service](https://www.eurocontrol.int/articles/ddr2-web-portal) under Dataset
Files > Airspace Environment Datasets. You may not be entitled access to those
data.

Should you have no such access, basic FIRs are provided in `eurofirs` from
`traffic.data`.

- I want to know more about Eurocontrol AIXM files

When you import `aixm_airspaces` from `traffic.data`, you need to set a path to
a directory containing AIRAC files. These are XML files following the
[AIXM](http://aixm.aero/) standard and produced by Eurocontrol. We download
those files from Eurocontrol [Network Manager B2B web
service](https://eurocontrol.int/service/network-manager-business-business-b2b-web-services).
You may not be entitled access to those data.

Should you have no such access, basic FIRs are provided in `eurofirs` from
`traffic.data`.
7 changes: 3 additions & 4 deletions scripts/export_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
from pathlib import Path
from typing import List, Optional


# Convenient one-liner for getting sector types
# set(s.type for s in sectors.parse(".*"))


def export_json(patterns: List[str], output_file: Optional[Path]):
from traffic.data import airac as sectors
from traffic.data import nm_airspaces

if output_file is None:
for pattern in patterns:
for sector in sectors.parse(pattern):
for sector in nm_airspaces.parse(pattern):
print(f"{sector.name}/{sector.type}")
return

Expand All @@ -22,7 +21,7 @@ def export_json(patterns: List[str], output_file: Optional[Path]):
[
s.export_json()
for pattern in patterns
for s in sectors.search(pattern)
for s in nm_airspaces.search(pattern)
],
fh,
sort_keys=False,
Expand Down
5 changes: 3 additions & 2 deletions scripts/plot_europe_firs.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import matplotlib.pyplot as plt
from cartopy.crs import TransverseMercator

from traffic.drawing import countries, rivers, lakes, ocean, TransverseMercator
from traffic.data import eurofirs
from traffic.drawing import countries, lakes, ocean, rivers

fig = plt.figure(figsize=(15, 10))
ax = fig.add_subplot(111, projection=TransverseMercator(10, 45))
Expand All @@ -14,7 +15,7 @@
ax.add_feature(ocean(scale="50m"))

for key, fir in eurofirs.items():
fir.plot(ax, edgecolor="#3a3aaa", lw=2, alpha=.5)
fir.plot(ax, edgecolor="#3a3aaa", lw=2, alpha=0.5)
if key not in ["ENOB", "LPPO", "GCCC"]:
fir.annotate(
ax,
Expand Down
19 changes: 11 additions & 8 deletions scripts/so6_clip_sector.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,30 @@
from pathlib import Path

from tqdm import tqdm
from traffic.core import Sector # for typing
from traffic.data import airac as sectors

from traffic.core import Airspace
from traffic.data import nm_airspaces
from traffic.data.so6 import SO6


def clip(so6: SO6, sector: Sector) -> SO6:
return so6.inside_bbox(sector).intersects(sector)
def clip(so6: SO6, airspace: Airspace) -> SO6:
return so6.inside_bbox(airspace).intersects(airspace)


def unpack_and_clip(filename: str, sectorname: str) -> SO6:
so6 = SO6.parse_so6(filename)
sector = sectors[sectorname]
so6 = SO6.from_file(filename)
sector = nm_airspaces[sectorname]
if sector is None:
raise ValueError("Sector not found")
raise ValueError("Airspace not found")
if so6 is None:
raise RuntimeError
return clip(so6, sector)


def prepare_all(filename: Path, output_dir: Path, sectorname: str) -> None:
so6 = unpack_and_clip(filename.as_posix(), sectorname)
output_name = filename.with_suffix(".pkl").name
so6.to_pkl((output_dir / output_name).as_posix())
so6.to_pickle((output_dir / output_name).as_posix())


def glob_all(
Expand Down

0 comments on commit 26f1835

Please sign in to comment.