Smoothing trajectories¶
To smooth trajectories, we can use a Kalman filter. The implemented KalmanSmootherCV is based on the assumption of a nearly-constant velocity (CV) model. To use KalmanSmootherCV, the optional dependency StoneSoup
needs to be installed.
A closely related type of operation is trajectory generalization which is coverd in a separate notebook.
In [ ]:
import pandas as pd
import geopandas as gpd
import movingpandas as mpd
import shapely as shp
import hvplot.pandas
import matplotlib.pyplot as plt
from geopandas import GeoDataFrame, read_file
from shapely.geometry import Point, LineString, Polygon
from datetime import datetime, timedelta
from holoviews import opts, dim
import warnings
warnings.filterwarnings('ignore')
plot_defaults = {'linewidth':5, 'capstyle':'round', 'figsize':(9,3), 'legend':True}
opts.defaults(opts.Overlay(active_tools=['wheel_zoom']))
hvplot_defaults = {'tiles':'CartoLight', 'frame_height':320, 'frame_width':320, 'cmap':'Viridis', 'colorbar':True}
mpd.show_versions()
MovingPandas 0.18.1 SYSTEM INFO ----------- python : 3.10.14 | packaged by conda-forge | (main, Mar 20 2024, 12:40:08) [MSC v.1938 64 bit (AMD64)] executable : c:\Users\Agarkovam\AppData\Local\miniforge3\envs\mpd-ex\python.exe machine : Windows-10-10.0.19045-SP0 GEOS, GDAL, PROJ INFO --------------------- GEOS : None GEOS lib : None GDAL : 3.8.5 GDAL data dir: None PROJ : 9.4.0 PROJ data dir: C:\Users\Agarkovam\AppData\Local\miniforge3\envs\mpd-ex\Library\share\proj PYTHON DEPENDENCIES ------------------- geopandas : 0.14.4 pandas : 2.2.2 fiona : 1.9.6 numpy : 1.26.4 shapely : 2.0.4 rtree : 1.2.0 pyproj : 3.6.1 matplotlib : 3.8.4 mapclassify: 2.6.1 geopy : 2.4.1 holoviews : 1.17.1 hvplot : 0.8.3 geoviews : 1.9.6 stonesoup : 1.2
In [ ]:
gdf = read_file('../data/geolife_small.gpkg')
tc = mpd.TrajectoryCollection(gdf, 'trajectory_id', t='t')
In [ ]:
split = mpd.ObservationGapSplitter(tc).split(gap=timedelta(minutes=15))
KalmanSmootherCV¶
This smoother operates on the assumption of a nearly-constant velocity (CV) model. The process_noise_std
and measurement_noise_std
parameters can be used to tune the smoother:
process_noise_std
governs the uncertainty associated with the adherence of the new (smooth) trajectories to the CV model assumption; higher values relax the assumption, therefore leading to less-smooth trajectories, and vice-versa.measurement_noise_std
controls the assumed error in the original trajectories; higher values dictate that the original trajectories are expected to be noisier (and therefore, less reliable), thus leading to smoother trajectories, and vice-versa.
Try tuning these parameters and observe the resulting trajectories:
In [ ]:
smooth = mpd.KalmanSmootherCV(split).smooth(process_noise_std=0.1, measurement_noise_std=10)
print(smooth)
TrajectoryCollection with 11 trajectories
In [ ]:
kwargs = {**hvplot_defaults, 'line_width':4}
(split.hvplot(title='Original Trajectories', **kwargs) +
smooth.hvplot(title='Smooth Trajectories', **kwargs))
Out[ ]:
In [ ]:
kwargs = {**hvplot_defaults, 'c':'speed', 'line_width':7, 'clim':(0,20)}
smooth.add_speed()
(split.trajectories[2].hvplot(title='Original Trajectory', **kwargs) +
smooth.trajectories[2].hvplot(title='Smooth Trajectory', **kwargs))
Out[ ]:
OutlierCleaner¶
In [ ]:
traj = split.trajectories[8]
cleaned = traj.copy()
cleaned = mpd.OutlierCleaner(cleaned).clean(alpha=2)
smoothed = mpd.KalmanSmootherCV(cleaned).smooth(process_noise_std=0.1, measurement_noise_std=10)
(traj.hvplot(title='Original Trajectory', **kwargs) +
cleaned.hvplot(title='Cleaned Trajectory', **kwargs) +
smoothed.hvplot(title='Cleaned & Smoothed Trajectory', **kwargs))
Out[ ]:
In [ ]: