# 车辆连续路线 ## 1. Scope / Trigger 适用于 `scripts/lib/vehicle-route.js` 生成的路线 JSON,以及 `scripts/lib/cesium-preview.js` 对车辆巡航路线的读取与展示。 触发:修改路线生成、OSM 转向标签解析、车辆选择菜单或路线 JSON 字段时。 路线仅用于 Cesium 验证预览,不构成交通仿真或法规级导航。 ## 2. Signatures ```js buildVehicleRoute(osmPath) => { source, bounds, generatedAt, speedMetersPerSecond, loop, routes, segments } allowedTurns(tags, direction) => Set<"left" | "through" | "right"> classifyConnection(incomingEdge, outgoingEdge) => "left" | "through" | "right" | "u_turn" ``` 浏览器运行时调用 `addVehicleCruises(viewer, routeData, vehicleModelName)`;它首先读取 `routeData.routes`,仅在其不存在时回退到 `routeData.segments`。 ## 3. Contracts - `routes` 是当前主字段;`segments` 必须是同一数组的兼容别名,供旧预览使用。 - 每个路线至少包含 `id`、`coordinates`、`centerlineCoordinates`、`lengthMeters`、 `maneuvers` 与 `edgeIds`。`coordinates` 是右侧车道偏移后的闭合巡航轨迹。 - 有 `oneway=yes`(及等价真值)的 way 只能按 OSM 原始方向生成 edge,绝不能生成反向 `:backward` edge;`oneway=-1` 仅允许反向 edge。 - 去程在路口按入边方向读取 `turn:lanes:forward` 或 `turn:lanes:backward`,只有标签中的 `left`、`through`、`right` 才是候选出口;无标签时允许这三类非 U-turn 动作。 - 返程是展示路线的原路回返,不以反向 `turn:lanes` 再次否决,但依旧不可逆行单行道。 - 路网没有闭环时,在去程和返程端点插入平滑调头曲线;不得在 way 端点或路口瞬移。 - 选择菜单使用 `#编号 · 长度 m · 左 N / 右 N / 直 N`,因为一条路线可跨越多个道路名称。 ## 4. Validation & Error Matrix | 条件 | 结果 | |---|---| | 缺少或无法读取 route JSON | 预览继续加载,只取消巡航控件 | | `routes` 存在但为空 | 不回退到旧 `segments`;没有可播放车辆 | | 可行驶 way 少于两个节点或不在区域范围 | 不生成 edge | | 只存在反向单行可达路径 | 不生成违反单行限制的路线 | | 路口夹角接近掉头 | 分类为 `u_turn`,不作为去程出口 | | 候选路线不足五条 | 输出实际可用数量,预览按已有路线加载 | ## 5. Good/Base/Bad Cases - 正常:树状道路网产生多条跨 way 往返路线,车辆经过左、右、直三种连接并在端点平滑掉头。 - 基础:旧 JSON 只有 `segments` 时,预览仍能创建车辆与 Follow 控制。 - 错误:对返程再次套用反向 `turn:lanes`,使原路返回在树状网络中被错误过滤。 ## 6. Tests Required - `node scripts/test-preview-assets.js`:断言路线闭合、端点调头、`turn:lanes` 拆分、左/右/直 分类、单行道不逆行,以及 `segments === routes`。 - `node --check scripts/lib/vehicle-route.js` 与 `node --check scripts/lib/cesium-preview.js`:保证 Node 与浏览器直载脚本语法可用。 - 对目标区域运行 `npm run build:area -- --config config/areas/.json --stages preview`,确认 `routes` 中存在左、右、直动作,且 Cesium 下拉标签显示编号、长度与动作统计。 ## 7. Wrong vs Correct 错误:优先使用旧字段,导致新路线元数据无法被消费。 ```js const segments = routeData.segments || routeData.routes || []; ``` 正确:新字段优先,旧字段仅作兼容回退。 ```js const routes = routeData.routes || routeData.segments || []; ``` 错误:为使路线闭合而生成单行道路的反向 edge。 ```js edges.push(makeEdge(way, refs.reverse(), coords.reverse(), "backward")); ``` 正确:单行仅保留其允许的方向,树状网络用端点调头闭合预览路线。 ```js if (oneway !== "-1") edges.push(makeEdge(way, refs, coords, "forward")); if (!isOneWay(oneway)) edges.push(makeEdge(way, [...refs].reverse(), [...coords].reverse(), "backward")); ```