4. 路线请求与响应解析

路线请求是导航 API 集成的核心操作。本章将详细解析如何构建一个标准的路线请求(Route Request),向服务端提交起点、终点及途经点坐标,然后接收并解析返回的 JSON 响应,从中提取出可视化的路线几何图形以及逐段驾驶指令(Maneuvers / Steps)。理解这一过程,是后续实现地图渲染、导航播报和行程分析的基础。

4.1 核心原理

路线计算通常基于 RESTful API 或 GraphQL 接口。客户端通过 HTTP POST 或 GET 请求,将坐标序列、交通偏好、车辆类型等参数编码为查询字符串或请求体(JSON),提交到服务端。服务端路由引擎(如 OSRM、GraphHopper、Valhalla)在道路网络上执行 Dijkstra 或 A* 算法,返回一个包含几何路径、总距离、预计耗时、分段指令的 JSON 响应。

核心流程(请求 → 解析 → 提取):
1. 序列化参数(起点、终点、途经点、语言、单位、车辆类型)。
2. 发送 HTTP 请求,设置正确的 Content-Type 和 。
3. 接收 JSON 响应体,检查 HTTP 状态码和响应码(code/status)。
4. 解析 JSON 对象,提取 routes[0] 或 paths[0] 主路线。
5. 从主路线中提取几何编码(polyline / coordinates)和步骤数组(steps / legs)。
6. 按步骤提取每个驾驶指令(图标、方向文字、距离、持续时间)。

4.2 发起路线请求:典型 API 示例

以下以 OSRM(Open Source Routing Machine) 为例展示一个完整的 GET 请求构建。OSRM 的 API 采用轻量级 URL 参数传递,响应使用紧凑的 polyline5 编码压缩几何数据。

// 请求 URL 模板(OSRM v5.27)
GET https://router.project-osrm.org/route/v1/driving/
  113.3324,22.9607;113.5481,22.2050?overview=full&geometries=polyline&steps=true&alternatives=false
参数 说明 示例值
coordinates 起点和终点坐标(经度,纬度;经度,纬度)。支持途经点追加。 113.3324,22.9607;113.5481,22.2050
profile 交通工具类型:driving, walking, cycling driving
overview 几何概览级别:full / simplified / false full
geometries 几何编码格式:polyline / polyline6 / geojson polyline
steps 是否返回分段驾驶指令 true
alternatives 是否返回备选路线 false

对于多数现代地图服务(如 Mapbox、TomTom、HERE),则推荐使用 POST 发送 JSON 请求体,以支持更复杂的参数(如避开区域、车辆尺寸限制)。

// POST 请求体示例(类比 Mapbox Directions API)
{
  "coordinates": [
    [113.3324, 22.9607],
    [113.5481, 22.2050]
  ],
  "profile": "driving-traffic",
  "instructions": "text",
  "language": "zh-CN",
  "steps": true,
  "geometries": "polyline6"
}

4.3 解析 JSON 响应结构

服务端返回的标准 JSON 响应包含元数据和路线列表。以下为 OSRM 风格响应的一个简化示例:

{
  "code": "Ok",
  "routes": [
    {
      "geometry": "_p~iF~ps|U_ulLnnqC_ _mqN~xqB...",
      "legs": [
        {
          "steps": [
            {
              "maneuver": {
                "type": "turn",
                "modifier": "left",
                "location": [113.421, 22.531]
              },
              "driving_side": "right",
              "name": "深南大道",
              "distance": 1250.4,
              "duration": 72.3,
              "instruction": "左转进入深南大道"
            },
            {
              "maneuver": {
                "type": "new name",
                "modifier": "straight",
                "location": [113.433, 22.523]
              },
              "name": "南海大道",
              "distance": 3400.1,
              "duration": 180.0,
              "instruction": "继续直行,进入南海大道"
            }
          ],
          "distance": 4650.5,
          "duration": 252.3,
          "summary": "深南大道, 南海大道"
        }
      ],
      "distance": 4650.5,
      "duration": 252.3
    }
  ]
}
字段路径 类型 内容说明
code string 状态码,Ok 或 NoRoute、InvalidOptions 等
routes[0].geometry string Polyline 编码的路径点序列
routes[0].legs[].steps[] object[] 每段行程内的驾驶步骤,包含操作类型、方向、道路名、距离、时长、文字指令
routes[0].distance number 总路线距离,单位米
routes[0].duration number 总预计耗时,单位秒

4.4 提取路线几何(Geometry Decoding)

路径几何常用 Polyline 编码 压缩大量经纬度坐标。JavaScipt 前端常使用 polyline 或 @mapbox/polyline 模块解码。在服务端(Node.js / Python),可使用原生库或手动实现解码算法。

// JavaScript 解码 polyline5 示例
import polyline from '@mapbox/polyline';

const encoded = "_p~iF~ps|U_ulLnnqC_ _mqN~xqB...";
const decoded = polyline.decode(encoded, 5);
// decoded 格式:[[lat, lng], [lat, lng], ...]
console.log(`解码后坐标点数:${decoded.length}`);
// 输出:解码后坐标点数:386
// Python 解码 polyline5 示例(使用 polyline 库)
import polyline

encoded = "_p~iF~ps|U_ulLnnqC_ _mqN~xqB..."
decoded = polyline.decode(encoded, precision=5)
print(f"解码后坐标点数:{len(decoded)}")
# 输出:解码后坐标点数:386

解码后的经纬度数组可直接注入地图渲染引擎(如 Leaflet、MapLibre GL、Mapbox GL JS)作为 LineString 或 Polyline 的坐标源。

4.5 提取驾驶指令(Steps / Maneuvers)

每个 step 对象包含转向信息,是导航播报的核心数据。提取步骤通常需要遍历 legs 下的 steps 数组,若支持多段行程(legs),还需要嵌套处理。以下为典型提取逻辑:

// 解析 OSRM 风格的步骤指令
function extractInstructions(response) {
  const instructions = [];
  response.routes[0].legs.forEach(leg => {
    leg.steps.forEach(step => {
      const instruction = {
        type: step.maneuver.type,       // 'turn', 'new name', 'depart', 'arrive'
        modifier: step.maneuver.modifier, // 'left', 'right', 'straight', 'uturn'
        roadName: step.name,
        distance: step.distance,         // 米
        duration: step.duration,         // 秒
        text: step.instruction,          // 本地化文字
        coordinates: step.maneuver.location  // [lng, lat] 或 [lat, lng]
      };
      instructions.push(instruction);
    });
  });
  return instructions;
}
注意坐标顺序:OSRM 和 Mapbox 返回的 geometry 坐标顺序为 [经度, 纬度],但部分 API(如 GraphHopper)返回 [纬度, 经度]。在提取 maneuver.location 后,必须与目标地图库的期望顺序保持一致,否则会渲染到错误的地理位置,甚至导致浏览器控制台报错。

4.6 整体流程 SVG 图

下图完整展示了从参数构建到驾驶指令提取的数据流,帮助理解各处理阶段的位置:

路线请求与响应解析流程 1. 构建请求 坐标/语言/偏好 2. HTTP发送 GET/POST + 3. 服务端路由 Dijkstra计算 4. JSON接收 code/status检查 5. 主路线提取 routes[0] / paths[0] 6. 解码几何 polyline → coordinates 7. 提取指令 steps / maneuvers 输出 几何 + 驾驶指令 ⚠ 常见陷阱 • 坐标顺序(lng/lat vs lat/lng) • Polyline精度级别(5 或 6) • 响应状态码检查

4.7 调试与常见错误

  • HTTP 4xx 错误: 检查 是否有效、请求限额是否耗尽、坐标数量是否超过限制(通常 100-500 个航点)。
  • 响应 code 为 NoRoute: 起止点之间未找到合法路径(例如跨海无桥梁、部分区域管制)。可尝试添加途经点、更换交通方式(driving→walking)。
  • Polyline 解码异常: 解码后坐标数量为 0 或出现 NaN。确认 polyline 编码精度(5 位 vs 6 位),并确认传入的字符串未截断。
  • 指令缺失: 确认请求中已设置 steps=true 或 instructions=enabled。某些 API 在不包含该参数时默认不返回驾驶步骤。
  • 坐标顺序颠倒: 在前端地图显示时,路线偏离实际道路。在解码后统一用 [lng, lat] 或调用地图库的 reverse() 调整。

4.8 性能与稳定性优化

优化方向 具体措施
减少请求次数 在客户端节流,避免用户拖动地图时连续发送路线请求。建议设置 500ms~1s 的防抖(debounce)。
缓存响应 对相同的起止点坐标组合,在内存或 localStorage 中缓存最近 50-100 条路线,有效降低 API 调用频率。
精简几何精度 若仅需概览路线,使用 overview=simplified 减少解码后的坐标点数量,提升渲染帧率。
分页获取指令 对于超长路线(行径 1000+ 步骤),应分批请求或仅展开前 20 步,其余按需加载。
高优先级请求 对于用户交互产生的路线,使用 fetch priority = high 或设置独立请求队列,避免被后台任务阻塞。
生产环境建议:在 Node.js 服务端完成路线请求和 JSON 解析,然后将精简后的几何与指令以 GraphQL 或 REST 代理接口暴露给前端。这样可以隐藏原始 ,同时在前端减少 Polyline 解码和步骤重组的工作量,提高首屏加载速度。

4.9 部署注意事项

将路线请求集成到正式环境中,建议遵循以下部署策略:

  • 保护:禁止在前端明文字段写入 ,必须通过环境变量注入后端代理,或使用 API Gateway 的 URL 签名鉴权。
  • 错误兜底: 当服务端返回异常或无路线时,前端应展示默认的直线预览(显示起止点标记),并提示“当前路线计算失败,请稍后重试”。
  • 负载测试: 在地理围栏变化(如早晚高峰通勤)时段,对 API 并发请求进行压测,确保代理层和上游服务不达到速率限制上限。
  • 日志记录: 使用结构化日志(JSON 格式)记录每个请求的坐标、返回状态码、耗时,便于排查坐标乱序或响应超时问题。