feat: add OSRM road-snapping for GPS routes and mileage (#21)

Routes now follow actual roads instead of cutting through buildings:
- New OsrmService calls free OSRM Match API to snap GPS points to roads
- Position history endpoint accepts ?matched=true for road-snapped geometry
- Stats use OSRM road distance instead of Haversine crow-flies distance
- Frontend shows solid blue polylines for matched routes, dashed for raw
- Handles chunking (100 coord limit), rate limiting, graceful fallback
- Distance badge shows accurate road miles on route trails

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 17:03:47 +01:00
parent d93919910b
commit 33fda57cc6
6 changed files with 351 additions and 16 deletions

View File

@@ -132,6 +132,7 @@ export class GpsController {
/**
* Get a driver's location history (for route trail display)
* Query param 'matched=true' returns OSRM road-snapped route
*/
@Get('locations/:driverId/history')
@Roles(Role.ADMINISTRATOR)
@@ -139,9 +140,17 @@ export class GpsController {
@Param('driverId') driverId: string,
@Query('from') fromStr?: string,
@Query('to') toStr?: string,
@Query('matched') matched?: string,
) {
const from = fromStr ? new Date(fromStr) : undefined;
const to = toStr ? new Date(toStr) : undefined;
// If matched=true, return OSRM road-matched route
if (matched === 'true') {
return this.gpsService.getMatchedRoute(driverId, from, to);
}
// Otherwise return raw GPS points
return this.gpsService.getDriverLocationHistory(driverId, from, to);
}