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

Flutter调用MapQuest Directions API绘制路线显示异常问题

路线渲染异常核心原因
  • API请求参数经纬度错配:构造MapQuest请求URL时,两个途经点的经纬度交叉写反,第一个点传入的是「起点纬度+终点经度」,第二个点传入的是「终点纬度+起点经度」,请求返回的路线坐标本身就和实际起终点位置完全不匹配。
  • 路线坐标提取逻辑错误:MapQuest Directions接口返回的maneuvers数组仅存储转向节点坐标,两个转向节点之间的完整道路形状坐标存储在route.shapePoints字段下,仅提取转向点连线只会得到跨建筑、跨道路的生硬折线,无法贴合实际道路走向。
  • 地图视角逻辑存在缺陷:自定义MapWidget每次执行build时,都会强制把相机动画拉回当前定位点、固定18倍缩放,用户既无法自由拖动查看完整路线,组件也不会自动适配路线范围调整视野。
  • URL构造方式不规范:手动拼接JSON字符串到请求参数中,未做URL编码,特殊场景下会出现请求解析失败的问题。
可行修复方案

1. 修复MapQuest API请求层逻辑

修正经纬度传参错误,改用标准Uri构造方法避免编码问题,同时提取完整道路形状点而非仅转向点:

import 'dart:math';

class MapQuestDataProvider {
  Future<MapQuestDirection> getDirections(
      LatLng source, LatLng destination) async {
    try {
      MapQuestDirection result = MapQuestDirection(locations: []);
      const apiKey = "[API_KEY]";
      
      // 标准Uri构造,自动处理参数编码
      final uri = Uri.https(
        "www.mapquestapi.com",
        "/directions/v2/optimizedroute",
        {
          "key": apiKey,
          "shapeFormat": "raw", // 返回未压缩的原始坐标点,无需额外解压
          "json": jsonEncode({
            "locations": [
              "${source.latitude},${source.longitude}", // 修正:起点完整经纬度
              "${destination.latitude},${destination.longitude}" // 修正:终点完整经纬度
            ]
          })
        }
      );

      final response = await http.get(uri);
      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        final route = data["route"];
        final shapePoints = route["shapePoints"] as List;
        // shapePoints按[纬度, 经度, 纬度, 经度]顺序排列,两两解析为坐标
        for (int i = 0; i < shapePoints.length; i += 2) {
          final lat = shapePoints[i] as double;
          final lng = shapePoints[i+1] as double;
          result.locations.add(LatLng(lat, lng));
        }
        return result;
      } else {
        throw Exception('Failed to load route');
      }
    } catch (e) {
      print("Request exception: $e");
      throw e;
    }
  }
}

2. 修复MapWidget相机控制逻辑

移除每次build强制重置视角的逻辑,新增路线自动适配视野能力:

class _MapWidgetState extends State<MapWidget> {
  GoogleMapController? mapController;
  final defaultMapZooming = 18.0;
  bool _initialLocationSetted = false;

  // 自动计算路线边界,调整相机视野覆盖全部路径点
  void _fitRouteBounds(List<LatLng> points) {
    if (mapController == null || points.isEmpty) return;
    double minLat = points.first.latitude;
    double maxLat = points.first.latitude;
    double minLng = points.first.longitude;
    double maxLng = points.first.longitude;
    for (final point in points) {
      minLat = min(minLat, point.latitude);
      maxLat = max(maxLat, point.latitude);
      minLng = min(minLng, point.longitude);
      maxLng = max(maxLng, point.longitude);
    }
    final bounds = LatLngBounds(
      southwest: LatLng(minLat, minLng),
      northeast: LatLng(maxLat, maxLng)
    );
    mapController!.animateCamera(CameraUpdate.newLatLngBounds(bounds, 80));
  }

  @override
  void didUpdateWidget(covariant MapWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 折线更新时自动适配路线视野
    if (widget.polylines.isNotEmpty) {
      final allPoints = widget.polylines.values.expand((poly) => poly.points).toList();
      _fitRouteBounds(allPoints);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(child: _mapView(context));
  }

  Widget _mapView(BuildContext context) {
    // 仅首次拿到定位时初始化视角,避免每次build重置用户操作
    if (mapController != null && widget.currentLocation != null && !_initialLocationSetted) {
      _initialLocationSetted = true;
      mapController!.animateCamera(CameraUpdate.newLatLngZoom(
          LatLng(widget.currentLocation!.latitude,
              widget.currentLocation!.longitude),
          defaultMapZooming));
    }

    return GoogleMap(
      onMapCreated: (controller) => mapController = controller,
      zoomControlsEnabled: false,
      myLocationEnabled: false,
      mapType: MapType.normal,
      onLongPress: widget.onTab,
      markers: widget.markers,
      polylines: widget.polylines.values.toSet(),
      minMaxZoomPreference: const MinMaxZoomPreference(12, 25),
      initialCameraPosition: CameraPosition(
        target: _getLocationOrDefault(),
        zoom: defaultMapZooming,
      ),
    );
  }

  LatLng _getLocationOrDefault() {
    return widget.currentLocation ??
        const LatLng(21.215415017175165, 72.8879595194489);
  }
}

3. 优化折线构造逻辑

移除手动重复插入起终点的逻辑,直接使用接口返回的完整路径点绘制,可额外添加圆角样式优化显示效果:

void setPathFromMapQuestData(List<LatLng> locations) {
  final polylineCoordinates = List<LatLng>.from(locations);
  const polylineId = PolylineId("poly");
  final poly = Polyline(
    polylineId: polylineId,
    color: Colors.deepPurpleAccent,
    points: polylineCoordinates,
    width: 8,
    startCap: Cap.roundCap,
    endCap: Cap.roundCap,
    jointType: JointType.round
  );
  polylines[polylineId] = poly;
  setState(() {
    this.polylines = Map.from(polylines);
  });
}
  • 调试阶段可先打印接口返回的坐标列表,确认首尾点和实际起终点位置匹配后再渲染,快速验证参数是否正确。
  • 正式环境如果需要减少接口传输体积,可将shapeFormat改为cmp6,引入对应解压工具处理压缩坐标即可。

内容的提问来源于stack exchange,提问作者moaad cole

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 02:27:16