Run: panel serve .\stopdetection-app.ipynb --show --autoreload
import movingpandas as mpd
import os
import pandas as pd
import panel as pn
import warnings
from datetime import timedelta
from geopandas import read_file
warnings.filterwarnings('ignore')
try:
gdf = read_file('../data/geolife_small.gpkg')
except:
gdf = read_file(os.path.join(os.getcwd(),'data/geolife_small.gpkg'))
traj_collection = mpd.TrajectoryCollection(gdf, 'trajectory_id', t='t')
traj_collection = mpd.MinTimeDeltaGeneralizer(traj_collection).generalize(tolerance=timedelta(seconds=30))
def get_traj(traj_id=1, smooth=False):
my_traj = traj_collection.get_trajectory(traj_id)
if smooth:
smoother = mpd.trajectory_smoother.KalmanSmootherCV(my_traj)
my_traj = smoother.smooth(process_noise_std=0.1, measurement_noise_std=10)
return my_traj
def detect_stops(my_traj, max_diameter=20, min_duration=1):
detector = mpd.TrajectoryStopDetector(my_traj)
stop_points = detector.get_stop_points(min_duration=timedelta(minutes=min_duration), max_diameter=max_diameter)
return stop_points
def make_plot(my_traj, stop_points):
traj_plot = my_traj.hvplot(title='Trajectory {}'.format(my_traj.id), line_width=5.0, tiles='CartoLight', color='slategray', frame_width=400, frame_height=400)
if len(stop_points) == 0:
return traj_plot
map_plot = traj_plot * stop_points.hvplot(geo=True, size='duration_s', color='deeppink', alpha=0.7, hover_cols='all')
stop_points['duration_min'] = round(stop_points['duration_s'] / 60, 1)
table = pd.DataFrame(stop_points).hvplot.table(columns=['start_time', 'duration_min'], sortable=True, frame_width=200)
return (map_plot + table).cols(2)
def get_info(my_traj, stop_points, smooth=False):
smooth = 'smoothed' if smooth else 'original'
stops = 'stop' if len(stop_points)==1 else 'stops'
md_pane = pn.pane.Markdown(
f"Trajectory {my_traj.id} ({smooth})\n\n"+
f"# {len(stop_points)} {stops}"
)
return md_pane
pn.extension(template="fast")
pn.state.template.param.update(site="MovingPandas", title="Stop Detection App", accent_base_color="#A01346", header_background="#A01346")
pn.config.sizing_mode = "stretch_width"
traj_select = pn.widgets.Select(name='Trajectory ID', options=[i for i in range(1,6)]).servable(target="sidebar")
pn.pane.Markdown("## Settings").servable(target="sidebar")
max_diameter_slider = pn.widgets.IntSlider(name='Maximum stop diameter (meters)', start=20, end=200, step=20).servable(target="sidebar")
min_duration_slider = pn.widgets.IntSlider(name='Minimum stop duration (minutes)', start=1, end=10, step=1).servable(target="sidebar")
pn.pane.Markdown("### Preprocessing").servable(target="sidebar")
smooth_checkbox = pn.widgets.Checkbox(name='Smooth trajectory with Kalman filter').servable(target="sidebar")
pn.pane.Markdown("Powered by:").servable(target="sidebar")
mpd_logo = pn.pane.PNG('https://anitagraser.github.io/movingpandas/assets/img/movingpandas.png', height=100, align='center')
pn_logo = pn.pane.PNG('https://panel.holoviz.org/_static/logo_stacked.png', height=100, align='center')
logo = pn.Row(mpd_logo, pn_logo).servable(target="sidebar")
my_traj = pn.bind(get_traj, traj_select, smooth_checkbox)
stop_points = pn.bind(detect_stops, my_traj, max_diameter_slider, min_duration_slider)
map_plot = pn.bind(make_plot, my_traj, stop_points)
info_pane = pn.bind(get_info, my_traj, stop_points, smooth_checkbox)
pn.panel(info_pane).servable(target="main")
pn.panel(map_plot).servable(target="main")