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

2.8 KiB
Raw Blame History

reserve 内手柄在当前服务端实现下不可达

第 3 步人工验证时发现:点遍所有道路都看不到灰色(不可拖)手柄。经实测确认这是正确行为,不是漏测。

实测数据

test/fixtures/fengshu-er-road.osm24 条方向道路、32 个 reserve

road handles        : 126
road NOT editable   : 0
with disabledReason : 0
junction handles    : 96   JunctionTools 所有,主地图已过滤)

为什么不可达

makeRoadHandles()editable: false 的唯一来源是 unavailable

const start = Math.min(1, startReserve);
const end = Math.max(0, 1 - endReserve);
const unavailable = start >= end;

junctionReserves() 给每端的保留区比例有硬上限:

const fraction = Math.min(0.45, cutback / length);

于是 startReserve ≤ 0.45endReserve ≤ 0.45,即 start ≤ 0.45end ≥ 0.55start >= 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.tsdisabled 手柄被保留、 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 任务范围), 不在本任务范围内,故此处只记录不实施。