flutteropenstreetmapfluttermap

Flutter Map Polyline and current user movement. How to hide distance cover in the polyline while navigating


I'm building a flutter navigation app using flutter_map, routing_client_dart and openstreetMap. I've managed to listen to live location and update it accordingly. Currently I'm displaying the polyline using as follows

PolylineLayer(
  polylines: [
    Polyline(
      points: mapsServices.road!.polyline!
               .map((point) =>LatLng(point.lat, point.lng)).toList(),
      color: Colors.blue,
      strokeWidth: 8,
      borderStrokeWidth: 3,
      borderColor: Colors.black.withValues(alpha: 0.3),
     ),
 ],
),

I want the polyline to move along with the user, i.e., remove part of the polyline the use has traveled but I just cant get it right. my best attempt was to sublist the polyline as in the snippet bellow but it didn't produce the required result. Sometime the polyline will be far ahead and sometimes behind the user's location.

List<LngLat> updatePolyline(LatLng currentLocation) {
    if (road == null || road!.polyline == null) return [];

    List<LngLat> polylinePointsLngLat = road!.polyline!;
    List<LatLng> polylinePoints = polylinePointsLngLat
        .map((LngLat lngLat) => LatLng(lngLat.lat, lngLat.lng))
        .toList();

    bool isUserOnPath =
        manager.isOnPath(road!, currentLocation.toLngLat(), tolerance: 10);

    if (isUserOnPath) {
      LatLng closestPoint = findClosestPoint(currentLocation, polylinePoints);
      int index = polylinePoints.indexOf(closestPoint);

      if (index != -1) {
        List<LngLat> updatedPolyline = polylinePointsLngLat.sublist(index);
        updatedPolyline.insert(0, currentLocation.toLngLat());
        return updatedPolyline;
      }
    }

    return polylinePointsLngLat;
  }

  LatLng findClosestPoint(LatLng userLocation, List<LatLng> polyline) {
    final Distance distance = Distance();
    LatLng closestPoint = polyline.first;
    double minDistance = distance(userLocation, polyline.first);

    for (var point in polyline) {
      double dist = distance(userLocation, point);
      if (dist < minDistance) {
        minDistance = dist;
        closestPoint = point;
      }
    }
    return closestPoint;
  }

  List<LatLng> trimPolyline(List<LatLng> polyline, LatLng userLocation) {
    LatLng closestPoint = findClosestPoint(userLocation, polyline);
    int index = polyline.indexOf(closestPoint);
    if (index != -1) {
      return polyline.sublist(index);
    }
    return polyline;
  }

Solution

  • This is the solution i came up with. we were initially using the closest point as the polyline start but that isnt necessaryly at the same point with the user so i thought it would make sense to insert a point on the polyline at the point where the user is and the sublist from that point.

    I you forsee an improvement that can be made on this code. let me know.

     List<LngLat> updatePolyline(LatLng currentLocation) {
        if (road == null || road!.polyline!.isEmpty) return [];
    
        final fullPolyline =
            road!.polyline!.map((p) => LatLng(p.lat, p.lng)).toList();
        final (insertIndex, projectedPoint) =
            _findClosestPerpendicularPoint(currentLocation, fullPolyline);
    
        fullPolyline.insert(insertIndex, projectedPoint);
        final trimmedPolyline = fullPolyline.sublist(insertIndex);
    
        return trimmedPolyline
            .map((p) => LngLat(lng: p.longitude, lat: p.latitude))
            .toList();
      }
    
      (int, LatLng) _findClosestPerpendicularPoint(
          LatLng userLocation, List<LatLng> polyline) {
        final distanceCalculator = Distance();
        double minDistance = double.infinity;
        int closestSegmentIndex = 0;
        LatLng closestProjectedPoint = polyline.first;
    
        for (int i = 0; i < polyline.length - 1; i++) {
          final p1 = polyline[i];
          final p2 = polyline[i + 1];
    
          final projectedPoint = _getPerpendicularPoint(userLocation, p1, p2);
          final distance = distanceCalculator(userLocation, projectedPoint);
    
          if (distance < minDistance) {
            minDistance = distance;
            closestSegmentIndex = i;
            closestProjectedPoint = projectedPoint;
          }
        }
    
        return (closestSegmentIndex + 1, closestProjectedPoint);
      }
    
      LatLng _getPerpendicularPoint(LatLng p, LatLng a, LatLng b) {
        double ax = a.latitude, ay = a.longitude;
        double bx = b.latitude, by = b.longitude;
        double px = p.latitude, py = p.longitude;
    
        double dx = bx - ax;
        double dy = by - ay;
        double t = ((px - ax) * dx + (py - ay) * dy) / (dx * dx + dy * dy);
    
        t = t.clamp(0.0, 1.0);
    
        double lat = ax + t * dx;
        double lon = ay + t * dy;
    
        return LatLng(lat, lon);
      }