Files
road-compiler/.trellis/tasks/08-26-direct-edit-map-editor/research/reserve-handles-unreachable.md
que01 bc4b9a9717 feat: add direct edit handles behind directEdit flag
Steps 1-3 of the main map road interval editor.

EditSession keeps the command stack, undo/redo and previewSeq arbitration as
pure logic with no OpenLayers reference, so all of it is unit-tested in node.
Pointer displacement converts to meters through EPSG:4326 and spherical
distance: treating a 3857 delta as meters desyncs the geometry from the cursor
by 1/cos(latitude). Handle drags project onto the axis the manifest declares
and clamp to its range, so the client never writes a coordinate into a road
polygon.

All of it sits behind a directEdit flag that defaults to off. With the flag off
the workbench requests no manifest, creates no extra source and registers no
interaction, so behaviour matches main.

The ol-ext probe passed its three gates but is not adopted for road handles.
Transform translates by the raw pointer delta, so a handle detaches from its
clamped constraint value: a drag reading -24.1 m produced a draft of -5.4 m.
Production needs the handle position derived from the constraint instead, which
means owning the position update, so native OL PointerInteraction will carry
the drag. ol-ext stays out of package.json; the probe is kept as a manual
harness. Reserve handles are unreachable with the current solver, recorded in
research/ rather than worked around.

Also names the dead backend when an API response is empty, instead of
surfacing "Unexpected end of JSON input" from response.json().

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 14:59:11 +08:00

71 lines
2.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# reserve 内手柄在当前服务端实现下不可达
第 3 步人工验证时发现:点遍所有道路都看不到灰色(不可拖)手柄。经实测确认这是**正确行为**,不是漏测。
## 实测数据
`test/fixtures/fengshu-er-road.osm`24 条方向道路、32 个 reserve
```
road handles : 126
road NOT editable : 0
with disabledReason : 0
junction handles : 96 JunctionTools 所有,主地图已过滤)
```
## 为什么不可达
`makeRoadHandles()``editable: false` 的唯一来源是 `unavailable`
```js
const start = Math.min(1, startReserve);
const end = Math.max(0, 1 - endReserve);
const unavailable = start >= end;
```
`junctionReserves()` 给每端的保留区比例有硬上限:
```js
const fraction = Math.min(0.45, cutback / length);
```
于是 `startReserve ≤ 0.45``endReserve ≤ 0.45`,即 `start ≤ 0.45``end ≥ 0.55`
`start >= end` 结构上永远为假,`unavailable` 分支是死代码。
实测输出里能直接看到上限生效的样子——两端都撞到 0.45 的短路段:
```
segment:way/858770822/1 window=[0.450,0.550]
segment:way/117947564/0 window=[0.450,0.550]
segment:way/851989492/2 window=[0.450,0.550]
```
另外手柄的 station 取自 `(start + end) / 2`,即无保留区窗口的正中,
所以三类道路手柄的位置也永远不会落进 reserve。
## 对验收标准的影响
prd.md 的「落在 junction reserve 内的手柄不可拖动,并提示进入 JunctionTools」
对当前三类道路手柄是**空真**:它们永远不落在 reserve 内,所以约束自动成立。
客户端一侧的置灰与原因提示路径有单测覆盖(`selection.test.ts`disabled 手柄被保留、
`disabledReasonOf` 始终给出原因),只是服务端从不产生这种输入。因此第 3 步不需要改动。
reserve 边界真正变得用户可见是在第 4 步的**区间范围手柄**:它可以被拖向 reserve 边界,
`projectIntervalEnd()` 钳到 `window.minStation` / `maxStation`。届时这条边界才有可演示的行为。
## 留给用户决定的设计问题
那个死分支暗示原作者期望「短路段应完全归 JunctionTools」。但 0.45 的上限把结果改成了:
一条几何上被两个大路口主导的短路,仍然会在正中间得到一条仅占全长 10% 的可编辑带
(上面 `[0.450,0.550]` 那几条)。
两种取向都讲得通,需要产品判断:
- 保持现状:任何道路都留一条可编辑带,哪怕很窄。
- 改为:当 `cutback / length` 两端之和超过某阈值时,整条路标 `editable: false`
引导用户进入 JunctionTools。这会让死分支复活也让验收标准变成可演示的。
改动落在 `src/compile/direct-edit-solver.js`(属已归档的 `direct-edit-solver-api` 任务范围),
不在本任务范围内,故此处只记录不实施。