@nika-js/onlymap 0.6.26 → 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 (33) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +2 -2
  3. package/dist/{LercDecode.es-B4KknLMs.js → LercDecode.es-CYOgDeVu.js} +1 -1
  4. package/dist/{basemap-C4bmWq6M.js → basemap-0rGUZkQT.js} +1 -1
  5. package/dist/elements/om-map.d.ts +9 -1
  6. package/dist/{geoparquet-BPMda753.js → geoparquet-Cau8fzH7.js} +1 -1
  7. package/dist/{index-DMT_7aV3.js → index-BF3xZe9d.js} +1 -1
  8. package/dist/{index-DK5w4OxN.js → index-Bt3U7sbb.js} +1 -1
  9. package/dist/{index-B5hsHnPl.js → index-CpSUZ-J3.js} +1 -1
  10. package/dist/{index-DtU_XhTX.js → index-CriPOt1s.js} +2 -2
  11. package/dist/{index-COuIQk2Z.js → index-CxOS31XG.js} +11193 -10962
  12. package/dist/index.d.ts +1 -1
  13. package/dist/layer-registry.d.ts +10 -0
  14. package/dist/{lerc-BNW5A6zN.js → lerc-aeqKo4fK.js} +2 -2
  15. package/dist/onlymap.standalone.js +29316 -28616
  16. package/dist/onlymapjs.js +91 -89
  17. package/dist/{raster-eMqzBMQf.js → raster-D70FrkLt.js} +1049 -790
  18. package/dist/raster-identify.d.ts +19 -0
  19. package/dist/{raster-pipeline-Dg-GSDP3.js → raster-pipeline-A-zvBnKU.js} +660 -450
  20. package/dist/raster-pipeline.d.ts +109 -14
  21. package/dist/raster.d.ts +91 -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-DmrbmSNx.js} +10 -3
  27. package/docs/design/cog-v2.md +223 -0
  28. package/llms.txt +1 -1
  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,45 @@
1
1
  import { COGLayer } from "@developmentseed/deck.gl-geotiff";
2
2
  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. */
3
+ import { type RasterIdentifyResult } from "./raster-identify";
4
+ /** COGLayer styling scalars (rescale window, colormap, nodata, bands). Shared with the Zarr layer; re-exported for consumer wrappers. */
4
5
  export type OmCOGLayerExtraProps = RasterStyle;
6
+ /** TIFF SampleFormat → readable dtype for the dev notice. */
7
+ export declare function dtypeName(tags: {
8
+ bitsPerSample?: ArrayLike<number>;
9
+ sampleFormat?: ArrayLike<number>;
10
+ }): string;
11
+ export declare function isUint8(tags: {
12
+ bitsPerSample?: ArrayLike<number>;
13
+ sampleFormat?: ArrayLike<number>;
14
+ }): boolean;
15
+ export type AutoWindow = {
16
+ min: number | [number, number, number];
17
+ max: number | [number, number, number];
18
+ source: "stats" | "overview";
19
+ };
20
+ /** Deterministic path: per-band GDAL STATISTICS tags from the header — no pixel pass. Null when any selected band lacks them. */
21
+ export declare function statsWindow(gdalMetadata: unknown, bands: number[]): AutoWindow | null;
22
+ /** Fallback: one small read of the coarsest overview's first tile — data-derived, so the notice warns harder. */
23
+ export declare function overviewWindow(image: {
24
+ fetchTile(x: number, y: number, opts: object): Promise<{
25
+ array: import("./raster-pipeline").RasterArrayLike & {
26
+ nodata?: number | null;
27
+ };
28
+ }>;
29
+ }, bands: number[], pool: unknown): Promise<AutoWindow | null>;
30
+ /**
31
+ * Palette legend (COG v2 phase 5, the gl#1014 ask): a paletted GeoTIFF
32
+ * renders through its embedded color table upstream — this derives legend
33
+ * CLASS rows from it when the number of DISTINCT indices actually used
34
+ * (sampled from the coarsest overview tile) is small. Beyond the cap the
35
+ * legend widget's single-swatch fallback stands; TIFF palettes carry no
36
+ * class names, so labels are the palette indices.
37
+ */
38
+ export declare const PALETTE_LEGEND_MAX_CLASSES = 12;
39
+ export declare function paletteLegendEntries(colorMap: ArrayLike<number>, arr: import("./raster-pipeline").RasterArrayLike): {
40
+ color: string;
41
+ label: string;
42
+ }[] | null;
5
43
  type AnyProps = Record<string, unknown>;
6
44
  declare const OmCOGLayer_base: new (...props: AnyProps[]) => InstanceType<typeof COGLayer>;
7
45
  /**
@@ -16,7 +54,59 @@ export declare class OmCOGLayer extends OmCOGLayer_base {
16
54
  rescaleMax: null;
17
55
  colormap: null;
18
56
  nodataOverride: null;
57
+ bands: null;
58
+ stretch: null;
59
+ gamma: null;
60
+ reverse: null;
19
61
  };
20
62
  constructor(props: AnyProps);
63
+ /**
64
+ * Upstream's `_parseGeoTIFF` EAGERLY infers a render pipeline at its tail
65
+ * and HARD-THROWS for int/float sources ("Inferring render pipeline for
66
+ * non-unsigned integers not yet supported") — before `state.geotiff` ever
67
+ * lands, which would kill the exact auto-route that exists to serve those
68
+ * sources. Conditional retry: the normal run stays untouched (uint8 infers
69
+ * fine, one header fetch); on the inference throw only, re-run with a
70
+ * props OVERLAY (prototype chain — the real props object is never mutated)
71
+ * whose callback pair suppresses the infer, so state.geotiff lands and the
72
+ * resolvers below route the source through the styled pipeline. The retry
73
+ * costs one extra header fetch, paid only by sources that were a dead
74
+ * layer before this existed.
75
+ */
76
+ /** Structured runtime errors (phase 6) through the injected onRasterError → core.onRuntimeError channel — the layer stays mounted, the map stays alive. */
77
+ private omReportError;
78
+ _parseGeoTIFF(): Promise<void>;
79
+ private omRetryWithoutInference;
80
+ private omExtra;
81
+ private omAuthoredStyled;
82
+ private omGeotiff;
83
+ /**
84
+ * The bit-depth auto-route (the "my COG is blank" fix): a non-8-bit
85
+ * source with NO styling attrs enters the styled pipeline anyway, with a
86
+ * window from the header. Paletted sources (ColorMap tag) never route —
87
+ * upstream renders the palette.
88
+ */
89
+ private omAutoWanted;
90
+ /** Resolved auto window, kicking off the one-shot overview sample when stats are absent. Undefined while pending/unavailable. */
91
+ private omAutoWindow;
92
+ /** Feeds the resolved window to the consumer (legend derivation) via the injected onAutoWindow deck prop — the onTilesetLoad compose precedent. */
93
+ private omPublishWindow;
94
+ private omStyledActive;
95
+ /** Authored style + auto window filling any unauthored window end. */
96
+ private omEffectiveStyle;
97
+ /**
98
+ * Pixel identify (COG v2 phase 4): lngLat → source CRS (the descriptor's
99
+ * own projector) → full-res pixel (geotiff.index) → the FINEST currently-
100
+ * loaded tile containing it → all bands sampled from the retained decode.
101
+ * No fetch, no GPU readback; null when the point is outside the image or
102
+ * no covering tile is resident.
103
+ */
104
+ identify(lngLat: [number, number]): RasterIdentifyResult | null;
105
+ updateState(params: Parameters<InstanceType<typeof COGLayer>["updateState"]>[0]): void;
106
+ /** One-shot palette legend derivation per opened file (phase 5). */
107
+ private omMaybePaletteLegend;
108
+ finalizeState(context: Parameters<InstanceType<typeof COGLayer>["finalizeState"]>[0]): void;
109
+ _getTileDataCallback(): any;
110
+ _renderTileCallback(): any;
21
111
  }
22
112
  export type { OmTileData };