S苏哈出行开发者文档

JavaScript 步行导航:可运行页面与接入详解

本章提供独立 HTML + JavaScript 案例,以及地图选点、真实定位、规划 / 开始分离、偏航、到达、语音和清理流程。使用 load() 返回的 sdk.WalkingNavi,不直接实例化需要内部 Client 的底层导出类。

下载完整步行案例源码 · Android 步行教程 · JavaScript 导航 API

1. 两个名字相近的类,功能不同

类 用途 返回 / 行为
sdk.Walking 查询步行路线 search() 返回含 routes 的规划结果
sdk.WalkingNavi 前台真实步行导航 calculateWalkRoute() 返回一条 Route,开始后订阅浏览器定位并输出进度
sdk.Navi 驾车导航 与 WalkingNavi 的开始签名、回调字段不同
sdk.Polyline 显示折线 不会自动产生导航、定位或语音

WalkingNavi 不内置地图页面;宿主需要把规划结果的 route.path 画到地图。它的 startNavi() 无参数;不是驾车 startNavi(fix),也不接受 Android 的 GPSNaviMode。不要把 Walking.search() 的整个结果传给开始方法。

2. 运行完整网页案例

  1. 从下载页下载当前配套 SDK,保留 browser 文件夹内 JS、CSS、资源和录音目录。
  2. 下载本章案例 ZIP,将其解压到 SDK 包根目录,得到下面结构。源码不含真实 Key,不含 SDK 实现。
  3. 用 HTTP 服务打开包根目录;电脑本机可以使用 http://localhost,手机访问需要受浏览器允许的安全上下文,正式环境使用 HTTPS。file:// 不能作为定位测试环境。
  4. 打开 examples/walking/index.html,填写自己的浏览器 Key、服务地址、已授权数据集与语言,点击初始化。
  5. 点击“定位到我”,允许浏览器定位,再点击地图上的可达终点。按“规划步行 → 开始步行导航”操作。
SDK 解压目录/
├─ browser/
│  ├─ sukha.js
│  ├─ style.css
│  └─ … 配套资源保持原样
└─ examples/walking/
   ├─ index.html
   ├─ walking.js
   └─ WalkingGuideActivity.java   # Android 示例,网页不加载它

本机临时服务可在包根目录运行:

python -m http.server 8080 --bind 127.0.0.1

然后访问 http://localhost:8080/examples/walking/index.html。Key 的域名 / Origin 约束需要允许实际测试地址。手机浏览 http://电脑局域网IP 通常不具备定位要求的安全上下文,不应把这类失败当作路线接口异常。

完整案例有事件日志:可观察 calculateRouteSuccess、startNavi、reCalculateRoute、arriveDestination 及三类错误。点击“停止”、换终点、重新初始化或页面退出时均会清理旧任务;异步请求返回后会检查它是否仍属于当前页面和行程。

3. 最小调用链与音频解锁

先完成 load(),再绑定按钮。以下是核心顺序,完整源码另外包含取消和页面生命周期保护:

const sdk = await window.SukhaSDK.load({
  key: 'YOUR_BROWSER_KEY',
  serviceBaseUrl: 'https://map.sukha.cn',
  datasetId: 'YOUR_DATASET_ID',
  coordinateSystem: 'WGS84',
  language: 'zh-CN',
  voiceLanguage: 'zh-CN'
});
const walk = new sdk.WalkingNavi();
const geo = new sdk.Geolocation({enableHighAccuracy:true, timeout:15000, maximumAge:0});

startButton.onclick = async () => {
  // 必须在用户点击的同步调用栈内,且在任何 await 之前。
  walk.unlockVoice();
  try {
    const fix = await geo.getCurrentPosition();
    // destination 为用户选择的 [经度, 纬度],与公开坐标模式一致。
    const route = await walk.calculateWalkRoute(fix.position, destination);
    drawRoute(route.path);  // 宿主替换自己的折线。
    walk.startNavi();       // 同步、无参数;由它持续订阅后续浏览器定位。
  } catch (error) {
    showError(error.code, error.message, error.requestId);
  }
};

startButton、destination、drawRoute、showError 是宿主 UI,不是 SDK 全局变量。直接运行请使用下载案例。实际项目应禁用重复开始,并用页面 / 请求代次阻止“用户退出后异步返回又启动导航”。

为什么完整案例把“规划”和“开始”分开:用户可以先看距离和路线,再确认出发。规划按钮不自动开始,开始按钮在点击栈先解锁语音,再检查新定位与原规划起点。不要先 await 定位或算路,再调用 unlockVoice()。

4. 返回值、坐标和时间

JavaScript 坐标数组始终是 [经度, 纬度]。例如 [100.02228,21.67623];不要照搬 Android LatLonPoint(纬度,经度) 的顺序。

calculateWalkRoute resolve 的是 一条路线:

字段 含义
route.path 路线点,可传给 Polyline
route.distance 规划总距离,米
route.time 规划预计耗时,秒
route.steps 步行提示和路线点索引;不要假设与驾车完整导航路线结构一致
route.travelMode 步行路线应为 walking
route.guidanceMode 步行控制器使用包含几何引导信息的路线
route.datasetVersion 用于联调记录当前路线所用数据版本

初始化为 WGS84 时,选点、规划、自己注入的 Fix 都使用 WGS84;初始化为 GCJ02 时使用对应公开模式。SDK 浏览器定位会处理浏览器原始 WGS84 到公开模式的转换,不要二次转换。

手动定位注入的完整格式:

const fix = {
  position: [longitude, latitude], // 与公开坐标模式一致
  accuracy: accuracyMeters,       // 米,保留真实精度
  timestamp: originalTimestamp,   // Unix 毫秒,原始采样时间
  speed: speedMetersPerSecond,    // 米/秒;未知传 null
  heading: headingDegrees        // 角度;未知传 null
};
walk.updateLocation(fix);

这个接口只在导航已开始时处理。当前 WalkingNavi.startNavi 本身会订阅浏览器定位,公开接口没有“外部定位独占模式”选项。普通网页直接使用内置定位即可,不额外建第二个 watch 再重复喂入;混合 App 若需要独占原生定位,不要假设注入接口会禁用浏览器定位,先确认所需集成方式。

不要以 Date.now() 替换旧采样时间,不要把失败位置写成 [0,0],不要给未知精度填 0。案例的开始前检查要求位置不超过 8 秒、精度不大于 35 米;规划起点移动超过 30 米时要求重算,这是案例策略,不是新的 SDK 配置项。

5. 事件名称和真实载荷

事件 载荷 推荐行为
calculateRouteSuccess {route, rerouted?} 首次与重算都会触发;删除旧 Polyline,再绘制新路线
startNavi 无需读取业务字段 显示导航中
naviInfoUpdate WalkingUpdate 字段直接在对象上 更新剩余米数和有效引导
reCalculateRoute 无需读取业务字段 只展示正在重算,SDK 已发起请求
arriveDestination 无需读取业务字段 展示到达,不自动完成业务订单
locationError {error} 定位权限、超时等;提示排查定位
voiceError {error} 解锁 / 录音 / 播放问题;与路线错误分开
error {error} 自动重算失败等异步错误
offlinePreparation 离线准备状态 显示语音准备进度
speechComplete {text, source} 录音提示播放完成,可做调试记录

首次规划失败通过 calculateWalkRoute() 的 Promise rejection 处理,不要只监听 error 而漏掉 try/catch。事件大小写是 reCalculateRoute,不是 recalculateRoute。

walk.on('naviInfoUpdate', u => {
  const text = !u.reliable ? '等待可靠定位'
    : u.offRoute ? '已偏离路线'
    : u.cue?.instruction || '沿路线前行';
  status.textContent = `${text} · 剩余 ${Math.round(u.remainingMeters)} 米`;
});

WalkingUpdate 包含 accepted、reliable、offRoute、arrived、requestReroute、progressMeters、remainingMeters、deviationMeters、可空的 cue。cue 包含 id、instruction、geometryIndex。cue 为空是正常状态,不代表导航失败。

步行没有驾车 info.instruction、remainingSeconds、repeat()、locationChange、wakeLockUnavailable 的完整同名合同。不要复制驾车 HUD 后假定这些字段或事件都存在。语音由 SDK 安排,不要在每次更新时另外播放一遍。

6. 四个可复用案例

案例 A:地点卡片直接出发

传入地点 ID / 名称和公开模式终点坐标。用户点击“步行”时同步 unlockVoice,再取真实定位、规划、显示路线并开始。规划期间锁住开始按钮,换终点时 stopNavi() 并使旧请求代次失效。下载案例的 plan、start、stop 可直接参考。

案例 B:先预览,准备录音后离线出发

规划后先显示 distance / time;读取 getOfflineStatus() 或监听 offlinePreparation。如需明确等待,调用并 await prepareOfflineNavigation()。准备完成后点击开始仍需新鲜定位和音频解锁。

状态字段 意义
routeReady 当前控制器已有路线,不代表能离线计算新路线
voiceState idle / preparing / ready / incomplete
voiceCompleted / voiceTotal 已完成与总录音数量
offlineRerouting 当前为 false
backgroundNavigation 当前为 false

incomplete 要提示恢复网络后重试,不能显示“离线就绪”。承诺范围是当前页面、已准备的路线与语音;不要把它扩展成刷新页面后自动恢复、离线偏航重算或永久缓存保证。

案例 C:偏航与断网重算

SDK 根据连续有效定位判断偏航并触发重算。UI 显示重算状态,成功后替换折线;不要再次 startNavi。重算失败监听 error,保留明确的失败提示,让用户恢复网络或停止后重新规划,不伪装已经有新路线。

案例 D:离开页面、返回与到达

完整案例在 visibilitychange 进入后台时主动 stop,返回后要求重新规划;在 pagehide 销毁控制器、定位服务、覆盖物和地图。若从浏览器往返缓存恢复,页面提示重新初始化。SPA 在组件卸载时执行相同清理,并移除自己注册的 DOM 监听。

SDK 也会在页面隐藏时取消当前播报,但这不代表浏览器提供后台连续导航。到达事件后不要立刻 stopNavi() 截断刚开始的到达录音;用户关闭页面时仍需正常销毁。

7. 常见错误与处理

现象 / 错误 原因方向 处理
初始化授权失败 Key 类型 / Origin / 数据集不匹配 使用浏览器 Key,核对实际协议、域名、端口和授权
NO_ROUTE 尚未成功规划就开始 / 准备语音 用 READY 状态控制按钮
CANCELLED 换终点、停止或新请求取消旧请求 旧请求不再覆盖 UI,一般不弹失败框
DESTROYED 卸载后仍持有旧对象 新页面创建新实例,不复用已销毁控制器
无定位弹窗 / 定位失败 非安全上下文、系统定位关闭、浏览器拒绝 HTTPS / localhost,检查站点和系统权限
有路线无进度 未 start / 位置过旧 / 精度不足 / 页面后台 记录真实定位与 reliable,保持前台
没有声音 await 后才 unlock、媒体限制、资源错误或静音 点击栈先解锁,检查 voiceError、资源请求和设备音量
路线越画越多 重算时未销毁旧折线 每个行程只保留一个路线图层引用
已退出却又导航 异步操作没有页面和请求代次检查 像示例一样用 active(session) 与 run 检查
与真实位置明显偏移 纬经度顺序或坐标模式错误 JS 经度在前,保持公开模式一致
步行剩余分钟 undefined 使用了驾车更新字段 读取规划 route.time,实时更新只承诺文档列出的字段

排错日志建议保留时间、事件名、错误 code / requestId、规划的 datasetVersion、当前位置精度和采样年龄;不要把 Key、完整用户轨迹等直接写进公共日志。

8. 上线前验收

分别验证初始化失败、首次拒绝权限、恢复权限、弱定位、规划时改终点、规划时离开页面、开始前已移动、连续点击、重算成功 / 失败、语音解锁与静音、切后台、刷新、浏览器后退再返回、到达后退出。再用授权数据集中的真实道路做户外步行验收。

完整 walking.js 在文档构建时对配套 ZIP 的 TypeScript 声明做类型检查;不要求升级 JavaScript SDK。接口检查不等于各浏览器的定位、录音与后台行为验收。