snapToRoute static method

Map<String, dynamic> snapToRoute(
  1. LatLng gpsPos,
  2. List<LatLng> geometry, {
  3. double? userBearing,
})

Implementation

static Map<String, dynamic> snapToRoute(
  LatLng gpsPos,
  List<LatLng> geometry, {
  double? userBearing,
}) {
  if (geometry.isEmpty) return {};
  if (geometry.length == 1) {
    return {
      'snappedPos': geometry.first,
      'segmentIndex': 0,
      'distanceTraveled': 0.0,
      'offRouteDistance': haversineDistance(gpsPos, geometry.first),
    };
  }

  double minDistance = double.infinity;
  int bestIndex = 0;
  LatLng bestSnapped = geometry.first;

  for (int i = 0; i < geometry.length - 1; i++) {
    final p1 = geometry[i];
    final p2 = geometry[i + 1];

    final snapped = nearestPointOnSegment(gpsPos, p1, p2);
    double dist = haversineDistance(gpsPos, snapped);

    if (userBearing != null && haversineDistance(p1, p2) > 1.0) {
      final roadBearing = computeBearing(p1, p2);
      final diffRad = (userBearing - roadBearing) * math.pi / 180.0;
      final dotProduct = math.cos(diffRad);
      final penalty = (1.0 - dotProduct) * 40.0; // Up to 80m penalty
      dist += penalty;
    }

    if (dist < minDistance) {
      minDistance = dist;
      bestIndex = i;
      bestSnapped = snapped;
    }
  }

  double totalDist = 0.0;
  for (int i = 0; i < bestIndex; i++) {
    totalDist += haversineDistance(geometry[i], geometry[i + 1]);
  }
  totalDist += haversineDistance(geometry[bestIndex], bestSnapped);

  final trueOffRouteDist = haversineDistance(gpsPos, bestSnapped);

  return {
    'snappedPos': bestSnapped,
    'segmentIndex': bestIndex,
    'distanceTraveled': totalDist,
    'offRouteDistance': trueOffRouteDist,
  };
}