@orbat-mapper/tactical-draw 0.2.0-alpha.34 → 0.2.0-alpha.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +60 -445
- package/dist/index.mjs +533 -437
- package/dist/map-adapter-meRo5VY2.d.mts +361 -0
- package/dist/testing.d.mts +25 -0
- package/dist/testing.mjs +247 -0
- package/package.json +11 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,393 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as PointerEventInfo, _ as fillPatternImageDimension, a as MapAdapter, b as EditPointerDriver, c as MapEventHandler, d as PickOptions, f as PixelCoordinate, g as HatchPattern, h as HATCH_PATTERNS, i as LayerId, l as PickEvent, m as TextStyleOptions, n as HandleStyle, o as MapCursor, p as StyleOptions, r as IconResource, s as MapEvent, t as FeatureGroup, u as PickHandler, v as fillPatternImageId, x as PointerCallback, y as makeFillPatternImage } from "./map-adapter-meRo5VY2.mjs";
|
|
2
|
+
import { AmplifierPlacement, AmplifierPlacements, ControlMeasure, ControlMeasureKind, ControlMeasureRender, ControlMeasureStyle, TextAmplifiers } from "@orbat-mapper/control-measures";
|
|
2
3
|
import { Feature, Position } from "geojson";
|
|
3
4
|
|
|
4
|
-
//#region src/gestures/types.d.ts
|
|
5
|
-
interface PointerEventInfo {
|
|
6
|
-
/** Per-event hit-tolerance override (e.g. larger for touch). */
|
|
7
|
-
hitTolerance?: number;
|
|
8
|
-
/**
|
|
9
|
-
* Whether the Alt/Option modifier was held for this pointer event. The edit
|
|
10
|
-
* controller reads this on pointer-down to turn a vertex hit into a delete
|
|
11
|
-
* (the GIS-standard Alt+click-to-delete gesture) instead of a drag. Only the
|
|
12
|
-
* mouse paths populate it; touch leaves it `undefined`.
|
|
13
|
-
*/
|
|
14
|
-
altKey?: boolean;
|
|
15
|
-
/**
|
|
16
|
-
* Whether Shift was held for this pointer event. Rotation gestures use the
|
|
17
|
-
* live value to constrain transform-box angles to 15-degree increments.
|
|
18
|
-
*/
|
|
19
|
-
shiftKey?: boolean;
|
|
20
|
-
/** Engine-native event, for drivers that need to call preventDefault etc. */
|
|
21
|
-
native?: unknown;
|
|
22
|
-
}
|
|
23
|
-
interface PointerCallback {
|
|
24
|
-
(pixel: PixelCoordinate, info?: PointerEventInfo): void;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Engine-agnostic pointer driver consumed by `TacticalDraw`'s edit controller.
|
|
28
|
-
* The controller owns handle rendering and hit-tests handles in lonLat space;
|
|
29
|
-
* the driver only carries raw pointer events, drag-pan toggle, and pixel
|
|
30
|
-
* projection.
|
|
31
|
-
*/
|
|
32
|
-
interface EditPointerDriver {
|
|
33
|
-
onPointerDown(cb: PointerCallback): () => void;
|
|
34
|
-
onPointerMove(cb: PointerCallback): () => void;
|
|
35
|
-
onPointerUp(cb: PointerCallback): () => void;
|
|
36
|
-
setDragPanEnabled(enabled: boolean): void;
|
|
37
|
-
toPixel(lonLat: Position): PixelCoordinate | null;
|
|
38
|
-
fromPixel(pixel: PixelCoordinate): Position;
|
|
39
|
-
/** Idempotent. */
|
|
40
|
-
dispose(): void;
|
|
41
|
-
}
|
|
42
|
-
//#endregion
|
|
43
|
-
//#region src/fill-pattern.d.ts
|
|
44
|
-
/**
|
|
45
|
-
* Shared hatch fill-pattern tiles for area control-measure fills (ADR-0035
|
|
46
|
-
* `FillPattern`, excluding `"solid"` which every adapter already renders as a
|
|
47
|
-
* flat color). Both the MapLibre adapter (raster `addImage` pattern) and the
|
|
48
|
-
* OpenLayers adapter (canvas `Pattern` fill) need the *same* diagonal-hachure
|
|
49
|
-
* texture, so it is rasterized once here in pure math — no `HTMLCanvasElement`
|
|
50
|
-
* — so it works in adapter code and in jsdom/node tests alike.
|
|
51
|
-
*/
|
|
52
|
-
type HatchPattern = Exclude<FillPattern, "solid">;
|
|
53
|
-
/**
|
|
54
|
-
* Every non-solid `FillPattern` as a runtime list — the value-level companion
|
|
55
|
-
* to {@link HatchPattern}, so adapters can test membership and build filter
|
|
56
|
-
* expressions without re-spelling the pattern vocabulary.
|
|
57
|
-
*/
|
|
58
|
-
declare const HATCH_PATTERNS: readonly HatchPattern[];
|
|
59
|
-
/**
|
|
60
|
-
* Stable id for a rasterized tile, keyed by pattern and color. Adapters use
|
|
61
|
-
* this as the image-registry key (MapLibre `addImage` id, OpenLayers pattern
|
|
62
|
-
* cache key) so the same color+pattern never gets rasterized twice. Color is
|
|
63
|
-
* lowercased so `"#FF0000"` and `"#ff0000"` share one texture.
|
|
64
|
-
*/
|
|
65
|
-
declare function fillPatternImageId(color: string, pattern: HatchPattern): string;
|
|
66
|
-
/**
|
|
67
|
-
* Tile edge length in device pixels for a given `pixelRatio`. Must be a power
|
|
68
|
-
* of two — MapLibre requires POT dimensions for seamless WebGL texture wrap —
|
|
69
|
-
* and scales linearly with `pixelRatio` for the ratios that matter in
|
|
70
|
-
* practice (1x/2x displays: 32 -> 64, both already POT). Non-POT products
|
|
71
|
-
* (e.g. a 3x ratio, 32*3=96) round up to the next POT rather than losing the
|
|
72
|
-
* power-of-two guarantee.
|
|
73
|
-
*/
|
|
74
|
-
declare function fillPatternImageDimension(pixelRatio: number): number;
|
|
75
|
-
/**
|
|
76
|
-
* Rasterize one seamless hatch tile as a straight RGBA pixel buffer — no
|
|
77
|
-
* canvas involved, so this runs identically in a browser adapter and in
|
|
78
|
-
* jsdom/node tests. Diagonal lines are snapped to slope ±1 (45°) so the
|
|
79
|
-
* mod-based coverage math in {@link lineCoverage} tiles exactly. `"hachure"`
|
|
80
|
-
* draws the "/" diagonal, `"backhachure"` its "\" mirror, and `"cross"` unions
|
|
81
|
-
* both via `Math.max` coverage.
|
|
82
|
-
*/
|
|
83
|
-
declare function makeFillPatternImage(color: string, pattern: HatchPattern, pixelRatio?: number): {
|
|
84
|
-
width: number;
|
|
85
|
-
height: number;
|
|
86
|
-
data: Uint8ClampedArray;
|
|
87
|
-
};
|
|
88
|
-
//#endregion
|
|
89
|
-
//#region src/map-adapter.d.ts
|
|
90
|
-
type PixelCoordinate = [number, number];
|
|
91
|
-
type MapCursor = "" | "default" | "crosshair" | "grab" | "grabbing" | "pointer";
|
|
92
|
-
interface MapEvent {
|
|
93
|
-
type: string;
|
|
94
|
-
coordinate: Position;
|
|
95
|
-
pixel: PixelCoordinate;
|
|
96
|
-
originalEvent: unknown;
|
|
97
|
-
}
|
|
98
|
-
type MapEventHandler = (event: MapEvent) => void;
|
|
99
|
-
interface StyleOptions {
|
|
100
|
-
strokeColor?: string;
|
|
101
|
-
strokeWidth?: number;
|
|
102
|
-
strokeDash?: number[];
|
|
103
|
-
fillColor?: string;
|
|
104
|
-
pointFillColor?: string;
|
|
105
|
-
pointRadius?: number;
|
|
106
|
-
text?: TextStyleOptions;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Text (label) style for a layer. When `labelProperty` is set, adapters also
|
|
110
|
-
* read per-feature label properties: `rotation` (radians, rendered as
|
|
111
|
-
* `π − rotation`), the size anchor's `textSizeMeters` (ground) or
|
|
112
|
-
* `textSizePixels` (screen) (ADR-0028), and `textAnchor` (`"start"` = text
|
|
113
|
-
* begins at the label point, `"end"` = text ends there; absent = centered).
|
|
114
|
-
* A ground-anchored label renders at
|
|
115
|
-
* `clamp(textSizeMeters / resolution, textSizeMinPixels, textSizeMaxPixels)`;
|
|
116
|
-
* a screen-anchored one at a constant `textSizePixels`, unclamped.
|
|
117
|
-
*/
|
|
118
|
-
interface TextStyleOptions {
|
|
119
|
-
text?: string;
|
|
120
|
-
labelProperty?: string;
|
|
121
|
-
font?: string;
|
|
122
|
-
fillColor?: string;
|
|
123
|
-
strokeColor?: string;
|
|
124
|
-
strokeWidth?: number;
|
|
125
|
-
rotation?: number;
|
|
126
|
-
/**
|
|
127
|
-
* Lower edge of the ground-anchored label clamp band (ADR-0028): text never
|
|
128
|
-
* renders below this pixel size, however far the view zooms out.
|
|
129
|
-
* @default 8
|
|
130
|
-
*/
|
|
131
|
-
textSizeMinPixels?: number;
|
|
132
|
-
/**
|
|
133
|
-
* Upper edge of the ground-anchored label clamp band (ADR-0028): text never
|
|
134
|
-
* renders above this pixel size, however far the view zooms in.
|
|
135
|
-
* @default 24
|
|
136
|
-
*/
|
|
137
|
-
textSizeMaxPixels?: number;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Per-feature style for edit handles (vertex / midpoint / translate / rotate).
|
|
141
|
-
* The producer (`TacticalDraw`) rides this on `properties.style`; the adapter
|
|
142
|
-
* reads it back when rendering the handle, so it is part of the adapter
|
|
143
|
-
* contract rather than the tactical-draw authoring surface. `pointRadius` is a
|
|
144
|
-
* UI affordance not present on `ControlMeasureStyle`. See ADR-0006.
|
|
145
|
-
*/
|
|
146
|
-
interface HandleStyle {
|
|
147
|
-
strokeColor?: string;
|
|
148
|
-
strokeWidth?: number;
|
|
149
|
-
strokeDash?: readonly number[];
|
|
150
|
-
fillColor?: string;
|
|
151
|
-
/**
|
|
152
|
-
* How `fillColor` paints a filled part's interior. Absent or `"solid"` is
|
|
153
|
-
* today's flat fill; `"hachure"`/`"backhachure"`/`"cross"` request the shared hatch texture
|
|
154
|
-
* ({@link makeFillPatternImage}). Alpha rides in `fillColor` — there is no
|
|
155
|
-
* separate opacity field. Adapters that cannot rasterize a pattern degrade
|
|
156
|
-
* to solid. Per-feature only: layer-level `StyleOptions` has no equivalent.
|
|
157
|
-
*/
|
|
158
|
-
fillPattern?: "solid" | HatchPattern;
|
|
159
|
-
pointRadius?: number;
|
|
160
|
-
}
|
|
161
|
-
type LayerId = string;
|
|
162
|
-
/** Canonical SVG resource shared by point features through `iconId`. */
|
|
163
|
-
interface IconResource {
|
|
164
|
-
id: string;
|
|
165
|
-
svg: string;
|
|
166
|
-
width: number;
|
|
167
|
-
height: number;
|
|
168
|
-
anchor: [number, number];
|
|
169
|
-
/**
|
|
170
|
-
* Direct visible mark used to rank icon picks, in unscaled canvas coordinates.
|
|
171
|
-
* Hits elsewhere in the full canvas measure their distance from this rectangle;
|
|
172
|
-
* omit it to preserve whole-canvas distance-zero behavior.
|
|
173
|
-
*/
|
|
174
|
-
coreRect?: {
|
|
175
|
-
x: number;
|
|
176
|
-
y: number;
|
|
177
|
-
width: number;
|
|
178
|
-
height: number;
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Click-style hit event delivered by {@link MapAdapter.onPick}.
|
|
183
|
-
*
|
|
184
|
-
* `id` is the control measure id, already split off the feature id by the
|
|
185
|
-
* adapter (see `controlMeasureIdFromFeature`).
|
|
186
|
-
*
|
|
187
|
-
* `originalEvent` is whatever the engine surfaces — OpenLayers normalises to
|
|
188
|
-
* `PointerEvent`, MapLibre delivers `MouseEvent`, Leaflet delivers
|
|
189
|
-
* `MouseEvent | TouchEvent`. Do not narrow to `PointerEvent` at the consumer.
|
|
190
|
-
*/
|
|
191
|
-
interface PickEvent {
|
|
192
|
-
id: string;
|
|
193
|
-
feature: Feature;
|
|
194
|
-
pixel: PixelCoordinate;
|
|
195
|
-
coordinate: Position;
|
|
196
|
-
originalEvent: MouseEvent | PointerEvent | TouchEvent;
|
|
197
|
-
}
|
|
198
|
-
type PickHandler = (event: PickEvent) => void;
|
|
199
|
-
interface PickOptions {
|
|
200
|
-
signal?: AbortSignal;
|
|
201
|
-
}
|
|
202
|
-
interface MapAdapter {
|
|
203
|
-
/**
|
|
204
|
-
* Add a vector layer to the map.
|
|
205
|
-
* @returns unique layer ID
|
|
206
|
-
*/
|
|
207
|
-
addVectorLayer(options?: {
|
|
208
|
-
style?: StyleOptions;
|
|
209
|
-
}): LayerId;
|
|
210
|
-
/**
|
|
211
|
-
* Remove a layer from the map.
|
|
212
|
-
*/
|
|
213
|
-
removeLayer(layerId: LayerId): void;
|
|
214
|
-
/**
|
|
215
|
-
* Set the style for a specific layer.
|
|
216
|
-
*/
|
|
217
|
-
setLayerStyle(layerId: LayerId, style: StyleOptions): void;
|
|
218
|
-
/** Reconcile the layer's desired shared icon resources. */
|
|
219
|
-
reconcileLayerIconResources(layerId: LayerId, resources: readonly IconResource[]): void;
|
|
220
|
-
/**
|
|
221
|
-
* Set features on a vector layer.
|
|
222
|
-
* Features should be standard GeoJSON Features.
|
|
223
|
-
*/
|
|
224
|
-
setLayerFeatures(layerId: LayerId, features: Feature[]): void;
|
|
225
|
-
/**
|
|
226
|
-
* Clear all features from a layer.
|
|
227
|
-
*/
|
|
228
|
-
clearLayer(layerId: LayerId): void;
|
|
229
|
-
/**
|
|
230
|
-
* Reconcile features on a layer against the incoming set, keyed by `id`.
|
|
231
|
-
*
|
|
232
|
-
* Semantics:
|
|
233
|
-
* - features with an `id` that already exists are updated in place;
|
|
234
|
-
* - features with a fresh `id` are added;
|
|
235
|
-
* - features in the layer whose `id` is missing from the incoming set are
|
|
236
|
-
* removed;
|
|
237
|
-
* - the incoming array order is authoritative for render order — later
|
|
238
|
-
* features draw on top, so a call that only reorders is a visible change.
|
|
239
|
-
*
|
|
240
|
-
* Features must carry a stable `id`. Use this when you can compute the
|
|
241
|
-
* complete desired state of a layer in one go; for partial removals use
|
|
242
|
-
* {@link removeFeatures}.
|
|
243
|
-
*/
|
|
244
|
-
updateLayerFeatures(layerId: LayerId, features: readonly Feature[]): void;
|
|
245
|
-
/**
|
|
246
|
-
* Remove features by `id` from a layer. Ids that are not present are
|
|
247
|
-
* silently ignored.
|
|
248
|
-
*/
|
|
249
|
-
removeFeatures(layerId: LayerId, ids: readonly string[]): void;
|
|
250
|
-
/**
|
|
251
|
-
* Defer an upcoming removal from `layerId` until the most recent update to
|
|
252
|
-
* `updatedLayerId` has completed in the engine. Optional because synchronous
|
|
253
|
-
* adapters already make cross-layer write order visible atomically.
|
|
254
|
-
*/
|
|
255
|
-
deferLayerRemovalUntilLayerUpdate?(layerId: LayerId, updatedLayerId: LayerId): void;
|
|
256
|
-
/**
|
|
257
|
-
* Subscribe to click-style "pick" events for a specific layer. Basemap and
|
|
258
|
-
* other-layer clicks never fire the handler.
|
|
259
|
-
*
|
|
260
|
-
* For control-measure features, adapters must set `PickEvent.id` to the
|
|
261
|
-
* originating control-measure id (not the rendered part's feature id).
|
|
262
|
-
* Features that do not satisfy the control-measure feature-id contract must
|
|
263
|
-
* be ignored rather than dispatched.
|
|
264
|
-
*
|
|
265
|
-
* Bound to the engine's tap-suppressing click event (OL `singleclick`,
|
|
266
|
-
* MapLibre `click`, Leaflet `click`) so the handler does not fire at the
|
|
267
|
-
* end of a pan or pinch-zoom gesture.
|
|
268
|
-
*
|
|
269
|
-
* Hit tolerance is pointer-type aware: 4 px for mouse/pen, 12 px for touch.
|
|
270
|
-
* Outline-only polygon interiors additionally have bounded inward selection
|
|
271
|
-
* reach from visible viewport-clipped boundaries: 24 px for mouse/pen and
|
|
272
|
-
* 36 px for touch. A visible solid or patterned fill owns its full interior.
|
|
273
|
-
*
|
|
274
|
-
* Returns an unsubscribe function. The returned function and the optional
|
|
275
|
-
* `signal` are both honoured; either teardown path stops further events,
|
|
276
|
-
* and both are idempotent.
|
|
277
|
-
*/
|
|
278
|
-
onPick(layerId: LayerId, handler: PickHandler, opts?: PickOptions): () => void;
|
|
279
|
-
/**
|
|
280
|
-
* Map Event Listeners
|
|
281
|
-
*
|
|
282
|
-
* Ordering contract: a `"click"` handler and the `onPick` handlers for the
|
|
283
|
-
* same user click MUST observe the pick first — adapters deliver picks and
|
|
284
|
-
* `"click"` from the same underlying engine event, in registration order.
|
|
285
|
-
* The draw/edit controllers register their pick subscriptions before their
|
|
286
|
-
* click handler and read the stashed pick inside it (click-away close /
|
|
287
|
-
* close-then-pick re-dispatch); an adapter that fires `"click"` from an
|
|
288
|
-
* earlier engine event than its picks breaks that contract (e.g. OL
|
|
289
|
-
* `click` vs `singleclick`).
|
|
290
|
-
*/
|
|
291
|
-
on(eventType: "click" | "pointermove" | "dblclick", handler: MapEventHandler): void;
|
|
292
|
-
off(eventType: "click" | "pointermove" | "dblclick", handler: MapEventHandler): void;
|
|
293
|
-
/**
|
|
294
|
-
* Projection / Coordinate Utilities
|
|
295
|
-
*/
|
|
296
|
-
toLonLat(coordinate: number[]): Position;
|
|
297
|
-
fromLonLat(position: Position): number[];
|
|
298
|
-
getPixelFromCoordinate(position: Position): PixelCoordinate | null;
|
|
299
|
-
/**
|
|
300
|
-
* Convert a viewport pixel to longitude/latitude. Optional so third-party
|
|
301
|
-
* adapters compiled against an older ABI remain valid.
|
|
302
|
-
*/
|
|
303
|
-
getCoordinateFromPixel?(pixel: PixelCoordinate): Position | null;
|
|
304
|
-
/**
|
|
305
|
-
* Get current viewport size in CSS pixels.
|
|
306
|
-
*/
|
|
307
|
-
getViewportSize(): {
|
|
308
|
-
width: number;
|
|
309
|
-
height: number;
|
|
310
|
-
};
|
|
311
|
-
/**
|
|
312
|
-
* Pan the map by pixel delta.
|
|
313
|
-
* Positive delta means desired on-screen feature shift (right/down).
|
|
314
|
-
*/
|
|
315
|
-
panByPixels(delta: PixelCoordinate, options?: {
|
|
316
|
-
durationMs?: number;
|
|
317
|
-
}): void;
|
|
318
|
-
/**
|
|
319
|
-
* Set the map viewport cursor. Pass an empty string to restore engine default styling.
|
|
320
|
-
*/
|
|
321
|
-
setCursor(cursor: MapCursor): void;
|
|
322
|
-
/**
|
|
323
|
-
* Toggle the engine's native double-click-to-zoom gesture. `TacticalDraw`
|
|
324
|
-
* disables this for the lifetime of a variable-length draw so the commit
|
|
325
|
-
* `dblclick` doesn't also zoom the map.
|
|
326
|
-
*/
|
|
327
|
-
setDoubleClickZoomEnabled(enabled: boolean): void;
|
|
328
|
-
/**
|
|
329
|
-
* Get current view resolution (meters per pixel at center or generally).
|
|
330
|
-
* returns undefined if not supported or not ready.
|
|
331
|
-
*/
|
|
332
|
-
getResolution(): number | undefined;
|
|
333
|
-
/**
|
|
334
|
-
* Get current map zoom level
|
|
335
|
-
*/
|
|
336
|
-
getZoom(): number | undefined;
|
|
337
|
-
/**
|
|
338
|
-
* Get current view bearing, radians — the clockwise-from-north compass
|
|
339
|
-
* direction that screen-up points to (MapLibre-bearing semantics). `0` for
|
|
340
|
-
* engines/views that don't rotate.
|
|
341
|
-
*/
|
|
342
|
-
getBearing(): number;
|
|
343
|
-
/**
|
|
344
|
-
* Listen for view changes (zoom/resolution)
|
|
345
|
-
*/
|
|
346
|
-
onViewChange(handler: () => void): void;
|
|
347
|
-
offViewChange(handler: () => void): void;
|
|
348
|
-
/**
|
|
349
|
-
* The engine evaluates the meters→pixels clamp for ground-sized point
|
|
350
|
-
* symbols inside its own style layer, so meters-sized point symbols don't
|
|
351
|
-
* need view-change re-renders for size.
|
|
352
|
-
*/
|
|
353
|
-
readonly scalesGroundPointSymbolsNatively?: boolean;
|
|
354
|
-
/**
|
|
355
|
-
* Construct an {@link EditPointerDriver} for `TacticalDraw`'s edit
|
|
356
|
-
* controller. The controller subscribes to pointer events on the returned
|
|
357
|
-
* driver to wire handle drags (vertex / midpoint / translate / rotate).
|
|
358
|
-
*/
|
|
359
|
-
createEditPointerDriver(): EditPointerDriver;
|
|
360
|
-
/**
|
|
361
|
-
* Release all adapter-owned map resources. Adapters must not be used after
|
|
362
|
-
* destruction.
|
|
363
|
-
*/
|
|
364
|
-
destroy(): void;
|
|
365
|
-
/**
|
|
366
|
-
* Returns the features currently set on the layer — the adapter-side cache
|
|
367
|
-
* of the last `setLayerFeatures`/`updateLayerFeatures`/`removeFeatures`/
|
|
368
|
-
* `clearLayer` state. Empty array for an unknown layer. Callers must not
|
|
369
|
-
* mutate the returned array or features.
|
|
370
|
-
*/
|
|
371
|
-
getLayerFeatures(layerId: LayerId): readonly Feature[];
|
|
372
|
-
}
|
|
373
|
-
//#endregion
|
|
374
5
|
//#region src/base-map-adapter.d.ts
|
|
375
|
-
interface FeatureOps {
|
|
376
|
-
adds: Feature[];
|
|
377
|
-
updates: Feature[];
|
|
378
|
-
/** Features whose id was absent from the incoming set. */
|
|
379
|
-
removes?: Feature[];
|
|
380
|
-
/**
|
|
381
|
-
* Complete, ordered snapshot of the layer after this op. The order is
|
|
382
|
-
* authoritative for render order (later features draw on top), so
|
|
383
|
-
* `applyFeatureOps` must leave the layer rendering in `full`'s order — a
|
|
384
|
-
* pure reorder ships identical content in a new order and still counts as a
|
|
385
|
-
* change. Adapters that rebuild the layer from `full` satisfy this for free;
|
|
386
|
-
* incremental adapters must reconcile order against it.
|
|
387
|
-
*/
|
|
388
|
-
full: Feature[];
|
|
389
|
-
replace?: boolean;
|
|
390
|
-
}
|
|
391
6
|
type NormalizedEventHandler = (lonLat: Position, pixel: PixelCoordinate, originalEvent: unknown) => void;
|
|
392
7
|
interface NativeEventEntry<TEventToken> {
|
|
393
8
|
type: string;
|
|
@@ -397,10 +12,8 @@ interface NativeEventEntry<TEventToken> {
|
|
|
397
12
|
* Abstract base class shared by all `MapAdapter` implementations.
|
|
398
13
|
*
|
|
399
14
|
* Owns shared bookkeeping (layer registry, feature cache, event-token
|
|
400
|
-
* tracking) and orchestrates the public methods over a small
|
|
401
|
-
*
|
|
402
|
-
* `docs/adr/0001-map-adapter-base-class.md` for the rationale, in particular
|
|
403
|
-
* around the `applyFeatureOps` primitive shape.
|
|
15
|
+
* tracking) and orchestrates the public methods over a small protected
|
|
16
|
+
* grouped desired-state primitive that subclasses implement.
|
|
404
17
|
*/
|
|
405
18
|
declare abstract class BaseMapAdapter<TLayerHandle, TEventToken, TViewToken> implements MapAdapter {
|
|
406
19
|
protected layers: Map<string, TLayerHandle>;
|
|
@@ -409,6 +22,7 @@ declare abstract class BaseMapAdapter<TLayerHandle, TEventToken, TViewToken> imp
|
|
|
409
22
|
protected featureCache: Map<string, Feature<import("geojson").Geometry, import("geojson").GeoJsonProperties>[]>;
|
|
410
23
|
protected featuresChangedListeners: Map<string, Set<(features: Feature[]) => void>>;
|
|
411
24
|
protected iconResources: Map<string, IconResource>;
|
|
25
|
+
private featureGroupState;
|
|
412
26
|
private layerIconResourceIds;
|
|
413
27
|
private layerCounter;
|
|
414
28
|
private destroyed;
|
|
@@ -417,13 +31,9 @@ declare abstract class BaseMapAdapter<TLayerHandle, TEventToken, TViewToken> imp
|
|
|
417
31
|
style?: StyleOptions;
|
|
418
32
|
}): LayerId;
|
|
419
33
|
removeLayer(layerId: LayerId): void;
|
|
420
|
-
|
|
421
|
-
setLayerFeatures(layerId: LayerId, features: readonly Feature[]): void;
|
|
422
|
-
updateLayerFeatures(layerId: LayerId, features: readonly Feature[]): void;
|
|
423
|
-
removeFeatures(layerId: LayerId, ids: readonly string[]): void;
|
|
34
|
+
setLayerFeatureGroups(layerId: LayerId, groups: readonly FeatureGroup[]): void;
|
|
424
35
|
abstract onPick(layerId: LayerId, handler: PickHandler, opts?: PickOptions): () => void;
|
|
425
36
|
abstract createEditPointerDriver(): EditPointerDriver;
|
|
426
|
-
clearLayer(layerId: LayerId): void;
|
|
427
37
|
on(eventType: "click" | "pointermove" | "dblclick", handler: MapEventHandler): void;
|
|
428
38
|
off(eventType: "click" | "pointermove" | "dblclick", handler: MapEventHandler): void;
|
|
429
39
|
onViewChange(handler: () => void): void;
|
|
@@ -434,11 +44,13 @@ declare abstract class BaseMapAdapter<TLayerHandle, TEventToken, TViewToken> imp
|
|
|
434
44
|
protected destroyEngineResources(): void;
|
|
435
45
|
protected applyIconResources(resources: readonly IconResource[]): void;
|
|
436
46
|
protected getIconResource(id: string): IconResource | undefined;
|
|
47
|
+
protected getFeatureGroupOrder(layerId: LayerId, featureId: string | number | undefined): number | undefined;
|
|
437
48
|
protected removeIconResource(id: string): void;
|
|
438
49
|
private pruneIconResources;
|
|
439
50
|
protected abstract createLayer(layerId: LayerId, style?: StyleOptions): TLayerHandle;
|
|
440
51
|
protected abstract destroyLayer(handle: TLayerHandle): void;
|
|
441
|
-
|
|
52
|
+
/** Apply one validated, complete grouped desired state. */
|
|
53
|
+
protected abstract applyFeatureGroups(layerId: LayerId, groups: readonly FeatureGroup[]): void;
|
|
442
54
|
protected abstract attachNativeEvent(type: "click" | "pointermove" | "dblclick", onEvent: NormalizedEventHandler): NativeEventEntry<TEventToken>;
|
|
443
55
|
protected abstract detachNativeEvent(entry: NativeEventEntry<TEventToken>): void;
|
|
444
56
|
protected abstract attachViewChange(handler: () => void): TViewToken;
|
|
@@ -529,6 +141,8 @@ interface PickCandidate {
|
|
|
529
141
|
viewport: ScreenBounds;
|
|
530
142
|
presentation: ResolvedPickPresentation;
|
|
531
143
|
visualOrder: number;
|
|
144
|
+
/** Lower values mean a visually higher atomic feature group. */
|
|
145
|
+
featureGroupOrder?: number;
|
|
532
146
|
iconFootprint?: readonly PixelCoordinate[];
|
|
533
147
|
labelExtent?: LabelExtent;
|
|
534
148
|
}
|
|
@@ -540,6 +154,11 @@ interface PickCandidateInput {
|
|
|
540
154
|
* native ordering to topmost-first before calling into evaluation.
|
|
541
155
|
*/
|
|
542
156
|
visualOrder: number;
|
|
157
|
+
/**
|
|
158
|
+
* Authoritative bottom-to-top group order normalized to lower = visually
|
|
159
|
+
* higher. Unlike `visualOrder`, native engine query order cannot override it.
|
|
160
|
+
*/
|
|
161
|
+
featureGroupOrder?: number;
|
|
543
162
|
/** The engine already reported this part as hit; retained as a fallback for engine-only marks. */
|
|
544
163
|
nativeHit?: boolean;
|
|
545
164
|
icon?: IconResource;
|
|
@@ -1281,8 +900,7 @@ interface TransformSession {
|
|
|
1281
900
|
* - **Removed** members (id in the current set, absent from the next) are
|
|
1282
901
|
* committed at their current working state.
|
|
1283
902
|
* - **Added** members (id only in the next set) join at their passed input
|
|
1284
|
-
* state
|
|
1285
|
-
* like initial members.
|
|
903
|
+
* state and use their host-provided Graphic-stack slot when present.
|
|
1286
904
|
*
|
|
1287
905
|
* The transform box re-derives from the next member set at the session's
|
|
1288
906
|
* current box angle. Session-scoped state (`sizeAnchor`, box angle,
|
|
@@ -1299,11 +917,12 @@ interface TransformSession {
|
|
|
1299
917
|
* size left untouched, then cloned and rendered); an empty array when nothing
|
|
1300
918
|
* was removed. The outer `editMany` promise resolves with the snapshots of
|
|
1301
919
|
* the members in the set **at close time only**. Removed members are NOT
|
|
1302
|
-
*
|
|
1303
|
-
* returned
|
|
1304
|
-
* contract as [[close]]. A removed member's exit snapshot is also
|
|
1305
|
-
* through [[onCommit]] — the recommended channel, since it covers
|
|
1306
|
-
* value and the close-time promise resolution through a single
|
|
920
|
+
* retained in its Graphic-stack slot until the host acknowledges it by
|
|
921
|
+
* folding the returned snapshot into its document and calling `td.render()`,
|
|
922
|
+
* the same contract as [[close]]. A removed member's exit snapshot is also
|
|
923
|
+
* delivered through [[onCommit]] — the recommended channel, since it covers
|
|
924
|
+
* this return value and the close-time promise resolution through a single
|
|
925
|
+
* subscription.
|
|
1307
926
|
* Returns `undefined` (a silent no-op) after the session has settled.
|
|
1308
927
|
*
|
|
1309
928
|
* `setGraphics([])` on a live session delegates to [[close]]: it commits every
|
|
@@ -1378,9 +997,8 @@ interface TransformSession {
|
|
|
1378
997
|
* emission, so a listener that clears selection state observes a document
|
|
1379
998
|
* that already contains the exit snapshots.
|
|
1380
999
|
* - On every path it fires **after** the facade has already cleared its
|
|
1381
|
-
* slot — `td.activeTransformSession` is `null
|
|
1382
|
-
*
|
|
1383
|
-
* works from inside the handler.
|
|
1000
|
+
* slot — `td.activeTransformSession` is `null`, so fold-then-render works
|
|
1001
|
+
* from inside the handler.
|
|
1384
1002
|
* - `reason` distinguishes commit from abort but is deliberately coarse —
|
|
1385
1003
|
* no payload; snapshots were already delivered through [[onCommit]].
|
|
1386
1004
|
*
|
|
@@ -1778,6 +1396,7 @@ declare class TacticalDraw {
|
|
|
1778
1396
|
private readonly pointSymbols;
|
|
1779
1397
|
private snappingOptions;
|
|
1780
1398
|
private readonly resolveSnap;
|
|
1399
|
+
private readonly graphicStack;
|
|
1781
1400
|
private destroyed;
|
|
1782
1401
|
private hasRendered;
|
|
1783
1402
|
private readonly activeInteraction;
|
|
@@ -1821,12 +1440,7 @@ declare class TacticalDraw {
|
|
|
1821
1440
|
private idCounter;
|
|
1822
1441
|
private editCounter;
|
|
1823
1442
|
private transformCounter;
|
|
1824
|
-
/**
|
|
1825
|
-
* Snapshot of the host's last `render()` argument. The edit slice uses it
|
|
1826
|
-
* to recompute the graphics-layer state during a lift / restore — the
|
|
1827
|
-
* façade owns the policy because controllers shouldn't need to know the
|
|
1828
|
-
* surrounding collection state.
|
|
1829
|
-
*/
|
|
1443
|
+
/** Snapshot of the host's latest authoritative Graphic membership and order. */
|
|
1830
1444
|
private lastRenderedMeasures;
|
|
1831
1445
|
private snapGeometryFeatures;
|
|
1832
1446
|
/**
|
|
@@ -1848,31 +1462,31 @@ declare class TacticalDraw {
|
|
|
1848
1462
|
setSnappingOptions(options?: SnappingOptions): void;
|
|
1849
1463
|
/** Clear transient host affordances when snapping or its interaction ends. */
|
|
1850
1464
|
private clearSnapResult;
|
|
1851
|
-
/**
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
private
|
|
1465
|
+
/** Render one atomic Graphic group for the engine-neutral graphics stack. */
|
|
1466
|
+
private renderGraphicGroup;
|
|
1467
|
+
/** Submit one complete grouped desired state for the host stack plus overrides. */
|
|
1468
|
+
private reconcileGraphicsStack;
|
|
1469
|
+
/** Atomically publish several live renders into their host-owned Graphic slots. */
|
|
1470
|
+
private publishWorkingGraphics;
|
|
1471
|
+
/** Settle several stack overrides in one graphics-layer reconciliation. */
|
|
1472
|
+
private settleWorkingGraphics;
|
|
1859
1473
|
/**
|
|
1860
1474
|
* Re-render the graphics layer on a view (zoom) change so pixel-denominated
|
|
1861
1475
|
* and not-yet-committed label-sized measures rescale live. Skipped entirely
|
|
1862
1476
|
* when nothing rendered still reads live view state — meter geometry with a
|
|
1863
1477
|
* captured label size is zoom-independent, so a rebuild would be wasted
|
|
1864
|
-
* layer writes.
|
|
1865
|
-
*
|
|
1866
|
-
*
|
|
1478
|
+
* layer writes. A single-control-measure edit publishes through the stack
|
|
1479
|
+
* coordinator; point symbols and transform members already present in the
|
|
1480
|
+
* host stack do the same. Graphics without a host slot remain on preview.
|
|
1867
1481
|
*/
|
|
1868
1482
|
private readonly handleViewChange;
|
|
1869
1483
|
/**
|
|
1870
|
-
* Every
|
|
1871
|
-
*
|
|
1872
|
-
* reconciliation because the preview layer (not graphics) owns their
|
|
1873
|
-
* working copy. Empty while idle.
|
|
1484
|
+
* Every Graphic id owned by an active interaction. Used for highlight and
|
|
1485
|
+
* pick routing; not every such Graphic is excluded from stack reconciliation.
|
|
1874
1486
|
*/
|
|
1875
1487
|
private excludedInteractionIds;
|
|
1488
|
+
/** Interaction ids still rendered on the legacy preview surface. */
|
|
1489
|
+
private graphicsStackExcludedIds;
|
|
1876
1490
|
/**
|
|
1877
1491
|
* Live member ids of the active group transform, or `undefined` when none.
|
|
1878
1492
|
* Falls back to the session ref because `onSession` fires from inside the
|
|
@@ -1886,24 +1500,24 @@ declare class TacticalDraw {
|
|
|
1886
1500
|
* Idempotent. Throws synchronously on duplicate measure ids *before*
|
|
1887
1501
|
* mutating any layer. An empty list before any prior render is a no-op.
|
|
1888
1502
|
*
|
|
1889
|
-
* Cross-layer semantics with an active edit
|
|
1890
|
-
* - The
|
|
1891
|
-
*
|
|
1892
|
-
* must stay in place.
|
|
1503
|
+
* Cross-layer semantics with an active edit:
|
|
1504
|
+
* - The stack coordinator replaces the edited Graphic's host-owned slot
|
|
1505
|
+
* with its working render; membership and order remain host-authoritative.
|
|
1893
1506
|
* - If the edited measure is absent from `measures`, the pending `edit`
|
|
1894
1507
|
* promise rejects on the **next microtask** with reason `"removed"`.
|
|
1895
1508
|
* `render` itself returns normally so host collection ops stay synchronous.
|
|
1896
|
-
* - If the edited measure is present and its `style` changed,
|
|
1897
|
-
* picks up the new style on this render. `controlPoints` changes on the
|
|
1509
|
+
* - If the edited measure is present and its `style` changed, its working
|
|
1510
|
+
* group picks up the new style on this render. `controlPoints` changes on the
|
|
1898
1511
|
* edited measure are routed nowhere — the user's in-flight drag is never
|
|
1899
|
-
* yanked out from under them.
|
|
1512
|
+
* yanked out from under them. A point symbol's complete working group remains
|
|
1513
|
+
* authoritative until settle, including any changed icon resource.
|
|
1900
1514
|
*
|
|
1901
1515
|
* Cross-layer semantics with an active draw: `render` only touches the
|
|
1902
1516
|
* graphics layer, so the draw's preview is undisturbed.
|
|
1903
1517
|
*
|
|
1904
1518
|
* Reentrancy: calling `td.render(...)` from inside `EditSession.onChange` is
|
|
1905
|
-
* safe — `render` never re-enters the active edit or its
|
|
1906
|
-
*
|
|
1519
|
+
* safe — `render` never re-enters the active edit or its handle layer (the
|
|
1520
|
+
* only edit-facing side effect, `notifyExternalMeasure`, is a
|
|
1907
1521
|
* pure style replay).
|
|
1908
1522
|
*/
|
|
1909
1523
|
render(measures: readonly Graphic[]): void;
|
|
@@ -1912,7 +1526,7 @@ declare class TacticalDraw {
|
|
|
1912
1526
|
* idempotent (mirrors {@link render}): a non-interactive outline box is drawn
|
|
1913
1527
|
* around each still-rendered graphic on the dedicated highlight layer — no
|
|
1914
1528
|
* draw / edit / transform session, no promise, no pick subscription. Passing
|
|
1915
|
-
* `[]` clears every highlight. Unknown ids and ids currently
|
|
1529
|
+
* `[]` clears every highlight. Unknown ids and ids currently active in an
|
|
1916
1530
|
* active edit / transform session are skipped (the live session's own visuals
|
|
1917
1531
|
* supersede the passive outline). Hosts use it for Excalidraw-style
|
|
1918
1532
|
* "locked selection" chrome.
|
|
@@ -1921,9 +1535,9 @@ declare class TacticalDraw {
|
|
|
1921
1535
|
/**
|
|
1922
1536
|
* Project the current {@link highlightedIds} into one outline `LineString`
|
|
1923
1537
|
* per graphic and reconcile them onto the highlight layer. Idempotent and
|
|
1924
|
-
*
|
|
1925
|
-
*
|
|
1926
|
-
* unknown (not in the last render) or
|
|
1538
|
+
* grouped by graphic id, so desired-state replacement removes stale outlines
|
|
1539
|
+
* and an unchanged set is a cheap no-op write. Skips ids that are
|
|
1540
|
+
* unknown (not in the last render) or active in an interaction session, and
|
|
1927
1541
|
* bails gracefully when the adapter cannot invert pixels to coordinates
|
|
1928
1542
|
* (`getCoordinateFromPixel` is optional in the MapAdapter ABI).
|
|
1929
1543
|
*/
|
|
@@ -2004,9 +1618,10 @@ declare class TacticalDraw {
|
|
|
2004
1618
|
edit(measure: ControlMeasure, options?: EditOptions): Promise<GraphicSnapshot<ControlMeasure>>;
|
|
2005
1619
|
/**
|
|
2006
1620
|
* Start an interactive group transform (multi-select) over `measures`
|
|
2007
|
-
* (ADR-0017 "Multi-select" slice). Every member
|
|
2008
|
-
*
|
|
2009
|
-
*
|
|
1621
|
+
* (ADR-0017 "Multi-select" slice). Every host-provided member stays in its
|
|
1622
|
+
* Graphic-stack slot; only a member without a host slot uses preview. The box
|
|
1623
|
+
* (corner scale, rotate grip, drag-anywhere body translate) applies rigidly
|
|
1624
|
+
* to all of them at once. Returns a Promise that
|
|
2010
1625
|
* resolves with `GraphicSnapshot<ControlMeasure>[]` on `session.close()` and rejects
|
|
2011
1626
|
* with `TacticalDrawAbortError` on signal / Escape / destroy / preempt /
|
|
2012
1627
|
* `session.abort()`. Key the resolved snapshots by `snapshot.graphic.id`:
|
|
@@ -2275,4 +1890,4 @@ declare function nearestWorldCopyLng(lng: number, refLng: number): number;
|
|
|
2275
1890
|
/** `nearestWorldCopyLng` applied to a lon/lat position. */
|
|
2276
1891
|
declare function nearestWorldCopyPosition(position: Position, refLng: number): Position;
|
|
2277
1892
|
//#endregion
|
|
2278
|
-
export { BaseMapAdapter, type CombinedAbort, DEFAULT_LABEL_SIZE_PIXELS, DEFAULT_TEXT_SIZE_MAX_PIXELS, DEFAULT_TEXT_SIZE_MIN_PIXELS, type DrawMeasureDraft, type DrawOptions, type DrawPointSymbolDraft, type DrawSession, type EditChangeEvent, type EditManyOptions, type EditMode, type EditOptions, type EditPointerDriver, type EditSession, type
|
|
1893
|
+
export { BaseMapAdapter, type CombinedAbort, DEFAULT_LABEL_SIZE_PIXELS, DEFAULT_TEXT_SIZE_MAX_PIXELS, DEFAULT_TEXT_SIZE_MIN_PIXELS, type DrawMeasureDraft, type DrawOptions, type DrawPointSymbolDraft, type DrawSession, type EditChangeEvent, type EditManyOptions, type EditMode, type EditOptions, type EditPointerDriver, type EditSession, type FeatureGroup, type Graphic, type GraphicEditChangeEvent, type GraphicEditSession, type GraphicGeometrySnappingOptions, type GraphicRender, type GraphicSnapshot, type GuideStyle, HATCH_PATTERNS, type HandleStyle, type HatchPattern, type IconResource, type InteractionHit, type InteractionStyle, type LabelExtent, type LayerId, type MapAdapter, type MapCursor, type MapEvent, type MapEventHandler, type MergedPickFeature, type NativeEventEntry, type PickCandidate, type PickCandidateHit, type PickCandidateHitKind, type PickCandidateInput, type PickEvaluationContext, type PickEvent, type PickHandler, type PickOptions, type PickPointerType, type PixelCoordinate, type PointSymbol, type PointSymbolAnchor, type PointSymbolCapability, PointSymbolCapabilityRequiredError, type PointSymbolColors, type PointSymbolFootprint, type PointSymbolGraphicModifiers, type PointSymbolRender, type PointSymbolSize, type PointSymbolStyle, type PointerCallback, PointerCallbackHub, type PointerEventInfo, type ResolvedPickPresentation, type RotatedScreenBounds, type ScreenBounds, type SessionChangeSource, type SessionHistory, type SessionHistoryState, type SettleReason, type SizeAnchor, type SnapCandidate, type SnapCandidateProvider, type SnapCandidateRequest, type SnapInteraction, type SnapResult, type SnappingOptions, type StyleOptions, TEXT_HALO_COLOR, TOUCH_HIT_TOLERANCE_PX, TacticalDraw, TacticalDrawAbortError, type TacticalDrawAbortReason, TacticalDrawDestroyedError, type TacticalDrawLayerOptions, type TacticalDrawOptions, type TextSizeProps, type TextStyleOptions, type ToPixel, type TransformBox, type TransformChangeEvent, type TransformSession, type TransientChangeEvent, type TransientPhase, bindAbortable, clonePointSymbol, coerceHandleStyle, combineWithHostSignal, effectivePointSymbolPixelSize, evaluatePickCandidates, fillPatternImageDimension, fillPatternImageId, getSizeAnchor, hitTestFeature, hitTestIconFeature, ignoreAbort, isPointSymbol, isTacticalDrawAbortError, makeFillPatternImage, mergeNativeAndCachedPickFeatures, nearestWorldCopyLng, nearestWorldCopyPosition, pickHitTolerance, pickPointerType, pointSymbolFootprint, renderOrderToVisualOrder, resizePointSymbolSize, resolveGroundTextSizePixels, resolvePickPresentation, resolveTextHaloWidth, resolveTextSizePixels, tryControlMeasureIdFromFeature };
|