@walkthru-earth/objex 1.1.0 → 1.2.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 (72) hide show
  1. package/README.md +3 -1
  2. package/dist/components/browser/FileBrowser.svelte +25 -14
  3. package/dist/components/browser/FileTreeSidebar.svelte +42 -6
  4. package/dist/components/layout/ConnectionDialog.svelte +100 -1
  5. package/dist/components/layout/Sidebar.svelte +43 -25
  6. package/dist/components/viewers/CodeViewer.svelte +23 -0
  7. package/dist/components/viewers/CogControls.svelte +208 -0
  8. package/dist/components/viewers/CogControls.svelte.d.ts +12 -0
  9. package/dist/components/viewers/CogViewer.svelte +353 -1160
  10. package/dist/components/viewers/CogViewer.svelte.d.ts +1 -1
  11. package/dist/components/viewers/DatabaseViewer.svelte +345 -37
  12. package/dist/components/viewers/MarkdownViewer.svelte +1 -1
  13. package/dist/components/viewers/TableViewer.svelte +123 -41
  14. package/dist/components/viewers/ZarrMapViewer.svelte +29 -0
  15. package/dist/components/viewers/ZarrViewer.svelte +1 -4
  16. package/dist/constants.d.ts +6 -2
  17. package/dist/constants.js +6 -2
  18. package/dist/file-icons/index.d.ts +1 -1
  19. package/dist/file-icons/index.js +12 -2
  20. package/dist/i18n/ar.js +24 -0
  21. package/dist/i18n/en.js +24 -0
  22. package/dist/i18n/index.svelte.d.ts +0 -1
  23. package/dist/i18n/index.svelte.js +0 -3
  24. package/dist/index.d.ts +2 -0
  25. package/dist/index.js +1 -0
  26. package/dist/query/engine.d.ts +20 -4
  27. package/dist/query/index.d.ts +2 -1
  28. package/dist/query/index.js +1 -0
  29. package/dist/query/source.d.ts +30 -0
  30. package/dist/query/source.js +37 -0
  31. package/dist/query/wasm.d.ts +7 -5
  32. package/dist/query/wasm.js +138 -85
  33. package/dist/storage/providers.d.ts +47 -0
  34. package/dist/storage/providers.js +160 -0
  35. package/dist/stores/files.svelte.d.ts +1 -2
  36. package/dist/stores/files.svelte.js +1 -2
  37. package/dist/stores/tabs.svelte.d.ts +9 -2
  38. package/dist/stores/tabs.svelte.js +11 -2
  39. package/dist/types.d.ts +11 -0
  40. package/dist/utils/cog.d.ts +244 -0
  41. package/dist/utils/cog.js +1039 -0
  42. package/dist/utils/deck.d.ts +0 -18
  43. package/dist/utils/deck.js +0 -36
  44. package/dist/utils/geometry-type.d.ts +52 -0
  45. package/dist/utils/geometry-type.js +76 -0
  46. package/dist/utils/markdown-sql.d.ts +1 -1
  47. package/dist/utils/markdown-sql.js +3 -4
  48. package/dist/utils/pmtiles-tile.d.ts +0 -2
  49. package/dist/utils/pmtiles-tile.js +0 -8
  50. package/dist/utils/url-state.d.ts +6 -0
  51. package/dist/utils/url-state.js +34 -26
  52. package/dist/utils/url.d.ts +13 -9
  53. package/dist/utils/url.js +16 -25
  54. package/dist/utils/zarr-tab.d.ts +22 -0
  55. package/dist/utils/zarr-tab.js +30 -0
  56. package/dist/utils/zarr.d.ts +0 -2
  57. package/dist/utils/zarr.js +73 -44
  58. package/package.json +47 -43
  59. package/dist/components/ui/tabs/index.d.ts +0 -5
  60. package/dist/components/ui/tabs/index.js +0 -7
  61. package/dist/components/ui/tabs/tabs-content.svelte +0 -17
  62. package/dist/components/ui/tabs/tabs-content.svelte.d.ts +0 -4
  63. package/dist/components/ui/tabs/tabs-list.svelte +0 -16
  64. package/dist/components/ui/tabs/tabs-list.svelte.d.ts +0 -4
  65. package/dist/components/ui/tabs/tabs-trigger.svelte +0 -20
  66. package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +0 -4
  67. package/dist/components/ui/tabs/tabs.svelte +0 -19
  68. package/dist/components/ui/tabs/tabs.svelte.d.ts +0 -4
  69. package/dist/components/viewers/MapViewer.svelte +0 -234
  70. package/dist/components/viewers/MapViewer.svelte.d.ts +0 -7
  71. package/dist/components/viewers/StyleEditorOverlay.svelte +0 -27
  72. package/dist/components/viewers/StyleEditorOverlay.svelte.d.ts +0 -7
@@ -266,6 +266,148 @@ export const PROVIDERS = {
266
266
  schemes: []
267
267
  }
268
268
  };
269
+ export const CORS_HELP = {
270
+ s3: {
271
+ defaultEnabled: false,
272
+ docsUrl: 'https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html',
273
+ note: 'Enable via S3 Console: Bucket > Permissions > CORS, or use the AWS CLI.'
274
+ },
275
+ gcs: {
276
+ defaultEnabled: false,
277
+ docsUrl: 'https://cloud.google.com/storage/docs/using-cors',
278
+ note: 'CORS cannot be configured via the Cloud Console. Use the gcloud CLI.',
279
+ cliSteps: [
280
+ 'Create a cors.json file:\n[\n {\n "origin": ["*"],\n "method": ["GET", "HEAD"],\n "responseHeader": [\n "Content-Type",\n "Content-Length",\n "Content-Range",\n "Accept-Ranges",\n "ETag"\n ],\n "maxAgeSeconds": 3600\n }\n]',
281
+ 'gcloud storage buckets update gs://BUCKET --cors-file=cors.json'
282
+ ]
283
+ },
284
+ r2: {
285
+ defaultEnabled: false,
286
+ docsUrl: 'https://developers.cloudflare.com/r2/buckets/cors/',
287
+ note: 'Enable via R2 Dashboard: Bucket > Settings > CORS Policy.'
288
+ },
289
+ azure: {
290
+ defaultEnabled: false,
291
+ docsUrl: 'https://learn.microsoft.com/en-us/rest/api/storageservices/cross-origin-resource-sharing--cors--support-for-the-azure-storage-services',
292
+ note: 'Enable via Azure Portal: Storage Account > Blob Service > CORS, or use the Azure CLI.',
293
+ cliSteps: [
294
+ 'az storage cors add --services b --methods GET HEAD \\\n --origins "*" --allowed-headers "*" \\\n --exposed-headers "*" --max-age 3600 \\\n --account-name ACCOUNT'
295
+ ]
296
+ },
297
+ minio: {
298
+ defaultEnabled: true,
299
+ docsUrl: 'https://docs.min.io/enterprise/aistor-object-store/reference/cli/mc-cors/',
300
+ note: 'MinIO allows all origins by default. For custom rules, use mc cors set.'
301
+ },
302
+ storj: {
303
+ defaultEnabled: true,
304
+ note: 'Storj S3 gateway returns CORS headers by default.'
305
+ },
306
+ b2: {
307
+ defaultEnabled: false,
308
+ docsUrl: 'https://www.backblaze.com/docs/cloud-storage-cross-origin-resource-sharing-rules',
309
+ note: 'Enable via B2 Console: Bucket Settings > CORS Rules, or use the B2 CLI.',
310
+ cliSteps: [
311
+ 'b2 bucket update --cors-rules \'[{\n "corsRuleName": "allow-all",\n "allowedOrigins": ["*"],\n "allowedOperations": ["s3_head", "s3_get"],\n "allowedHeaders": ["*"],\n "maxAgeSeconds": 3600\n}]\' BUCKET allPublic'
312
+ ]
313
+ },
314
+ digitalocean: {
315
+ defaultEnabled: false,
316
+ docsUrl: 'https://docs.digitalocean.com/products/spaces/how-to/configure-cors/',
317
+ note: 'Enable via Control Panel: Space > Settings > CORS Configurations.'
318
+ },
319
+ wasabi: {
320
+ defaultEnabled: true,
321
+ docsUrl: 'https://docs.wasabi.com/docs/bucket-policy',
322
+ note: 'Wasabi returns CORS headers by default for all buckets.'
323
+ },
324
+ contabo: {
325
+ defaultEnabled: false,
326
+ note: 'S3-compatible CORS via the AWS CLI.',
327
+ cliSteps: [
328
+ 'Create a cors.json file:\n{\n "CORSRules": [{\n "AllowedOrigins": ["*"],\n "AllowedMethods": ["GET", "HEAD"],\n "AllowedHeaders": ["*"],\n "ExposeHeaders": ["ETag", "Content-Length", "Content-Type", "Content-Range", "Accept-Ranges"],\n "MaxAgeSeconds": 3600\n }]\n}',
329
+ 'aws s3api put-bucket-cors --bucket BUCKET \\\n --cors-configuration file://cors.json \\\n --endpoint-url https://REGION.contaboobj.com'
330
+ ]
331
+ },
332
+ hetzner: {
333
+ defaultEnabled: false,
334
+ docsUrl: 'https://docs.hetzner.com/storage/object-storage/howto-protect-objects/cors/',
335
+ note: 'S3-compatible CORS via the AWS CLI.',
336
+ cliSteps: [
337
+ 'Create a cors.json file:\n{\n "CORSRules": [{\n "AllowedOrigins": ["*"],\n "AllowedMethods": ["GET", "HEAD"],\n "AllowedHeaders": ["*"],\n "ExposeHeaders": ["ETag", "Content-Length", "Content-Type", "Content-Range", "Accept-Ranges"],\n "MaxAgeSeconds": 3600\n }]\n}',
338
+ 'aws s3api put-bucket-cors --bucket BUCKET \\\n --cors-configuration file://cors.json \\\n --endpoint-url https://REGION.your-objectstorage.com \\\n --region REGION'
339
+ ]
340
+ },
341
+ linode: {
342
+ defaultEnabled: false,
343
+ docsUrl: 'https://www.linode.com/docs/guides/working-with-cors-linode-object-storage/',
344
+ note: 'S3-compatible CORS via the AWS CLI.',
345
+ cliSteps: [
346
+ 'Create a cors.json file:\n{\n "CORSRules": [{\n "AllowedOrigins": ["*"],\n "AllowedMethods": ["GET", "HEAD"],\n "AllowedHeaders": ["*"],\n "ExposeHeaders": ["ETag", "Content-Length", "Content-Type", "Content-Range", "Accept-Ranges"],\n "MaxAgeSeconds": 3600\n }]\n}',
347
+ 'aws s3api put-bucket-cors --bucket BUCKET \\\n --cors-configuration file://cors.json \\\n --endpoint-url https://REGION.linodeobjects.com'
348
+ ]
349
+ },
350
+ ovhcloud: {
351
+ defaultEnabled: false,
352
+ docsUrl: 'https://help.ovhcloud.com/csm/en-public-cloud-storage-s3-cors?id=kb_article_view&sysparm_article=KB0058291',
353
+ note: 'S3-compatible CORS via the AWS CLI.',
354
+ cliSteps: [
355
+ 'Create a cors.json file:\n{\n "CORSRules": [{\n "AllowedOrigins": ["*"],\n "AllowedMethods": ["GET", "HEAD"],\n "AllowedHeaders": ["*"],\n "ExposeHeaders": ["ETag", "Content-Length", "Content-Type", "Content-Range", "Accept-Ranges"],\n "MaxAgeSeconds": 3600\n }]\n}',
356
+ 'aws s3api put-bucket-cors --bucket BUCKET \\\n --cors-configuration file://cors.json \\\n --endpoint-url https://s3.REGION.io.cloud.ovh.net'
357
+ ]
358
+ }
359
+ };
360
+ export const READ_ONLY_HELP = {
361
+ s3: {
362
+ note: 'Use IAM policies to create a read-only user, or apply a bucket policy that allows only s3:GetObject and s3:ListBucket.',
363
+ docsUrl: 'https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-policies-s3.html'
364
+ },
365
+ gcs: {
366
+ note: 'Assign the Storage Object Viewer role (roles/storage.objectViewer) to the service account.',
367
+ docsUrl: 'https://cloud.google.com/storage/docs/access-control/iam-roles'
368
+ },
369
+ r2: {
370
+ note: 'Create an API token with Object Read permissions in the R2 dashboard.',
371
+ docsUrl: 'https://developers.cloudflare.com/r2/api/tokens/'
372
+ },
373
+ azure: {
374
+ note: 'Generate a SAS token with Read and List permissions only. Avoid granting Write or Delete.',
375
+ docsUrl: 'https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview'
376
+ },
377
+ b2: {
378
+ note: 'Create an application key with readFiles and listBuckets capabilities only.',
379
+ docsUrl: 'https://www.backblaze.com/docs/cloud-storage-application-keys'
380
+ },
381
+ hetzner: {
382
+ note: 'Keys have full read/write by default. Use a bucket policy with the correct ARN format to deny write and policy actions. To undo, generate a new admin key from the Hetzner Console.',
383
+ docsUrl: 'https://docs.hetzner.com/storage/object-storage/faq/s3-credentials/#how-do-i-restrict-access-per-key',
384
+ cliSteps: [
385
+ 'Find your project ID from the Hetzner Console URL:\nhttps://console.hetzner.com/projects/<PROJECT_ID>/servers',
386
+ 'Create a policy.json file:\n{\n "Version": "2012-10-17",\n "Statement": [\n {\n "Sid": "DenyWrites",\n "Effect": "Deny",\n "Principal": {\n "AWS": "arn:aws:iam:::user/p<PROJECT_ID>:<ACCESS_KEY>"\n },\n "Action": [\n "s3:PutObject",\n "s3:DeleteObject",\n "s3:AbortMultipartUpload",\n "s3:PutBucketPolicy",\n "s3:DeleteBucketPolicy"\n ],\n "Resource": [\n "arn:aws:s3:::BUCKET",\n "arn:aws:s3:::BUCKET/*"\n ]\n }\n ]\n}',
387
+ 'aws s3api put-bucket-policy --bucket BUCKET \\\n --policy file://policy.json \\\n --endpoint-url https://REGION.your-objectstorage.com \\\n --region REGION',
388
+ 'Note: This key can no longer modify the policy.\nTo restore write access, generate a new key in the\nHetzner Console and use it to delete the policy.'
389
+ ]
390
+ },
391
+ minio: {
392
+ note: 'Create a read-only policy with mc admin policy, or use the built-in readonly canned policy.',
393
+ docsUrl: 'https://docs.min.io/enterprise/aistor-object-store/administration/iam/access/'
394
+ },
395
+ digitalocean: {
396
+ note: 'Spaces keys are project-wide. Use a bucket policy to restrict write actions for a specific key.',
397
+ docsUrl: 'https://docs.digitalocean.com/products/spaces/how-to/manage-access/'
398
+ },
399
+ wasabi: {
400
+ note: 'Create a sub-user with a read-only policy in the Wasabi Console.',
401
+ docsUrl: 'https://docs.wasabi.com/docs/creating-a-user-account-and-access-key'
402
+ },
403
+ contabo: {
404
+ note: 'S3-compatible bucket policies. Use a Deny policy for write actions with the key ARN.',
405
+ cliSteps: [
406
+ 'Create a policy.json with a Deny statement for s3:PutObject and s3:DeleteObject.',
407
+ 'aws s3api put-bucket-policy --bucket BUCKET \\\n --policy file://policy.json \\\n --endpoint-url https://REGION.contaboobj.com'
408
+ ]
409
+ }
410
+ };
269
411
  // ---------------------------------------------------------------------------
270
412
  // Helpers
271
413
  // ---------------------------------------------------------------------------
@@ -316,3 +458,21 @@ export function buildProviderBaseUrl(provider, endpoint, bucket, region) {
316
458
  export function isGcsProvider(provider, endpoint) {
317
459
  return provider === 'gcs' || (!!endpoint && /storage\.googleapis\.com/i.test(endpoint));
318
460
  }
461
+ export function getAccessMode(conn) {
462
+ if (conn.provider === 'azure')
463
+ return 'sas-https';
464
+ // Anonymous buckets: every provider serves files over plain HTTPS without
465
+ // signing (AWS path/vhost, GCS, R2 public, Storj, Wasabi, DO, etc.).
466
+ if (conn.anonymous)
467
+ return 'public-https';
468
+ // Authenticated: needs SigV4 signing.
469
+ return 'signed-s3';
470
+ }
471
+ /**
472
+ * True when the connection's files can be fetched by any HTTP client
473
+ * (fetch/img/video/DuckDB httpfs/COG/Zarr/etc.) without the storage adapter.
474
+ */
475
+ export function isPubliclyStreamable(conn) {
476
+ const mode = getAccessMode(conn);
477
+ return mode === 'public-https' || mode === 'sas-https';
478
+ }
@@ -1,6 +1,6 @@
1
1
  import type { FileEntry } from '../types.js';
2
2
  import { type SortConfig, type SortField } from '../utils/file-sort.js';
3
- export declare const fileStore: {
3
+ export declare const files: {
4
4
  readonly entries: FileEntry[];
5
5
  readonly currentPath: string;
6
6
  readonly loading: boolean;
@@ -12,4 +12,3 @@ export declare const fileStore: {
12
12
  setError(message: string | null): void;
13
13
  sort(field: SortField): void;
14
14
  };
15
- export { fileStore as files };
@@ -40,5 +40,4 @@ function createFilesStore() {
40
40
  }
41
41
  };
42
42
  }
43
- export const fileStore = createFilesStore();
44
- export { fileStore as files };
43
+ export const files = createFilesStore();
@@ -1,5 +1,13 @@
1
1
  import type { Tab } from '../types.js';
2
- export declare const tabStore: {
2
+ /**
3
+ * Tab-id for eagerly-opened direct-URL tabs (`source: 'url'`).
4
+ * The Sidebar's host-detection auto-migration closes an eager tab by its id
5
+ * and re-opens as a remote tab once a connection is available; the eager id
6
+ * is built in `+page.svelte::openUrlTab` and matched in `Sidebar.svelte::
7
+ * handleAutoDetection`, so both sides must agree on the format.
8
+ */
9
+ export declare function eagerUrlTabId(url: string): string;
10
+ export declare const tabs: {
3
11
  readonly items: Tab[];
4
12
  readonly activeTabId: string | null;
5
13
  readonly active: Tab | undefined;
@@ -14,4 +22,3 @@ export declare const tabStore: {
14
22
  setActive(id: string): void;
15
23
  update(id: string, partial: Partial<Omit<Tab, "id">>): void;
16
24
  };
17
- export { tabStore as tabs };
@@ -1,6 +1,16 @@
1
1
  import { tabResources } from './tab-resources.svelte.js';
2
2
  /** Maximum number of viewer instances kept alive (mounted but hidden). */
3
3
  const MAX_ALIVE = 5;
4
+ /**
5
+ * Tab-id for eagerly-opened direct-URL tabs (`source: 'url'`).
6
+ * The Sidebar's host-detection auto-migration closes an eager tab by its id
7
+ * and re-opens as a remote tab once a connection is available; the eager id
8
+ * is built in `+page.svelte::openUrlTab` and matched in `Sidebar.svelte::
9
+ * handleAutoDetection`, so both sides must agree on the format.
10
+ */
11
+ export function eagerUrlTabId(url) {
12
+ return `url:${url}`;
13
+ }
4
14
  function releaseDuckDbMemory() {
5
15
  import('../query/index.js')
6
16
  .then(({ getQueryEngine }) => getQueryEngine().then((engine) => engine.releaseMemory()))
@@ -106,5 +116,4 @@ function createTabsStore() {
106
116
  }
107
117
  };
108
118
  }
109
- export const tabStore = createTabsStore();
110
- export { tabStore as tabs };
119
+ export const tabs = createTabsStore();
package/dist/types.d.ts CHANGED
@@ -38,6 +38,17 @@ export interface Tab {
38
38
  connectionId?: string;
39
39
  extension: string;
40
40
  size?: number;
41
+ /**
42
+ * When set, the tab reads data from a SQL FROM-clause target (e.g. an
43
+ * attached DuckLake/DuckDB/SQLite table) rather than a file URL. The ref
44
+ * is inserted directly into generated SQL, so it must be fully-qualified
45
+ * and pre-quoted, e.g. `__objex_db__."main"."air_quality"`.
46
+ *
47
+ * When `sourceRef` is set, file-specific loading paths (hyparquet
48
+ * metadata, `parquet_kv_metadata`, etc.) are skipped, and schema / CRS /
49
+ * row count are derived from the SQL source directly via DuckDB.
50
+ */
51
+ sourceRef?: string;
41
52
  }
42
53
  export interface WriteResult {
43
54
  key: string;
@@ -0,0 +1,244 @@
1
+ import type { GetTileDataOptions, MinimalDataT } from '@developmentseed/deck.gl-geotiff';
2
+ import type { RenderTileResult } from '@developmentseed/deck.gl-raster';
3
+ import type { GeoTIFF as GeoTIFFType, Overview } from '@developmentseed/geotiff';
4
+ import { GeoTIFF } from '@developmentseed/geotiff';
5
+ import type { EpsgResolver } from '@developmentseed/proj';
6
+ import type maplibregl from 'maplibre-gl';
7
+ /** SampleFormat tag value → human label. */
8
+ 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;
15
+ export interface BandConfig {
16
+ mode: 'rgb' | 'single';
17
+ /** 0-indexed band indices for RGB channels */
18
+ rBand: number;
19
+ gBand: number;
20
+ bBand: number;
21
+ /** 0-indexed band index for single-band mode */
22
+ band: number;
23
+ colorRamp: ColorRampId;
24
+ }
25
+ /** Create a sensible default band config based on COG metadata. */
26
+ export declare function defaultBandConfig(bandCount: number, sampleFormat: number): BandConfig;
27
+ /** Check if the config matches the default for this COG (no user changes). */
28
+ export declare function isDefaultBandConfig(config: BandConfig, bandCount: number, sampleFormat: number): boolean;
29
+ export interface CogTagInfo {
30
+ /** TIFF SampleFormat[0] value. 1=uint, 2=int, 3=float. Defaults to 1 when absent. */
31
+ sampleFormat: number;
32
+ /** True when SampleFormat[0] === 1 (unsigned integer). */
33
+ isUint: boolean;
34
+ /**
35
+ * True when Photometric === 3 (Palette) and the ColorMap tag is present.
36
+ * These COGs should defer to the library's default Colormap GPU module,
37
+ * not our custom JS pipeline, so the embedded palette renders correctly.
38
+ */
39
+ isPaletteIndexed: boolean;
40
+ }
41
+ /**
42
+ * Inspect the TIFF tags that drive pipeline selection. Centralizes the
43
+ * Photometric.Palette === 3 magic number and the SampleFormat fallback in one
44
+ * place so viewers don't reimplement raw tag reads. Photometric values come
45
+ * from the @cogeotiff/core Photometric enum.
46
+ */
47
+ export declare function inspectCogTags(geotiff: GeoTIFFType): CogTagInfo;
48
+ /**
49
+ * Check if a given band config requires a custom pipeline (vs library default).
50
+ * Library default only works for uint with standard RGB band order, or for
51
+ * palette-indexed uint COGs where the embedded ColorMap tag auto-renders.
52
+ */
53
+ export declare function needsCustomPipelineForConfig(geotiff: GeoTIFFType, config: BandConfig): boolean;
54
+ /**
55
+ * Min/max rescale values applied via the `LinearRescale` shader module. Values
56
+ * are in normalized shader space [0, 1]. Default `{ min: 0, max: 1 }` is a
57
+ * no-op and the library-default pipeline is used as-is.
58
+ */
59
+ export interface RescaleConfig {
60
+ min: number;
61
+ max: number;
62
+ }
63
+ export declare const DEFAULT_RESCALE: RescaleConfig;
64
+ /** True when the rescale values would produce a visible change on the GPU. */
65
+ export declare function isRescaleActive(cfg: RescaleConfig): boolean;
66
+ /**
67
+ * Build a `getTileData` + `renderTile` pair that reuses the library-default
68
+ * uint pipeline (via `inferRenderPipeline`) and appends `LinearRescale` to the
69
+ * returned render pipeline. Only safe to use when the default pipeline would
70
+ * have been chosen anyway, i.e. `needsCustomPipelineForConfig(geotiff, cfg)`
71
+ * is false. For non-uint or custom band configs the custom JS pipeline already
72
+ * bakes RGBA in CPU and a GPU rescale would be cosmetic.
73
+ *
74
+ * `inferRenderPipeline` needs the GPU `Device` which arrives in the first
75
+ * tile's `GetTileDataOptions`, so the pipeline is built lazily on first call.
76
+ */
77
+ export declare function createRescaledPipeline(geotiff: GeoTIFFType, rescale: RescaleConfig): {
78
+ getTileData: (image: GeoTIFFType | Overview, options: GetTileDataOptions) => Promise<MinimalDataT>;
79
+ renderTile: (data: MinimalDataT) => RenderTileResult;
80
+ };
81
+ /**
82
+ * Apply the two upstream-bug workarounds a GeoTIFF needs before being handed
83
+ * to `COGLayer`:
84
+ * 1. Strip oversized overviews (image smaller than tile size). These produce
85
+ * out-of-domain proj4 NaN during pre-flight reprojection.
86
+ * 2. Clamp EPSG:4326 bbox to Web Mercator's safe range. Global 4326 COGs with
87
+ * ±90° extents crash the tile matrix generator.
88
+ *
89
+ * Mutates the GeoTIFF in place. Safe to call repeatedly. Kept out of the
90
+ * Svelte component so MultiCOG/Mosaic can apply the same fix per sub-COG.
91
+ */
92
+ export declare function normalizeCogGeotiff(geotiff: GeoTIFFType): void;
93
+ /**
94
+ * Resolved COGLayer data props. Empty object means "library default pipeline".
95
+ * Spread into `new COGLayer({ ..., ...resolved })` to activate.
96
+ *
97
+ * COGLayer's data-prop types are a discriminated XOR and the four pipelines we
98
+ * dispatch to return different DataT shapes (`CustomTileData`, `MinimalDataT`).
99
+ * Typing this as `Record<string, any>` matches the `customProps` pattern
100
+ * already used at the COGLayer boundary and keeps the dispatch site simple.
101
+ */
102
+ export type ResolvedCogPipeline = Record<string, any>;
103
+ export interface SelectCogPipelineOptions {
104
+ /** Active band/color config, or null/undefined when not yet resolved. */
105
+ bandConfig?: BandConfig | null;
106
+ /** Linear rescale GPU module values. No-op when omitted or at defaults. */
107
+ rescale?: RescaleConfig;
108
+ }
109
+ /**
110
+ * Decide which getTileData/renderTile pair COGLayer should use for a GeoTIFF.
111
+ * Four outcomes, in priority order:
112
+ *
113
+ * 1. Custom configurable (band swap, color ramp) — when bandConfig is active
114
+ * and needsCustomPipelineForConfig is true (non-uint, mode=single, or
115
+ * non-standard RGB band order).
116
+ * 2. Custom non-uint (Int/Float source) — when no bandConfig yet but the
117
+ * GeoTIFF itself forces custom handling.
118
+ * 3. Library default + LinearRescale — uint path is fine AND the user moved
119
+ * the rescale slider away from defaults.
120
+ * 4. Library default — returns `{}`, caller spreads into COGLayer props.
121
+ *
122
+ * Pure dispatch. Kept separate from the Svelte component so MultiCOG/Mosaic
123
+ * viewers can call it per sub-COG without re-implementing the decision tree.
124
+ */
125
+ export declare function selectCogPipeline(geotiff: GeoTIFFType, opts?: SelectCogPipelineOptions): ResolvedCogPipeline;
126
+ export interface GeoBounds {
127
+ west: number;
128
+ south: number;
129
+ east: number;
130
+ north: number;
131
+ }
132
+ export interface CogInfo {
133
+ width: number;
134
+ height: number;
135
+ bandCount: number;
136
+ dataType: string;
137
+ bounds: GeoBounds;
138
+ downsampled?: boolean;
139
+ }
140
+ /** Safely clamp a number to a range, treating NaN/Infinity as the fallback. */
141
+ export declare function safeClamp(v: number, lo: number, hi: number, fallback: number): number;
142
+ /** Clamp geographic bounds to valid MapLibre web-Mercator range. */
143
+ export declare function clampBounds(b: GeoBounds): GeoBounds;
144
+ /**
145
+ * Build a data-type label from GeoTIFF sample format and bits per sample.
146
+ * e.g. "uint8", "float32", "int16"
147
+ */
148
+ export declare function buildDataTypeLabel(sampleFormat: number, bitsPerSample: number): string;
149
+ /**
150
+ * Query the GPU's MAX_TEXTURE_SIZE from MapLibre's WebGL context.
151
+ * Falls back to 4096 (lowest common denominator for mobile GPUs).
152
+ */
153
+ export declare function getMaxTextureSize(map: maplibregl.Map): number;
154
+ /**
155
+ * Fit the map to COG bounds with responsive padding.
156
+ * Uses smaller padding on mobile to zoom in closer, ensuring overviews load
157
+ * properly instead of appearing black at very low zoom levels.
158
+ * After fitting, bumps zoom +2 when the viewport settles at a very low level.
159
+ */
160
+ export declare function fitCogBounds(map: maplibregl.Map, b: GeoBounds): void;
161
+ /** Remove the native bitmap source/layer from the map (idempotent). */
162
+ export declare function cleanupNativeBitmap(map: maplibregl.Map): void;
163
+ /**
164
+ * Render a non-tiled GeoTIFF as a MapLibre native image source (bitmap).
165
+ * Opens the file with @developmentseed/geotiff, reads band 0, normalizes
166
+ * to grayscale RGBA, and adds to the map as a raster layer.
167
+ *
168
+ * Returns CogInfo for the metadata panel.
169
+ */
170
+ export declare function renderNonTiledBitmap(options: {
171
+ url: string;
172
+ map: maplibregl.Map;
173
+ signal: AbortSignal;
174
+ geotiff?: GeoTIFF;
175
+ }): Promise<CogInfo>;
176
+ /** Result type returned by our custom getTileData. */
177
+ export interface CustomTileData {
178
+ imageData: ImageData;
179
+ width: number;
180
+ height: number;
181
+ }
182
+ /**
183
+ * Check whether a GeoTIFF needs a custom render pipeline.
184
+ * v0.3's inferRenderPipeline only supports unsigned integers (SampleFormat 1).
185
+ * Signed int (2) and float (3) need custom getTileData/renderTile.
186
+ */
187
+ export declare function needsCustomPipeline(geotiff: GeoTIFFType): boolean;
188
+ /**
189
+ * Create custom getTileData for non-uint COGs.
190
+ * Reads band 0, normalizes using GDAL statistics / per-tile adaptive stretch,
191
+ * applies terrain color ramp for single-band data.
192
+ */
193
+ export declare function createCustomGetTileData(geotiff: GeoTIFFType): (image: GeoTIFFType | Overview, options: {
194
+ x: number;
195
+ y: number;
196
+ pool: unknown;
197
+ signal?: AbortSignal;
198
+ }) => Promise<CustomTileData>;
199
+ /**
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.
204
+ */
205
+ export declare function customRenderTile(data: CustomTileData): {
206
+ image: ImageData;
207
+ };
208
+ /**
209
+ * Create a configurable getTileData that respects BandConfig.
210
+ * Supports both RGB mode (multi-band → R,G,B) and single-band mode (color ramp).
211
+ */
212
+ export declare function createConfigurableGetTileData(geotiff: GeoTIFFType, config: BandConfig): (image: GeoTIFFType | Overview, options: {
213
+ x: number;
214
+ y: number;
215
+ pool: unknown;
216
+ signal?: AbortSignal;
217
+ }) => Promise<CustomTileData>;
218
+ export interface PixelValue {
219
+ lng: number;
220
+ lat: number;
221
+ values: number[];
222
+ row: number;
223
+ col: number;
224
+ }
225
+ /**
226
+ * Create an async EPSG resolver for `@developmentseed/deck.gl-geotiff`.
227
+ * Looks up the numeric EPSG code in the bundled WKT database and returns the
228
+ * `ProjectionDefinition` produced by `parseWkt`. Throws a clear error when the
229
+ * code is not present in the database.
230
+ */
231
+ export declare function createEpsgResolver(): EpsgResolver;
232
+ /**
233
+ * Resolve a proj4-compatible definition for a CRS read from a GeoTIFF.
234
+ * For numeric EPSG codes this returns the WKT string from the bundled EPSG
235
+ * database, which `proj4()` accepts directly. For ProjJSON it falls back to a
236
+ * JSON string. Returns null for EPSG:4326 (no conversion needed) or when the
237
+ * code is not present in the database.
238
+ */
239
+ export declare function resolveProj4Def(crs: number | unknown, _signal: AbortSignal): Promise<string | null>;
240
+ /**
241
+ * Read pixel values at a given lng/lat from a GeoTIFF.
242
+ * Converts WGS84 → source CRS → pixel coords, fetches the tile, reads all bands.
243
+ */
244
+ export declare function readPixelAtLngLat(geotiff: GeoTIFFType, lng: number, lat: number, proj4Def: string | null, pool: any, signal?: AbortSignal): Promise<PixelValue | null>;