{ "cells": [ { "cell_type": "markdown", "id": "99c9b3f9", "metadata": {}, "source": [ "# Lachesis Stop Detection" ] }, { "cell_type": "markdown", "id": "d4284375", "metadata": {}, "source": [ "The first stop detection algorithm implemented in ```nomad``` is a sequential algorithm insipired by the one in _Project Lachesis: Parsing and Modeling Location Histories_ (Hariharan & Toyama). This algorithm for extracting stays is dependent on two parameters: the roaming distance and the stay duration. \n", "\n", "* Roaming distance represents the maximum distance an object can move away from a point location and still be considered to be staying at that location.\n", "* Stop duration is the minimum amount of time an object must spend within the roaming distance of a location to qualify as a stop.\n", "\n", "The algorithm identifies stops as contiguous sequences of pings that stay within the roaming distance for at least the duration of the stop duration.\n", "\n", "This algorithm has the following parameters, which determine the size of the resulting stops:\n", "* ```dur_min```: Minimum duration for a stay in minutes.\n", "* ```dt_max```: Maximum time gap permitted between consecutive pings in a stay in minutes (dt_max should be greater than dur_min).\n", "* ```delta_roam```: Maximum roaming distance for a stay in meters." ] }, { "cell_type": "code", "execution_count": 1, "id": "20befc65", "metadata": { "execution": { "iopub.execute_input": "2026-08-13T02:17:45.896420Z", "iopub.status.busy": "2026-08-13T02:17:45.896420Z", "iopub.status.idle": "2026-08-13T02:17:48.179005Z", "shell.execute_reply": "2026-08-13T02:17:48.179005Z" } }, "outputs": [], "source": [ "# %matplotlib inline\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "\n", "# Imports\n", "import nomad.io.base as loader\n", "import geopandas as gpd\n", "from shapely.geometry import box\n", "from nomad.stop_detection.viz import plot_stops_barcode, plot_time_barcode, plot_stops, plot_pings\n", "from nomad.stop_detection.sequential_algs import lachesis\n", "import nomad.data as data_folder\n", "from pathlib import Path\n", "\n", "# Load data\n", "data_dir = Path(data_folder.__file__).parent\n", "city = gpd.read_parquet(data_dir / 'garden-city-buildings-mercator.parquet')\n", "outer_box = box(*city.total_bounds)\n", "\n", "filepath_root = data_dir / \"gc_data_long\"\n", "tc = {\"user_id\": \"gc_identifier\", \"x\": \"dev_x\", \"y\": \"dev_y\", \"timestamp\": \"unix_ts\"}\n", "\n", "users = ['admiring_brattain']\n", "traj = loader.sample_from_file(filepath_root, format='parquet', users=users, filters=('date','==', '2024-01-01'), traj_cols=tc)\n", "\n", "# Lachesis (sequential stop detection)\n", "stops = lachesis(traj, delta_roam=20, dt_max = 60, dur_min=5, complete_output=True, keep_col_names=True, traj_cols=tc)" ] }, { "cell_type": "code", "execution_count": 2, "id": "438253fa", "metadata": { "execution": { "iopub.execute_input": "2026-08-13T02:17:48.182006Z", "iopub.status.busy": "2026-08-13T02:17:48.181006Z", "iopub.status.idle": "2026-08-13T02:17:48.402817Z", "shell.execute_reply": "2026-08-13T02:17:48.402817Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "C:\\Users\\franc\\AppData\\Local\\Temp\\ipykernel_19104\\305457037.py:15: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown\n", " plt.show()\n" ] } ], "source": [ "fig, (ax_map, ax_barcode) = plt.subplots(2, 1, figsize=(6,6.5),\n", " gridspec_kw={'height_ratios':[10,1]})\n", "\n", "gpd.GeoDataFrame(geometry=[outer_box], crs='EPSG:3857').plot(ax=ax_map, color='#d3d3d3')\n", "city.plot(ax=ax_map, edgecolor='white', linewidth=1, color='#8c8c8c')\n", "\n", "plot_stops(stops, ax=ax_map, cmap='Blues')\n", "plot_pings(traj, ax=ax_map, s=6, color='black', alpha=0.5, traj_cols=tc)\n", "ax_map.set_axis_off()\n", "\n", "plot_time_barcode(traj['unix_ts'], ax=ax_barcode, set_xlim=True)\n", "plot_stops_barcode(stops, ax=ax_barcode, cmap='Blues', set_xlim=False, timestamp='unix_ts')\n", "\n", "plt.tight_layout(pad=0.1)\n", "plt.show()" ] } ], "metadata": { "jupytext": { "cell_metadata_filter": "all", "formats": "ipynb,py:percent" }, "kernelspec": { "display_name": "base", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.0" } }, "nbformat": 4, "nbformat_minor": 5 }