GPS Trip Detection, History & Playback #23

Closed
opened 2026-02-08 08:37:16 -08:00 by kyle · 4 comments
Owner

Trip Detection, History & Playback

Problem

  • GPS routes are shown as one continuous blob with no concept of individual trips
  • Old routes clutter the live map
  • No way to review historical trips or compare them
  • OSRM route calculations are slow when done on-the-fly (computed at request time)

Solution

Pre-compute trips from GPS data, store them in the database, and build a rich trip history UI with playback.


Phase 1: Database & Trip Detection Engine

Schema: New GpsTrip model

  • id, deviceId, driverId
  • startTime, endTime
  • startLatitude, startLongitude, endLatitude, endLongitude
  • distanceMiles (OSRM road distance, pre-computed)
  • durationMinutes
  • routeGeometry (JSON - pre-computed OSRM coordinates for instant display)
  • confidence (OSRM match confidence)
  • pointCount (number of GPS points in trip)
  • status: ACTIVE | COMPLETED | MERGED
  • mergedIntoId (for consolidated trips)
  • createdAt, updatedAt

Trip Detection Cron Job (runs every 2 minutes)

  • Scans GPS history for each active device
  • Trip starts: driver moves >100m from stationary position
  • Trip ends: driver stationary >5 minutes
  • When a trip is detected as complete:
    1. Create GpsTrip record with start/end times and locations
    2. Call OSRM to get road-snapped route + accurate distance
    3. Store pre-computed route geometry in the trip record
    4. Mark trip as COMPLETED
  • Only processes NEW data since last check (not full history rescan)

Trip Consolidation Endpoint

  • POST /gps/trips/merge - merge multiple trip IDs into one
  • Updates status to MERGED on child trips, recalculates distance/duration on parent
  • Use case: driver made a quick gas stop that split one logical journey into two trips

Phase 2: Backend API Endpoints

Trip CRUD

  • GET /gps/trips?driverId=X&from=DATE&to=DATE - list trips with filters
  • GET /gps/trips/:tripId - single trip with full route geometry
  • GET /gps/trips/active?driverId=X - get currently active trip (for live map)
  • POST /gps/trips/merge - consolidate trips
  • GET /gps/trips/summary?driverId=X&from=DATE&to=DATE - aggregate stats (total miles, trips, driving time)

Key design: route geometry is PRE-COMPUTED and stored in the trip record.
No more on-the-fly OSRM calls when viewing trips = instant load.


Phase 3: Live Map Cleanup

Current behavior: Shows all GPS history as one continuous trail
New behavior:

  • Live map shows ONLY markers + active trip trail (currently driving or stopped <5 min)
  • No completed trip routes on the live map
  • Active trip uses OSRM route (fetched from /gps/trips/active)
  • When driver stops and trip completes, trail fades from live map

Phase 4: Driver Statistics Overhaul

Trip List Side Panel

  • When a driver is selected in Stats tab, show a side panel with their trips
  • Each trip row shows:
    • Time range (e.g., "7:55 AM - 8:12 AM")
    • Duration (e.g., "17 min")
    • Distance (e.g., "12.3 mi")
    • Toggle checkbox to show/hide on map
  • Last (most recent) trip is auto-toggled ON, others off by default
  • Date picker to view different days
  • Daily totals at top: total miles, total driving time, trip count
  • "Merge Trips" button: select 2+ trips to consolidate

Trip Map

  • Embedded map on the Stats page showing selected trip routes
  • Each toggled trip drawn in a different color
  • Start/end markers for each trip

Phase 5: Trip Playback

Playback Controls (on the Stats map)

  • Play/Pause button - animates a marker along the trip route
  • Speed controls: 1x, 2x, 5x, 10x fast-forward
  • Rewind / skip backward
  • Timeline scrubber bar showing trip progress
  • Current speed and timestamp displayed during playback
  • Works with OSRM road-snapped route (smooth animation along roads)

Implementation: Animate a marker along the pre-computed route coordinates using requestAnimationFrame, interpolating between points based on playback speed.


Implementation Order

  1. Phase 1 - Schema + trip detection cron (backend only)
  2. Phase 2 - API endpoints (backend only)
  3. Phase 3 - Live map cleanup (frontend)
  4. Phase 4 - Stats page trip list + map (frontend)
  5. Phase 5 - Playback controls (frontend)

Each phase is independently deployable and testable.

## Trip Detection, History & Playback ### Problem - GPS routes are shown as one continuous blob with no concept of individual trips - Old routes clutter the live map - No way to review historical trips or compare them - OSRM route calculations are slow when done on-the-fly (computed at request time) ### Solution Pre-compute trips from GPS data, store them in the database, and build a rich trip history UI with playback. --- ## Phase 1: Database & Trip Detection Engine **Schema: New `GpsTrip` model** - `id`, `deviceId`, `driverId` - `startTime`, `endTime` - `startLatitude`, `startLongitude`, `endLatitude`, `endLongitude` - `distanceMiles` (OSRM road distance, pre-computed) - `durationMinutes` - `routeGeometry` (JSON - pre-computed OSRM coordinates for instant display) - `confidence` (OSRM match confidence) - `pointCount` (number of GPS points in trip) - `status`: `ACTIVE` | `COMPLETED` | `MERGED` - `mergedIntoId` (for consolidated trips) - `createdAt`, `updatedAt` **Trip Detection Cron Job** (runs every 2 minutes) - Scans GPS history for each active device - Trip starts: driver moves >100m from stationary position - Trip ends: driver stationary >5 minutes - When a trip is detected as complete: 1. Create `GpsTrip` record with start/end times and locations 2. Call OSRM to get road-snapped route + accurate distance 3. Store pre-computed route geometry in the trip record 4. Mark trip as `COMPLETED` - Only processes NEW data since last check (not full history rescan) **Trip Consolidation Endpoint** - `POST /gps/trips/merge` - merge multiple trip IDs into one - Updates `status` to `MERGED` on child trips, recalculates distance/duration on parent - Use case: driver made a quick gas stop that split one logical journey into two trips --- ## Phase 2: Backend API Endpoints **Trip CRUD** - `GET /gps/trips?driverId=X&from=DATE&to=DATE` - list trips with filters - `GET /gps/trips/:tripId` - single trip with full route geometry - `GET /gps/trips/active?driverId=X` - get currently active trip (for live map) - `POST /gps/trips/merge` - consolidate trips - `GET /gps/trips/summary?driverId=X&from=DATE&to=DATE` - aggregate stats (total miles, trips, driving time) **Key design: route geometry is PRE-COMPUTED and stored in the trip record.** No more on-the-fly OSRM calls when viewing trips = instant load. --- ## Phase 3: Live Map Cleanup **Current behavior:** Shows all GPS history as one continuous trail **New behavior:** - Live map shows ONLY markers + active trip trail (currently driving or stopped <5 min) - No completed trip routes on the live map - Active trip uses OSRM route (fetched from `/gps/trips/active`) - When driver stops and trip completes, trail fades from live map --- ## Phase 4: Driver Statistics Overhaul **Trip List Side Panel** - When a driver is selected in Stats tab, show a side panel with their trips - Each trip row shows: - Time range (e.g., "7:55 AM - 8:12 AM") - Duration (e.g., "17 min") - Distance (e.g., "12.3 mi") - Toggle checkbox to show/hide on map - **Last (most recent) trip is auto-toggled ON**, others off by default - Date picker to view different days - Daily totals at top: total miles, total driving time, trip count - "Merge Trips" button: select 2+ trips to consolidate **Trip Map** - Embedded map on the Stats page showing selected trip routes - Each toggled trip drawn in a different color - Start/end markers for each trip --- ## Phase 5: Trip Playback **Playback Controls** (on the Stats map) - Play/Pause button - animates a marker along the trip route - Speed controls: 1x, 2x, 5x, 10x fast-forward - Rewind / skip backward - Timeline scrubber bar showing trip progress - Current speed and timestamp displayed during playback - Works with OSRM road-snapped route (smooth animation along roads) **Implementation:** Animate a marker along the pre-computed route coordinates using requestAnimationFrame, interpolating between points based on playback speed. --- ## Implementation Order 1. **Phase 1** - Schema + trip detection cron (backend only) 2. **Phase 2** - API endpoints (backend only) 3. **Phase 3** - Live map cleanup (frontend) 4. **Phase 4** - Stats page trip list + map (frontend) 5. **Phase 5** - Playback controls (frontend) Each phase is independently deployable and testable.
Author
Owner

Implemented in cc3375e. All 5 phases complete:

Backend:

  • GpsTrip model + migration (TripStatus: ACTIVE/COMPLETED/PROCESSING/FAILED)
  • Auto-detect trips in syncPositions cron (5-min idle threshold)
  • Pre-compute OSRM routes on trip completion
  • API endpoints: list/detail/active/merge/backfill

Frontend:

  • Stats tab overhaul: trip list side panel + map with colored polylines per trip
  • Trip playback: animated marker, progressive trail, 1x-16x speed controls
  • Live map shows only active trip trail (not full day history)
  • Trip merge UI and historical backfill button

Deployed to production - trip detection already working live.

Implemented in cc3375e. All 5 phases complete: **Backend:** - GpsTrip model + migration (TripStatus: ACTIVE/COMPLETED/PROCESSING/FAILED) - Auto-detect trips in syncPositions cron (5-min idle threshold) - Pre-compute OSRM routes on trip completion - API endpoints: list/detail/active/merge/backfill **Frontend:** - Stats tab overhaul: trip list side panel + map with colored polylines per trip - Trip playback: animated marker, progressive trail, 1x-16x speed controls - Live map shows only active trip trail (not full day history) - Trip merge UI and historical backfill button Deployed to production - trip detection already working live.
kyle closed this issue 2026-02-08 09:29:58 -08:00
Author
Owner

positions.txt Here was a recent trip. But the image.pngperhaps we need to reimagine how the trips are begun and ended, we can't have that many trips to consolidate, there has to be an easy way to decide what a 'trip' is. I don't know why it made so many. See if you can fix this so it does not have SOOO many trips when it was obviously 1. trip there and 1 trip back.

[positions.txt](/attachments/8b19dbd6-04af-4b62-8e65-4c01dabe9d57) Here was a recent trip. But the <img width="280" alt="image.png" src="attachments/5c48513d-c2dc-4238-b837-c72c8bb8fbbd">perhaps we need to reimagine how the trips are begun and ended, we can't have that many trips to consolidate, there has to be an easy way to decide what a 'trip' is. I don't know why it made so many. See if you can fix this so it does not have SOOO many trips when it was obviously 1. trip there and 1 trip back.
kyle reopened this issue 2026-02-08 09:43:25 -08:00
Author
Owner

Fixed trip detection algorithm:

  1. Idle threshold 5min ? 10min - 11-min destination stop correctly splits into 2 trips, brief GPS gaps don't cause splits
  2. No more overlapping trips - only starts new trips from positions AFTER previous trip ended
  3. Duplicate prevention - guards against creating trips at same timestamp
  4. Micro-trip cleanup - auto-deletes trips < 0.1 mi or < 60 seconds
  5. Smart idle detection - uses GPS timestamps not wall clock, handles sparse data

Cleared all bad trips, redeployed. Use Backfill button in Stats tab to regenerate.

Fixed trip detection algorithm: 1. **Idle threshold 5min ? 10min** - 11-min destination stop correctly splits into 2 trips, brief GPS gaps don't cause splits 2. **No more overlapping trips** - only starts new trips from positions AFTER previous trip ended 3. **Duplicate prevention** - guards against creating trips at same timestamp 4. **Micro-trip cleanup** - auto-deletes trips < 0.1 mi or < 60 seconds 5. **Smart idle detection** - uses GPS timestamps not wall clock, handles sparse data Cleared all bad trips, redeployed. Use Backfill button in Stats tab to regenerate.
Author
Owner

Simplified: Now using Traccar built-in trip detection. Removed ~490 lines of custom detection code. Traccar correctly returns 3 trips vs our broken 5+. Added geocoded addresses to trip cards. Commit: 53eb82c

Simplified: Now using Traccar built-in trip detection. Removed ~490 lines of custom detection code. Traccar correctly returns 3 trips vs our broken 5+. Added geocoded addresses to trip cards. Commit: 53eb82c
kyle closed this issue 2026-02-08 10:44:01 -08:00
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kyle/vip-coordinator#23