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;
}
[经度, 纬度],但部分 API(如 GraphHopper)返回 [纬度, 经度]。在提取 maneuver.location 后,必须与目标地图库的期望顺序保持一致,否则会渲染到错误的地理位置,甚至导致浏览器控制台报错。
4.6 整体流程 SVG 图
下图完整展示了从参数构建到驾驶指令提取的数据流,帮助理解各处理阶段的位置:
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 或设置独立请求队列,避免被后台任务阻塞。 |
4.9 部署注意事项
将路线请求集成到正式环境中,建议遵循以下部署策略:
- 保护:禁止在前端明文字段写入 ,必须通过环境变量注入后端代理,或使用 API Gateway 的 URL 签名鉴权。
- 错误兜底: 当服务端返回异常或无路线时,前端应展示默认的直线预览(显示起止点标记),并提示“当前路线计算失败,请稍后重试”。
- 负载测试: 在地理围栏变化(如早晚高峰通勤)时段,对 API 并发请求进行压测,确保代理层和上游服务不达到速率限制上限。
- 日志记录: 使用结构化日志(JSON 格式)记录每个请求的坐标、返回状态码、耗时,便于排查坐标乱序或响应超时问题。