findNearestRouteIndex method

int findNearestRouteIndex(
  1. LatLng tapLocation, {
  2. double maxDistanceMeters = 35.0,
  3. double? zoom,
  4. double touchRadiusPixels = 56.0,
})

Finds the route index whose polyline is closest to tapLocation.

Uses screen-space Web Mercator projection to measure distance in pure screen pixels (default 48.0px touch target radius). This ensures 100% tap accuracy across all zoom levels and platforms without DPI discrepancies.

Implementation

int findNearestRouteIndex(
  LatLng tapLocation, {
  double maxDistanceMeters = 35.0,
  double? zoom,
  double touchRadiusPixels = 56.0,
}) {
  if (_currentRoutes.isEmpty) return -1;

  final effectiveZoom = (zoom != null && zoom > 0)
      ? zoom
      : (_controller.cameraPosition?.zoom ?? 12.0);
  final tapPx = _latLngToMercatorPixels(tapLocation, effectiveZoom);

  // Calculate minimum pixel distance from tap to each route
  final routePixelDistances = List<double>.filled(
    _currentRoutes.length,
    double.infinity,
  );
  for (int i = 0; i < _currentRoutes.length; i++) {
    final geom = _currentRoutes[i].geometry;
    if (geom.length < 2) continue;

    var prevPx = _latLngToMercatorPixels(geom[0], effectiveZoom);
    for (int j = 1; j < geom.length; j++) {
      final nextPx = _latLngToMercatorPixels(geom[j], effectiveZoom);

      // Fast bounding box reject
      final minX = math.min(prevPx.x, nextPx.x) - touchRadiusPixels;
      final maxX = math.max(prevPx.x, nextPx.x) + touchRadiusPixels;
      final minY = math.min(prevPx.y, nextPx.y) - touchRadiusPixels;
      final maxY = math.max(prevPx.y, nextPx.y) + touchRadiusPixels;
      if (tapPx.x >= minX &&
          tapPx.x <= maxX &&
          tapPx.y >= minY &&
          tapPx.y <= maxY) {
        final d = _pointToSegmentDistancePixels(tapPx, prevPx, nextPx);
        if (d < routePixelDistances[i]) {
          routePixelDistances[i] = d;
        }
      }
      prevPx = nextPx;
    }
  }

  // 1. Check for alternative routes within touch radius
  int? bestAltIndex;
  double bestAltDist = double.infinity;

  for (int i = 0; i < _currentRoutes.length; i++) {
    if (i != _selectedRouteIndex &&
        routePixelDistances[i] <= touchRadiusPixels) {
      if (routePixelDistances[i] < bestAltDist) {
        bestAltDist = routePixelDistances[i];
        bestAltIndex = i;
      }
    }
  }

  final selectedDist =
      (_selectedRouteIndex >= 0 &&
          _selectedRouteIndex < routePixelDistances.length)
      ? routePixelDistances[_selectedRouteIndex]
      : double.infinity;

  // 2. If ANY alternative route was hit within touch target, SWITCH TO IT!
  // On touchscreens, when multiple routes run in close proximity (parallel roads/forks),
  // the user's finger covers both lines. The user's explicit intent when tapping the route
  // bundle in preview mode is to select the alternative route.
  if (bestAltIndex != null) {
    return bestAltIndex;
  }

  // 3. If only the selected route was hit:
  if (selectedDist <= touchRadiusPixels) {
    return _selectedRouteIndex;
  }

  return -1;
}