@walkthru-earth/objex 1.2.1 → 1.3.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 (49) hide show
  1. package/README.md +6 -3
  2. package/dist/components/layout/ConnectionDialog.svelte +35 -3
  3. package/dist/components/layout/Sidebar.svelte +1 -2
  4. package/dist/components/viewers/CodeViewer.svelte +51 -14
  5. package/dist/components/viewers/CodeViewer.svelte.d.ts +11 -1
  6. package/dist/components/viewers/CogControls.svelte +151 -22
  7. package/dist/components/viewers/CogControls.svelte.d.ts +5 -1
  8. package/dist/components/viewers/CogViewer.svelte +24 -7
  9. package/dist/components/viewers/MultiCogViewer.svelte +416 -0
  10. package/dist/components/viewers/MultiCogViewer.svelte.d.ts +9 -0
  11. package/dist/components/viewers/StacMapViewer.svelte +11 -5
  12. package/dist/components/viewers/StacMapViewer.svelte.d.ts +1 -0
  13. package/dist/components/viewers/StacMosaicViewer.svelte +699 -0
  14. package/dist/components/viewers/StacMosaicViewer.svelte.d.ts +9 -0
  15. package/dist/components/viewers/StacTabViewer.svelte +254 -0
  16. package/dist/components/viewers/StacTabViewer.svelte.d.ts +13 -0
  17. package/dist/components/viewers/ViewerRouter.svelte +155 -2
  18. package/dist/components/viewers/ViewerRouter.svelte.d.ts +1 -1
  19. package/dist/components/viewers/ZarrMapViewer.svelte +143 -4
  20. package/dist/components/viewers/ZarrMapViewer.svelte.d.ts +8 -2
  21. package/dist/components/viewers/ZarrViewer.svelte +1 -0
  22. package/dist/i18n/ar.js +27 -0
  23. package/dist/i18n/en.js +27 -0
  24. package/dist/index.d.ts +4 -0
  25. package/dist/index.js +2 -0
  26. package/dist/query/stac-geoparquet.d.ts +31 -0
  27. package/dist/query/stac-geoparquet.js +136 -0
  28. package/dist/stores/connections.svelte.d.ts +38 -23
  29. package/dist/stores/connections.svelte.js +105 -114
  30. package/dist/utils/cog.d.ts +80 -18
  31. package/dist/utils/cog.js +187 -125
  32. package/dist/utils/colormap-sprite.d.ts +39 -0
  33. package/dist/utils/colormap-sprite.js +77 -0
  34. package/dist/utils/connection-identity.d.ts +51 -0
  35. package/dist/utils/connection-identity.js +97 -0
  36. package/dist/utils/host-detection.js +48 -302
  37. package/dist/utils/parquet-metadata.d.ts +7 -1
  38. package/dist/utils/parquet-metadata.js +35 -1
  39. package/dist/utils/stac-geoparquet.d.ts +90 -0
  40. package/dist/utils/stac-geoparquet.js +223 -0
  41. package/dist/utils/stac-hydrate.d.ts +38 -0
  42. package/dist/utils/stac-hydrate.js +243 -0
  43. package/dist/utils/stac.d.ts +136 -0
  44. package/dist/utils/stac.js +176 -0
  45. package/dist/utils/storage-url.d.ts +26 -0
  46. package/dist/utils/storage-url.js +164 -28
  47. package/dist/utils/zarr.d.ts +34 -0
  48. package/dist/utils/zarr.js +94 -0
  49. package/package.json +14 -13
@@ -1,13 +1,56 @@
1
1
  import { STORAGE_KEYS } from '../constants.js';
2
+ import { connectionIdentityKey } from '../utils/connection-identity.js';
2
3
  import { loadFromStorage, persistToStorage } from '../utils/local-storage.js';
3
4
  import { credentialStore, storeToNative } from './credentials.svelte.js';
4
- // ---------------------------------------------------------------------------
5
- // Store
6
- // ---------------------------------------------------------------------------
5
+ function toConnection(id, config) {
6
+ return {
7
+ id,
8
+ name: config.name,
9
+ provider: config.provider,
10
+ endpoint: config.endpoint,
11
+ bucket: config.bucket,
12
+ region: config.region,
13
+ anonymous: config.anonymous,
14
+ authMethod: config.authMethod,
15
+ rootPrefix: config.rootPrefix
16
+ };
17
+ }
18
+ function applyCredentials(id, config) {
19
+ if (config.anonymous) {
20
+ credentialStore.remove(id);
21
+ return;
22
+ }
23
+ if (config.sas_token) {
24
+ const creds = { type: 'sas-token', sasToken: config.sas_token };
25
+ credentialStore.set(id, creds);
26
+ storeToNative(id, creds).catch(() => { });
27
+ return;
28
+ }
29
+ if (config.access_key && config.secret_key) {
30
+ const creds = {
31
+ type: 'sigv4',
32
+ accessKey: config.access_key,
33
+ secretKey: config.secret_key
34
+ };
35
+ credentialStore.set(id, creds);
36
+ storeToNative(id, creds).catch(() => { });
37
+ return;
38
+ }
39
+ credentialStore.remove(id);
40
+ }
7
41
  function createConnectionsStore() {
8
42
  let connections = $state([]);
9
43
  let loaded = $state(false);
10
44
  let dialogRequest = $state(0);
45
+ function persist() {
46
+ persistToStorage(STORAGE_KEYS.CONNECTIONS, connections);
47
+ }
48
+ function findByIdentity(input, excludeId) {
49
+ const key = connectionIdentityKey(input);
50
+ if (!key)
51
+ return undefined;
52
+ return connections.find((c) => c.id !== excludeId && connectionIdentityKey(c) === key);
53
+ }
11
54
  return {
12
55
  get items() {
13
56
  return connections;
@@ -17,7 +60,7 @@ function createConnectionsStore() {
17
60
  },
18
61
  /**
19
62
  * Load connections from localStorage.
20
- * Safe to call multiple times subsequent calls are no-ops.
63
+ * Safe to call multiple times, subsequent calls are no-ops.
21
64
  */
22
65
  async load() {
23
66
  if (loaded)
@@ -25,112 +68,58 @@ function createConnectionsStore() {
25
68
  connections = loadFromStorage(STORAGE_KEYS.CONNECTIONS, []);
26
69
  loaded = true;
27
70
  },
28
- /**
29
- * Force-reload connections.
30
- */
31
71
  async reload() {
32
72
  loaded = false;
33
73
  await this.load();
34
74
  },
35
75
  /**
36
- * Save a new connection to localStorage.
76
+ * Persist a connection. If an existing connection shares the same
77
+ * identity (see `connectionIdentityKey`), it's reused and its
78
+ * credentials are refreshed from the new config instead of spawning
79
+ * a duplicate record. Returns `{ id, existed }` so UI can distinguish
80
+ * "created" from "merged".
37
81
  */
38
82
  async save(config) {
39
- const id = crypto.randomUUID();
40
- const conn = {
41
- id,
42
- name: config.name,
43
- provider: config.provider,
44
- endpoint: config.endpoint,
45
- bucket: config.bucket,
46
- region: config.region,
47
- anonymous: config.anonymous,
48
- authMethod: config.authMethod,
49
- rootPrefix: config.rootPrefix
50
- };
51
- connections = [...connections, conn];
52
- persistToStorage(STORAGE_KEYS.CONNECTIONS, connections);
53
- // Store credentials in memory (never persisted to localStorage).
54
- if (!config.anonymous) {
55
- if (config.sas_token) {
56
- const creds = { type: 'sas-token', sasToken: config.sas_token };
57
- credentialStore.set(id, creds);
58
- storeToNative(id, creds).catch(() => { });
59
- }
60
- else if (config.access_key && config.secret_key) {
61
- const creds = {
62
- type: 'sigv4',
63
- accessKey: config.access_key,
64
- secretKey: config.secret_key
65
- };
66
- credentialStore.set(id, creds);
67
- storeToNative(id, creds).catch(() => { });
68
- }
83
+ const existing = findByIdentity(config);
84
+ if (existing) {
85
+ applyCredentials(existing.id, config);
86
+ return { id: existing.id, existed: true };
69
87
  }
70
- return id;
88
+ const id = crypto.randomUUID();
89
+ connections = [...connections, toConnection(id, config)];
90
+ persist();
91
+ applyCredentials(id, config);
92
+ return { id, existed: false };
71
93
  },
72
94
  /**
73
- * Update an existing connection.
95
+ * Update an existing connection. Throws `DuplicateConnectionError`
96
+ * when the new identity would collide with a different saved row,
97
+ * rather than silently overwriting and leaving a phantom duplicate.
74
98
  */
75
99
  async update(id, config) {
76
100
  const idx = connections.findIndex((c) => c.id === id);
77
101
  if (idx === -1)
78
102
  return false;
79
- connections[idx] = {
80
- ...connections[idx],
81
- name: config.name,
82
- provider: config.provider,
83
- endpoint: config.endpoint,
84
- bucket: config.bucket,
85
- region: config.region,
86
- anonymous: config.anonymous,
87
- authMethod: config.authMethod,
88
- rootPrefix: config.rootPrefix
89
- };
103
+ const collision = findByIdentity(config, id);
104
+ if (collision) {
105
+ throw new DuplicateConnectionError(collision.id, collision.name);
106
+ }
107
+ connections[idx] = toConnection(id, config);
90
108
  connections = [...connections];
91
- persistToStorage(STORAGE_KEYS.CONNECTIONS, connections);
109
+ persist();
92
110
  // Invalidate cached adapter for this connection
93
111
  import('../storage/index.js').then(({ clearAdapterCache }) => clearAdapterCache(id));
94
- // Update in-memory credentials.
95
- if (!config.anonymous) {
96
- if (config.sas_token) {
97
- const creds = { type: 'sas-token', sasToken: config.sas_token };
98
- credentialStore.set(id, creds);
99
- storeToNative(id, creds).catch(() => { });
100
- }
101
- else if (config.access_key && config.secret_key) {
102
- const creds = {
103
- type: 'sigv4',
104
- accessKey: config.access_key,
105
- secretKey: config.secret_key
106
- };
107
- credentialStore.set(id, creds);
108
- storeToNative(id, creds).catch(() => { });
109
- }
110
- else {
111
- credentialStore.remove(id);
112
- }
113
- }
114
- else {
115
- credentialStore.remove(id);
116
- }
112
+ applyCredentials(id, config);
117
113
  return true;
118
114
  },
119
- /**
120
- * Remove a connection by ID.
121
- */
122
115
  async remove(id) {
123
116
  const before = connections.length;
124
117
  connections = connections.filter((c) => c.id !== id);
125
- persistToStorage(STORAGE_KEYS.CONNECTIONS, connections);
118
+ persist();
126
119
  credentialStore.remove(id);
127
- // Invalidate cached adapter for this connection
128
120
  import('../storage/index.js').then(({ clearAdapterCache }) => clearAdapterCache(id));
129
121
  return connections.length < before;
130
122
  },
131
- /**
132
- * Test whether a connection is reachable via a lightweight list.
133
- */
134
123
  async test(id) {
135
124
  const { getAdapter } = await import('../storage/index.js');
136
125
  const adapter = getAdapter('remote', id);
@@ -143,23 +132,11 @@ function createConnectionsStore() {
143
132
  */
144
133
  async testWithConfig(config, existingId) {
145
134
  const tempId = existingId ?? `temp-test-${Date.now()}`;
146
- const tempConn = {
147
- id: tempId,
148
- name: config.name,
149
- provider: config.provider,
150
- endpoint: config.endpoint,
151
- bucket: config.bucket,
152
- region: config.region,
153
- anonymous: config.anonymous,
154
- authMethod: config.authMethod,
155
- rootPrefix: config.rootPrefix
156
- };
157
- // Temporarily register connection + credentials so the adapter can find them
135
+ const tempConn = toConnection(tempId, config);
158
136
  const hadConn = connections.some((c) => c.id === tempId);
159
137
  const prevCreds = credentialStore.get(tempId);
160
138
  if (!hadConn) {
161
139
  connections = [...connections, tempConn];
162
- // Don't persist — this is a temp test connection
163
140
  }
164
141
  if (!config.anonymous) {
165
142
  if (config.sas_token) {
@@ -180,56 +157,55 @@ function createConnectionsStore() {
180
157
  return true;
181
158
  }
182
159
  finally {
183
- // Cleanup: remove temp connection if we added it
184
160
  if (!hadConn) {
185
161
  connections = connections.filter((c) => c.id !== tempId);
186
- // Don't persist — was never in localStorage
187
162
  }
188
- // Restore previous credentials or remove temp ones
189
163
  if (prevCreds) {
190
164
  credentialStore.set(tempId, prevCreds);
191
165
  }
192
166
  else if (!hadConn) {
193
167
  credentialStore.remove(tempId);
194
168
  }
195
- // Also clear any cached adapter for the temp connection
196
169
  import('../storage/index.js').then(({ clearAdapterCache }) => clearAdapterCache(tempId));
197
170
  }
198
171
  },
199
- /** True when a dialog open has been requested and not yet consumed. */
200
172
  get dialogRequested() {
201
173
  return dialogRequest > 0;
202
174
  },
203
- /** Request opening the new-connection dialog from anywhere. */
204
175
  requestDialog() {
205
176
  dialogRequest++;
206
177
  },
207
- /** Mark the dialog request as consumed. */
208
178
  clearDialogRequest() {
209
179
  dialogRequest = 0;
210
180
  },
211
- /**
212
- * Synchronous lookup by ID (from the already-loaded list).
213
- */
214
181
  getById(id) {
215
182
  return connections.find((c) => c.id === id);
216
183
  },
217
184
  /**
218
- * Find an existing connection that matches bucket + endpoint.
185
+ * Find an already-saved connection that matches the canonical identity
186
+ * of `input` (provider + bucket + endpoint/region per provider rules).
187
+ * Used by auto-detect, manual-add dedup, and edit-collision checks.
219
188
  */
220
- findByBucketEndpoint(bucket, endpoint) {
221
- return connections.find((c) => c.bucket === bucket && c.endpoint === endpoint);
189
+ findByIdentity(input) {
190
+ return findByIdentity(input);
222
191
  },
223
192
  /**
224
- * Create a connection from a DetectedHost, deduplicating by bucket+endpoint.
225
- * Returns the connection ID (existing or newly created).
193
+ * Auto-connect path for a URL-detected bucket. Reuses an existing
194
+ * connection when identity matches, otherwise creates one anonymously.
195
+ * Always returns the final connection ID.
226
196
  */
227
197
  async saveHostConnection(detected) {
228
- const existing = this.findByBucketEndpoint(detected.bucket, detected.endpoint);
198
+ const identity = {
199
+ provider: detected.provider,
200
+ endpoint: detected.endpoint,
201
+ bucket: detected.bucket,
202
+ region: detected.region
203
+ };
204
+ const existing = findByIdentity(identity);
229
205
  if (existing)
230
206
  return existing.id;
231
207
  const name = detected.bucket === '$web' ? `Azure Static Web` : detected.bucket;
232
- const id = await this.save({
208
+ const result = await this.save({
233
209
  name,
234
210
  provider: detected.provider === 'unknown' ? 's3' : detected.provider,
235
211
  endpoint: detected.endpoint,
@@ -238,9 +214,24 @@ function createConnectionsStore() {
238
214
  anonymous: true,
239
215
  rootPrefix: detected.rootPrefix || undefined
240
216
  });
241
- return id;
217
+ return result.id;
242
218
  }
243
219
  };
244
220
  }
221
+ /**
222
+ * Thrown by `update()` when the proposed identity collides with a different
223
+ * saved connection. Lets the UI tell the user which connection already owns
224
+ * that identity instead of silently producing a phantom duplicate.
225
+ */
226
+ export class DuplicateConnectionError extends Error {
227
+ existingId;
228
+ existingName;
229
+ constructor(existingId, existingName) {
230
+ super(`A connection already exists for this bucket: "${existingName}"`);
231
+ this.name = 'DuplicateConnectionError';
232
+ this.existingId = existingId;
233
+ this.existingName = existingName;
234
+ }
235
+ }
245
236
  export const connectionStore = createConnectionsStore();
246
237
  export { connectionStore as connections };
@@ -1,17 +1,20 @@
1
1
  import type { GetTileDataOptions, MinimalDataT } from '@developmentseed/deck.gl-geotiff';
2
- import type { RenderTileResult } from '@developmentseed/deck.gl-raster';
2
+ import type { RasterModule, RenderTileResult } from '@developmentseed/deck.gl-raster';
3
3
  import type { GeoTIFF as GeoTIFFType, Overview } from '@developmentseed/geotiff';
4
4
  import { GeoTIFF } from '@developmentseed/geotiff';
5
5
  import type { EpsgResolver } from '@developmentseed/proj';
6
+ import type { Device } from '@luma.gl/core';
6
7
  import type maplibregl from 'maplibre-gl';
8
+ import { type ColormapName } from './colormap-sprite.js';
7
9
  /** SampleFormat tag value → human label. */
8
10
  export declare const SF_LABELS: Record<number, string>;
9
- export type ColorRampId = 'grayscale' | 'terrain' | 'viridis' | 'magma' | 'turbo' | 'spectral';
10
- export declare const COLOR_RAMP_STOPS: Record<ColorRampId, [number, number, number][]>;
11
- /** Interpolate a normalized value (0..1) into an RGB color from a ramp. */
12
- export declare function interpolateRamp(stops: [number, number, number][], t: number): [number, number, number];
13
- /** Generate a CSS linear-gradient string for a color ramp. */
14
- export declare function rampToGradientCss(id: ColorRampId): string;
11
+ /**
12
+ * Any of the 107 named ramps shipped in `@developmentseed/deck.gl-raster`'s
13
+ * `colormaps.png` sprite (matplotlib + rio-tiler + cmocean). Rendering is
14
+ * GPU-side via the `Colormap` shader module; switching ramps is a uniform
15
+ * update, no tile re-decode.
16
+ */
17
+ export type ColorRampId = ColormapName;
15
18
  export interface BandConfig {
16
19
  mode: 'rgb' | 'single';
17
20
  /** 0-indexed band indices for RGB channels */
@@ -78,6 +81,18 @@ export declare function createRescaledPipeline(geotiff: GeoTIFFType, rescale: Re
78
81
  getTileData: (image: GeoTIFFType | Overview, options: GetTileDataOptions) => Promise<MinimalDataT>;
79
82
  renderTile: (data: MinimalDataT) => RenderTileResult;
80
83
  };
84
+ export interface BandRenderPipelineOptions {
85
+ /** Value treated as "no-data" and zeroed out by `FilterNoDataVal`. */
86
+ noDataVal?: number | null;
87
+ /** Linear rescale applied after no-data masking. Omit for no rescaling. */
88
+ rescale?: RescaleConfig;
89
+ }
90
+ /**
91
+ * Build a `renderPipeline` array for `MultiCOGLayer` / raster mosaics.
92
+ * Combines optional `FilterNoDataVal` + `LinearRescale` stages in the order
93
+ * the GPU expects (no-data mask first, then rescale).
94
+ */
95
+ export declare function buildBandRenderPipeline(opts?: BandRenderPipelineOptions): RasterModule[];
81
96
  /**
82
97
  * Apply the two upstream-bug workarounds a GeoTIFF needs before being handed
83
98
  * to `COGLayer`:
@@ -105,6 +120,11 @@ export interface SelectCogPipelineOptions {
105
120
  bandConfig?: BandConfig | null;
106
121
  /** Linear rescale GPU module values. No-op when omitted or at defaults. */
107
122
  rescale?: RescaleConfig;
123
+ /**
124
+ * Forwarded to the CPU bake factories in single-band mode; receives a
125
+ * 64-bin histogram of normalized data after each tile for slider UI.
126
+ */
127
+ onHistogram?: (bins: Uint32Array) => void;
108
128
  }
109
129
  /**
110
130
  * Decide which getTileData/renderTile pair COGLayer should use for a GeoTIFF.
@@ -178,42 +198,83 @@ export interface CustomTileData {
178
198
  imageData: ImageData;
179
199
  width: number;
180
200
  height: number;
201
+ /**
202
+ * `sampler2DArray` colormap texture for single-band renders. Set by
203
+ * `createConfigurableGetTileData` / `createCustomGetTileData` when the
204
+ * first tile resolves the device-bound sprite texture; `undefined` for
205
+ * RGB-mode tiles (no colormap needed). Passed through to `renderTile`
206
+ * so the Colormap shader module can bind it on every layer.
207
+ */
208
+ colormapTexture?: Texture;
209
+ /**
210
+ * Normalized `color.r` sentinel value for nodata pixels in single-band
211
+ * mode. The `Colormap` shader module overwrites all 4 output channels
212
+ * from the 1D ramp sample, destroying the α=0 flag, so we reserve
213
+ * `r = 0` for nodata and renormalize valid data into `(0, 1]`.
214
+ * `FilterNoDataVal` then discards matching fragments before the ramp
215
+ * lookup. `undefined` for RGB tiles.
216
+ */
217
+ nodataSentinel?: number;
181
218
  }
219
+ type Texture = import('@luma.gl/core').Texture;
182
220
  /**
183
221
  * Check whether a GeoTIFF needs a custom render pipeline.
184
222
  * v0.3's inferRenderPipeline only supports unsigned integers (SampleFormat 1).
185
223
  * Signed int (2) and float (3) need custom getTileData/renderTile.
186
224
  */
187
225
  export declare function needsCustomPipeline(geotiff: GeoTIFFType): boolean;
226
+ /** Shared options for the CPU tile-baking factories. */
227
+ export interface CustomGetTileDataOptions {
228
+ /**
229
+ * Called after each baked tile with a 64-bin histogram of normalized
230
+ * single-band values (0..1, nodata excluded). Bins accumulate across
231
+ * tiles; receivers should treat the array as monotonically growing and
232
+ * debounce UI updates. Never invoked in RGB mode.
233
+ */
234
+ onHistogram?: (bins: Uint32Array) => void;
235
+ }
236
+ /** Number of histogram buckets produced by the CPU bake. */
237
+ export declare const HISTOGRAM_BIN_COUNT = 64;
188
238
  /**
189
239
  * Create custom getTileData for non-uint COGs.
190
240
  * Reads band 0, normalizes using GDAL statistics / per-tile adaptive stretch,
191
- * applies terrain color ramp for single-band data.
241
+ * bakes a grayscale `r`-channel image so the GPU `Colormap` shader module
242
+ * (wired downstream by `selectCogPipeline`) can apply the ramp by sampling
243
+ * `colormaps.png`. Reserves `r = 0` for nodata so `FilterNoDataVal` can
244
+ * discard those fragments before the ramp sample.
192
245
  */
193
- export declare function createCustomGetTileData(geotiff: GeoTIFFType): (image: GeoTIFFType | Overview, options: {
246
+ export declare function createCustomGetTileData(geotiff: GeoTIFFType, opts?: CustomGetTileDataOptions): (image: GeoTIFFType | Overview, options: {
194
247
  x: number;
195
248
  y: number;
196
249
  pool: unknown;
197
250
  signal?: AbortSignal;
251
+ device: Device;
198
252
  }) => Promise<CustomTileData>;
199
253
  /**
200
- * Custom renderTile for non-uint COGs.
201
- * v0.5 RasterLayer requires a RenderTileResult with `image` or `renderPipeline`.
202
- * We produce an ImageData and pass it through the `image` slot. deck.gl manages
203
- * the texture lifecycle and prepends a CreateTexture module automatically.
254
+ * Custom renderTile for COGs that use the CPU pipeline. For RGB mode (and
255
+ * legacy multi-band non-uint), the `image` slot carries a fully-baked RGBA
256
+ * `ImageData` and there is nothing to append on the GPU. For single-band
257
+ * mode, the image carries a normalized `r`-channel and this function
258
+ * appends `FilterNoDataVal` (to discard r=0 nodata sentinels), optional
259
+ * `LinearRescale` (brightness/contrast slider), and the sprite-based
260
+ * `Colormap` module so switching ramps is a uniform update — no tile
261
+ * re-decode required. The `colormapTexture` is stashed on `data` by the
262
+ * corresponding `getTileData` factory; if the sprite failed to resolve we
263
+ * fall back to the plain grayscale image.
204
264
  */
205
- export declare function customRenderTile(data: CustomTileData): {
206
- image: ImageData;
207
- };
265
+ export declare function buildCustomRenderTile(config: BandConfig, rescale?: RescaleConfig): (data: CustomTileData) => RenderTileResult;
208
266
  /**
209
267
  * Create a configurable getTileData that respects BandConfig.
210
- * Supports both RGB mode (multi-band → R,G,B) and single-band mode (color ramp).
268
+ * Supports RGB mode (multi-band → R,G,B with alpha=255, fully baked) and
269
+ * single-band mode (band N normalized into the `r` channel; the ramp is
270
+ * applied downstream by the GPU `Colormap` module via `buildCustomRenderTile`).
211
271
  */
212
- export declare function createConfigurableGetTileData(geotiff: GeoTIFFType, config: BandConfig): (image: GeoTIFFType | Overview, options: {
272
+ export declare function createConfigurableGetTileData(geotiff: GeoTIFFType, config: BandConfig, opts?: CustomGetTileDataOptions): (image: GeoTIFFType | Overview, options: {
213
273
  x: number;
214
274
  y: number;
215
275
  pool: unknown;
216
276
  signal?: AbortSignal;
277
+ device: Device;
217
278
  }) => Promise<CustomTileData>;
218
279
  export interface PixelValue {
219
280
  lng: number;
@@ -242,3 +303,4 @@ export declare function resolveProj4Def(crs: number | unknown, _signal: AbortSig
242
303
  * Converts WGS84 → source CRS → pixel coords, fetches the tile, reads all bands.
243
304
  */
244
305
  export declare function readPixelAtLngLat(geotiff: GeoTIFFType, lng: number, lat: number, proj4Def: string | null, pool: any, signal?: AbortSignal): Promise<PixelValue | null>;
306
+ export {};