@nika-js/onlymap 0.6.0 → 0.6.2

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.
Files changed (80) hide show
  1. package/CHANGELOG.md +63 -1
  2. package/README.md +39 -14
  3. package/THIRD-PARTY-LICENSES.md +27 -0
  4. package/dist/{LercDecode.es-fbo6h5rt.js → LercDecode.es-BOJJr6Gx.js} +1 -1
  5. package/dist/{basemap-CprSan9q.js → basemap-BofXgXxy.js} +13 -3
  6. package/dist/basemap.d.ts +10 -1
  7. package/dist/cityjson-D_V5GY8b.js +332 -0
  8. package/dist/clip-box-controller.d.ts +94 -0
  9. package/dist/clip-box.d.ts +109 -0
  10. package/dist/crs-Ciu7Xs7a.js +108 -0
  11. package/dist/crs.d.ts +50 -0
  12. package/dist/ctx.d.ts +8 -1
  13. package/dist/data-layer.d.ts +11 -3
  14. package/dist/draw-controller.d.ts +75 -1
  15. package/dist/draw.d.ts +22 -4
  16. package/dist/elements/om-map.d.ts +45 -2
  17. package/dist/elements/om-overlay.d.ts +13 -1
  18. package/dist/elements/om-widget.d.ts +4 -0
  19. package/dist/feature-colors.d.ts +41 -0
  20. package/dist/geodesy.d.ts +29 -3
  21. package/dist/{geoparquet-Bm8pfxyo.js → geoparquet-DapHATA0.js} +1 -1
  22. package/dist/html-data.d.ts +2 -2
  23. package/dist/ifc-Dc-TdSJr.js +877 -0
  24. package/dist/ifc.d.ts +406 -0
  25. package/dist/{index-Bz-rbmhj.js → index-7B-6Cbzu.js} +31619 -26565
  26. package/dist/{index-DSRIZR81.js → index-BF9iO8Tq.js} +1 -1
  27. package/dist/{index-GOwMjqm6.js → index-BmX6IId3.js} +1 -1
  28. package/dist/{index-UzmNhuS0.js → index-SPJVn_n_.js} +1 -1
  29. package/dist/{index-CCQttJzO.js → index-xmJjZxQJ.js} +2 -2
  30. package/dist/index.d.ts +13 -2
  31. package/dist/ir-snapshot.d.ts +3 -1
  32. package/dist/layer-registry.d.ts +14 -0
  33. package/dist/layers/bim-layer.d.ts +127 -0
  34. package/dist/layers/feature-mesh-layer.d.ts +288 -0
  35. package/dist/layers/gltf-scene-walk.d.ts +55 -0
  36. package/dist/layers/popup-layer.d.ts +13 -0
  37. package/dist/legend-spec.d.ts +1 -1
  38. package/dist/{lerc-CuifOkoA.js → lerc-C6k7EzSN.js} +2 -2
  39. package/dist/license.d.ts +12 -5
  40. package/dist/measure-controller.d.ts +458 -4
  41. package/dist/meshopt_simplifier-Co6uRDDA.js +370 -0
  42. package/dist/onlymap.standalone.js +57077 -50729
  43. package/dist/onlymapjs.js +72 -69
  44. package/dist/parse-manifest.d.ts +3 -0
  45. package/dist/programmatic.d.ts +39 -5
  46. package/dist/{raster-dysYIQ_k.js → raster-Cuzhibe2.js} +2 -2
  47. package/dist/{raster-pipeline-ZdCOGfqV.js → raster-pipeline-DLrnJK8y.js} +1 -1
  48. package/dist/react/om-layer.d.ts +6 -1
  49. package/dist/react/om-overlay.d.ts +9 -0
  50. package/dist/react.js +176 -169
  51. package/dist/region-export-controller.d.ts +37 -0
  52. package/dist/region-export.d.ts +56 -0
  53. package/dist/runtime-core.d.ts +260 -3
  54. package/dist/selection.d.ts +44 -1
  55. package/dist/site-placement.d.ts +107 -0
  56. package/dist/snapping.d.ts +88 -0
  57. package/dist/terrain-heightfield.d.ts +53 -0
  58. package/dist/terrain-sample.d.ts +30 -0
  59. package/dist/terrain.d.ts +6 -1
  60. package/dist/testing.d.ts +6 -2
  61. package/dist/tile3d-metadata.d.ts +185 -0
  62. package/dist/units.d.ts +27 -0
  63. package/dist/version.d.ts +1 -1
  64. package/dist/volumetrics-run.d.ts +13 -0
  65. package/dist/volumetrics-worker.d.ts +1 -0
  66. package/dist/volumetrics.d.ts +201 -0
  67. package/dist/widget-registry.d.ts +11 -0
  68. package/dist/widgets/ifc-clash.d.ts +18 -0
  69. package/dist/widgets/ifc.d.ts +41 -0
  70. package/dist/{zarr-OewK7k2K.js → zarr-CUzEX8fB.js} +79 -66
  71. package/docs/3d-assets.md +48 -0
  72. package/docs/live-data.md +9 -1
  73. package/docs/testing.md +4 -2
  74. package/llms.txt +10 -5
  75. package/onlymapjs.html-data.json +313 -2
  76. package/package.json +6 -2
  77. package/skills/onlymapjs/SKILL.md +14 -2
  78. package/skills/onlymapjs/references/react.md +2 -2
  79. package/skills/onlymapjs/references/syntax.md +314 -15
  80. package/dist/cityjson-urQeujQv.js +0 -407
package/dist/crs.d.ts ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Bundled coordinate reference systems, shared by every decoder that has to
3
+ * turn projected coordinates into lon/lat.
4
+ *
5
+ * Extracted from cityjson.ts so the IFC path can reuse it. An IFC file's
6
+ * `IfcMapConversion` states its position in a projected CRS named by
7
+ * `IfcProjectedCRS`, exactly as CityJSON states one in
8
+ * `metadata.referenceSystem`, so both need the same table and the same lazy
9
+ * proj4 import. Duplicating it would have meant a Dutch model georeferencing
10
+ * correctly as CityJSON and 108 m out as IFC.
11
+ *
12
+ * proj4 is imported LAZILY at the point of use, never at module scope: most
13
+ * files need no projection at all, and the ones that do are already paying for
14
+ * a parse.
15
+ */
16
+ export interface CrsEntry {
17
+ def: string;
18
+ /** Source stores [northing, easting] / [lat, lon] — swap before handing to proj4. */
19
+ swapXY?: boolean;
20
+ /** Already angular: no reprojection, but horizontal metrics need a local metric frame. */
21
+ geographic?: boolean;
22
+ }
23
+ /** Compound (horizontal + vertical) codes → the horizontal component that actually projects. */
24
+ export declare const COMPOUND_TO_HORIZONTAL: Record<number, number>;
25
+ export declare const CRS_DEFS: Record<number, CrsEntry>;
26
+ /** The EPSG codes this build can convert without help. */
27
+ export declare function bundledCrsCodes(): number[];
28
+ /**
29
+ * Projected (or geographic) coordinates in EPSG:`code` -> `[lon, lat]`.
30
+ *
31
+ * Returns null when the code is not bundled, which callers must treat as "I
32
+ * cannot place this" rather than silently substituting something else — a
33
+ * wrong position looks exactly like a right one.
34
+ *
35
+ * `x`/`y` are given easting-first; entries flagged `swapXY` (Japan's plane
36
+ * systems, RD's formal axis order) are swapped here so callers never have to
37
+ * know which convention a CRS uses.
38
+ */
39
+ export declare function projectedToLonLat(code: number, x: number, y: number): Promise<[number, number] | null>;
40
+ /**
41
+ * Bearing of the projection's GRID north at a point, in degrees clockwise from
42
+ * true north — the grid convergence.
43
+ *
44
+ * Measured rather than derived: unproject the point and a second one a short
45
+ * step further north IN GRID SPACE, then take the true bearing between them.
46
+ * That works for any projection in the table without per-CRS formulae, and it
47
+ * matters because a model aligned to a national grid is NOT aligned to true
48
+ * north. Dutch RD at Rotterdam is off by -0.735 degrees.
49
+ */
50
+ export declare function gridConvergence(code: number, x: number, y: number, step?: number): Promise<number | null>;
package/dist/ctx.d.ts CHANGED
@@ -35,6 +35,12 @@ export interface RuntimeContext {
35
35
  project(lngLat: [number, number]): [number, number];
36
36
  };
37
37
  selection: Selection | null;
38
+ /**
39
+ * The decoded EXT_structural_metadata property table for a `pick-features`
40
+ * layer, indexed BY feature ID, or null before its first tile has loaded.
41
+ * Watch the `features` token to re-render when it arrives.
42
+ */
43
+ features(layerId: string): Record<string, unknown>[] | null;
38
44
  /** Manifest undo/redo state (spec: "Undo / Redo — Manifest History") — drive it via emit("undo"/"redo"). Always false on the programmatic/React path (the app owns state there). */
39
45
  history: {
40
46
  canUndo: boolean;
@@ -49,6 +55,7 @@ export interface RuntimeContext {
49
55
  }
50
56
  export interface BuildCtxDeps {
51
57
  layerIRs: ReadonlyMap<string, LayerIR>;
58
+ featureTables?: ReadonlyMap<string, Record<string, unknown>[]>;
52
59
  core: RuntimeCore;
53
60
  mapEl: Element;
54
61
  selection: Selection | null;
@@ -62,4 +69,4 @@ export declare function viewportBounds(viewport: ReturnType<RuntimeCore["getView
62
69
  /** `ctx.layers` / the `layers` external store — one builder for both (spec: "External-Store Contract"). */
63
70
  export declare function buildLayerMetas(layerIRs: ReadonlyMap<string, LayerIR>): LayerMetaSnapshot[];
64
71
  /** Built fresh on every widget-render invocation — never mutated, never reused. */
65
- export declare function buildCtx({ layerIRs, core, mapEl, selection, history }: BuildCtxDeps): RuntimeContext;
72
+ export declare function buildCtx({ layerIRs, core, mapEl, selection, history, featureTables }: BuildCtxDeps): RuntimeContext;
@@ -90,6 +90,14 @@ export interface DataFormat {
90
90
  export declare function rowsToColumnar(rows: Record<string, unknown>[]): ColumnarData | undefined;
91
91
  /** `OmMap.registerFormat(format)` — plug in a parser for a format the library doesn't ship (spec: "v0.2 / Tier 1"). */
92
92
  export declare function registerFormat(format: DataFormat): void;
93
+ /** Opaque controller/element identity used to reference-count data transports. */
94
+ export type DataTransportOwner = object;
95
+ /** Start one descriptor reconciliation transaction for an owner. */
96
+ export declare function beginDataOwnerUpdate(owner: DataTransportOwner): void;
97
+ /** Release transports no longer named by the owner's complete descriptor document. */
98
+ export declare function endDataOwnerUpdate(owner: DataTransportOwner): void;
99
+ /** Release every fetch/poller/socket held by an owner (unmount, dispose, or suspend). */
100
+ export declare function releaseDataOwner(owner: DataTransportOwner): void;
93
101
  /**
94
102
  * A source plugin supplies domain decoding (and optionally a subscription
95
103
  * handshake); the transport (WebSocket, reconnect, flush) is generic.
@@ -128,7 +136,7 @@ export interface DataRequestConfig {
128
136
  }
129
137
  export declare function configureData(config: DataRequestConfig): void;
130
138
  /** @internal Shared request-policy chokepoint for non-row assets (ImageOverlay); not re-exported from the package root. */
131
- export declare function fetchConfiguredResource(url: string): Promise<Response>;
139
+ export declare function fetchConfiguredResource(url: string, signal?: AbortSignal): Promise<Response>;
132
140
  /** @internal Shared Content-Length license backstop for row and non-row sources. */
133
141
  export declare function assertFetchSizeWithinEntitlement(response: Response): void;
134
142
  /** `data="draw:sketch"` — the reserved scheme for a widget-fed sketch layer (never collides with http/ws/inline). */
@@ -162,9 +170,9 @@ export declare function getDrawData(url: string): LayerData;
162
170
  * call for a given URL, and invokes `onLoaded` on every resolution/flush (the
163
171
  * caller re-reconciles from there).
164
172
  */
165
- export declare function getData(url: string, onLoaded: () => void, opts?: StreamOptions): LayerData;
173
+ export declare function getData(url: string, onLoaded: () => void, opts?: StreamOptions, owner?: DataTransportOwner): LayerData;
166
174
  /** Is a fetch for this URL still in flight? Feeds the `om-map-ready` signal — errored fetches count as settled (readiness must not hang on a bad URL). */
167
- export declare function isPendingData(url: string): boolean;
175
+ export declare function isPendingData(url: string, owner?: DataTransportOwner): boolean;
168
176
  /**
169
177
  * Minimal feature-shape detection (spec: "Feature Field Resolution & Data
170
178
  * Shape" — full per-layer caching/registry treatment is a later phase; this
@@ -22,6 +22,49 @@ export declare class DrawController {
22
22
  private readonly autosaveKeys;
23
23
  private readonly observers;
24
24
  private active;
25
+ /**
26
+ * rAF handle coalescing hover-driven preview updates (spec: "Cut/fill
27
+ * volume measurement" — found debugging click-reliability in standalone
28
+ * mode). `point()` used to call `renderPreview()` (which patches the
29
+ * per-frame channel and rebuilds every deck.gl layer) SYNCHRONOUSLY on
30
+ * every raw mousemove — far more often than the "per-frame" name implies,
31
+ * since mousemove fires at native pointer-event rate, not rAF rate. That
32
+ * rebuild storm intermittently corrupted deck.gl's own click/pick pipeline
33
+ * (mjolnir gesture recognition losing a click shortly after a hover-driven
34
+ * rebuild) — confirmed by disabling hover-triggered rebuilds entirely and
35
+ * watching the click-loss disappear. Coalescing to one rebuild per actual
36
+ * animation frame is what `patchAnimatedProps`'s own doc comment already
37
+ * describes as "the deck-idiomatic rAF pattern" — this was a gap between
38
+ * that intent and this call site, not a design change.
39
+ */
40
+ private hoverRaf;
41
+ /**
42
+ * The coordinate `commit()` just used to close a shape (its live cursor
43
+ * position at that moment), or null. The native `dblclick` DOM event
44
+ * (which drives `commit()`) fires as soon as the second tap's mouseup
45
+ * lands — but deck.gl's own click-gesture recognition is independently
46
+ * delayed (Hammer's Tap recognizer defers confirming a "click" until it's
47
+ * sure a second tap isn't coming, per its `requireFailure`/`interval`
48
+ * mechanics). One of the double-click's own two taps can therefore still
49
+ * arrive here as a late, ordinary "click" a few ms *after* commit() already
50
+ * ran, at ~that same coordinate — with pending now empty, session.click's
51
+ * own same-spot dedup (see `point()`'s click branch) has nothing to
52
+ * compare against, so it silently starts a fresh one-vertex shape at the
53
+ * spot the user just finished closing.
54
+ *
55
+ * Guarding on position (not a time window) is what keeps this from
56
+ * swallowing a genuinely new, deliberate click that happens to follow
57
+ * immediately after a commit — e.g. redrawing a second shape back-to-back
58
+ * in a test with no elapsed wall-clock time — since that click lands at a
59
+ * different coordinate and passes straight through. The match is a small
60
+ * PIXEL-radius tolerance (see `point()`), not exact equality: a real
61
+ * double-click's two taps are two independent physical presses and rarely
62
+ * land at the bit-identical screen pixel, let alone the bit-identical
63
+ * unprojected lng/lat — exact equality let real hand jitter slip past the
64
+ * guard even though a synthetic, pixel-perfect test wouldn't reveal that.
65
+ * Consumed (cleared) by the very next click regardless of whether it matched.
66
+ */
67
+ private lastClosePos;
25
68
  constructor(mapEl: Element, core: RuntimeCore);
26
69
  /** A draw tool is active — om-map routes points here and suppresses selection. */
27
70
  isCapturing(): boolean;
@@ -32,14 +75,45 @@ export declare class DrawController {
32
75
  * geodesic readouts + labels live off the shared draw stack. `null` unsubs.
33
76
  */
34
77
  setObserver(target: string, cb: ((session: DrawSession) => void) | null): void;
78
+ /** This map's own committed-feature count for `target` (spec: "Cut/fill volume measurement") — see DrawSession.count's doc comment for why this beats reading the shared getDrawData mirror. */
79
+ featureCount(target: string): number;
35
80
  private notify;
36
- /** Enable localStorage autosave for a target and restore any saved sketch (draw-config action). */
81
+ /** Enable localStorage autosave for a target and restore any saved sketch, and/or an auto-styled committed-geometry layer (draw-config action). */
37
82
  configure(target: string, opts: {
38
83
  autosave?: string;
84
+ fillColor?: number[];
85
+ lineColor?: number[];
39
86
  }): void;
87
+ /**
88
+ * Opt-in committed-geometry visualization (spec: issue #34's region-export
89
+ * demo surfaced this — a closed shape with no author-added layer is
90
+ * indistinguishable from "double-click didn't work," since the ONLY
91
+ * built-in visual is the in-progress preview, which clears the instant a
92
+ * shape commits). Bound directly to the SAME reactive `draw:<target>` data
93
+ * URL an author's own manual layer would use — this is pure convenience
94
+ * layered on top, not a replacement; an author who wants custom styling
95
+ * (or a non-GeoJsonLayer rendering) still adds their own layer instead,
96
+ * and both can coexist. One committed layer per TARGET (not a singleton
97
+ * like `PREVIEW_ID`), since multiple draw targets can exist on one map.
98
+ *
99
+ * `depthTest: false` (patched, not attribute-expressible — same non-
100
+ * string-prop precedent as the volume gizmo's `mesh`): a flat 2D ring has
101
+ * no z of its own, so on a page with 3D content (the exact case this was
102
+ * built for — outlining a footprint over a BIM building to export) it can
103
+ * sit at an elevation UNDER opaque building geometry, fully occluded from
104
+ * a normal camera angle — the identical depth-occlusion class already
105
+ * fixed for the measure tool's cut-prism guides. This is a SELECTION
106
+ * indicator, not real in-world geometry, so it's correct for it to always
107
+ * read on top regardless of what 3D content surrounds it.
108
+ */
109
+ private ensureCommittedLayer;
40
110
  setMode(target: string, mode: DrawMode | null): void;
41
111
  private deactivate;
42
112
  point(coord: [number, number] | null, kind: "click" | "hover"): void;
113
+ /** Drop a queued hover-coalesced render (a following immediate render already supersedes it). */
114
+ private cancelHoverRaf;
115
+ /** `CLOSE_GUARD_PIXELS` converted to real meters at the current zoom/latitude (standard Web Mercator meters-per-pixel). */
116
+ private closeGuardMeters;
43
117
  /** Close the in-progress line/polygon — from the double-click DOM event or Enter. */
44
118
  commit(): void;
45
119
  cancel(): void;
package/dist/draw.d.ts CHANGED
@@ -50,6 +50,7 @@ export interface DrawSessionOpts {
50
50
  /** Fired whenever the COMMITTED feature set changes (commit / delete / clear / seed) — D2 wires this to the store push. */
51
51
  onChange?: (fc: FeatureCollection) => void;
52
52
  }
53
+ export declare function samePoint(a: Position, b: Position): boolean;
53
54
  /**
54
55
  * One sketch session. Owns the committed features and the single in-progress
55
56
  * shape. Every mutation that changes the committed set fires `onChange` once.
@@ -74,10 +75,16 @@ export declare class DrawSession {
74
75
  /** Cursor moved — updates the rubber-band target. No committed-set change. */
75
76
  move(pos: Position): void;
76
77
  /**
77
- * Finalize the in-progress line/polygon (from double-click / Enter). A
78
- * trailing duplicate vertex the second click of a double-click — is
79
- * dropped first. No-op (shape kept pending) if there aren't enough
80
- * distinct vertices yet. Returns the committed feature or null.
78
+ * Finalize the in-progress line/polygon (from double-click / Enter). The
79
+ * two clicks that make up a double-click are individually unreliable
80
+ * deck.gl's click/dblclick gesture disambiguation can drop either or both
81
+ * of them depending on exact timing so rather than depend on them having
82
+ * landed in `pending`, the closing vertex is the live cursor position
83
+ * (already updated by the hover that preceded the double-click, via a
84
+ * separate, non-gesture-gated code path). A trailing duplicate — the
85
+ * cursor exactly repeating the last real vertex — is dropped. No-op (shape
86
+ * kept pending) if there aren't enough distinct vertices yet. Returns the
87
+ * committed feature or null.
81
88
  */
82
89
  commit(): DrawFeature | null;
83
90
  /** Esc — discard the in-progress shape, keep the tool active. */
@@ -93,6 +100,17 @@ export declare class DrawSession {
93
100
  features?: unknown;
94
101
  } | null | undefined): void;
95
102
  toFeatureCollection(): FeatureCollection;
103
+ /**
104
+ * Committed-feature count for THIS session object (spec: "Cut/fill volume
105
+ * measurement"). Distinct from reading the shared `getDrawData` store
106
+ * mirror: that mirror is a module-global `Map` keyed by URL string with no
107
+ * per-map/per-session isolation, so it can carry a PRIOR session's data
108
+ * (a different `<om-map>` sharing the same runtime-internal target id, or —
109
+ * the case that surfaced this — a freshly re-mounted map in a test) until
110
+ * this session's own first commit overwrites it. This reads `this.features`
111
+ * directly, which always starts empty for a genuinely new session.
112
+ */
113
+ count(): number;
96
114
  /** The in-progress shape for the preview layer, or null when nothing is pending. */
97
115
  preview(): DrawPreview | null;
98
116
  private finalize;
@@ -20,14 +20,16 @@
20
20
  import { HTMLElementBase } from "../env";
21
21
  import { type RuntimeContext } from "../ctx";
22
22
  import { type LightingIR } from "../scene-lighting";
23
+ import { type ClipBoxIR } from "../clip-box";
23
24
  import { type TerrainIR } from "../terrain";
24
25
  import { type WidgetSlot } from "../widget-layout";
25
26
  import { type SnapshotOptions } from "../snapshot";
27
+ import { type ValidationEntry } from "../validation";
26
28
  import type { LayerIR } from "../ir";
27
29
  import type { Selection } from "../selection";
28
30
  import type { MapViewport } from "../basemap";
29
31
  interface OverlayHost {
30
- updatePosition(viewport: MapViewport | undefined, selection: Selection | null): void;
32
+ updatePosition(viewport: MapViewport | undefined, selection: Selection | null, pickType?: "hover" | "click"): void;
31
33
  /**
32
34
  * Screen rect for collision-dim (spec: "Overlap avoidance") — non-null
33
35
  * only when the overlay is VISIBLE and participates in dimming. Returns
@@ -43,17 +45,32 @@ export declare class OmMapElement extends HTMLElementBase {
43
45
  private observer;
44
46
  private history;
45
47
  private keydownHandler?;
48
+ private keyupHandler?;
46
49
  private dblclickHandler?;
47
50
  private contextmenuHandler?;
48
51
  private mount;
49
52
  private reconcilePending;
53
+ private readonly dataOwner;
54
+ /** Decoded property tables per pick-features layer — see RuntimeCoreCallbacks.onFeatureTable. */
55
+ private featureTables;
50
56
  private layerIRs;
51
57
  private selection;
58
+ /**
59
+ * The pointer event behind the most recent pick — kept SEPARATELY from
60
+ * `selection` because empty picks store `selection = null`, losing their
61
+ * type, yet `selection-type`-scoped overlays must tell a click on empty
62
+ * space (dismisses a click popup) from a hover over empty space (inert).
63
+ */
64
+ private lastPickType;
52
65
  private widgets;
53
66
  private overlays;
54
67
  private overlayFlushPending;
55
68
  private errorPanel;
56
69
  private runtimeErrors;
70
+ /** Dedup guard for the approximate-BIM-georeferencing warning — same shape as applyLicenseGates' own `warned` set, keyed per layer id. */
71
+ private warnedApproximatePlacement;
72
+ /** Dedup guard for the no-terrain-for-elevation error — same shape as `warnedApproximatePlacement`. */
73
+ private erroredNoTerrainForElevation;
57
74
  private readyFired;
58
75
  private visibleSizeChecked;
59
76
  /** Custom-state holder for the `om-collapsed` height-floor marker — see applyHeightFloor. */
@@ -231,7 +248,7 @@ export declare class OmMapElement extends HTMLElementBase {
231
248
  * by mountForTest's pick()/clearSelection(), not part of the public
232
249
  * authoring surface.
233
250
  */
234
- injectPickInternal(selection: Selection | null): void;
251
+ injectPickInternal(selection: Selection | null, pickType?: "hover" | "click"): void;
235
252
  injectDragPickInternal(selection: Selection): void;
236
253
  /** Every click/hover map coordinate (spec: "Manual Drawing"): drives the
237
254
  * internal draw controller AND fires the public `om-map-point` event, so
@@ -243,6 +260,20 @@ export declare class OmMapElement extends HTMLElementBase {
243
260
  * Fires at pointer rate on hover (no debounce — vertex capture needs
244
261
  * every point); heavy listeners should throttle their own work. */
245
262
  private handleMapPoint;
263
+ /**
264
+ * XY snapping's snap-tip (spec: issue #34 Part A — "a snap tip shows
265
+ * which agent fired on which layer, otherwise users can't tell why a
266
+ * vertex jumped"). A SEPARATE runtime overlay from the shared
267
+ * `show-tooltip` singleton (`actions.ts`'s `getOrCreateTooltipOverlay`):
268
+ * that one is `anchor-from="selection"` (tied to `ctx.selection`) and
269
+ * shared across every `show-tooltip` behavior a page authors (this
270
+ * page's own z-tooltip, for one) — reusing it here would fight over the
271
+ * same element on every hover. This one anchors at a plain, static
272
+ * `anchor="[lng,lat]"` instead, since a snap position has nothing to do
273
+ * with whatever `ctx.selection` currently holds.
274
+ */
275
+ private ensureSnapTipOverlay;
276
+ private handleSnapPoint;
246
277
  /** Harness map-point injection (spec: "Consumer Testing Surface") — the
247
278
  * same path a real deck click/hover coordinate takes, so the om-map-point
248
279
  * event and custom capture tools are testable without a GPU. */
@@ -271,6 +302,8 @@ export declare class OmMapElement extends HTMLElementBase {
271
302
  getLightingInternal(): LightingIR | null;
272
303
  /** Resolved terrain IR (headless test inspection) — null = no surface. */
273
304
  getTerrainInternal(): TerrainIR | null;
305
+ /** Resolved clip-box IR (headless test inspection) — null = no active box. */
306
+ getClipBoxInternal(): ClipBoxIR | null;
274
307
  /**
275
308
  * Canvas-only scene snapshot (spec: "Snapshot API") — basemap + deck
276
309
  * composited at device pixels. DOM widgets/overlays/badge/attribution are
@@ -341,6 +374,16 @@ export declare class OmMapElement extends HTMLElementBase {
341
374
  * still runs validation and fires the event, just skips the on-page panel
342
375
  * (spec: "for teams building their own error UI").
343
376
  */
377
+ /**
378
+ * Reports a runtime-discovered validation entry — deck.gl crashes and
379
+ * license-limit violations already flow through this (via `onRuntimeError`
380
+ * below); this is also the entry point for facts a WIDGET discovers
381
+ * directly (no round trip through RuntimeCore) since it already holds an
382
+ * `<om-map>` reference — see `ifc-loader`'s approximate-georeferencing
383
+ * warning. Same access-pattern precedent as `registerWidgetInternal`
384
+ * above: real and callable, not part of the documented public API.
385
+ */
386
+ reportRuntimeErrorInternal(entry: ValidationEntry): void;
344
387
  private publishValidation;
345
388
  }
346
389
  declare global {
@@ -36,7 +36,19 @@ export declare class OmOverlayElement extends HTMLElementBase {
36
36
  * simply doesn't update this remembered anchor; only a matching
37
37
  * selection moves it, and only an explicit `visible="false"` hides it.
38
38
  */
39
+ /**
40
+ * A 3rd (z) component survives here specifically for the projection at
41
+ * the bottom of `updatePosition` — a selection's real elevation (a depth
42
+ * pick against terrain/3D content, see `Selection.coordinate`'s own doc
43
+ * comment) matters for WHERE on screen the anchor lands under a pitched
44
+ * camera, even though this class only ever positions in 2D. Dropping it
45
+ * (projecting at an implied z=0) put a tooltip over an elevated pick tens
46
+ * to hundreds of pixels from the actual cursor — the anchor was correct
47
+ * in lng/lat, just projected as if the picked surface were at sea level.
48
+ */
39
49
  private lastAnchor;
50
+ /** Cached [w, h] for `clip-to-map` (offsetWidth forces layout; flushes run per pointer move) — invalidated by setContent. */
51
+ private clipSizeCache;
40
52
  /** Memoized feature anchor — recomputed only when the layer's data reference or the target id changes (live layers swap data). */
41
53
  private featureAnchorCache;
42
54
  connectedCallback(): void;
@@ -86,5 +98,5 @@ export declare class OmOverlayElement extends HTMLElementBase {
86
98
  * must not flicker widgets). Only stable author popups drive dimming.
87
99
  */
88
100
  getVisibleRect(): DOMRect | null;
89
- updatePosition(viewport: MapViewport | undefined, selection: Selection | null): void;
101
+ updatePosition(viewport: MapViewport | undefined, selection: Selection | null, pickType?: "hover" | "click"): void;
90
102
  }
@@ -22,6 +22,10 @@ export declare class OmWidgetElement extends HTMLElementBase implements WidgetHo
22
22
  private renderFn?;
23
23
  private watchTokens;
24
24
  private hostStyle;
25
+ /** The registered implementation behind `type="…"` — kept so real removal can call its optional destroy() hook. */
26
+ private impl;
27
+ /** One pending real-removal check at a time — a remove→insert→remove within a single task must not run destroy twice. */
28
+ private destroyCheckPending;
25
29
  /** Live placement attributes (spec: "Widget Layout Manager") — position/order edits (incl. undo replays) re-slot without a remount. */
26
30
  static readonly observedAttributes: string[];
27
31
  connectedCallback(): void;
@@ -0,0 +1,41 @@
1
+ /** Colour-blind-safe categorical default, cycled when a field has more values than entries. */
2
+ export declare const FEATURE_PALETTE: string[];
3
+ export declare const FEATURE_RAMP: string[];
4
+ export declare function resolveFeatureColor(entry: unknown): [number, number, number];
5
+ /**
6
+ * The GLOBAL half of feature colouring — everything derived from the WHOLE
7
+ * accumulated table, none of it indexed by feature ID. Serializable on
8
+ * purpose (`order` is entries, not a Map): its identity is the memo key that
9
+ * keeps per-tile style tables (and their GPU textures) stable across
10
+ * reconciles and animation frames.
11
+ */
12
+ export interface FeatureColorMapping {
13
+ kind: "categorical" | "graduated";
14
+ field: string;
15
+ /** graduated: the max across every loaded tile (colours stay comparable as tiles stream in). */
16
+ max?: number;
17
+ /** categorical: global value → first-seen order (assigns each value a stable palette slot). */
18
+ order?: [string, number][];
19
+ /** Resolved palette/ramp — author `feature-palette` or the built-in defaults. */
20
+ colors: unknown[];
21
+ strength: number;
22
+ }
23
+ /**
24
+ * Builds the mapping from the layer's ACCUMULATED rows (all tiles).
25
+ * Returns null when neither colouring attribute is asked for — the model then
26
+ * renders in its own IFC surface colours. Authored `feature-styles` always
27
+ * wins upstream: the declarative attributes are sugar over the same table,
28
+ * never an override of it.
29
+ */
30
+ export declare function buildFeatureColorMapping(rows: Record<string, unknown>[], categorical: string | undefined, graduated: string | undefined, palette: unknown[] | undefined, strength: number): FeatureColorMapping | null;
31
+ /**
32
+ * Applies the mapping to ONE tile's own rows — the per-tile style table, index
33
+ * = that tile's local feature ID. A category unseen in the global order (a
34
+ * race while tiles stream; the next accumulate pass rebuilds the mapping)
35
+ * falls back to slot 0 rather than being unstyled, matching the pre-split
36
+ * behavior for unknown values.
37
+ */
38
+ export declare function applyFeatureColorMapping(rows: Record<string, unknown>[], mapping: FeatureColorMapping): {
39
+ color: [number, number, number];
40
+ strength: number;
41
+ }[];
package/dist/geodesy.d.ts CHANGED
@@ -23,6 +23,22 @@ export type LngLat = [number, number];
23
23
  export declare function distanceMeters(a: LngLat, b: LngLat, radius?: number): number;
24
24
  /** Total haversine length of a polyline in meters (0 for < 2 points). */
25
25
  export declare function pathLengthMeters(points: LngLat[], radius?: number): number;
26
+ export interface PathSample {
27
+ position: LngLat;
28
+ /** Cumulative distance (m) from the path's start. */
29
+ distance: number;
30
+ }
31
+ /**
32
+ * `count` evenly-spaced points along a multi-segment polyline, by cumulative
33
+ * distance (spec item E — elevation-profile samples). Walks the segment
34
+ * lengths to find which segment each target distance falls in, then
35
+ * `intermediate()`s within it — a straight generalization of that function
36
+ * from "interpolate one segment" to "interpolate a whole path." `count=1`
37
+ * returns just the start point; `points.length < 2` returns each input point
38
+ * at distance 0 (nothing to interpolate); a zero-length path (every point
39
+ * coincident) returns `count` copies of the start point at distance 0.
40
+ */
41
+ export declare function resamplePath(points: LngLat[], count: number): PathSample[];
26
42
  /**
27
43
  * Spherical polygon area in m² via the Chamberlain–Duquette line integral (the
28
44
  * exact index form Turf's `ringArea` uses, so results match Turf's fixtures).
@@ -40,8 +56,18 @@ export declare function ringPerimeterMeters(ring: LngLat[], radius?: number): nu
40
56
  */
41
57
  export declare function ringEnclosesPole(ring: LngLat[]): boolean;
42
58
  /**
43
- * Great-circle midpoint of a segment antimeridian-safe (converts to Cartesian
44
- * so a segment crossing ±180° gets its label on the shorter arc, not the middle
45
- * of the wrong way round). Returns [lng (normalized to (−180, 180]), lat].
59
+ * Point a `fraction` of the way from `a` to `b` along the great-circle path
60
+ * (spherical slerp) spec item E's evenly-spaced elevation-profile samples
61
+ * need this; only the f=0.5 special case (`midpoint`) existed before.
62
+ * Antimeridian-safe (interpolates the Cartesian unit vectors, not lng/lat
63
+ * directly, so a segment crossing ±180° doesn't wrap the wrong way).
64
+ * `f=0` returns `a`, `f=1` returns `b`. Returns [lng (normalized to
65
+ * (−180, 180]), lat].
66
+ */
67
+ export declare function intermediate(a: LngLat, b: LngLat, fraction: number): LngLat;
68
+ /**
69
+ * Great-circle midpoint of a segment — `intermediate(a, b, 0.5)`, kept as its
70
+ * own name since "the point halfway along a segment" is the common case
71
+ * (badge label anchors) and reads better than a bare 0.5 at every call site.
46
72
  */
47
73
  export declare function midpoint(a: LngLat, b: LngLat): LngLat;
@@ -1,4 +1,4 @@
1
- import { r as p, n as d } from "./index-Bz-rbmhj.js";
1
+ import { r as p, n as d } from "./index-7B-6Cbzu.js";
2
2
  function g(t) {
3
3
  if (t == null) return !0;
4
4
  const e = t, n = e.id?.code;
@@ -15,9 +15,9 @@ interface TagData {
15
15
  }[];
16
16
  }
17
17
  /** Hand-authored action list — validated against hasAction() in html-data.test.ts. */
18
- export declare const BUILTIN_ACTIONS: readonly ["toggle-layer", "zoom-in", "zoom-out", "filter-layer", "highlight-feature", "fade", "pulse", "populate", "trace", "story-play", "story-pause", "story-seek", "fly-to", "zoom-to-feature", "show-overlay", "hide-overlay", "show-tooltip", "hide-tooltip", "draw-mode", "draw-commit", "draw-cancel", "draw-delete", "draw-clear", "draw-config", "draw-save", "measure-mode", "measure-clear", "measure-units", "set-basemap", "set-lighting", "set-terrain", "set-widgets-visible", "undo", "redo"];
18
+ export declare const BUILTIN_ACTIONS: readonly ["toggle-layer", "zoom-in", "zoom-out", "filter-layer", "highlight-feature", "fade", "pulse", "populate", "trace", "story-play", "story-pause", "story-seek", "fly-to", "zoom-to-feature", "show-overlay", "hide-overlay", "show-tooltip", "hide-tooltip", "draw-mode", "draw-commit", "draw-cancel", "draw-delete", "draw-clear", "draw-config", "draw-save", "export-region-3d", "measure-mode", "measure-clear", "measure-units", "set-basemap", "set-lighting", "set-terrain", "set-clip-box", "clip-box-edit", "set-widgets-visible", "undo", "redo"];
19
19
  /** Hand-authored widget types — validated against the widget registry in html-data.test.ts. */
20
- export declare const BUILTIN_WIDGETS: readonly ["legend", "layer-switcher", "zoom-controls", "scale-bar", "attribution", "filter", "vega-lite", "player", "draw", "measure", "basemap-switcher", "lighting", "undo-redo", "widgets-toggle"];
20
+ export declare const BUILTIN_WIDGETS: readonly ["legend", "layer-switcher", "zoom-controls", "scale-bar", "attribution", "filter", "vega-lite", "dynamic-chart", "player", "draw", "measure", "basemap-switcher", "lighting", "clip-box", "undo-redo", "widgets-toggle", "ifc-browser", "ifc-legend", "feature-inspector", "ifc-inspector", "ifc-loader", "ifc-clash"];
21
21
  export declare function buildHtmlCustomData(): {
22
22
  version: number;
23
23
  tags: TagData[];