Quickstart

Loading ADS-B traffic

The Traffic class offers a from_file classmethod for loading traffic data. A pandas DataFrame is loaded into a structure offering various methods for iteration, analysis and display.

The basic representation of a Traffic object is a summary view of the data: the structure tries to infer how to discriminate flights based on a flight_id column (if none, on a pair (icao24, callsign)) and returns a number of sample points for each trajectory.

from traffic.data.samples import quickstart as t
Traffic with 397 identifiers
count
icao24 callsign
4ca84d RYR3YM 2859
393320 AFR27GH 2770
505c98 RAM667 2752
3944ef HOP87DJ 2731
4ca574 IBK5111 2706
393322 AFR23FK 2665
40643a EZY57FT 2656
394c18 AFR140W 2613
344692 VLG2972 2599
400cd1 EZY81GE 2579

A Traffic object can be indexed by flight_id (or by callsign and icao24): it returns a Flight object with a specific representation.

t['AFR27GH']
Flight AFR27GH
  • aircraft: 393320 / F-GMZA (A321)
  • from: 2017-07-16 19:30:00+00:00
  • to: 2017-07-16 20:16:10+00:00

The access to the underlying DataFrame is direct:

t['AFR27GH'].data.iloc[50:60,:7]
alert altitude callsign geoaltitude groundspeed hour icao24
136988 False 33000.0 AFR27GH 34750.0 424.614753 1500231600 393320
136989 False 32975.0 AFR27GH 34750.0 424.528784 1500231600 393320
136990 False 32975.0 AFR27GH 34725.0 424.528784 1500231600 393320
136991 False 32975.0 AFR27GH 34750.0 424.614753 1500231600 393320
136992 False 32975.0 AFR27GH 34750.0 424.614753 1500231600 393320
136993 False 32975.0 AFR27GH 34750.0 424.614753 1500231600 393320
136994 False 32975.0 AFR27GH 34750.0 424.528784 1500231600 393320
136995 False 32975.0 AFR27GH 34750.0 424.528784 1500231600 393320
136996 False 32975.0 AFR27GH 34750.0 424.614753 1500231600 393320
136997 False 32975.0 AFR27GH 34750.0 424.614753 1500231600 393320

Basic treatments

Specific methods are provided to pretreat/posttreat data. The following plot shows the same flight displayed with non-processed data and columns pretreated with a cascade of median filters. All timestamps are plotted in UTC.

%matplotlib inline
import matplotlib.pyplot as plt

with plt.style.context('traffic'):
    fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True)
    t['HOP87DJ'].plot_time(ax1, ['altitude', 'groundspeed'], 'groundspeed')
    t['HOP87DJ'].filter().plot_time(ax2, ['altitude', 'groundspeed'], 'groundspeed')
quickstart_plot_time

Display trajectories

The most basic displaying option uses Cartopy for producing Matplotlib plots. A projection it taken from the drawing module (here Lambert 93) and all traffic structures adapt to the projection passed to Matplotlib.

%matplotlib inline
import matplotlib.pyplot as plt

from traffic.drawing import Lambert93, rivers, countries

with plt.style.context('traffic'):
    fig, ax = plt.subplots(
        subplot_kw=dict(projection=Lambert93())
    )
    ax.add_feature(countries(edgecolor='#524c50', alpha=.3))
    ax.add_feature(rivers(alpha=.2))
    ax.set_global()

    # The map becomes too noisy if we plot everything
    t.plot(ax, nb_flights=100)

    # Plot a specific flight
    t['AFR27GH'].plot(ax)
quickstart_full

Airspaces

European FIRs are embedded in the package. The representation displays a bullet list of min/max altitudes with corresponding extruded polygons.

FIRs usually consist of simple polygons extruded from the ground to FL195.

from traffic.data import eurofirs
eurofirs['LOVV']
WIEN (FIR)
  • 0, 999

If you have access to AIRAC files (published by Eurocontrol), you may get information about more complex zones, here the Terminal Maneuvering Area (TMA) for Toulouse.

Warning

The path to the directory containing the AIRAC files must be set in a configuration file, as detailed in the relevant section.

from traffic.data import nm_airspaces
nm_airspaces['LFBOTMA']
LFBOTMA (TMA)
  • -inf, 115.0
  • 115.0, 145.0
  • 145.0, inf

Iteration

A Traffic object can be iterated: it splits flights by flight_id. If flights are yielded by icao24/callsign, they are also splitted so as to remain with no empty 10 minute interval.

We create here a new Traffic object limited to flights intersecting the TMA.

from traffic.core import Traffic

t_tma = Traffic.from_flights(
    flight for flight in t
    if flight.intersects(nm_airspaces['LFBOTMA'])
)
Traffic with 37 identifiers
count
icao24 callsign
4ca84d RYR3YM 2859
393320 AFR27GH 2770
393322 AFR23FK 2665
40643a EZY57FT 2656
344692 VLG2972 2599
400cd1 EZY81GE 2579
4ca2c9 EIN056 2526
400954 MON64PM 2417
40666b EZY72LE 2413
4071e0 EZY154E 2346
%matplotlib inline
import matplotlib.pyplot as plt

from traffic.drawing import Lambert93, rivers, countries, location

with plt.style.context('traffic'):
    fig, ax = plt.subplots(
        subplot_kw=dict(projection=Lambert93())
    )
    ax.add_feature(countries())

    # The request is sent to OpenStreetMap
    location('Occitanie').plot(ax, linestyle='dotted')
    ax.set_extent('Occitanie')

    # Airspaces can plot their footprint
    nm_airspaces['LFBOTMA'].plot(ax, linewidth=2, linestyle='dashed')

    t_tma.plot(ax)
quickstart_tma

There are still many flights which are definitely not landing or taking off from Toulouse airport. That is probably due to the definition of Toulouse TMA which includes a polygon without a max altitude limit.

Let’s limit ourselves to aircraft trajectories coming under 2000ft.

t_airport = Traffic.from_flights(
    flight for flight in t_tma
    if flight.data.altitude.min() < 2000
)
Traffic with 13 identifiers
count
icao24 callsign
4ca84d RYR3YM 2859
393320 AFR27GH 2770
393322 AFR23FK 2665
344692 VLG2972 2599
400cd1 EZY81GE 2579
44ce6f BEL7NG 2182
406134 EZY819T 2050
4010eb EZY743L 1769
4ca589 RYR17G 1643
405b67 EZY43UC 1632
%matplotlib inline
import matplotlib.pyplot as plt

from traffic.drawing import Lambert93, rivers, countries, location

with plt.style.context('traffic'):
    fig, ax = plt.subplots(
        subplot_kw=dict(projection=Lambert93())
    )
    ax.add_feature(countries())

    location('Occitanie').plot(ax, linestyle='dotted')
    ax.set_extent('Occitanie')

    nm_airspaces['LFBOTMA'].plot(ax, linewidth=2, linestyle='dashed')

    t_airport.plot(ax)
quickstart_airport

If we iterate on this new traffic we can be specific about each trajectory for their plotting.

%matplotlib inline
import matplotlib.pyplot as plt

from traffic.drawing import Lambert93, rivers, countries, location

with plt.style.context('traffic'):
    fig, ax = plt.subplots(
        subplot_kw=dict(projection=Lambert93())
    )
    ax.add_feature(countries())

    location('Occitanie').plot(ax, linestyle='dotted')
    ax.set_extent('Occitanie')

    nm_airspaces['LFBOTMA'].plot(ax, linewidth=2, linestyle='dashed')

    for flight in t_airport:
        flight.plot(
            ax, alpha=.5,
            color=(
                'crimson'
                if flight.data.vertical_rate.mean() < 0
                else 'steelblue'
            )
        )
quickstart_landing

Instant snapshots

In the following example, we limit ourselves to aircraft landing at Toulouse around 8pm UTC and display their full trajectories in one color, their positions at a specific time and their trajectories before that time in a different color.

Notice the use of:

  • a method guessing where the aircraft lands;
  • a query can be addressed as a numexpr to take a subsample of our trajectories.
%matplotlib inline
import matplotlib.pyplot as plt

from traffic.drawing import Lambert93, rivers, countries, location
from traffic.data import airports

with plt.style.context('traffic'):
    fig, ax = plt.subplots(subplot_kw=dict(projection=Lambert93()))
    ax.add_feature(countries(edgecolor='#524c50', alpha=.3))
    ax.add_feature(rivers(alpha=.2))

    # Limit the extent to the TMA
    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')

    # Fill this list for the upcoming example
    demo = []

    for flight in t_airport:

        # Just get rid of irrelevant flights
        guess = flight.guess_landing_airport()
        if guess.distance > 5000 and guess.airport.icao != 'LFBO':
            continue

        flight.plot(ax, color='#aaaaaa', alpha=.5)
        flight_before = flight.before('2017-07-16 20:00')

        if 1000 < flight_before.at().altitude < 20000:

            flight_before.plot(
                ax, alpha=.5,
                color='crimson'
            )
            flight_before.at().plot(
                ax, s=20,
                text_kw=dict(s=flight.callsign)
            )

        # Append flights trajectories when they fly under 25000ft
        demo.append(flight.query('altitude < 25000'))
quickstart_instant