Impact of COVID-19 on worldwide aviation

https://zenodo.org/badge/DOI/10.5281/zenodo.3737102.svg

Information

The dataset constructed for the analysis on this page is now available online, with a proper introduction on the OpenSky Network blog. Many thanks to the whole team.

The pandemic of coronavirus is having a serious impact on aviation around the world. The slowdown appears on data, with some regional peculiarities. The underlying data is currently updated daily.

Similar initiatives to analyse the impact of COVID-19 on aviation are available here:

With the same dataset, we found:

  • a focus on American airports by @ethanklapper on Twitter

  • an excellent Observable notebook by @lounjukk replaying all the traffic on a world map

  • Baptiste Coulmont analyses the impact of the lockdown in France based on various sources of open data, including this dataset (in French)

  • French newspaper Liberation did a similar job few days later, with more data visualisations

  • How COVID-19 has rocked US domestic flights by @EvDenmark <https://twitter.com/EvDenmark/status/1260922351732101120>

This list may not be exhaustive. You may open an issue on the github of the library in order to reference more websites.

Flight evolution per airport

The following plot displays the current trend in number of departing aircraft from airports in various areas around the world (covered by The OpenSky Network).

In earlier days, the trend showed:

  • a slow decrease from February in Asian airports (an early one in Hong-Kong);

  • European airports plummetting since early day of March;

  • America started dropping later;

  • India almost stopped all traffic (VABB, VIDP).

In the past few days, some airports and airlines seem to have experienced a slight increase of activity.

Flight evolution per airline

Currently, the trend shows:

  • decreasing patterns for regular airlines depending on the geography;

  • more low-cost airlines stopping their activity;

  • cargo airlines carrying on (see activities by @simon_sat on Twitter)

Data collection and preparation

On the Impala shell, a particular table contains flight lists with associated origin and destination airport. The data has been downloaded using the opensky.flightlist method, curated, then aggregated with aircraft and flight number information before being published here. Download the data and run the following:

from pathlib import Path
import pandas as pd

flightlist = pd.concat(
    pd.read_csv(file, parse_dates=["firstseen", "lastseen", "day"])
    for file in Path("path/to/folder").glob("flightlist_*.csv.gz")
)

We will select a few subset of airports for visualisation and build a specific table limited to these airports: the idea is to plot the number of departing aircraft per day for each of the following airports. The plot for airlines goes along the same idea.

from traffic.data import airports
import altair as alt

airports_subset = [
    # Europe
    ["LFPG", "EGLL", "EHAM", "EDDF", "LEMD", "LIRF", "LSZH", "UUEE"],
    # Eastern Asia
    ["VHHH", "RJBB", "RJTT", "RKSI", "RCTP", "RPLL"],
    # Asia (other)
    ["YSSY", "YMML", "OMDB", "VABB", "VIDP", "WSSS"],
    # Americas
    ["CYYZ", "KSFO", "KLAX", "KATL", "KJFK", "SBGR"],
]

data = pd.concat(
    (
        flightlist.query(f'origin == "{airport}"')
        # count the number of departing aircraft per day
        .groupby("day")
        .agg(dict(callsign="count"))
        # label the current chunk with the name of the airport
        .rename(columns=dict(callsign=airport))
        # iterate on all airports in the list hereabove
        for airport in sum(airports_subset, [])
    ),
    axis=1,
)

chart = alt.Chart(
    data.reset_index()
    # prepare data for altair
    .melt("day", var_name="airport", value_name="count")
    # include the name of the city associated with the airport code
    .merge(
        airports.data[["icao", "municipality"]],
        left_on="airport",
        right_on="icao",
        how="left",
    )[["day", "airport", "count", "municipality"]]
    # rename this feature 'city'
    .rename(columns=dict(municipality="city"))
)


def full_chart(source, subset, subset_name):

    # We have many airports, only pick a subset
    chart = source.transform_filter(
        alt.FieldOneOfPredicate(field="airport", oneOf=subset)
    )

    # When we come close to a line, highlight it
    highlight = alt.selection(
        type="single", nearest=True, on="mouseover", fields=["airport"]
    )

    # The scatter plot
    points = (
        chart.mark_point()
        .encode(
            x="day",
            y=alt.Y("count", title="# of departing flights"),
            color=alt.Color("airport", legend=alt.Legend(title=subset_name)),
            # add some legend next to  point
            tooltip=["day", "airport", "city", "count"],
            # not too noisy please
            opacity=alt.value(0.5),
        )
        .add_selection(highlight)
    )

    # The trend plot
    lines = (
        chart.mark_line()
        .encode(
            x="day",
            y="count",
            color="airport",
            size=alt.condition(~highlight, alt.value(1), alt.value(3)),
        )
        # the cloud is a bit messy, draw a trend through it
        .transform_loess("day", "count", groupby=["airport"], bandwidth=0.2)
    )

    return lines + points


# Concatenate several plots
result = alt.vconcat(
    *[
        full_chart(chart, airport_, subset_name).properties(width=600, height=150)
        for subset_name, airport_ in zip(
            [
                "European airports",
                "East-Asian airports",
                "Asian/Australian airports",
                "American airports",
            ],
            airports_subset,
        )
    ]
).resolve_scale(color="independent")

result