pycontrails.core.flight

Flight Data Handling.

Module Attributes

MAX_AIRPORT_ELEVATION

Max airport elevation, [\(ft\)] See Daocheng_Yading_Airport

MIN_CRUISE_ALTITUDE

Min estimated cruise altitude, [\(ft\)]

SHORT_HAUL_DURATION

Short haul duration cutoff, [\(s\)]

MAX_ON_GROUND_SPEED

Set maximum speed compatible with "on_ground" indicator, [\(mph\)] Thresholds assessed based on scatter plot (150 knots = 278 km/h)

Functions

filter_altitude(time, altitude_ft[, ...])

Filter noisy altitude on a single flight.

segment_duration(time[, dtype])

Calculate the time difference between waypoints.

segment_phase(rocd, altitude_ft, *[, ...])

Identify the phase of flight (climb, cruise, descent) for each segment.

segment_rocd(segment_duration, altitude_ft)

Calculate the rate of climb and descent (ROCD).

Classes

Flight([data, longitude, latitude, ...])

A single flight trajectory.

FlightPhase(value[, names, module, ...])

Flight phase enumeration.

class pycontrails.core.flight.Flight(data=None, *, longitude=None, latitude=None, altitude=None, altitude_ft=None, level=None, time=None, attrs=None, copy=True, fuel=None, drop_duplicated_times=False, **attrs_kwargs)

Bases: GeoVectorDataset

A single flight trajectory.

Expect latitude-longitude coordinates in WGS 84. Expect altitude in [\(m\)]. Expect pressure level (level) in [\(hPa\)].

Parameters:
  • data (dict[str, np.ndarray] | pd.DataFrame | VectorDataDict | VectorDataset | None) – Flight trajectory waypoints as data dictionary or pandas.DataFrame. Must include columns time, latitude, longitude, altitude or level. Keyword arguments for time, latitude, longitude, altitude or level will override data inputs. Expects altitude in meters and time as a DatetimeLike (or array that can processed with pd.to_datetime()). Additional waypoint-specific data can be included as additional keys/columns.

  • longitude (npt.ArrayLike, optional) – Flight trajectory waypoint longitude. Defaults to None.

  • latitude (npt.ArrayLike, optional) – Flight trajectory waypoint latitude. Defaults to None.

  • altitude (npt.ArrayLike, optional) – Flight trajectory waypoint altitude, [\(m\)]. Defaults to None.

  • altitude_ft (npt.ArrayLike, optional) – Flight trajectory waypoint altitude, [\(ft\)].

  • level (npt.ArrayLike, optional) – Flight trajectory waypoint pressure level, [\(hPa\)]. Defaults to None.

  • time (npt.ArrayLike, optional) – Flight trajectory waypoint time. Defaults to None.

  • attrs (dict[str, Any], optional) – Additional flight properties as a dictionary. While different models may utilize Flight attributes differently, pycontrails applies the following conventions:

    • flight_id: An internal flight identifier. Used internally for Fleet interoperability.

    • aircraft_type: Aircraft type ICAO, e.g. "A320".

    • wingspan: Aircraft wingspan, [\(m\)].

    • n_engine: Number of aircraft engines.

    • engine_uid: Aircraft engine unique identifier. Used for emissions calculations with the ICAO Aircraft Emissions Databank (EDB).

    • max_mach_number: Maximum Mach number at cruise altitude. Used by some aircraft performance models to clip true airspeed.

    Numeric quantities that are constant over the entire flight trajectory should be included as attributes.

  • copy (bool, optional) – Copy data on Flight creation. Defaults to True.

  • fuel (Fuel, optional) – Fuel used in flight trajectory. Defaults to JetA.

  • drop_duplicated_times (bool, optional) – Drop duplicate times in flight trajectory. Defaults to False.

  • **attrs_kwargs (Any) – Additional flight properties passed as keyword arguments.

Raises:

KeyError – Raises if data input does not contain at least time, latitude, longitude, (altitude or level).

Notes

The Traffic library has many helpful flight processing utilities.

See traffic.core.Flight for more information.

Examples

>>> import numpy as np
>>> import pandas as pd
>>> from pycontrails import Flight
>>> # Create `Flight` from a DataFrame.
>>> df = pd.DataFrame({
...     "longitude": np.linspace(20, 30, 500),
...     "latitude": np.linspace(40, 10, 500),
...     "altitude": 10500,
...     "time": pd.date_range('2021-01-01T10', '2021-01-01T15', periods=500),
... })
>>> fl = Flight(data=df, flight_id=123)  # specify a flight_id by keyword
>>> fl
Flight [4 keys x 500 length, 1 attributes]
Keys: longitude, latitude, altitude, time
Attributes:
time                [2021-01-01 10:00:00, 2021-01-01 15:00:00]
longitude           [20.0, 30.0]
latitude            [10.0, 40.0]
altitude            [10500.0, 10500.0]
flight_id           123
>>> # Create `Flight` from keywords
>>> fl = Flight(
...     longitude=np.linspace(20, 30, 200),
...     latitude=np.linspace(40, 30, 200),
...     altitude=11000 * np.ones(200),
...     time=pd.date_range('2021-01-01T12', '2021-01-01T14', periods=200),
... )
>>> fl
Flight [4 keys x 200 length, 0 attributes]
    Keys: longitude, latitude, time, altitude
    Attributes:
    time                [2021-01-01 12:00:00, 2021-01-01 14:00:00]
    longitude           [20.0, 30.0]
    latitude            [30.0, 40.0]
    altitude            [11000.0, 11000.0]
>>> # Access the underlying data as DataFrame
>>> fl.dataframe.head()
   longitude   latitude                          time  altitude
0  20.000000  40.000000 2021-01-01 12:00:00.000000000   11000.0
1  20.050251  39.949749 2021-01-01 12:00:36.180904522   11000.0
2  20.100503  39.899497 2021-01-01 12:01:12.361809045   11000.0
3  20.150754  39.849246 2021-01-01 12:01:48.542713567   11000.0
4  20.201005  39.798995 2021-01-01 12:02:24.723618090   11000.0
clean_and_resample(freq='1min', fill_method='geodesic', geodesic_threshold=100000.0, nominal_rocd=12.7, kernel_size=17, cruise_threshold=120.0, force_filter=False, drop=True, keep_original_index=False, climb_descend_at_end=False)

Resample and (possibly) filter a flight trajectory.

Waypoints are resampled according to the frequency freq. If the original flight data has a short sampling period, filter_altitude will also be called to clean the data. Large gaps in trajectories may be interpolated as step climbs through _altitude_interpolation.

Parameters:
  • freq (str, optional) – Resampling frequency, by default “1min”

  • fill_method ({"geodesic", "linear"}, optional) – Choose between "geodesic" and "linear", by default "geodesic". In geodesic mode, large gaps between waypoints are filled with geodesic interpolation and small gaps are filled with linear interpolation. In linear mode, all gaps are filled with linear interpolation.

  • geodesic_threshold (float, optional) – Threshold for geodesic interpolation, [\(m\)]. If the distance between consecutive waypoints is under this threshold, values are interpolated linearly.

  • nominal_rocd (float, optional) – Nominal rate of climb / descent for aircraft type. Defaults to constants.nominal_rocd.

  • kernel_size (int, optional) – Passed directly to scipy.signal.medfilt(), by default 11. Passed also to scipy.signal.medfilt()

  • cruise_theshold (float, optional) – Minimal length of time, in seconds, for a flight to be in cruise to apply median filter

  • force_filter (bool, optional) – If set to true, meth:filter_altitude will always be called. otherwise, it will only be called if the flight has a median sample period under 10 seconds

  • drop (bool, optional) – Drop any columns that are not resampled and filled. Defaults to True, dropping all keys outside of “time”, “latitude”, “longitude” and “altitude”. If set to False, the extra keys will be kept but filled with nan or None values, depending on the data type.

  • keep_original_index (bool, optional) – Keep the original index of the Flight in addition to the new resampled index. Defaults to False. .. versionadded:: 0.45.2

  • climb_or_descend_at_end (bool) – If true, the climb or descent will be placed at the end of each segment rather than the start. Default is false (climb or descent immediately).

Returns:

Flight – Filled Flight

copy(**kwargs)

Return a copy of this instance.

Parameters:

**kwargs (Any) – Additional keyword arguments passed into the constructor of the returned class.

Returns:

Self – Copy of class

distance_to_coords(distance)

Convert distance along flight path to geodesic coordinates.

Will return a tuple containing (lat, lon, index), where index indicates which flight segment contains the returned coordinate.

Parameters:

distance (ArrayOrFloat) – Distance along flight path, [\(m\)]

Returns:

(ArrayOrFloat, ArrayOrFloat, int | npt.NDArray[int]) – latitude, longitude, and segment index cooresponding to distance.

property duration

Determine flight duration.

Returns:

pd.Timedelta – Difference between terminal and initial time

filter(mask, copy=True, **kwargs)

Filter data according to a boolean array mask.

Entries corresponding to mask == True are kept.

Parameters:
  • mask (npt.NDArray[np.bool_]) – Boolean array with compatible shape.

  • copy (bool, optional) – Copy data on filter. Defaults to True. See numpy best practices for insight into whether copy is appropriate.

  • **kwargs (Any) – Additional keyword arguments passed into the constructor of the returned class.

Returns:

Self – Containing filtered data

Raises:

TypeError – If mask is not a boolean array.

filter_altitude(kernel_size=17, cruise_threshold=120.0)

Filter noisy altitude on a single flight.

Currently runs altitude through a median filter using scipy.signal.medfilt() with kernel_size, then a Savitzky-Golay filter to filter noise. The median filter is only applied during cruise segments that are longer than cruise_threshold.

Parameters:
Returns:

Flight – Filtered Flight

Notes

Algorithm is derived from traffic.core.Flight.filter().

The traffic algorithm also computes thresholds on sliding windows and replaces unacceptable values with NaNs.

Errors may raised if the kernel_size is too large.

See also

traffic.core.flight.Flight.filter(), scipy.signal.medfilt()

filter_by_first()

Keep first row of group of waypoints with identical coordinates.

Chaining this method with resample_and_fill often gives a cleaner trajectory when using noisy flight waypoints.

Returns:

Flight – Filtered Flight instance

Examples

>>> from datetime import datetime
>>> import pandas as pd
>>> df = pd.DataFrame()
>>> df['longitude'] = [0, 0, 50]
>>> df['latitude'] = 0
>>> df['altitude'] = 0
>>> df['time'] = [datetime(2020, 1, 1, h) for h in range(3)]
>>> fl = Flight(df)
>>> fl.filter_by_first().dataframe
   longitude  latitude  altitude                time
0        0.0       0.0       0.0 2020-01-01 00:00:00
1       50.0       0.0       0.0 2020-01-01 02:00:00
fuel

Fuel used in flight trajectory

property length

Return flight length based on WGS84 geodesic.

Returns:

float – Total flight length, [\(m\)]

Examples

>>> import numpy as np
>>> fl = Flight(
...     longitude=np.linspace(20, 30, 200),
...     latitude=np.linspace(40, 30, 200),
...     altitude=11000 * np.ones(200),
...     time=pd.date_range('2021-01-01T12', '2021-01-01T14', periods=200),
... )
>>> fl.length
np.float64(1436924.67...)
length_met(key, threshold=1.0)

Calculate total horizontal distance where column key exceeds threshold.

Parameters:
  • key (str) – Column key in data

  • threshold (float) – Consider trajectory waypoints whose associated key value exceeds threshold, by default 1.0

Returns:

float – Total distance, [\(m\)]

Raises:

KeyErrordata does not contain column key

Examples

>>> from datetime import datetime
>>> import pandas as pd
>>> import numpy as np
>>> from pycontrails.datalib.ecmwf import ERA5
>>> from pycontrails import Flight
>>> # Get met data
>>> times = (datetime(2022, 3, 1, 0),  datetime(2022, 3, 1, 3))
>>> variables = ["air_temperature", "specific_humidity"]
>>> levels = [300, 250, 200]
>>> era5 = ERA5(time=times, variables=variables, pressure_levels=levels)
>>> met = era5.open_metdataset()
>>> # Build flight
>>> df = pd.DataFrame()
>>> df["time"] = pd.date_range("2022-03-01T00", "2022-03-01T03", periods=11)
>>> df["longitude"] = np.linspace(-20, 20, 11)
>>> df["latitude"] = np.linspace(-20, 20, 11)
>>> df["altitude"] = np.linspace(9500, 10000, 11)
>>> fl = Flight(df).resample_and_fill("10s")
>>> # Intersect and attach
>>> fl["air_temperature"] = fl.intersect_met(met["air_temperature"])
>>> fl["air_temperature"]
array([235.94657007, 235.55745645, 235.56709768, ..., 234.59917962,
       234.60387402, 234.60845312])
>>> # Length (in meters) of waypoints whose temperature exceeds 236K
>>> fl.length_met("air_temperature", threshold=236)
np.float64(3589705.998...)
>>> # Proportion (with respect to distance) of waypoints whose temperature exceeds 236K
>>> fl.proportion_met("air_temperature", threshold=236)
np.float64(0.576...)
property max_distance_gap

Return maximum distance gap between waypoints along flight trajectory.

Distance is calculated based on WGS84 geodesic.

Returns:

float – Maximum distance between waypoints, [\(m\)]

Examples

>>> import numpy as np
>>> fl = Flight(
...     longitude=np.linspace(20, 30, 200),
...     latitude=np.linspace(40, 30, 200),
...     altitude=11000 * np.ones(200),
...     time=pd.date_range('2021-01-01T12', '2021-01-01T14', periods=200),
... )
>>> fl.max_distance_gap
np.float64(7391.27...)
property max_time_gap

Return maximum time gap between waypoints along flight trajectory.

Returns:

pd.Timedelta – Gap size

Examples

>>> import numpy as np
>>> fl = Flight(
...     longitude=np.linspace(20, 30, 200),
...     latitude=np.linspace(40, 30, 200),
...     altitude=11000 * np.ones(200),
...     time=pd.date_range('2021-01-01T12', '2021-01-01T14', periods=200),
... )
>>> fl.max_time_gap
Timedelta('0 days 00:00:36.180...')
plot(**kwargs)

Plot flight trajectory longitude-latitude values.

Parameters:

**kwargs (Any) – Additional plot properties to passed to pd.DataFrame.plot

Returns:

matplotlib.axes.Axes – Plot

plot_profile(**kwargs)

Plot flight trajectory time-altitude values.

Parameters:

**kwargs (Any) – Additional plot properties to passed to pd.DataFrame.plot

Returns:

matplotlib.axes.Axes – Plot

proportion_met(key, threshold=1.0)

Calculate proportion of flight with certain meteorological constraint.

Parameters:
  • key (str) – Column key in data

  • threshold (float) – Consider trajectory waypoints whose associated key value exceeds threshold, Defaults to 1.0

Returns:

float – Ratio

resample_and_fill(freq='1min', fill_method='geodesic', geodesic_threshold=100000.0, nominal_rocd=12.7, drop=True, keep_original_index=False, climb_descend_at_end=False)

Resample and fill flight trajectory with geodesics and linear interpolation.

Waypoints are resampled according to the frequency freq. Values for data columns longitude, latitude, and altitude are interpolated.

Resampled waypoints will include all multiples of freq between the flight start and end time. For example, when resampling to a frequency of 1 minute, a flight that starts at 2020/1/1 00:00:59 and ends at 2020/1/1 00:01:01 will return a single waypoint at 2020/1/1 00:01:00, whereas a flight that starts at 2020/1/1 00:01:01 and ends at 2020/1/1 00:01:59 will return an empty flight.

Parameters:
  • freq (str, optional) – Resampling frequency, by default “1min”

  • fill_method ({"geodesic", "linear"}, optional) – Choose between "geodesic" and "linear", by default "geodesic". In geodesic mode, large gaps between waypoints are filled with geodesic interpolation and small gaps are filled with linear interpolation. In linear mode, all gaps are filled with linear interpolation.

  • geodesic_threshold (float, optional) – Threshold for geodesic interpolation, [\(m\)]. If the distance between consecutive waypoints is under this threshold, values are interpolated linearly.

  • nominal_rocd (float | None, optional) – Nominal rate of climb / descent for aircraft type. Defaults to constants.nominal_rocd.

  • drop (bool, optional) – Drop any columns that are not resampled and filled. Defaults to True, dropping all keys outside of “time”, “latitude”, “longitude” and “altitude”. If set to False, the extra keys will be kept but filled with nan or None values, depending on the data type.

  • keep_original_index (bool, optional) – Keep the original index of the Flight in addition to the new resampled index. Defaults to False. .. versionadded:: 0.45.2

  • climb_or_descend_at_end (bool) – If true, the climb or descent will be placed at the end of each segment rather than the start. Default is false (climb or descent immediately).

Returns:

Flight – Filled Flight

Raises:

ValueError – Unknown fill_method

Examples

>>> from datetime import datetime
>>> import pandas as pd
>>> df = pd.DataFrame()
>>> df['longitude'] = [0, 0, 50]
>>> df['latitude'] = 0
>>> df['altitude'] = 0
>>> df['time'] = [datetime(2020, 1, 1, h) for h in range(3)]
>>> fl = Flight(df)
>>> fl.dataframe
   longitude  latitude  altitude                time
           0        0.0       0.0       0.0 2020-01-01 00:00:00
           1        0.0       0.0       0.0 2020-01-01 01:00:00
           2       50.0       0.0       0.0 2020-01-01 02:00:00
>>> fl.resample_and_fill('10min').dataframe  # resample with 10 minute frequency
    longitude  latitude  altitude                time
0    0.000000       0.0       0.0 2020-01-01 00:00:00
1    0.000000       0.0       0.0 2020-01-01 00:10:00
2    0.000000       0.0       0.0 2020-01-01 00:20:00
3    0.000000       0.0       0.0 2020-01-01 00:30:00
4    0.000000       0.0       0.0 2020-01-01 00:40:00
5    0.000000       0.0       0.0 2020-01-01 00:50:00
6    0.000000       0.0       0.0 2020-01-01 01:00:00
7    8.333333       0.0       0.0 2020-01-01 01:10:00
8   16.666667       0.0       0.0 2020-01-01 01:20:00
9   25.000000       0.0       0.0 2020-01-01 01:30:00
10  33.333333       0.0       0.0 2020-01-01 01:40:00
11  41.666667       0.0       0.0 2020-01-01 01:50:00
12  50.000000       0.0       0.0 2020-01-01 02:00:00
segment_angle()

Calculate sine and cosine for the angle between each segment and the longitudinal axis.

This is different from the usual navigational angle between two points known as bearing.

Bearing in 3D spherical coordinates is referred to as azimuth.

        (lon_2, lat_2)  X
                       /|
                      / |
                     /  |
                    /   |
                   /    |
                  /     |
                 /      |
(lon_1, lat_1)  X -------> longitude (x-axis)
Returns:

npt.NDArray[np.float64], npt.NDArray[np.float64] – Returns sin(a), cos(a), where a is the angle between the segment and the longitudinal axis. The final values are of both arrays are np.nan.

See also

geo.segment_angle(), units.heading_to_longitudinal_angle(), segment_azimuth(), geo.forward_azimuth()

Examples

>>> from pycontrails import Flight
>>> fl = Flight(
... longitude=np.array([1, 2, 3, 5, 8]),
... latitude=np.arange(5),
... altitude=np.full(shape=(5,), fill_value=11000),
... time=pd.date_range('2021-01-01T12', '2021-01-01T14', periods=5),
... )
>>> sin, cos = fl.segment_angle()
>>> sin
array([0.70716063, 0.70737598, 0.44819424, 0.31820671,        nan])
>>> cos
array([0.70705293, 0.70683748, 0.8939362 , 0.94802136,        nan])
segment_azimuth()

Calculate (forward) azimuth at each waypoint.

Method calls pyproj.Geod.inv, which is slow. See geo.forward_azimuth for an outline of a faster implementation.

Changed in version 0.33.7: The dtype of the output now matches the dtype of self["longitude"].

Returns:

npt.NDArray[np.float64] – Array of azimuths.

See also

segment_angle(), geo.forward_azimuth()

segment_duration(dtype=<class 'numpy.float32'>)

Compute time elapsed between waypoints in seconds.

np.nan appended so the length of the output is the same as number of waypoints.

Parameters:

dtype (np.dtype) – Numpy dtype for time difference. Defaults to np.float64

Returns:

npt.NDArray[np.float64] – Time difference between waypoints, [\(s\)]. Returns an array with dtype specified by``dtype``

segment_groundspeed(smooth=False, window_length=7, polyorder=1)

Return groundspeed across segments.

Calculate by dividing the horizontal segment length by the difference in waypoint times.

Parameters:
Returns:

npt.NDArray[np.float64] – Groundspeed of the segment, [\(m s^{-1}\)]

segment_haversine()

Compute Haversine (great circle) distance between flight waypoints.

Helper function used in resample_and_fill(). np.nan appended so the length of the output is the same as number of waypoints.

To account for vertical displacements when computing segment lengths, use segment_length().

Returns:

npt.NDArray[np.float64] – Array of great circle distances in [\(m\)] between waypoints

Examples

>>> from pycontrails import Flight
>>> fl = Flight(
... longitude=np.array([1, 2, 3, 5, 8]),
... latitude=np.arange(5),
... altitude=np.full(shape=(5,), fill_value=11000),
... time=pd.date_range('2021-01-01T12', '2021-01-01T14', periods=5),
... )
>>> fl.segment_haversine()
array([157255.03346286, 157231.08336815, 248456.48781503, 351047.44358851,
                   nan])
segment_length()

Compute spherical distance between flight waypoints.

Helper function used in length() and length_met(). np.nan appended so the length of the output is the same as number of waypoints.

Returns:

npt.NDArray[np.float64] – Array of distances in [\(m\)] between waypoints

Examples

>>> from pycontrails import Flight
>>> fl = Flight(
... longitude=np.array([1, 2, 3, 5, 8]),
... latitude=np.arange(5),
... altitude=np.full(shape=(5,), fill_value=11000),
... time=pd.date_range('2021-01-01T12', '2021-01-01T14', periods=5),
... )
>>> fl.segment_length()
array([157255.03346286, 157231.08336815, 248456.48781503, 351047.44358851,
                   nan])

See also

segment_length()

segment_mach_number(true_airspeed, air_temperature)

Calculate the mach number of each segment.

Parameters:
  • true_airspeed (npt.NDArray[np.float64]) – True airspeed of the segment, [\(m \ s^{-1}\)]. See segment_true_airspeed().

  • air_temperature (npt.NDArray[np.float64]) – Average air temperature of each segment, [\(K\)]

Returns:

npt.NDArray[np.float64] – Mach number of each segment

segment_phase(threshold_rocd=250.0, min_cruise_altitude_ft=20000.0, air_temperature=None)

Identify the phase of flight (climb, cruise, descent) for each segment.

Parameters:
  • threshold_rocd (float, optional) – ROCD threshold to identify climb and descent, [\(ft min^{-1}\)]. Currently set to 250 ft/min.

  • min_cruise_altitude_ft (float, optional) – Minimum altitude for cruise, [\(ft\)] This is specific for each aircraft type, and can be approximated as 50% of the altitude ceiling. Defaults to 20000 ft.

  • air_temperature (None | npt.NDArray[np.float64]) – Air temperature of each flight waypoint, [\(K\)]

Returns:

npt.NDArray[np.uint8] – Array of values enumerating the flight phase. See flight.FlightPhase for enumeration.

segment_rocd(air_temperature=None)

Calculate the rate of climb and descent (ROCD).

Parameters:

air_temperature (None | npt.NDArray[np.float64]) – Air temperature of each flight waypoint, [\(K\)]

Returns:

npt.NDArray[np.float64] – Rate of climb and descent over segment, [\(ft min^{-1}\)]

See also

segment_rocd()

segment_true_airspeed(u_wind=0.0, v_wind=0.0, smooth=True, window_length=7, polyorder=1)

Calculate the true airspeed [\(m/s\)] from the ground speed and horizontal winds.

The calculated ground speed will first be smoothed with a Savitzky-Golay filter if enabled.

Parameters:
  • u_wind (npt.NDArray[np.float64] | float) – U wind speed, [\(m \ s^{-1}\)]. Defaults to 0 for all waypoints.

  • v_wind (npt.NDArray[np.float64] | float) – V wind speed, [\(m \ s^{-1}\)]. Defaults to 0 for all waypoints.

  • smooth (bool, optional) – Smooth airspeed with Savitzky-Golay filter. Defaults to True.

  • window_length (int, optional) – Passed directly to scipy.signal.savgol_filter(), by default 7.

  • polyorder (int, optional) – Passed directly to scipy.signal.savgol_filter(), by default 1.

Returns:

npt.NDArray[np.float64] – True wind speed of each segment, [\(m \ s^{-1}\)]

sort(by)

Sort data by key(s).

This method always creates a copy of the data by calling pandas.DataFrame.sort_values().

Parameters:

by (str | list[str]) – Key or list of keys to sort by.

Returns:

Self – Instance with sorted data.

property time_end

Last waypoint time.

Returns:

pandas.Timestamp – Last waypoint time

property time_start

First waypoint time.

Returns:

pandas.Timestamp – First waypoint time

to_geojson_linestring()

Return trajectory as geojson FeatureCollection containing single LineString.

Returns:

dict[str, Any] – Python representation of geojson FeatureCollection

to_geojson_multilinestring(key=None, split_antimeridian=True)

Return trajectory as GeoJSON FeatureCollection of MultiLineStrings.

If key is provided, Flight data is grouped according to values of key. Each group gives rise to a Feature containing a MultiLineString geometry. Each MultiLineString can optionally be split over the antimeridian.

Parameters:
  • key (str, optional) – If provided, name of data column to group by.

  • split_antimeridian (bool, optional) – Split linestrings that cross the antimeridian. Defaults to True.

Returns:

dict[str, Any] – Python representation of GeoJSON FeatureCollection of MultiLinestring Features

Raises:

KeyErrorkey is provided but data does not contain column key

to_traffic()

Convert to :class:`traffic.core.Flight`instance.

Returns:

traffic.core.Flight – traffic flight instance

Raises:

ModuleNotFoundErrortraffic package not installed

class pycontrails.core.flight.FlightPhase(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: IntEnum

Flight phase enumeration.

Use segment_phase() or Flight.segment_phase() to determine flight phase.

CLIMB = 1

Waypoints at which the flight is in a climb phase

CRUISE = 2

Waypoints at which the flight is in a cruise phase

DESCENT = 3

Waypoints at which the flight is in a descent phase

LEVEL_FLIGHT = 4

Waypoints at which the flight is not in a climb, cruise, or descent phase. In practice, this category is used for waypoints at which the ROCD resembles that of a cruise phase, but the altitude is below the minimum cruise altitude.

NAN = 5

Waypoints at which the ROCD is not defined.

pycontrails.core.flight.MAX_AIRPORT_ELEVATION = 15000.0

Max airport elevation, [\(ft\)] See Daocheng_Yading_Airport

pycontrails.core.flight.MAX_ON_GROUND_SPEED = 150.0

Set maximum speed compatible with “on_ground” indicator, [\(mph\)] Thresholds assessed based on scatter plot (150 knots = 278 km/h)

pycontrails.core.flight.MIN_CRUISE_ALTITUDE = 20000.0

Min estimated cruise altitude, [\(ft\)]

pycontrails.core.flight.SHORT_HAUL_DURATION = 3600.0

Short haul duration cutoff, [\(s\)]

pycontrails.core.flight.filter_altitude(time, altitude_ft, kernel_size=17, cruise_threshold=120, air_temperature=None)

Filter noisy altitude on a single flight.

Currently runs altitude through a median filter using scipy.signal.medfilt() with kernel_size, then a Savitzky-Golay filter to filter noise. The median filter is only applied during cruise segments that are longer than cruise_threshold.

Parameters:
  • time (npt.NDArray[np.datetime64]) – Waypoint time in np.datetime64 format.

  • altitude_ft (npt.NDArray[np.float64]) – Altitude signal in feet

  • kernel_size (int, optional) – Passed directly to scipy.signal.medfilt(), by default 11. Passed also to scipy.signal.medfilt()

  • cruise_theshold (int, optional) – Minimal length of time, in seconds, for a flight to be in cruise to apply median filter

  • air_temperature (None | npt.NDArray[np.float64]) – Air temperature of each flight waypoint, [\(K\)]

Returns:

npt.NDArray[np.float64] – Filtered altitude

Notes

Algorithm is derived from traffic.core.Flight.filter().

The traffic filter algorithm also computes thresholds on sliding windows and replaces unacceptable values with NaNs.

Errors may raised if the kernel_size is too large.

pycontrails.core.flight.segment_duration(time, dtype=<class 'numpy.float32'>)

Calculate the time difference between waypoints.

np.nan appended so the length of the output is the same as number of waypoints.

Parameters:
  • time (npt.NDArray[np.datetime64]) – Waypoint time in np.datetime64 format.

  • dtype (np.dtype) – Numpy dtype for time difference. Defaults to np.float64

Returns:

npt.NDArray[np.float64] – Time difference between waypoints, [\(s\)]. This returns an array with dtype specified by``dtype``.

pycontrails.core.flight.segment_phase(rocd, altitude_ft, *, threshold_rocd=250.0, min_cruise_altitude_ft=20000.0)

Identify the phase of flight (climb, cruise, descent) for each segment.

Parameters:
  • rocd (pt.NDArray[np.float64]) – Rate of climb and descent across segment, [\(ft min^{-1}\)]. See output from segment_rocd().

  • altitude_ft (npt.NDArray[np.float64]) – Altitude, [\(ft\)]

  • threshold_rocd (float, optional) – ROCD threshold to identify climb and descent, [\(ft min^{-1}\)]. Defaults to 250 ft/min.

  • min_cruise_altitude_ft (float, optional) – Minimum threshold altitude for cruise, [\(ft\)] This is specific for each aircraft type, and can be approximated as 50% of the altitude ceiling. Defaults to MIN_CRUISE_ALTITUDE.

Returns:

npt.NDArray[np.uint8] – Array of values enumerating the flight phase. See flight.FlightPhase for enumeration.

Notes

Flight data derived from ADS-B and radar sources could contain noise leading to small changes in altitude and ROCD. Hence, an arbitrary threshold_rocd is specified to identify the different phases of flight.

The flight phase “level-flight” is when an aircraft is holding at lower altitudes. The cruise phase of flight only occurs above a certain threshold altitude.

pycontrails.core.flight.segment_rocd(segment_duration, altitude_ft, air_temperature=None)

Calculate the rate of climb and descent (ROCD).

Parameters:
  • segment_duration (npt.NDArray[np.float64]) – Time difference between waypoints, [\(s\)]. Expected to have numeric dtype, not “timedelta64”. See output from segment_duration().

  • altitude_ft (npt.NDArray[np.float64]) – Altitude of each waypoint, [\(ft\)]

  • air_temperature (None | npt.NDArray[np.float64]) – Air temperature of each flight waypoint, [\(K\)]

Returns:

npt.NDArray[np.float64] – Rate of climb and descent over segment, [\(ft min^{-1}\)]

Notes

The hydrostatic equation will be used to estimate the ROCD if air_temperature is provided. This will improve the accuracy of the estimated ROCD with a temperature correction. The estimated ROCD with the temperature correction are expected to differ by up to +-5% compared to those without the correction. These differences are important when the ROCD estimates are used as inputs to aircraft performance models.