@nika-js/onlymap 0.6.25 → 0.7.0

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 (37) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +5 -3
  3. package/dist/{LercDecode.es-DNwkPiJw.js → LercDecode.es-CYOgDeVu.js} +1 -1
  4. package/dist/{basemap-Da9yotkI.js → basemap-0rGUZkQT.js} +9 -1
  5. package/dist/basemap.d.ts +4 -0
  6. package/dist/elements/om-map.d.ts +9 -1
  7. package/dist/{geoparquet-D8-txekS.js → geoparquet-Cau8fzH7.js} +1 -1
  8. package/dist/hover-pick.d.ts +53 -0
  9. package/dist/html-data.d.ts +1 -1
  10. package/dist/{index-D6SeYdqX.js → index-BF3xZe9d.js} +1 -1
  11. package/dist/{index-CUnb2y9k.js → index-Bt3U7sbb.js} +1 -1
  12. package/dist/{index-Z6ZPKbXa.js → index-CpSUZ-J3.js} +1 -1
  13. package/dist/{index-31e3kTgK.js → index-CriPOt1s.js} +2 -2
  14. package/dist/{index-CPf0evzb.js → index-CxOS31XG.js} +11725 -11392
  15. package/dist/index.d.ts +1 -1
  16. package/dist/layer-registry.d.ts +10 -0
  17. package/dist/{lerc-CkqppWZk.js → lerc-aeqKo4fK.js} +2 -2
  18. package/dist/onlymap.standalone.js +29545 -28735
  19. package/dist/onlymapjs.js +91 -89
  20. package/dist/programmatic.d.ts +5 -0
  21. package/dist/{raster-CXPV5d4b.js → raster-D70FrkLt.js} +1049 -790
  22. package/dist/raster-identify.d.ts +19 -0
  23. package/dist/{raster-pipeline-CKW1mTzH.js → raster-pipeline-A-zvBnKU.js} +660 -450
  24. package/dist/raster-pipeline.d.ts +109 -14
  25. package/dist/raster.d.ts +91 -1
  26. package/dist/react.js +253 -234
  27. package/dist/runtime-core.d.ts +44 -0
  28. package/dist/version.d.ts +1 -1
  29. package/dist/widget-layout.d.ts +18 -0
  30. package/dist/{zarr-9YGMeWT7.js → zarr-DmrbmSNx.js} +10 -3
  31. package/docs/design/cog-v2.md +223 -0
  32. package/llms.txt +3 -2
  33. package/onlymapjs.attributes.json +9 -0
  34. package/onlymapjs.html-data.json +57 -0
  35. package/package.json +4 -2
  36. package/skills/onlymapjs/SKILL.md +1 -1
  37. package/skills/onlymapjs/references/syntax.md +17 -7
package/CHANGELOG.md CHANGED
@@ -8,6 +8,28 @@ Note: npm collapsed a few closely-spaced releases — the GPX/FlatGeobuf (0.5.4)
8
8
  and GeoParquet (0.5.5) work shipped to npm together as **0.5.6**, so npm's
9
9
  version list jumps 0.5.3 → 0.5.6. Each logical version is listed here regardless.
10
10
 
11
+ ## 0.7.0 — 2026-09-02
12
+
13
+ ### Added
14
+ - **Choose which bands of a GeoTIFF to display — including false-color composites.** `bands="4"` shows a single band through a colormap; `bands="[8,4,3]"` builds an RGB composite (the classic false-color vegetation view from a multispectral scene). Switching bands never re-downloads anything: every band is decoded once and kept, so flipping between views is instant. (#13)
15
+ - **DEMs and satellite scenes no longer render blank or black without hand-tuned min/max.** Non-8-bit rasters with no styling now stretch themselves automatically from the file's own statistics, so "my COG shows nothing" is gone — a console notice reports the window it derived. `rescale="auto"` requests the same treatment explicitly, and with a band triple each band gets its own window. Authored `min`/`max` always win, and both now accept per-band `[r,g,b]` triples. (#13)
16
+ - **Click a raster to read the pixel under the cursor.** A click or hover on a COG with no vector feature in the way now carries the pixel's values through the ordinary selection — `{{value}}` in an overlay shows the elevation under a click with zero extra wiring, and `{{band_1}}`…`{{band_n}}` address composites. `identify="off"` opts a layer out. (#13)
17
+ - **More control over how a raster looks:** `reverse` flips any colormap, `stretch="log"`/`"sqrt"` and `gamma` shape the display curve — all GPU-side, no re-downloads. Paletted GeoTIFFs (land-cover classifications) now render through their embedded color table and produce a proper classes legend. (#13)
18
+ - **Raster problems now say what's wrong instead of rendering nothing.** A server that refuses cross-origin Range requests, a plain non-cloud-optimized GeoTIFF, or a `bands` pick beyond what the file has — each surfaces as a structured validation error with a concrete fix, while the rest of the map keeps working. (#13)
19
+
20
+ ### Fixed
21
+ - **Double-click zoom works again on maps without a basemap.** A click-reliability improvement for the drawing tools had silently disabled double-click zoom in standalone mode; it's back — zooms in about the point you clicked, shift-double-click zooms out.
22
+ - **The provider attribution no longer overlaps the license badge on narrow maps.** The bottom chrome now measures itself and stacks cleanly at any width, instead of colliding at phone sizes.
23
+
24
+ ## 0.6.26 — 2026-09-02
25
+
26
+ ### Added
27
+ - **Hovering an interactive feature now shows a pointer cursor.** The cheapest "this map is clickable" signal there is — recipients of a shared map no longer have to click at random to discover popups. `pick-cursor` on `<om-map>` customizes the cursor; `pick-cursor="none"` turns it off. (#33)
28
+ - **A runtime popup switch per layer.** The new `set-pickable` action (`{layer, pickable: true|false|"3d"}`) turns a layer's picking — and with it popups, tooltips, and hover behaviors — on or off from a button, behavior, or story step. It's undoable and rewinds on story scrub like every scene action. (#31)
29
+
30
+ ### Changed
31
+ - **Hover no longer stalls the map on modest hardware.** Hovering over layers used to run a GPU hit-test on every mouse move — measured at over a second of freeze per move on integrated graphics with real data. Hover now tests once when the pointer comes to rest instead of continuously while it sweeps, which makes hover popups usable on the machines shared maps are actually opened on. Hover-follow UIs that want the old per-move behavior can opt back in with `hover-pick="continuous"` (or a throttle interval like `hover-pick="120"`); clicking is unaffected. (#31)
32
+
11
33
  ## 0.6.25 — 2026-09-02
12
34
 
13
35
  ### Fixed
package/README.md CHANGED
@@ -49,7 +49,7 @@ deck.gl is the best WebGL data-visualization engine there is — and OnlyMapJS i
49
49
  |---|---|---|
50
50
  | Setup | `new Deck({...})`, canvas + basemap sync wiring | one `<om-map>` element (or `<OmMap>` in React) |
51
51
  | State | your reducers/stores drive `setProps` | the manifest **is** the state — edit an attribute, the map reconciles; undo/redo built in |
52
- | Data loading | fetch + parse + reload yourself | `data="…"` — GeoJSON, CSV, Arrow/GeoArrow, Shapefile, KML, GPX, FlatGeobuf, GeoParquet, CityJSON, WebSocket streams, polled REST, tiled XYZ/vector (`TileLayer`/`MVTLayer`); GeoTIFF/COG rasters via `COGLayer`, chunked Zarr/GeoZarr via `ZarrLayer`; geotagged drone JPEGs via `ImageOverlay` |
52
+ | Data loading | fetch + parse + reload yourself | `data="…"` — GeoJSON, CSV, Arrow/GeoArrow, Shapefile, KML, GPX, FlatGeobuf, GeoParquet, CityJSON, WebSocket streams, polled REST, tiled XYZ/vector (`TileLayer`/`MVTLayer`); GeoTIFF/COG rasters via `COGLayer` (band selection, per-band windows, auto-stretch from file stats, stretch/gamma, pixel identify), chunked Zarr/GeoZarr via `ZarrLayer`; geotagged drone JPEGs via `ImageOverlay` |
53
53
  | Accessors | JS functions + `updateTriggers` bookkeeping | `get-*` expressions; update triggers derived automatically |
54
54
  | UI | build legends/popups/filters from scratch | built-in widgets, overlays, behaviors — declarative |
55
55
  | Testing | mock WebGL or ship untested | `OmMap.validate`, IR snapshots, headless behavioral harness |
@@ -72,7 +72,7 @@ Or with no build step at all, straight from a CDN:
72
72
 
73
73
  The bare package URL serves `dist/onlymap.standalone.js`, a single-file bundle built for exactly this (jsDelivr too). Use a CDN that serves the package's raw files — **not** a rebundling CDN like esm.sh, which re-splits the bundle into duplicate copies of the deck.gl/luma.gl runtime and breaks every layer's shader compilation.
74
74
 
75
- Then `npx @nika-js/onlymap init` wires up VS Code IntelliSense and `!`-prefixed manifest snippets for your project. The library ships with 1,208 unit/behavioral tests and 64 Playwright GPU tests.
75
+ Then `npx @nika-js/onlymap init` wires up VS Code IntelliSense and `!`-prefixed manifest snippets for your project. The library ships with 1,380 unit/behavioral tests and 70 Playwright GPU tests.
76
76
 
77
77
  The [examples](https://github.com/NikaGeospatial/onlymapjs/tree/main/examples) are the best tour: widgets, behaviors & overlays, basemaps, columnar/Arrow data, manual drawing, 3D models, scene lighting (with the native lighting widget), DEM terrain, a live WebSocket ship feed, and a polled driver fleet.
78
78
 
@@ -139,7 +139,9 @@ Private endpoints: `OmMap.configureData({ headers, credentials, fetch })` — ap
139
139
 
140
140
  ### Interaction
141
141
 
142
- Built-in actions wire to picks, widget buttons (`data-emit`), or script (`ctx.emit`) with one shared payload contract: `show-overlay`, `hide-overlay`, `show-tooltip`, `hide-tooltip`, `toggle-layer`, `highlight-feature`, `zoom-to-feature`, `filter-layer`, `set-basemap`, `zoom-in`, `zoom-out`, `undo`, `redo` — plus `OmMap.registerAction` for your own.
142
+ Built-in actions wire to picks, widget buttons (`data-emit`), or script (`ctx.emit`) with one shared payload contract: `show-overlay`, `hide-overlay`, `show-tooltip`, `hide-tooltip`, `toggle-layer`, `set-pickable` (a runtime popups-on/off switch per layer), `highlight-feature`, `zoom-to-feature`, `filter-layer`, `set-basemap`, `zoom-in`, `zoom-out`, `undo`, `redo` — plus `OmMap.registerAction` for your own.
143
+
144
+ Hovering a pickable feature shows a pointer cursor automatically, so recipients can tell a map is interactive without clicking at random (`pick-cursor` on `<om-map>` customizes or disables it). Hover GPU picking is rest-debounced by default — one pick when the pointer stops instead of one per mouse move — which keeps hover popups smooth even on integrated graphics; `hover-pick="continuous|off|<ms>"` tunes it.
143
145
 
144
146
  **Undo/redo:** user-facing manifest changes — layer toggles, filter changes, basemap switches, element edits, drawn sketches — are undoable out of the box; the manifest *is* the state, so history is recorded from the DOM itself. Add `<om-widget type="undo-redo">` for buttons, or just press Cmd/Ctrl-Z (Shift-Cmd/Ctrl-Z or Ctrl-Y to redo). Camera moves, hover effects, and story playback are deliberately not undo steps.
145
147
 
@@ -19,7 +19,7 @@ async function v(N = {}) {
19
19
  if (n) {
20
20
  const { createRequire: I } = await import(
21
21
  /*webpackIgnore:true*/
22
- "./lerc-CkqppWZk.js"
22
+ "./lerc-aeqKo4fK.js"
23
23
  ).then((g) => g._);
24
24
  var O = I(import.meta.url);
25
25
  }
@@ -1,4 +1,4 @@
1
- import { C as ny, L as sy, M as oy, m as ay, c as ka, a as Sd, z as oc, G as sm, V as ly, W as cy, b as uy, g as hy, d as Vu, f as Ed, e as dy, l as py, u as fy, D as Qf, h as my } from "./index-CPf0evzb.js";
1
+ import { C as ny, L as sy, M as oy, m as ay, c as ka, a as Sd, z as oc, G as sm, V as ly, W as cy, b as uy, g as hy, d as Vu, f as Ed, e as dy, l as py, u as fy, D as Qf, h as my } from "./index-CxOS31XG.js";
2
2
  const Cd = Math.PI / 180, gy = 180 / Math.PI;
3
3
  function em(Pe, J = 0) {
4
4
  const me = Math.min(180, Pe) * Cd;
@@ -25169,6 +25169,14 @@ class Ry {
25169
25169
  markBusy() {
25170
25170
  this.idleLatch = !1;
25171
25171
  }
25172
+ /** The overlay's internal Deck instance — the hover-pick gate wraps its pointermove handler (issue #31); same exact-pin `_deck` seam as getDeckLayers below. */
25173
+ getOverlayDeckInternal() {
25174
+ return this.overlay._deck;
25175
+ }
25176
+ /** The MapLibre canvas — the pick-cursor affordance writes its cursor style (issue #33). */
25177
+ getCanvasElement() {
25178
+ return this.map.getCanvas();
25179
+ }
25172
25180
  /** The overlay deck's live layers — whenSettled's readiness gate (issue #37). */
25173
25181
  getDeckLayers() {
25174
25182
  return this.overlay._deck?.getLayers?.();
package/dist/basemap.d.ts CHANGED
@@ -67,6 +67,10 @@ export declare class MapLibreBasemapAdapter {
67
67
  isIdle(): boolean;
68
68
  /** Camera writes from OUR side must clear the latch immediately — the map's own movestart fires a tick later, and a settle read in that gap would see stale idle. */
69
69
  private markBusy;
70
+ /** The overlay's internal Deck instance — the hover-pick gate wraps its pointermove handler (issue #31); same exact-pin `_deck` seam as getDeckLayers below. */
71
+ getOverlayDeckInternal(): unknown;
72
+ /** The MapLibre canvas — the pick-cursor affordance writes its cursor style (issue #33). */
73
+ getCanvasElement(): HTMLElement;
70
74
  /** The overlay deck's live layers — whenSettled's readiness gate (issue #37). */
71
75
  getDeckLayers(): {
72
76
  isLoaded: boolean;
@@ -117,7 +117,15 @@ export declare class OmMapElement extends HTMLElementBase {
117
117
  private foldBreakpointPx;
118
118
  private measureWidgetFold;
119
119
  private updateWidgetFoldForWidth;
120
- /** Lift the bottom-end row clear of the bottom drawer toggle while folded. Applied here AND in the fold pass so a lazily-created bottom-end container still gets it. */
120
+ /**
121
+ * Lift the bottom-end row clear of the bottom-start row and the folded
122
+ * drawer toggle (spec: "Widget Layout Manager / Overlap avoidance",
123
+ * structural half) — measured geometry, not a fixed offset: the license
124
+ * badge can wrap taller than the folded clearance, and an expanded
125
+ * provider attribution can grow wide enough to cross the whole bottom
126
+ * edge. Applied on fold flips, every resize tick, and container size
127
+ * changes; headless (zero rects) resolves to the plain floor.
128
+ */
121
129
  private syncFoldBottomOffset;
122
130
  private scheduleFoldPass;
123
131
  private shouldFoldWidget;
@@ -1,4 +1,4 @@
1
- import { r as p, n as d } from "./index-CPf0evzb.js";
1
+ import { r as p, n as d } from "./index-CxOS31XG.js";
2
2
  function g(t) {
3
3
  if (t == null) return !0;
4
4
  const e = t, n = e.id?.code;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Hover-pick gating (public issue #31). deck.gl picks the scene on EVERY
3
+ * animation frame that saw a pointer move — a full render to the picking
4
+ * framebuffer plus a GPU→CPU ReadPixels round-trip. On integrated graphics
5
+ * with real layers that measured ~1,145ms of main-thread stall per mouse
6
+ * move (850× the pickable="false" baseline), making any map with popups
7
+ * unusable on exactly the hardware map RECIPIENTS have.
8
+ *
9
+ * deck exposes no cadence knob, but `deck._onPointerMove` is an assignable
10
+ * instance property and the deck dependency is an EXACT pin (the
11
+ * `overlay._deck` / `_cacheBytes` seam precedent) — so the gate wraps it:
12
+ *
13
+ * - `rest` (the default): pointer moves are held until the pointer RESTS
14
+ * (restMs without further movement), then the LAST move is forwarded —
15
+ * one pick per rest instead of one per frame. Fast sweeps across the map
16
+ * cost zero picks; stopping on a feature picks once. Hover popups appear
17
+ * on rest, which is when a human is asking "what is this?" anyway.
18
+ * - a NUMBER (ms): leading + trailing throttle — an immediate pick, then at
19
+ * most one per interval while moving, plus the rest pick. For authors who
20
+ * want livelier hover-follow at a bounded cost.
21
+ * - `continuous`: deck's native behavior, unwrapped cadence.
22
+ * - `off`: hover picking disabled entirely (click picking is a separate
23
+ * deck path and is unaffected).
24
+ *
25
+ * Always forwarded regardless of policy: `pointerleave` (clears hover
26
+ * state) and button-down moves (deck's own drag handling early-returns on
27
+ * those, and gizmo drags depend on the stream).
28
+ *
29
+ * Pure and dependency-injected (timers/clock) so the policy is unit-testable
30
+ * without deck or a DOM.
31
+ */
32
+ export type HoverPickMode = "rest" | "continuous" | "off" | number;
33
+ export interface HoverGateTimers {
34
+ set(fn: () => void, ms: number): unknown;
35
+ clear(handle: unknown): void;
36
+ now(): number;
37
+ }
38
+ /** How long the pointer must sit still before the rest pick fires. One-ish frame of stillness reads as "stopped" without feeling laggy. */
39
+ export declare const HOVER_REST_MS = 32;
40
+ interface PointerMoveLike {
41
+ type?: string;
42
+ leftButton?: boolean;
43
+ rightButton?: boolean;
44
+ }
45
+ /**
46
+ * Wraps a deck-shaped `_onPointerMove` with the gating policy. `policy` is a
47
+ * GETTER so an attribute edit applies to the very next move with no
48
+ * re-wrapping; `forward` is deck's original handler.
49
+ */
50
+ export declare function createHoverGate<E extends PointerMoveLike>(forward: (event: E) => void, policy: () => HoverPickMode, timers?: HoverGateTimers): (event: E) => void;
51
+ /** Parses the `hover-pick` attribute (null/absent → the rest default; unknown values warn and fall back). */
52
+ export declare function parseHoverPickMode(raw: string | null, warnLabel: string): HoverPickMode;
53
+ export {};
@@ -15,7 +15,7 @@ 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", "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", "warm-tiles"];
18
+ export declare const BUILTIN_ACTIONS: readonly ["toggle-layer", "set-pickable", "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", "warm-tiles"];
19
19
  /** Hand-authored widget types — validated against the widget registry in html-data.test.ts. */
20
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", "time-slider", "ifc-browser", "ifc-legend", "feature-inspector", "ifc-inspector", "ifc-loader", "ifc-clash"];
21
21
  export declare function buildHtmlCustomData(): {
@@ -1,4 +1,4 @@
1
- import { ak as Bt, ax as Xt, ay as Yt, az as Us } from "./index-CPf0evzb.js";
1
+ import { ak as Bt, az as Xt, aA as Yt, aB as Us } from "./index-CxOS31XG.js";
2
2
  import { w as gs, t as Hs, f as Ws, m as Qs } from "./mgrs-BY9bIvp4.js";
3
3
  import { c as Xs } from "./convert-arrow-schema-DrAihRf9.js";
4
4
  import { y as Ys, A as bt, a as Ks, z as Vs, R as Zs } from "./recordbatch-Bpc0uxFn.js";
@@ -1,4 +1,4 @@
1
- import { ae as _t, af as Ct } from "./index-CPf0evzb.js";
1
+ import { ae as _t, af as Ct } from "./index-CxOS31XG.js";
2
2
  function Yt(e, r, t = 2, i, o = "xy") {
3
3
  const s = r && r.length, l = s ? r[0] * t : e.length;
4
4
  let c = Ut(e, 0, l, t, !0, i && i[0], o);
@@ -1,4 +1,4 @@
1
- import { ag as Be, ah as le, ai as ce, aj as De, ak as Oe, al as ke } from "./index-CPf0evzb.js";
1
+ import { ag as Be, ah as le, ai as ce, aj as De, ak as Oe, al as ke } from "./index-CxOS31XG.js";
2
2
  import { y as ve, a as he, z as G, R as J, A as ue, C as Fe, F as je, n as xe, S as Ie, E as Ne } from "./recordbatch-Bpc0uxFn.js";
3
3
  import { g as V, a as fe, b as Le, c as Ue, d as Me, e as qe, m as Ve } from "./table-accessors-CYWTzpQI.js";
4
4
  import { c as $e } from "./convert-arrow-schema-DrAihRf9.js";
@@ -1,5 +1,5 @@
1
- import { i as E, j as S, k as R, o as N, p as h, q as w, s as O, t as W, v as x, w as I, x as B, y as _, A as P, B as k, E as $, F as z, H as v, I as F, J as C, K as U, N as p, O as M } from "./index-CPf0evzb.js";
2
- import { P as $e, R as ze, _ as ve, Q as Ce, S as Ue, T as Me, U as je, X as De, Y as Je, Z as qe, $ as He, a0 as Ve, a1 as Ge, a2 as Ke, a3 as Qe, a4 as Xe, a5 as Ye, a6 as Ze, a7 as et, a8 as tt, a9 as nt, aa as rt, ab as at, ac as st, ad as ot } from "./index-CPf0evzb.js";
1
+ import { i as E, j as S, k as R, o as N, p as h, q as w, s as O, t as W, v as x, w as I, x as B, y as _, A as P, B as k, E as $, F as z, H as v, I as F, J as C, K as U, N as p, O as M } from "./index-CxOS31XG.js";
2
+ import { P as $e, R as ze, _ as ve, Q as Ce, S as Ue, T as Me, U as je, X as De, Y as Je, Z as qe, $ as He, a0 as Ve, a1 as Ge, a2 as Ke, a3 as Qe, a4 as Xe, a5 as Ye, a6 as Ze, a7 as et, a8 as tt, a9 as nt, aa as rt, ab as at, ac as st, ad as ot } from "./index-CxOS31XG.js";
3
3
  import { g as j, i as D } from "./table-accessors-CYWTzpQI.js";
4
4
  async function J(t, e, n = {}, r = {}) {
5
5
  const a = E(t), s = S.getWorkerFarm(n), { source: o } = n, c = { name: a, source: o };