Planning: parent design.md becomes the single authoritative contract (constraint model with 6 kinds, handle manifest, coordinate/unit layering, preview sequencing, storage layout and lazy migration, area config snapshot, API contract). Work is split into four independently verifiable child tasks with per-step gates and rollback points. Test infra: pin vitest 4.1.11, add test:client:unit for client pure logic, extend prettier globs to root *.ts so vitest.config.ts is checked. Add .gitignore: the repo had none, so inputs/, outputs/, workbench-data/ and the client build output were untracked rather than ignored. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
11 KiB
11 KiB
直接操纵道路编辑工作台
目标
让道路编译工作台支持类似 Drawtonomy 的直接操纵:用户能在地图上选择生成的道路面并通过有语义的控制柄调整细节,同时保留 OSM 作为输入来源、修改可解释且可审计,并能在重新编译或重新导入后可靠地重放。
已确认的事实
src/compile/native-road.js从 OSM 生成方向道路、车道、道路面、步行带、路口面、标线和连接路径;这些 GeoJSON 是派生输出,不是持久化编辑源。- 现有
native-road-overrides/v1已支持道路宽度/车道数/步行带开关、路口和车道连接、以及标线样式,且以稳定的 OSM 派生语义 ID 定位目标,并在 OSM 改动后识别失效项。 - 编译器使用 staging 目录原子发布输出;工作台已经具有单例 OpenLayers 地图、命令式图层 source 更新、选择高亮和暂存/保存/重新编译链路。
- Drawtonomy 的公开 SDK 表明其道路由共享
point、linestring和引用左右边界的lane构成;连接关系和导出几何从该对象图派生。其编辑器核心(手势状态机、选择、历史)不在公开仓库中,不能作为源码依赖。
产品要求
- 编辑目标必须是道路语义对象,而不是直接保存最终 GeoJSON 多边形。
- 拖拽时必须即时显示预览,并清楚呈现将被同步影响的道路面、车道、标线和路口对象。
- 每次持久化修改都必须可解释:谁在何处、以什么规则、相对什么输入锚点施加了何种约束。
- OSM 重新导入或编译器升级后,修改应自动重放、明确报告冲突/失效,绝不能静默改写到错误道路。
- 用户必须可以撤销/重做未保存编辑,并能查看、禁用或删除已保存编辑。
- 新架构应复用现有道路参数 overrides、ID 校验、编译和图层更新能力,而不是并行维护第二套地图数据。
- OSM 道路中心线和拓扑保持只读;直接操纵首期仅编辑由它派生的横断面与路口细节。
- 首期必须同时验证道路横断面与路口细节的联合编辑:道路外缘、步行带、车道分隔,以及路口进口、cutback 与转弯角部都属于首批能力。
- 道路横断面拖拽默认生成以拖拽站点为中心的局部区间,并在区间边界平滑过渡回基线;用户可以用范围控制柄修改影响区间。
- 拖拽时浏览器立即更新控制柄、辅助线与半透明 ghost;在短防抖后调用服务端权威预览,服务端使用与正式编译相同的约束求解器返回受影响派生图层。浏览器不维护第二套道路几何算法。
- 工作台必须支持可复现的存盘 revision:冻结一次编译所需的 OSM 输入、区域配置、已有 overrides、直接编辑约束、信号数据和编译身份;重新导入 OSM 创建新 revision,不覆盖旧 revision。
- 导入 OSM 自动创建基线 revision;用户显式保存命名检查点时创建不可变 revision;普通保存和预览只更新活动副本。
- 路口点击编辑进入专用
JunctionTools工作区,而非在主地图叠加完整路口控制面板;该工作区复用活动 revision 与直接编辑约束,并在操作后尽快重新求解生成道路拓扑和相关派生图层。 JunctionTools的草稿从首次拖拽起即显示即时 ghost 和服务端权威几何预览;“应用到工作区”仅合并有效草稿到主工作区,“保存”才持久化,“取消”丢弃本次会话草稿。- 一次
JunctionTools会话只编辑一个 node 或 cluster;切换相邻路口前必须应用或取消当前草稿。 - 主地图道路区间编辑仅拥有两个路口之间的内部区间;
JunctionTools独占路口保留区内的进口、cutback 与角部约束。求解器保证两侧连续,并以显式 junction approach 约束优先。 - 首个
JunctionTools交付只验证单一普通路口的进口宽度、cutback 和单个角部圆角;必须展示完整拟合预览并能应用回主工作区。 - 约束 kind 枚举必须唯一且与首期范围一一对应;车道分隔必须有自己的约束 kind,不得借用外缘偏移表达。
- 预览必须有明确时序保证:乱序响应不得覆盖较新预览;拖拽防抖与
pointerup最终请求分离;超出延迟预算时降级提示而不是几何闪烁。 - 持久化约束只使用米与归一化 station;坐标与单位换算的分层职责必须固定,客户端不得用投影坐标差充当米。
- 区域配置(含
nativeRoad.junctionTemplates)必须随活动副本与 revision 冻结为快照,编译与预览只读快照;否则 revision 不可复现。 - 同机多标签页并发保存必须被显式挡住(版本前置条件加冲突提示),不得静默互相覆盖。多人协作仍延后。
- revision 不自动删除;OSM 副本按内容寻址避免重复;派生产物缓存可清理且清理不影响可复现性。
- 编译器几何版本变化时,约束既不自动失效也不自动改值,必须经用户显式确认重放。
- 编辑能力只进 React 工作台;遗留 vanilla 客户端不同步、不在本任务内删除。
- 客户端纯逻辑必须可自动化测试;地图交互行为以固化在
implement.md的手测清单覆盖。
需要完成的研究与设计
- 梳理当前模型、输出 feature 属性和现有 overrides 可直接复用的锚点。
- 明确 Drawtonomy 的公开对象图、共享几何和派生原则,以及它与 OSM 编译流程不相同的边界。
- 比较参数反推、语义几何约束、局部补丁几何三种数据模型,并给出持久化、重放、冲突、撤销和迁移策略。
- 在确认编辑边界后,形成交互模型、编译边界、API 合约、版本化 schema 和分阶段实施计划。
- 在实现前验证
ol-ext的 Transform interaction 能否仅操作临时代理 feature,并与现有ol@10.10.0、React 生命周期和局部预览稳定协作;验证结果决定是否纳入正式依赖。 ol-ext验证必须是有时间上限、可独立删除的探针;失败仅替换输入 adapter,不得改变约束文档、求解器或编译架构。ol-ext探针限时一个工作日;必须同时满足“无地图重建、无基线 source 写入、拖拽稳定”,否则停止排障并采用 OpenLayers 原生 proxy + 小型语义 adapter。
任务地图
本任务是父任务:持有需求集、design.md 权威合约、跨子任务验收标准与最终集成验证。实现落在四个子任务,顺序依赖写在各自 prd.md / implement.md,不由树结构隐含。
| 子任务 | 交付物 | 对应父 implement.md 步骤 |
前置 |
|---|---|---|---|
08-26-direct-edit-documents |
v2 文档、活动副本、revision、area config 快照、测试基础设施 | 0、2、3 | 无 |
08-26-direct-edit-solver-api |
resolveDirectEditConstraints 与预览/保存/revision/rebase API |
4、5 | documents |
08-26-direct-edit-map-editor |
ol-ext 探针与主地图道路区间编辑 |
1、6 | solver-api 第 2 步(manifest) |
08-26-direct-edit-junction-tools |
单一普通路口的进口、cutback、单角部圆角 | 7 | solver-api、map-editor |
父任务自留第 8 步:跑完交付级验收标准并把 directEdit 开关默认开启。
延后项
- 不在当前前置架构阶段设计或实现高级复合路口模板编排、多个路口的联合会话、控制设施逐个手工布置、协作合并或全面的拓扑创作工具。
首个交付范围
- 版本化直接编辑文档、活动工作副本和命名 revision 检查点。
- 限时的
ol-ext代理 feature 验证及原生 OpenLayers adapter 回退。 - 服务端权威预览 API 与客户端即时 ghost。
- 主地图的道路内部区间:外缘、步行带、车道分隔和范围控制。
- 最小
JunctionTools:单一普通路口的进口、cutback、单角部圆角、预览/应用/取消。 - 约束重放的精确匹配、待确认和失效状态;旧 revision 永不被重导入覆盖。
验收标准(规划阶段)
- 已提供当前能力与可复用边界的证据清单。
- 已区分 Drawtonomy 的公开事实、可迁移原则和不可验证的编辑器内部实现。
- 已确定持久化编辑数据不是最终 GeoJSON,并有版本化、可重放、可失效诊断的数据方案。
- 已明确拖拽预览、应用、保存、重新编译、撤销/重做、OSM 重导入和 revision 检查点的行为。
- 已定义
ol-ext限时探针、原生 OpenLayers 回退与不改变数据合同的止损规则。 - 已定义首个 Road editor 与最小 JunctionTools 的范围、所有权和延后项。
- 约束 kind 枚举唯一,且覆盖首期全部 6 项能力(含车道分隔)。
- handle manifest、坐标分层、预览时序、area config 快照、单写者保护、留存策略均已在
design.md定义。 - 已明确遗留客户端边界与客户端测试基础设施的决定。
- 已形成
design.md和implement.md,供用户审阅后再开始实现。
验收标准(交付阶段)
每条都必须可执行验证,不接受"看起来对了"。
- 拖动道路外缘 → 保存 → 重新编译,道路面、步行带、车道线、标线与 connector 一致更新,约束状态为
exact。 - 车道分隔手柄可独立调整某条分隔线,且不等价于外缘偏移的副作用。
- 落在 junction reserve 内的道路手柄不可拖动,并给出引导进入
JunctionTools的原因。 JunctionTools单路口会话可调进口宽度、cutback 与一个角部圆角,预览包含全部受影响派生对象;应用后主地图继续显示同一预览,取消后回到进入前状态。- 违反最小车道宽 2.4m 或造成路口面自交的草稿返回阻塞性诊断,且不替换最后一个有效预览。
- 快速连续拖拽时乱序预览响应不会覆盖较新结果(可通过注入延迟的测试复现)。
- 同一 OSM 重新导入后,约束按
exact/pending/conflicted/stale分类报告;旧 revision 仍可打开并复现原结果。 - 修改 area config 后,旧 revision 的编译结果不变(证明快照生效)。
- 两个标签页并发保存时,后者收到 409 且不覆盖前者。
- 未保存编辑可撤销/重做;已保存编辑的撤销以反向操作或禁用约束体现,历史不被重写。
- 拖拽与选择过程中 OpenLayers
Map未被重建,只有受影响 source 被替换。 npm run format:check、npm run test、npm run test:client、新增客户端单元测试与npm run build全绿。