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>
This commit is contained in:
61
workbench/client/probe/ol-ext-transform/fixture.ts
Normal file
61
workbench/client/probe/ol-ext-transform/fixture.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
// Synthetic road for the probe, so the page runs without importing OSM or
|
||||
// touching the workbench API. Geometry helpers come from the real edit modules
|
||||
// on purpose: the probe has to exercise the production conversion, not a copy.
|
||||
|
||||
import { offsetCoordinate, polylineLengthMeters, type Coordinate } from '../../src/edit/meters';
|
||||
import type { EditHandle } from '../../src/edit/types';
|
||||
|
||||
/** The road heads due north, so its normal — the drag axis — points due east. */
|
||||
export const TANGENT_AZIMUTH = 0;
|
||||
export const AXIS_AZIMUTH = 90;
|
||||
export const WIDTH_METERS = 12;
|
||||
export const LANE_COUNT = 4;
|
||||
|
||||
export const CENTERLINE: Coordinate[] = [
|
||||
[116.397, 39.905],
|
||||
[116.397, 39.915],
|
||||
];
|
||||
|
||||
export const ROAD_LENGTH_METERS = polylineLengthMeters(CENTERLINE);
|
||||
|
||||
const MIDPOINT: Coordinate = [116.397, 39.91];
|
||||
|
||||
/** Outer ring of the baseline road surface, EPSG:4326. */
|
||||
export function roadSurfaceRing(): Coordinate[] {
|
||||
const half = WIDTH_METERS / 2;
|
||||
const left = CENTERLINE.map((point) => offsetCoordinate(point, TANGENT_AZIMUTH + 90, half));
|
||||
const right = CENTERLINE.map((point) => offsetCoordinate(point, TANGENT_AZIMUTH - 90, half));
|
||||
return [...left, ...right.reverse(), left[0]];
|
||||
}
|
||||
|
||||
/** Lane divider lines, to make it obvious if the baseline ever gets rewritten. */
|
||||
export function laneDividerLines(): Coordinate[][] {
|
||||
const laneWidth = WIDTH_METERS / LANE_COUNT;
|
||||
const lines: Coordinate[][] = [];
|
||||
for (let index = 1; index < LANE_COUNT; index += 1) {
|
||||
const lateral = -WIDTH_METERS / 2 + laneWidth * index;
|
||||
lines.push(CENTERLINE.map((point) => offsetCoordinate(point, TANGENT_AZIMUTH + 90, lateral)));
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* A left edge-offset handle shaped exactly like the one `makeRoadHandles()`
|
||||
* emits, so `projectHandleValue()` sees production input.
|
||||
*/
|
||||
export const PROBE_HANDLE: EditHandle = {
|
||||
handleId: 'probe:road-edge-offset:left',
|
||||
kind: 'road-edge-offset',
|
||||
anchor: {
|
||||
type: 'road-interval',
|
||||
roadId: 'probe:road/1:forward',
|
||||
startStation: 0.15,
|
||||
endStation: 0.85,
|
||||
side: 'left',
|
||||
},
|
||||
position: offsetCoordinate(MIDPOINT, AXIS_AZIMUTH, WIDTH_METERS / 2),
|
||||
axisAzimuth: AXIS_AZIMUTH,
|
||||
value: { current: 0, min: -WIDTH_METERS * 0.45, max: WIDTH_METERS * 0.45, unit: 'meter' },
|
||||
affects: [],
|
||||
editable: true,
|
||||
};
|
||||
Reference in New Issue
Block a user