Demo 4: Detecting Rendezvous with DBSCAN
Using Tracktable’s box-DBSCAN capabilities, we can detect trajectories that are traveling together for part or all of their journeys.
Algorithm Details
First, we decide during which portion of the journeys we want to detect rendezvous behavior. For instance, we can say from 40% to 60% of the trajectory length, so we will detect any trajectories that are together from 40% to 60% of their travels, or we could just leave this as the default of 0% to 100%, which will only detect trajectories that travel together during the entire duration of their journeys. For every trajectory, we create a feature vector, which we call the rendezvous feature vector, by taking a given number of equally-spaced points along the trajectory (during the portion we specified) and concatenating the longitude, latitude and timestamp from all of these points into a single vector. By default, we use 10 equally-spaced points along the trajectory, making our feature vectors 30-dimensional. Using box-DBSCAN, Tracktable clusters trajectories together based on the similarity of their feature vectors. Trajectories that travel together during the specified portion of their trajectories will occupy the same cluster.
In [1]:
from tracktable.render.render_trajectories import render_trajectories
import tracktable.examples.tutorials.tutorial_helper as tutorial
from tracktable.applications.cluster import *
Import Trajectories
We will use some sample maritime data for this demo, obtained from BOEM/NOAA.\(^1\)
In [2]:
trajectories = tutorial.get_trajectory_list('rendezvous')
Loading Trajectories: 125 trajectory [00:00, 1691.10 trajectory/s]
[2025-06-11 13:10:40.862987] [0x00000001f1aadf00] [info] Read a total of 125 trajectories.
Filter Idle Subtrajectories
Rendezvous detection relies on comparing the spatiotemporal information at a given number of points equally spaced along a portion of the trajectory to identify trajectories that travel together. If a boat stays mostly in port and travels for a short time, our equally-spaced points may miss the traveling behavior. Therefore, to make our rendezvous detection more effective, we delete portions of trajectories that are sitting idle, possibly splitting the trajectory into multiple trajectories if the trajectory moves, is idle, and then moves again.
In [3]:
trimmed_trajectories = tutorial.split_trajectories(trajectories)
Let’s also trim very short trajectories (less than 5km traveled) from our dataset.
In [4]:
filtered_trajectories = tutorial.filter_trajectories(trimmed_trajectories, min_length=5)
Option 1: Find trajectories that travel together from start to finish.
Now that we have a list of filtered trajectories, we can use Tracktable to create feature vectors for each, then use box-DBSCAN to cluster over those feature vectors.
By default, we create our feature vector using given number of equally-spaced points along the entire trajectory and concatenate the longitude, latitude and timestamp from all of these points into a single 30-dimensional vector.
In [5]:
clusters = cluster_trajectories_rendezvous(filtered_trajectories)
The clusters dictionary contains cluster numbers as keys, and a list of trajectories in that cluster for each corresponding value. These clusters and their sizes are printed above.
In our trajectory dataset, we have found one cluster, meaning one set of trajectories that was traveling together for their entire journeys. We will render this cluster below.
In [6]:
render_trajectories(clusters[1])
Out[6]:
The trajectories that did not cluster (outliers) are stored under key 0 of the clusters dictionary. Let’s look at the first ten of these trajectories that are not traveling with any others.
In [7]:
render_trajectories(clusters[0][:10])
Out[7]:
Option 2: Find trajectories that travel together for only a portion of their travel.
Alternatively, we can select a start and end fraction (proportional to trajectory length) that we wish to examine for rendezvous behavior. In the example below, we look for trajectories that are traveling together from 60% to 80% of their travels.
In [8]:
clusters = cluster_trajectories_rendezvous(filtered_trajectories, start_fraction=0.6, end_fraction=0.8)
Now we have found three clusters, meaning three sets of trajectories that are rendezvousing during the 60% to 80% portion of their trajectories. Let’s render each cluster below.
Note: The cluster that was found in the previous example, containing two trajectories that were traveling together during their entire journeys, will also be a cluster here, as they will also be traveling together during the 60% to 80% portion of their trajectories.
In [9]:
render_trajectories(clusters[1])
Out[9]:
In [10]:
render_trajectories(clusters[2])
Out[10]:
In [11]:
render_trajectories(clusters[3])
Out[11]:
\(^1\) Bureau of Ocean Energy Management (BOEM) and National Oceanic and Atmospheric Administration (NOAA). MarineCadastre.gov. AIS Data for 2020. Retrieved February 2021 from marinecadastre.gov/data. Data is for June 4-6, 2020 martime traffic near Virginia Beach.