@nika-js/onlymap 0.6.26 → 0.7.1

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 (33) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +2 -2
  3. package/dist/{LercDecode.es-B4KknLMs.js → LercDecode.es-gFG_4xNk.js} +1 -1
  4. package/dist/{basemap-C4bmWq6M.js → basemap-oVln_CKU.js} +1 -1
  5. package/dist/elements/om-map.d.ts +9 -1
  6. package/dist/{geoparquet-BPMda753.js → geoparquet-CufpN8G1.js} +1 -1
  7. package/dist/{index-DtU_XhTX.js → index--zvhxlLl.js} +2 -2
  8. package/dist/{index-B5hsHnPl.js → index-CLe7spTj.js} +1 -1
  9. package/dist/{index-COuIQk2Z.js → index-CnwhAoQ5.js} +11283 -11046
  10. package/dist/{index-DMT_7aV3.js → index-DEIMxmvY.js} +1 -1
  11. package/dist/{index-DK5w4OxN.js → index-EXrBWGX7.js} +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/layer-registry.d.ts +10 -0
  14. package/dist/{lerc-BNW5A6zN.js → lerc-nHFgnN0N.js} +2 -2
  15. package/dist/onlymap.standalone.js +29352 -28617
  16. package/dist/onlymapjs.js +91 -89
  17. package/dist/{raster-eMqzBMQf.js → raster-CE4jsgUi.js} +1086 -799
  18. package/dist/raster-identify.d.ts +19 -0
  19. package/dist/{raster-pipeline-Dg-GSDP3.js → raster-pipeline-ghFqryqb.js} +665 -453
  20. package/dist/raster-pipeline.d.ts +109 -14
  21. package/dist/raster.d.ts +135 -1
  22. package/dist/react.js +253 -234
  23. package/dist/runtime-core.d.ts +23 -0
  24. package/dist/version.d.ts +1 -1
  25. package/dist/widget-layout.d.ts +18 -0
  26. package/dist/{zarr-Dv7OiUcg.js → zarr-DjEYckHG.js} +10 -3
  27. package/docs/design/cog-v2.md +223 -0
  28. package/llms.txt +2 -2
  29. package/onlymapjs.attributes.json +9 -0
  30. package/onlymapjs.html-data.json +24 -0
  31. package/package.json +1 -1
  32. package/skills/onlymapjs/SKILL.md +1 -1
  33. package/skills/onlymapjs/references/syntax.md +12 -7
@@ -4,40 +4,135 @@
4
4
  * (the GPU modules), NOT on `@developmentseed/deck.gl-geotiff` or `zarrita`, so
5
5
  * neither lazy chunk drags the other's decoder in.
6
6
  *
7
- * The contract: a per-tile Float32 texture (single-band `r32float`, or three-
8
- * band `rgba32float`) plus the styling scalars, run through
9
- * FilterNoDataVal LinearRescale Colormap (single-band only). Restretch and
10
- * recolor are uniform-only updates on a fresh layer instance; the tile cache
11
- * survives because the getTileData callback that produced these textures is a
12
- * module-level stable reference in each layer.
7
+ * COG v2 (docs/design/cog-v2.md, issue #13): ONE pipeline for single-band and
8
+ * composite sources, built on CompositeBands per-band `r32float` textures
9
+ * created LAZILY from the retained decode, with an `ivec4 channelMap` uniform
10
+ * routing bands to RGB channels. A single-band source is the degenerate
11
+ * composite (slot 0 replicated to R=G=B), which keeps every styling feature
12
+ * written exactly once. Band switches re-run renderTile on a fresh layer
13
+ * instance; missing band textures are uploaded from the retained array — the
14
+ * tile cache, the HTTP cache, and the decode all survive. Nothing short of a
15
+ * `src` change refetches.
16
+ *
17
+ * Pipeline order (load-bearing): CreateTexture (base-texture insurance for
18
+ * the mesh) → CompositeBands (color = raw band values) → FilterNoDataVal
19
+ * (tests the RAW datum, so it precedes the rescale squash) → LinearRescale →
20
+ * Colormap (single-band only).
13
21
  */
14
22
  import type { Device, Texture } from "@luma.gl/core";
15
23
  import { type RasterModule } from "@developmentseed/deck.gl-raster/gpu-modules";
16
- /** A styled tile's GPU payload (satisfies deck.gl-raster's MinimalTileData + ours). */
24
+ /**
25
+ * The decoded source a tile retains — structural subset of
26
+ * `@developmentseed/geotiff`'s RasterArray (band-separate carries `bands`,
27
+ * pixel-interleaved carries `data` + `count`), typed here so the pipeline
28
+ * stays decoder-agnostic and tests fabricate it without a TIFF in sight.
29
+ */
30
+ export interface RasterArrayLike {
31
+ layout: "band-separate" | "pixel-interleaved";
32
+ width: number;
33
+ height: number;
34
+ bands?: ArrayLike<number>[];
35
+ data?: ArrayLike<number>;
36
+ count?: number;
37
+ }
38
+ /** A styled tile's payload: retained decode + lazily-created band textures. */
17
39
  export interface OmTileData {
18
40
  width: number;
19
41
  height: number;
20
42
  byteLength: number;
21
- texture: Texture;
43
+ /** The device the tile's textures live on — renderTile receives only the data, so lazy uploads need it here. */
44
+ device: Device;
45
+ /** Decoded source (ALL bands) retained for the tile-cache lifetime — band switches re-upload from it, identify samples it. Null when the source pre-uploads (Zarr) or retention is off. */
46
+ array: RasterArrayLike | null;
47
+ /** 0-based band index → its r32float texture. Lazily filled by ensureBandTextures; fully destroyed by destroyTileTextures. */
48
+ bandTextures: Map<number, Texture>;
22
49
  colormapTexture: Texture;
23
50
  bandCount: number;
24
51
  nodata: number | null;
25
52
  }
26
- /** The four styling scalars every raster source (COG, Zarr) exposes. */
53
+ /** The styling scalars every raster source (COG, Zarr) exposes. */
27
54
  export interface RasterStyle {
28
- /** Rescale window; both default to the 8-bit range when absent. */
29
- rescaleMin?: number | null;
30
- rescaleMax?: number | null;
55
+ /** Rescale window; both default to the 8-bit range when absent. A number broadcasts to every selected band; a triple gives per-band windows, positionally matching a `bands` triple (COG v2 decision: per-band ships in 0.7.0). */
56
+ rescaleMin?: number | number[] | null;
57
+ rescaleMax?: number | number[] | null;
31
58
  /** Sprite colormap name (single-band sources only). Unknown names warn + fall back to gray. */
32
59
  colormap?: string | null;
60
+ /** Reversed colormap (upstream Colormap's own `reversed` prop — the fork the design planned turned out unnecessary). */
61
+ reverse?: boolean | null;
33
62
  /** Overrides the source's own nodata sentinel. */
34
63
  nodataOverride?: number | null;
64
+ /** 1-based band selection (GDAL convention): a single band (colormap-eligible) or an [r,g,b] triple. Absent = band 1 / first three. */
65
+ bands?: number | number[] | null;
66
+ /** Non-linear stretch applied to the rescaled [0,1] value: linear (default) | log | sqrt. */
67
+ stretch?: string | null;
68
+ /** Power-law exponent > 0 (display gamma); 1 = identity. */
69
+ gamma?: number | null;
35
70
  }
36
71
  export declare function getColormapTexture(device: Device): Promise<Texture>;
37
72
  export declare function resolveColormapIndex(name: string): number;
38
73
  /**
39
- * The styled render pipeline for one tile. Nodata tests the RAW datum, so it
40
- * precedes the rescale squash; the colormap runs only for single-band sources.
74
+ * Authored `bands` (1-based) 0-based indices, validated against the
75
+ * source's band count. A single index means "single-band" (colormap
76
+ * pipeline); a triple means RGB composite. Anything unusable falls back to
77
+ * the source defaults (band 1 / first three) with one warning per shape —
78
+ * the layer renders SOMETHING rather than blanking (structured errors land
79
+ * in phase 6).
80
+ */
81
+ export declare function resolveBands(bands: number | number[] | null | undefined, bandCount: number): number[];
82
+ /** Every band's raw value at one pixel — the identify sampler (no per-band copies; reads the retained layout directly). */
83
+ export declare function sampleAllBands(arr: RasterArrayLike, px: number, py: number, bandCount: number): number[];
84
+ /** One band of a decoded array as Float32 regardless of layout — luma's r32float upload rejects uint8/16 TypedArrays, so non-Float32 sources copy (raw values preserved; that's the contract the rescale window depends on). */
85
+ export declare function extractBand(arr: RasterArrayLike, band: number): Float32Array;
86
+ /**
87
+ * Creates any missing textures for the selected bands from the retained
88
+ * array, and destroys textures for bands no longer selected (VRAM stays at
89
+ * ≤ the selected set). Returns the selected bands' textures in order.
90
+ * A tile whose array was dropped (identify off — phase 4) keeps whatever
91
+ * textures it already has; selections it can't satisfy fall back to the
92
+ * first available texture rather than crashing mid-frame.
93
+ */
94
+ export declare function ensureBandTextures(tile: OmTileData, bands: number[]): Texture[];
95
+ /** Frees every GPU texture a tile owns and drops the retained decode — the onTileUnload hook (the sprite texture is device-scoped and must NOT be destroyed here). */
96
+ export declare function destroyTileTextures(tile: OmTileData): void;
97
+ /**
98
+ * Per-band rescale windows (design decision: ships in 0.7.0, not a
99
+ * follow-up). Used ONLY when a triple window is authored — scalar windows
100
+ * keep upstream's LinearRescale so existing maps keep their exact shader
101
+ * (the phase-1 SSIM-1.0 parity stays intact).
102
+ */
103
+ export declare const PerBandRescale: RasterModule["module"];
104
+ /**
105
+ * Non-linear stretch + display gamma, applied AFTER the rescale squash (the
106
+ * value is in [0,1]) and BEFORE the colormap lookup. `log` is the bounded
107
+ * log10 curve `log(1+9x)/log 10` (monotone, hits 0→0 and 1→1, no -inf).
108
+ * Inserted only when non-identity, so linear/γ=1 maps keep their shader.
109
+ */
110
+ export declare const StretchGamma: RasterModule["module"];
111
+ /** stretch attribute → mode number (0 linear / 1 log / 2 sqrt); unknown warns once and stays linear. */
112
+ export declare function resolveStretchMode(stretch: string | null | undefined): number;
113
+ /** gamma attribute → validated exponent (> 0); anything else warns once and is identity. */
114
+ export declare function resolveGamma(gamma: number | null | undefined): number;
115
+ /**
116
+ * Normalizes the authored window(s) against the selection. Scalars
117
+ * broadcast; a triple pairs positionally with a `bands` triple. Returns
118
+ * either a scalar window (upstream LinearRescale — shader parity for
119
+ * existing maps) or per-band vec3s (PerBandRescale).
120
+ */
121
+ export declare function resolveWindow(style: RasterStyle, bands: number[]): {
122
+ kind: "scalar";
123
+ min: number;
124
+ max: number;
125
+ } | {
126
+ kind: "per-band";
127
+ minV: [number, number, number];
128
+ maxV: [number, number, number];
129
+ };
130
+ /** channelMap for a selection: single band replicates slot 0 to RGB (gray, colormap-eligible); a triple maps each channel to its band's slot. Alpha is always the shader's opaque -1. */
131
+ export declare function buildChannelMap(bands: number[], slotOf: (band: number) => number): [number, number, number, number];
132
+ /**
133
+ * The styled render pipeline for one tile. One shape for single-band and
134
+ * composite sources (design decision: UNIFY, with the Metal-ANGLE pixel
135
+ * parachute in phase-1 acceptance).
41
136
  */
42
137
  export declare function buildRenderPipeline(tile: OmTileData, style: RasterStyle): {
43
138
  renderPipeline: RasterModule[];
package/dist/raster.d.ts CHANGED
@@ -1,7 +1,79 @@
1
+ /**
2
+ * Native COG/GeoTIFF raster layer (spec: "Raster / GeoTIFF — native COG
3
+ * layer"). A LAZY chunk — dynamically imported the first time a manifest
4
+ * uses `type="COGLayer"` (see layer-registry's `loadClass` seam), so the
5
+ * eager bundle stays raster-free.
6
+ *
7
+ * Built on `@developmentseed/deck.gl-geotiff` + `deck.gl-raster` (MIT),
8
+ * compiled INSIDE the library so their `@deck.gl/*` imports resolve to the
9
+ * bundled deck — the class-identity requirement that rules out consuming
10
+ * these packages from a consumer's own node_modules.
11
+ *
12
+ * Two render paths, chosen per instance:
13
+ * - UNSTYLED (no min/max/colormap/nodata authored): COGLayer's own inferred
14
+ * default pipeline — correct for plain 8-bit RGB/gray COGs, zero config.
15
+ * - STYLED: a Float32-aware getTileData/renderTile pair (ported from a
16
+ * production consumer): tile pixels upload as r32float / rgba32float
17
+ * textures, then FilterNoDataVal (raw values, BEFORE rescale) →
18
+ * LinearRescale (min/max → [0,1]) → Colormap (single-band only, sampled
19
+ * from the bundled sprite). Restretch/recolor = uniform updates on a new
20
+ * layer instance; the tile cache survives because `getTileData` is a
21
+ * module-level stable reference.
22
+ */
23
+ import type { Device } from "@luma.gl/core";
1
24
  import { COGLayer } from "@developmentseed/deck.gl-geotiff";
2
25
  import { type OmTileData, type RasterStyle } from "./raster-pipeline";
3
- /** COGLayer styling scalars (rescale window, colormap, nodata). Shared with the Zarr layer; re-exported for consumer wrappers. */
26
+ import { type RasterIdentifyResult } from "./raster-identify";
27
+ /** COGLayer styling scalars (rescale window, colormap, nodata, bands). Shared with the Zarr layer; re-exported for consumer wrappers. */
4
28
  export type OmCOGLayerExtraProps = RasterStyle;
29
+ export declare const omGetTileData: (image: Parameters<NonNullable<ConstructorParameters<typeof COGLayer>[0]["getTileData"]>>[0], { device, x, y, signal, pool, dropDecode, }: {
30
+ device: Device;
31
+ x: number;
32
+ y: number;
33
+ signal?: AbortSignal;
34
+ pool: never;
35
+ /** identify="off": upload the selected bands eagerly and release the CPU decode (the memory-relief contract) — a later bands change refetches instead of re-uploading. The raw `bands` grammar value, resolved here against the tile's own band count. */
36
+ dropDecode?: {
37
+ bands: number | number[] | null | undefined;
38
+ };
39
+ }) => Promise<OmTileData>;
40
+ /** TIFF SampleFormat → readable dtype for the dev notice. */
41
+ export declare function dtypeName(tags: {
42
+ bitsPerSample?: ArrayLike<number>;
43
+ sampleFormat?: ArrayLike<number>;
44
+ }): string;
45
+ export declare function isUint8(tags: {
46
+ bitsPerSample?: ArrayLike<number>;
47
+ sampleFormat?: ArrayLike<number>;
48
+ }): boolean;
49
+ export type AutoWindow = {
50
+ min: number | [number, number, number];
51
+ max: number | [number, number, number];
52
+ source: "stats" | "overview";
53
+ };
54
+ /** Deterministic path: per-band GDAL STATISTICS tags from the header — no pixel pass. Null when any selected band lacks them. */
55
+ export declare function statsWindow(gdalMetadata: unknown, bands: number[]): AutoWindow | null;
56
+ /** Fallback: one small read of the coarsest overview's first tile — data-derived, so the notice warns harder. */
57
+ export declare function overviewWindow(image: {
58
+ fetchTile(x: number, y: number, opts: object): Promise<{
59
+ array: import("./raster-pipeline").RasterArrayLike & {
60
+ nodata?: number | null;
61
+ };
62
+ }>;
63
+ }, bands: number[], pool: unknown): Promise<AutoWindow | null>;
64
+ /**
65
+ * Palette legend (COG v2 phase 5, the gl#1014 ask): a paletted GeoTIFF
66
+ * renders through its embedded color table upstream — this derives legend
67
+ * CLASS rows from it when the number of DISTINCT indices actually used
68
+ * (sampled from the coarsest overview tile) is small. Beyond the cap the
69
+ * legend widget's single-swatch fallback stands; TIFF palettes carry no
70
+ * class names, so labels are the palette indices.
71
+ */
72
+ export declare const PALETTE_LEGEND_MAX_CLASSES = 12;
73
+ export declare function paletteLegendEntries(colorMap: ArrayLike<number>, arr: import("./raster-pipeline").RasterArrayLike): {
74
+ color: string;
75
+ label: string;
76
+ }[] | null;
5
77
  type AnyProps = Record<string, unknown>;
6
78
  declare const OmCOGLayer_base: new (...props: AnyProps[]) => InstanceType<typeof COGLayer>;
7
79
  /**
@@ -16,7 +88,69 @@ export declare class OmCOGLayer extends OmCOGLayer_base {
16
88
  rescaleMax: null;
17
89
  colormap: null;
18
90
  nodataOverride: null;
91
+ bands: null;
92
+ stretch: null;
93
+ gamma: null;
94
+ reverse: null;
19
95
  };
20
96
  constructor(props: AnyProps);
97
+ /**
98
+ * Upstream's `_parseGeoTIFF` EAGERLY infers a render pipeline at its tail
99
+ * and HARD-THROWS for int/float sources ("Inferring render pipeline for
100
+ * non-unsigned integers not yet supported") — before `state.geotiff` ever
101
+ * lands, which would kill the exact auto-route that exists to serve those
102
+ * sources. Conditional retry: the normal run stays untouched (uint8 infers
103
+ * fine, one header fetch); on the inference throw only, re-run with a
104
+ * props OVERLAY (prototype chain — the real props object is never mutated)
105
+ * whose callback pair suppresses the infer, so state.geotiff lands and the
106
+ * resolvers below route the source through the styled pipeline. The retry
107
+ * costs one extra header fetch, paid only by sources that were a dead
108
+ * layer before this existed.
109
+ */
110
+ /** Structured runtime errors (phase 6) through the injected onRasterError → core.onRuntimeError channel — the layer stays mounted, the map stays alive. */
111
+ private omReportError;
112
+ _parseGeoTIFF(): Promise<void>;
113
+ private omRetryWithoutInference;
114
+ private omExtra;
115
+ private omAuthoredStyled;
116
+ private omGeotiff;
117
+ /**
118
+ * The bit-depth auto-route (the "my COG is blank" fix): a non-8-bit
119
+ * source with NO styling attrs enters the styled pipeline anyway, with a
120
+ * window from the header. Paletted sources (ColorMap tag) never route —
121
+ * upstream renders the palette.
122
+ */
123
+ private omAutoWanted;
124
+ /** Resolved auto window, kicking off the one-shot overview sample when stats are absent. Undefined while pending/unavailable. */
125
+ private omAutoWindow;
126
+ /** Feeds the resolved window to the consumer (legend derivation) via the injected onAutoWindow deck prop — the onTilesetLoad compose precedent. */
127
+ private omPublishWindow;
128
+ private omStyledActive;
129
+ /** Authored style + auto window filling any unauthored window end. */
130
+ private omEffectiveStyle;
131
+ /**
132
+ * Pixel identify (COG v2 phase 4): lngLat → source CRS (the descriptor's
133
+ * own projector) → full-res pixel (geotiff.index) → the FINEST currently-
134
+ * loaded tile containing it → all bands sampled from the retained decode.
135
+ * No fetch, no GPU readback; null when the point is outside the image or
136
+ * no covering tile is resident.
137
+ */
138
+ identify(lngLat: [number, number]): RasterIdentifyResult | null;
139
+ updateState(params: Parameters<InstanceType<typeof COGLayer>["updateState"]>[0]): void;
140
+ /** One-shot palette legend derivation per opened file (phase 5). */
141
+ private omMaybePaletteLegend;
142
+ finalizeState(context: Parameters<InstanceType<typeof COGLayer>["finalizeState"]>[0]): void;
143
+ _getTileDataCallback(): any;
144
+ _renderTileCallback(): any;
145
+ /**
146
+ * identify="off" bakes the selection into fetch-time textures, so a bands
147
+ * change — or flipping identify back on, which must repopulate the
148
+ * retained arrays — has to REFETCH. Surfaced as the inner TileLayer's own
149
+ * `getTileData` update trigger by cloning what upstream renders (it
150
+ * passes no such trigger through, and `_renderTileLayer` is TS-private).
151
+ * Retained mode pins a constant, keeping the v2 contract: refetch keyed
152
+ * on the tileset descriptor alone, never on styling.
153
+ */
154
+ renderLayers(): any;
21
155
  }
22
156
  export type { OmTileData };