How to use arithmetic operators on trajectories?

Visualization with the | (or) operator

The | (prononce “or”) operator is very useful in interactive environments (IPython, Jupyter, etc.) in order to put data structures next to each other. The result of this operation is not designed to be stored in any variable.

For example, we can preview two trajectories on the same line:

from traffic.data.samples import belevingsvlucht

belevingsvlucht | belevingsvlucht.next("aligned_on_ils('EHAM')")

Flight

  • callsign: TRA051
  • aircraft: 484506 · 🇳🇱 PH-HZO (B738)
  • start: 2018-05-30 15:21:38+00:00
  • stop: 2018-05-30 20:22:56+00:00
  • duration: 0 days 05:01:18
  • sampling rate: 1 second(s)

Flight

  • callsign: TRA051
  • aircraft: 484506 · 🇳🇱 PH-HZO (B738)
  • start: 2018-05-30 20:14:29+00:00
  • stop: 2018-05-30 20:17:58+00:00
  • duration: 0 days 00:03:29
  • sampling rate: 1 second(s)

Concatenation with the + (plus) operator

The + operator is a simple concatenation operator. In other words, all the pandas DataFrames of the given structures are concatenated and wrapped in a Traffic object.

Flight.__add__(other)

Concatenation operator.

Parameters:

other (Union[Literal[0], Flight, Traffic]) – is the other Flight or Traffic.

Return type:

Traffic

Returns:

The sum of two Flights returns a Traffic collection. Summing a Flight with 0 returns a Traffic collection with only one trajectory, for compatibility reasons with the sum() builtin.

Traffic.__add__(other)

Concatenation operator.

Parameters:

other (Union[Literal[0], Flight, Traffic]) – is the other Flight or Traffic.

Return type:

Traffic

Returns:

The sum of two Traffic returns a Traffic collection. Summing a Traffic with 0 returns the same Traffic, for compatibility reasons with the sum() builtin.

With the same trajectories as above, we can construct a Traffic object:

from traffic.data.samples import belevingsvlucht, pixair_toulouse

belevingsvlucht + pixair_toulouse

Traffic

with 2 identifiers
    count
icao24 callsign  
484506 TRA051 16005
39b861 PXR31F 1819

The sum built-in function can be used to concatenate many flights into a Traffic collection.

Differences on trajectories with the - (minus) operator

The - operator serves a way to remove segments in a trajectory. The resulting structure will consist in 0, 1 or many trajectory segments wrapped in a FlightIterator object.

Flight.__sub__(other)

Difference operator.

Parameters:

other (Flight | FlightIterator | Interval | IntervalCollection) – refers to anything having one or several start and end (a.k.a. stop) dates.

Return type:

FlightIterator

Returns:

After intervals are pruned from a trajectory, unconnected segments may remain. You should iterate on the result of the - operator.

For example, on the following sample trajectory, the aircraft performs many landing attempts at Lelystad airport (EHLE) in the Netherlands, which are easily labelled as go-arounds. The difference operator will result in the trajectory section before and after all the landing attempts.

belevingsvlucht - belevingsvlucht.go_around("EHLE")

FlightIterator

Flight

  • callsign: TRA051
  • aircraft: 484506 · 🇳🇱 PH-HZO (B738)
  • start: 2018-05-30 15:21:38+00:00
  • stop: 2018-05-30 16:00:56+00:00
  • duration: 0 days 00:39:18
  • sampling rate: 1 second(s)

Flight

  • callsign: TRA051
  • aircraft: 484506 · 🇳🇱 PH-HZO (B738)
  • start: 2018-05-30 19:47:46+00:00
  • stop: 2018-05-30 20:22:56+00:00
  • duration: 0 days 00:35:10
  • sampling rate: 1 second(s)

Differences on collections with the - (minus) operator

The - operators also serves as a way to remove flights from a Traffic collection. The operator behaves slightly differently if both collections are equipped with a flight_id attribute or not.

Traffic.__sub__(other)

Remove trajectories from a Traffic object.

Parameters:

other (str | list[str] | set[str] | Flight | Traffic) –

  • When the other attribute is a string, or a list/set of strings, all flights matching the flight_id, callsign or icao24 attribute are removed from the collection;

  • When the other attribute is a Flight, the collection will be pruned of the trajectory with the same flight_id; or the segment of trajectory for that icao24 address between the start and stop timestamps;

  • When the other attribute is a Traffic object, the difference is computed based on the flight_id if both structures have one; otherwise, we iterate through flights and consider removing part of the trajectory.

Return type:

None | Traffic

Returns:

a new collection of trajectories as a Traffic object

For example, in the following sample dataset, we build a sub dataset of all trajectories going through a navaid point called “ODINA” at the border between Switzerland and Italy. The - operator produces a new dataset of trajectories not going through ODINA.

from traffic.data.samples import switzerland

through_odina = switzerland.has('aligned_on_navpoint("ODINA")').eval()
difference = switzerland - through_odina
through_odina | difference

Traffic

with 113 identifiers
    count
icao24 callsign  
440005 EZY12VJ 169
39e46f CCM531D 155
40123f BAW544K 147
3946e2 AFR85EZ 145
400e4a EXS88C 145
3950c0 AFR36RB 143
44c24d OOPRM 142
4bb852 PGT89P 142
396672 FPO09P 141
4ca2c0 RYR248Z 141

Traffic

with 1130 identifiers
    count
icao24 callsign  
500142 T7STK 282
4baa61 THY7WR 186
344417 IBE3279 185
40755f EZY24DP 184
4068cb EXS33W 180
3c5ee9 EWG5938 178
3420ca IBE31CW 176
4009f9 BAW585E 175
4006d6 CLJ6325 174
42428d AFL2603 174

Indexation with the [] (bracket) operator

Flight.__getitem__(key)

Indexation of flights.

Parameters:

key (str | Interval | IntervalCollection) – the key parameter passed in the brackets

Return type:

Any

Returns:

  • if key is a string, the bracket operator is equivalent to the dot notation(e.g. flight["duration"] is equivalent to flight.duration)

  • if key is an Interval, the bracket operator is equivalent to the between() method

  • if key is an IntervalCollection, the operator iterates on all the intervals provided

The indexation of a Traffic collection is intended to be used in the most versatile way. Any object that could identify a particular trajectory can be used as a key.

Traffic.__getitem__(key)

Indexation of collections.

Parameters:

key (Union[int, slice, str, List[str], Set[str], Series, DataFrame, Traffic]) –

  • if the key is an integer, will return a Flight object (in order of iteration);

  • if the key is a slice, will return a Traffic object (in order of iteration);

  • if the key is a string, will return a Flight object, based on the flight_id, icao24 or callsign;

  • if the key is a list of string, will return a Traffic object, based on the same criteria as above;

  • if the key is a pd.Series, will return a Flight object. The key must contain an icao24 feature. It may contain a callsign, a start (or firstSeen) timestamp, a stop (or lastSeen) timestamp. If it contains a flight_id column, this will be assigned to the Flight.

  • if the key is a pd.DataFrame, will return a Traffic object. The key must contain an icao24 feature. It may contain a callsign, a start (or firstSeen) timestamp, a stop (or lastSeen) timestamp. If it contains a flight_id column, this will be assigned to the Flight.

  • if the key is a Traffic object, will return a new Traffic collection, based on the flight_id elements present in key. If no flight_id is available, it will return the subset of trajectories in self that overlap with any trajectory in key (with the same icao24 indicator)

Return type:

Flight | Traffic

Returns:

According to the type of the key, the result could be a Flight or a Traffic object.

The following lets us get one or many trajectories:

# The first trajectory in the dataset
switzerland[0]
# The ten first trajectories in the dataset
switzerland[:10]
# The trajectory assigned with callsign ``EZY12VJ``
switzerland['EZY12VJ']

The DataFrame indexation can be useful for example with the following use case. We want to get full trajectories of aircraft entering the Swiss airspace at a particular hour. We first compute the statistics for each trajectory, then build a trajectory subcollection.

from traffic.data import eurofirs

stats = (
    switzerland.clip(eurofirs["LSAS"])
    .summary(["icao24", "callsign", "start", "stop"])
    .eval(max_workers=2)
)
stats
icao24 callsign start stop
0 34150f VLG86TM 2018-08-01 06:04:30+00:00 2018-08-01 06:19:30+00:00
1 341682 IBS3674 2018-08-01 19:24:10+00:00 2018-08-01 19:34:30+00:00
2 345101 VLG56RH 2018-08-01 18:37:30+00:00 2018-08-01 18:47:10+00:00
3 342441 IBE3122 2018-08-01 15:57:40+00:00 2018-08-01 16:15:20+00:00
4 04c117 KQA117 2018-08-01 20:43:30+00:00 2018-08-01 20:56:30+00:00
... ... ... ... ...
1218 a954ca CKS9557 2018-08-01 21:45:20+00:00 2018-08-01 21:56:20+00:00
1219 adf775 N9997X 2018-08-01 06:00:30+00:00 2018-08-01 06:14:00+00:00
1220 ab1deb N815WH 2018-08-01 10:08:00+00:00 2018-08-01 10:16:10+00:00
1221 e80444 LAN705 2018-08-01 18:31:10+00:00 2018-08-01 18:41:50+00:00
1222 c078b1 TSC300 2018-08-01 08:32:50+00:00 2018-08-01 08:41:30+00:00

1223 rows × 4 columns

# All trajectories quitting the airspace between 15:30 and 15:40
subset = stats.query('stop.dt.floor("10 min") == "2018-08-01 15:30Z"')
subset_1530 = switzerland[subset]  # subset is a pd.DataFrame
subset_1530

Traffic

with 13 identifiers
    count
icao24 callsign  
4006c8 BAW66GA 95
478771 NAX3YO 94
406ae0 TCX702 88
440005 EZY12VJ 88
3c6628 DLH52X 82
8005ec AIC143 81
4ca4ed RYR87WH 75
501d1d CTN476 66
484aa1 TRA93U 58
44064a EZY97QR 57

The start and stop timestamps are based on the clipping of the trajectories within the LSAS FIR boundaries. If we want to select the subset of the original trajectories matching those (shorter) selected in subset_1530, we can use the bracket operator again:

# We might prefer the full trajectories that have been matched in subset_1530
switzerland[subset_1530]  # subset is a Traffic object

Traffic

with 13 identifiers
    count
icao24 callsign  
440005 EZY12VJ 169
501d1d CTN476 159
8005ec AIC143 144
4006c8 BAW66GA 124
3c6628 DLH52X 114
4ca4ed RYR87WH 108
406ae0 TCX702 107
478771 NAX3YO 107
484aa1 TRA93U 98
4ca175 RYR31CR 98

Overlapping of trajectories with the & (and) operator

When applied on Flight structures, the & operator expresses the concurrency of two trajectories, i.e. the piece of trajectory that is flown while another aircraft is flying.

Flight.__and__(other)

Overlapping of trajectories.

Parameters:

other (Flight)

Return type:

None | Flight

Returns:

the segment of trajectory that overlaps other, if any.

from traffic.data.samples import dreamliner_airfrance

# expansion of the collection into two flights
video, dreamliner = dreamliner_airfrance
# the operator is not commutative
video & dreamliner | dreamliner & video

Flight

  • callsign: FWKDL
  • aircraft: 3900fb · 🇫🇷 F-WKDL (TBM7)
  • start: 2017-12-01 14:40:34+00:00
  • stop: 2017-12-01 15:59:58+00:00
  • duration: 0 days 01:19:24
  • sampling rate: 1 second(s)

Flight

  • callsign: AFR787V
  • aircraft: 39c424 · 🇫🇷 F-HRBE (B789)
  • start: 2017-12-01 14:40:34+00:00
  • stop: 2017-12-01 15:59:58+00:00
  • duration: 0 days 01:19:24
  • sampling rate: 1 second(s)

Intersection of collections with the & (and) operator

When applied on collections, the & operator returns the subset of trajectories that are present in both collections.

Traffic.__and__(other)

Intersection of collections.

Parameters:

other (Traffic)

Return type:

Optional[Traffic]

Returns:

the subset of trajectories present in both collections. The result is based on the flight_id if present, and on intervals otherwise.

# All trajectories entering or quitting the airspace between 15:30 and 15:40 through ODINA
result = through_odina & switzerland[subset_1530]
result

Traffic

with 2 identifiers
    count
icao24 callsign  
440005 EZY12VJ 169
4ca4ed RYR87WH 108
from traffic.data import navaids

ODINA = navaids['ODINA']
m = result.map_leaflet(
    center=ODINA.latlon,
    highlight=dict(red="aligned_on_navpoint('ODINA')")
)
m.add(ODINA)
m