You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于flutter_map的导航应用:如何隐藏Polyline已行驶路段

Flutter导航Polyline随用户移动动态截断的问题解决

我基于flutter_map、routing_client_dart和OpenStreetMap开发导航应用,已经实现实时位置监听,但在动态截断Polyline(移除已行驶路段)时遇到了问题:使用截取子列表的方法会导致Polyline超前或滞后于用户位置。

原始Polyline显示代码:

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),
     ),
 ],
),

尝试的截断代码(效果不符合预期):

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;
  }

原始代码问题分析

  1. 仅匹配离散点:findClosestPoint只寻找Polyline上距离用户最近的原始节点,而用户实际位置往往在两个节点之间的线段上,直接取节点索引截断必然出现偏差。
  2. 忽略行驶方向:如果用户短暂偏离路线后返回,可能匹配到路径后方的节点,导致错误截断已行驶路段。
  3. 破坏路径连续性:直接将用户位置插入Polyline头部,会导致路径出现突兀的转折,影响视觉效果。

修正方案:基于线段投影与累积距离的精准截断

通过计算用户在路径上的投影位置和前进距离,实现精准的Polyline截断:

1. 核心工具函数:计算用户在路径上的前进距离

import 'package:latlong2/latlong.dart';

double calculateProgressDistance(LatLng userLocation, List<LatLng> polyline) {
  double totalDistance = 0.0;
  final Distance distance = Distance();

  for (int i = 0; i < polyline.length - 1; i++) {
    final segmentStart = polyline[i];
    final segmentEnd = polyline[i + 1];

    // 计算用户到当前线段的投影点
    final projection = distance.projectOnSegment(userLocation, segmentStart, segmentEnd);
    if (projection.isOnSegment) {
      // 返回起点到投影点的累积距离
      return totalDistance + distance(segmentStart, projection.projectedPoint);
    }
    totalDistance += distance(segmentStart, segmentEnd);
  }
  // 用户已到达或超出终点,返回总路径长度
  return totalDistance;
}

2. 优化后的Polyline更新函数

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

  final List<LngLat> originalLngLat = road!.polyline!;
  final List<LatLng> originalPolyline = originalLngLat
      .map((lngLat) => LatLng(lngLat.lat, lngLat.lng))
      .toList();

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

  final double userProgress = calculateProgressDistance(currentLocation, originalPolyline);
  double accumulatedDistance = 0.0;
  final Distance distance = Distance();

  for (int i = 0; i < originalPolyline.length - 1; i++) {
    final segmentStart = originalPolyline[i];
    final segmentEnd = originalPolyline[i + 1];
    final segmentLength = distance(segmentStart, segmentEnd);

    if (accumulatedDistance + segmentLength >= userProgress) {
      // 计算线段上的截断点
      final remaining = userProgress - accumulatedDistance;
      final truncatedPoint = distance.offset(segmentStart, remaining, distance.bearing(segmentStart, segmentEnd));
      
      // 构建连续的新路径:当前位置 → 截断点 → 剩余路径
      final newPolyline = [
        currentLocation,
        truncatedPoint,
        ...originalPolyline.sublist(i + 1),
      ];

      return newPolyline.map((latLng) => LngLat(latLng.lng, latLng.lat)).toList();
    }
    accumulatedDistance += segmentLength;
  }

  // 用户到达终点,仅保留当前位置
  return [currentLocation.toLngLat()];
}

3. 调用方式

在位置更新回调中更新Polyline数据,刷新UI:

void onLocationUpdated(LatLng newLocation) {
  setState(() {
    _updatedPolyline = updatePolyline(newLocation);
  });
}

// 绘制更新后的Polyline
PolylineLayer(
  polylines: [
    Polyline(
      points: _updatedPolyline.map((p) => LatLng(p.lat, p.lng)).toList(),
      color: Colors.blue,
      strokeWidth: 8,
      borderStrokeWidth: 3,
      borderColor: Colors.black.withAlpha(30),
    ),
  ],
),

优化效果

  • 精准匹配位置:通过线段投影计算用户在路径上的真实位置,避免离散点匹配的偏差。
  • 方向感知:基于累积前进距离判断截断点,不会因短暂偏离路线导致错误截断。
  • 路径连续流畅:连接用户位置与截断点,保证Polyline视觉上的连贯性。

内容的提问来源于stack exchange,提问作者Jerry Matera

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 19:17:02