@walkthru-earth/objex 1.3.1 → 1.4.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 (184) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +20 -12
  3. package/dist/components/browser/FileTreeSidebar.svelte +32 -17
  4. package/dist/components/layout/AboutSheet.svelte +5 -2
  5. package/dist/components/layout/ConnectionDialog.svelte +1 -1
  6. package/dist/components/layout/SettingsSheet.svelte +237 -0
  7. package/dist/components/layout/SettingsSheet.svelte.d.ts +6 -0
  8. package/dist/components/layout/Sidebar.svelte +73 -6
  9. package/dist/components/layout/Sidebar.svelte.d.ts +4 -1
  10. package/dist/components/layout/StatusBar.svelte +1 -1
  11. package/dist/components/layout/TabBar.svelte +2 -2
  12. package/dist/components/ui/context-menu/context-menu-radio-group.svelte.d.ts +1 -1
  13. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +1 -1
  14. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +1 -1
  15. package/dist/components/ui/input/input.svelte.d.ts +1 -1
  16. package/dist/components/ui/resizable/index.d.ts +1 -1
  17. package/dist/components/ui/resizable/index.js +2 -2
  18. package/dist/components/ui/slider/index.d.ts +3 -0
  19. package/dist/components/ui/slider/index.js +5 -0
  20. package/dist/components/ui/slider/range-slider.svelte +94 -0
  21. package/dist/components/ui/slider/range-slider.svelte.d.ts +21 -0
  22. package/dist/components/ui/slider/slider.svelte +83 -0
  23. package/dist/components/ui/slider/slider.svelte.d.ts +7 -0
  24. package/dist/components/viewers/ArchiveViewer.svelte +2 -2
  25. package/dist/components/viewers/CodeViewer.svelte +31 -22
  26. package/dist/components/viewers/CogControls.svelte +338 -184
  27. package/dist/components/viewers/CogControls.svelte.d.ts +33 -10
  28. package/dist/components/viewers/CogViewer.svelte +263 -112
  29. package/dist/components/viewers/CopcViewer.svelte +1 -1
  30. package/dist/components/viewers/FlatGeobufViewer.svelte +1 -1
  31. package/dist/components/viewers/GeoParquetMapViewer.svelte +6 -6
  32. package/dist/components/viewers/GeoParquetMapViewer.svelte.d.ts +1 -1
  33. package/dist/components/viewers/ImageViewer.svelte +2 -2
  34. package/dist/components/viewers/MarkdownViewer.svelte +12 -9
  35. package/dist/components/viewers/MediaViewer.svelte +2 -2
  36. package/dist/components/viewers/ModelViewer.svelte +1 -1
  37. package/dist/components/viewers/MultiCogViewer.svelte +467 -102
  38. package/dist/components/viewers/MultiCogViewer.svelte.d.ts +1 -1
  39. package/dist/components/viewers/NotebookViewer.svelte +6 -3
  40. package/dist/components/viewers/PdfViewer.svelte +2 -2
  41. package/dist/components/viewers/PmtilesViewer.svelte +3 -6
  42. package/dist/components/viewers/RawViewer.svelte +6 -3
  43. package/dist/components/viewers/StacMapViewer.svelte +1 -1
  44. package/dist/components/viewers/StacMosaicViewer.svelte +1760 -408
  45. package/dist/components/viewers/StacMosaicViewer.svelte.d.ts +1 -1
  46. package/dist/components/viewers/StacTabViewer.svelte +24 -13
  47. package/dist/components/viewers/StacTabViewer.svelte.d.ts +1 -1
  48. package/dist/components/viewers/TableGrid.svelte +4 -4
  49. package/dist/components/viewers/TableStatusBar.svelte +1 -1
  50. package/dist/components/viewers/TableToolbar.svelte +1 -1
  51. package/dist/components/viewers/TableViewer.svelte +25 -17
  52. package/dist/components/viewers/TableViewer.svelte.d.ts +1 -0
  53. package/dist/components/viewers/ViewerRouter.svelte +16 -8
  54. package/dist/components/viewers/ZarrMapViewer.svelte +11 -9
  55. package/dist/components/viewers/ZarrViewer.svelte +4 -4
  56. package/dist/components/viewers/cog/ChannelPicker.svelte +83 -0
  57. package/dist/components/viewers/cog/ChannelPicker.svelte.d.ts +13 -0
  58. package/dist/components/viewers/cog/PixelInspectorPanel.svelte +87 -0
  59. package/dist/components/viewers/cog/PixelInspectorPanel.svelte.d.ts +17 -0
  60. package/dist/components/viewers/cog/buildRgbLayer.d.ts +78 -0
  61. package/dist/components/viewers/cog/buildRgbLayer.js +176 -0
  62. package/dist/components/viewers/map/AttributeTable.svelte +1 -1
  63. package/dist/components/viewers/map/MapContainer.svelte +37 -11
  64. package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +1 -1
  65. package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +1 -1
  66. package/dist/components/viewers/stac/StacDatetimeBar.svelte +175 -0
  67. package/dist/components/viewers/stac/StacDatetimeBar.svelte.d.ts +10 -0
  68. package/dist/components/viewers/stac/StacFilterPanel.svelte +243 -0
  69. package/dist/components/viewers/stac/StacFilterPanel.svelte.d.ts +14 -0
  70. package/dist/components/viewers/stac/StacItemInspector.svelte +223 -0
  71. package/dist/components/viewers/stac/StacItemInspector.svelte.d.ts +10 -0
  72. package/dist/components/viewers/stac/StacItemStrip.svelte +228 -0
  73. package/dist/components/viewers/stac/StacItemStrip.svelte.d.ts +12 -0
  74. package/dist/file-icons/index.d.ts +1 -1
  75. package/dist/file-icons/index.js +1 -1
  76. package/dist/i18n/ar.js +110 -2
  77. package/dist/i18n/en.js +110 -2
  78. package/dist/index.d.ts +2 -28
  79. package/dist/index.js +7 -23
  80. package/dist/query/engine.d.ts +10 -0
  81. package/dist/query/source.js +1 -1
  82. package/dist/query/stac-source-factory.d.ts +65 -0
  83. package/dist/query/stac-source-factory.js +77 -0
  84. package/dist/query/stac-source-parquet.d.ts +135 -0
  85. package/dist/query/stac-source-parquet.js +465 -0
  86. package/dist/query/wasm.d.ts +8 -0
  87. package/dist/query/wasm.js +304 -2
  88. package/dist/storage/presign.js +1 -1
  89. package/dist/storage/providers.js +5 -5
  90. package/dist/stores/config.svelte.d.ts +15 -0
  91. package/dist/stores/config.svelte.js +46 -0
  92. package/dist/stores/connections.svelte.d.ts +2 -2
  93. package/dist/stores/connections.svelte.js +1 -2
  94. package/dist/stores/files.svelte.d.ts +1 -1
  95. package/dist/stores/files.svelte.js +1 -1
  96. package/dist/stores/query-history.svelte.js +1 -1
  97. package/dist/stores/settings.svelte.d.ts +16 -1
  98. package/dist/stores/settings.svelte.js +104 -48
  99. package/dist/stores/tabs.svelte.d.ts +3 -0
  100. package/dist/stores/tabs.svelte.js +17 -0
  101. package/dist/utils/cog-histogram.d.ts +121 -0
  102. package/dist/utils/cog-histogram.js +424 -0
  103. package/dist/utils/cog.d.ts +177 -20
  104. package/dist/utils/cog.js +361 -76
  105. package/dist/utils/colormap-sprite.d.ts +0 -9
  106. package/dist/utils/colormap-sprite.js +0 -21
  107. package/dist/utils/deck.d.ts +16 -12
  108. package/dist/utils/deck.js +10 -4
  109. package/dist/utils/pmtiles-tile.js +2 -2
  110. package/dist/utils/{url.d.ts → signed-url.d.ts} +15 -1
  111. package/dist/utils/{url.js → signed-url.js} +32 -10
  112. package/dist/utils/url-state.d.ts +36 -0
  113. package/dist/utils/url-state.js +72 -2
  114. package/dist/utils/zarr-tab.d.ts +1 -2
  115. package/dist/utils/zarr-tab.js +1 -2
  116. package/dist/utils/zarr.d.ts +0 -17
  117. package/dist/utils/zarr.js +1 -45
  118. package/package.json +55 -84
  119. package/dist/components/browser/Breadcrumb.svelte +0 -50
  120. package/dist/components/browser/Breadcrumb.svelte.d.ts +0 -7
  121. package/dist/components/browser/CreateFolderDialog.svelte +0 -98
  122. package/dist/components/browser/CreateFolderDialog.svelte.d.ts +0 -6
  123. package/dist/components/browser/DeleteConfirmDialog.svelte +0 -90
  124. package/dist/components/browser/DeleteConfirmDialog.svelte.d.ts +0 -8
  125. package/dist/components/browser/DropZone.svelte +0 -83
  126. package/dist/components/browser/DropZone.svelte.d.ts +0 -7
  127. package/dist/components/browser/FileBrowser.svelte +0 -252
  128. package/dist/components/browser/FileBrowser.svelte.d.ts +0 -3
  129. package/dist/components/browser/FileRow.svelte +0 -117
  130. package/dist/components/browser/FileRow.svelte.d.ts +0 -9
  131. package/dist/components/browser/RenameDialog.svelte +0 -101
  132. package/dist/components/browser/RenameDialog.svelte.d.ts +0 -8
  133. package/dist/components/browser/SearchBar.svelte +0 -40
  134. package/dist/components/browser/SearchBar.svelte.d.ts +0 -6
  135. package/dist/components/browser/UploadButton.svelte +0 -65
  136. package/dist/components/browser/UploadButton.svelte.d.ts +0 -3
  137. package/dist/query/stac-geoparquet.d.ts +0 -31
  138. package/dist/query/stac-geoparquet.js +0 -136
  139. package/dist/utils/clipboard.d.ts +0 -13
  140. package/dist/utils/clipboard.js +0 -38
  141. package/dist/utils/cloud-url.d.ts +0 -27
  142. package/dist/utils/cloud-url.js +0 -61
  143. package/dist/utils/cog-pure.d.ts +0 -25
  144. package/dist/utils/cog-pure.js +0 -35
  145. package/dist/utils/column-types.d.ts +0 -5
  146. package/dist/utils/column-types.js +0 -137
  147. package/dist/utils/connection-identity.d.ts +0 -51
  148. package/dist/utils/connection-identity.js +0 -97
  149. package/dist/utils/error.d.ts +0 -8
  150. package/dist/utils/error.js +0 -12
  151. package/dist/utils/evidence-context.d.ts +0 -22
  152. package/dist/utils/evidence-context.js +0 -56
  153. package/dist/utils/export.d.ts +0 -22
  154. package/dist/utils/export.js +0 -76
  155. package/dist/utils/file-sort.d.ts +0 -20
  156. package/dist/utils/file-sort.js +0 -41
  157. package/dist/utils/format.d.ts +0 -24
  158. package/dist/utils/format.js +0 -78
  159. package/dist/utils/geoarrow.d.ts +0 -32
  160. package/dist/utils/geoarrow.js +0 -672
  161. package/dist/utils/geometry-type.d.ts +0 -52
  162. package/dist/utils/geometry-type.js +0 -76
  163. package/dist/utils/hex.d.ts +0 -10
  164. package/dist/utils/hex.js +0 -27
  165. package/dist/utils/host-detection.d.ts +0 -23
  166. package/dist/utils/host-detection.js +0 -95
  167. package/dist/utils/local-storage.d.ts +0 -16
  168. package/dist/utils/local-storage.js +0 -37
  169. package/dist/utils/markdown-sql.d.ts +0 -30
  170. package/dist/utils/markdown-sql.js +0 -72
  171. package/dist/utils/notebook.d.ts +0 -59
  172. package/dist/utils/notebook.js +0 -211
  173. package/dist/utils/parquet-metadata.d.ts +0 -64
  174. package/dist/utils/parquet-metadata.js +0 -262
  175. package/dist/utils/stac-geoparquet.d.ts +0 -90
  176. package/dist/utils/stac-geoparquet.js +0 -223
  177. package/dist/utils/stac-hydrate.d.ts +0 -38
  178. package/dist/utils/stac-hydrate.js +0 -243
  179. package/dist/utils/stac.d.ts +0 -136
  180. package/dist/utils/stac.js +0 -176
  181. package/dist/utils/storage-url.d.ts +0 -90
  182. package/dist/utils/storage-url.js +0 -568
  183. package/dist/utils/wkb.d.ts +0 -43
  184. package/dist/utils/wkb.js +0 -359
@@ -1,14 +1,26 @@
1
+ import { loadFromStorage, parseVisibilityParam, persistToStorage, resolveSetting } from '@walkthru-earth/objex-utils';
1
2
  import { STORAGE_KEYS } from '../constants.js';
2
- import { setLocale } from '../i18n/index.svelte.js';
3
- import { loadFromStorage, persistToStorage } from '../utils/local-storage.js';
4
- const SETTINGS_DEFAULTS = { theme: 'system', locale: 'en', featureLimit: 1000 };
5
- function loadSettings() {
6
- const stored = loadFromStorage(STORAGE_KEYS.SETTINGS, {});
7
- return {
8
- theme: stored.theme ?? SETTINGS_DEFAULTS.theme,
9
- locale: stored.locale ?? SETTINGS_DEFAULTS.locale,
10
- featureLimit: stored.featureLimit ?? SETTINGS_DEFAULTS.featureLimit
11
- };
3
+ import { appConfig } from './config.svelte.js';
4
+ /**
5
+ * Heuristic mobile detection. iOS Safari caps the WASM heap at ~1.8 GiB and
6
+ * Safari < 17.6 has no `credentialless` COEP, so OPFS spill rarely engages.
7
+ * A 2000-item stac-geoparquet scan with deep STRUCT `assets`/`links` columns
8
+ * blows the heap before the streaming engine can pace it. Clamp the default
9
+ * mosaic item limit lower on mobile so first-run mosaic loads succeed; users
10
+ * can still raise it explicitly in settings.
11
+ */
12
+ function isMobileLikeAtLoad() {
13
+ if (typeof navigator === 'undefined')
14
+ return false;
15
+ if (/Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent))
16
+ return true;
17
+ if (typeof window === 'undefined')
18
+ return false;
19
+ return Math.min(window.innerWidth, window.innerHeight) <= 820;
20
+ }
21
+ const MOBILE_MOSAIC_LIMIT = 200;
22
+ function loadUser() {
23
+ return loadFromStorage(STORAGE_KEYS.SETTINGS, {});
12
24
  }
13
25
  export function resolveTheme(theme) {
14
26
  if (theme !== 'system')
@@ -17,63 +29,107 @@ export function resolveTheme(theme) {
17
29
  return 'light';
18
30
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
19
31
  }
20
- function createSettingsStore() {
21
- const initial = loadSettings();
22
- let theme = $state(initial.theme);
23
- let locale = $state(initial.locale);
24
- let featureLimit = $state(initial.featureLimit);
25
- let resolved = $state(resolveTheme(initial.theme));
26
- // Sync i18n module and document dir with persisted locale
27
- setLocale(initial.locale);
28
- if (typeof document !== 'undefined') {
29
- const dir = initial.locale === 'ar' ? 'rtl' : 'ltr';
30
- document.documentElement.dir = dir;
31
- document.documentElement.lang = initial.locale;
32
+ function readVisibilityParam(name) {
33
+ if (typeof window === 'undefined')
34
+ return undefined;
35
+ try {
36
+ return parseVisibilityParam(new URL(window.location.href).searchParams.get(name));
32
37
  }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ function createSettingsStore() {
43
+ let user = $state(loadUser());
44
+ // Query params and mobile detection are static for the session, read once.
45
+ const railParam = readVisibilityParam('sidebar');
46
+ const treeParam = readVisibilityParam('tree');
47
+ const mobileLikeAtLoad = isMobileLikeAtLoad();
33
48
  function persist() {
34
- persistToStorage(STORAGE_KEYS.SETTINGS, { theme, locale, featureLimit });
49
+ persistToStorage(STORAGE_KEYS.SETTINGS, user);
35
50
  }
36
- function applyTheme(t) {
37
- theme = t;
38
- resolved = resolveTheme(t);
39
- persist();
40
- if (typeof document !== 'undefined') {
41
- document.documentElement.classList.toggle('dark', resolved === 'dark');
42
- }
51
+ function cfg() {
52
+ return appConfig.value;
43
53
  }
44
- function applyLocale(l) {
45
- locale = l;
46
- setLocale(l);
47
- persist();
48
- if (typeof document !== 'undefined') {
49
- const dir = l === 'ar' ? 'rtl' : 'ltr';
50
- document.documentElement.dir = dir;
51
- document.documentElement.lang = l;
52
- }
54
+ function effTheme() {
55
+ return resolveSetting(user.theme, cfg().defaults.theme, 'system');
53
56
  }
54
- // System theme changes are handled by the $effect in +layout.svelte
55
- // which properly cleans up the listener. No module-level listener needed.
56
57
  return {
57
58
  get theme() {
58
- return theme;
59
+ return effTheme();
59
60
  },
60
61
  get resolved() {
61
- return resolved;
62
+ return resolveTheme(effTheme());
62
63
  },
63
64
  get locale() {
64
- return locale;
65
+ return resolveSetting(user.locale, cfg().defaults.locale, 'en');
65
66
  },
66
67
  get featureLimit() {
67
- return featureLimit;
68
+ return resolveSetting(user.featureLimit, cfg().defaults.featureLimit, 1000);
69
+ },
70
+ get mosaicItemLimit() {
71
+ // Explicit user/query choice always wins, at any value.
72
+ if (user.mosaicItemLimit !== undefined)
73
+ return user.mosaicItemLimit;
74
+ const configured = resolveSetting(cfg().defaults.mosaicItemLimit, 2000);
75
+ // Mobile heap safety: clamp the default so API/static mosaic loads don't OOM.
76
+ return mobileLikeAtLoad ? Math.min(configured, MOBILE_MOSAIC_LIMIT) : configured;
77
+ },
78
+ get showConnectionRail() {
79
+ return resolveSetting(railParam, user.showConnectionRail, cfg().ui.showConnectionRail, true);
80
+ },
81
+ get showFileTree() {
82
+ return resolveSetting(treeParam, user.showFileTree, cfg().ui.showFileTree, true);
83
+ },
84
+ /** True when a link param is forcing the connection-rail visibility. */
85
+ get railLockedByParam() {
86
+ return railParam !== undefined;
87
+ },
88
+ /** True when a link param is forcing the file-tree visibility. */
89
+ get treeLockedByParam() {
90
+ return treeParam !== undefined;
91
+ },
92
+ /** The user-picked basemap id, or undefined to follow config/theme defaults. */
93
+ get basemapId() {
94
+ return user.basemapId;
68
95
  },
69
96
  setTheme(t) {
70
- applyTheme(t);
97
+ user = { ...user, theme: t };
98
+ persist();
71
99
  },
72
100
  setLocale(l) {
73
- applyLocale(l);
101
+ user = { ...user, locale: l };
102
+ persist();
74
103
  },
75
104
  setFeatureLimit(n) {
76
- featureLimit = n;
105
+ user = { ...user, featureLimit: Math.max(1, Math.floor(n)) };
106
+ persist();
107
+ },
108
+ setMosaicItemLimit(n) {
109
+ user = { ...user, mosaicItemLimit: Math.max(1, Math.floor(n)) };
110
+ persist();
111
+ },
112
+ setShowConnectionRail(v) {
113
+ user = { ...user, showConnectionRail: v };
114
+ persist();
115
+ },
116
+ setShowFileTree(v) {
117
+ user = { ...user, showFileTree: v };
118
+ persist();
119
+ },
120
+ setBasemap(id) {
121
+ if (id === undefined) {
122
+ const { basemapId: _omit, ...rest } = user;
123
+ user = rest;
124
+ }
125
+ else {
126
+ user = { ...user, basemapId: id };
127
+ }
128
+ persist();
129
+ },
130
+ /** Clear all user overrides, reverting every value to config or hardcoded fallback. */
131
+ reset() {
132
+ user = {};
77
133
  persist();
78
134
  }
79
135
  };
@@ -11,6 +11,9 @@ export declare const tabs: {
11
11
  readonly items: Tab[];
12
12
  readonly activeTabId: string | null;
13
13
  readonly active: Tab | undefined;
14
+ readonly migrating: boolean;
15
+ beginMigration(): void;
16
+ endMigration(): void;
14
17
  /**
15
18
  * Tabs that should be kept alive in the DOM (mounted but hidden if inactive).
16
19
  * Returns up to MAX_ALIVE tabs in MRU order. The active tab is always included.
@@ -22,6 +22,14 @@ function createTabsStore() {
22
22
  // LRU order: most recently activated tab IDs first.
23
23
  // Used to decide which tabs stay alive in the DOM.
24
24
  let recentOrder = $state([]);
25
+ // True while the Sidebar is migrating an eager URL tab to a remote tab
26
+ // (close eager → await credentials/connection save → open remote).
27
+ // During this window, `tabs.items` is briefly empty, but the tab-sync
28
+ // effect MUST NOT clear `?url=` / `#hash` — the new viewer needs them.
29
+ // User-initiated tab closes happen with `migrating === false` and DO
30
+ // clear URL state, so subsequent Sidebar remounts (e.g. mobile Sheet
31
+ // open/close) don't auto-re-open the tab the user just closed.
32
+ let migrating = $state(false);
25
33
  function touchRecent(id) {
26
34
  recentOrder = [id, ...recentOrder.filter((r) => r !== id)];
27
35
  // Dispose resources for tabs that fell out of the alive window.
@@ -50,6 +58,15 @@ function createTabsStore() {
50
58
  return undefined;
51
59
  return tabs.find((t) => t.id === activeTabId);
52
60
  },
61
+ get migrating() {
62
+ return migrating;
63
+ },
64
+ beginMigration() {
65
+ migrating = true;
66
+ },
67
+ endMigration() {
68
+ migrating = false;
69
+ },
53
70
  /**
54
71
  * Tabs that should be kept alive in the DOM (mounted but hidden if inactive).
55
72
  * Returns up to MAX_ALIVE tabs in MRU order. The active tab is always included.
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Streaming histogram + GDAL stats reader for COGs.
3
+ *
4
+ * This module owns the canonical `HISTOGRAM_BINS` constant (128) and a
5
+ * tile-by-tile histogram builder. The streamer fetches tiles from the
6
+ * coarsest overview, bins pixel values into a `Uint32Array` of length
7
+ * `HISTOGRAM_BINS`, and emits a snapshot via `onProgress` after each
8
+ * tile so the UI can fill in the histogram chart incrementally.
9
+ *
10
+ * When GDAL_METADATA `STATISTICS_MINIMUM` / `STATISTICS_MAXIMUM` are
11
+ * present we use them as fixed bin edges (single-pass streaming). When
12
+ * they are absent we fall back to a two-pass scan (min/max first, then
13
+ * bin), in which case progress fires only at the end, bin edges are
14
+ * not stable until the first pass finishes.
15
+ *
16
+ * Runs in workers and on the main thread. No DOM APIs (window,
17
+ * document, DOMParser), no Svelte runes. XML parsing is a tolerant
18
+ * regex over the GDAL_METADATA tag string, not a DOMParser pass.
19
+ */
20
+ import type { GeoTIFF as GeoTIFFType } from '@developmentseed/geotiff';
21
+ import { type RescaleConfig } from './cog.js';
22
+ /** Number of histogram buckets. PR #3 bumps from 64 to 128. */
23
+ export declare const HISTOGRAM_BINS = 128;
24
+ /** Per-band GDAL statistics block read from the GDAL_METADATA tag. */
25
+ export type GdalBandStats = {
26
+ min: number;
27
+ max: number;
28
+ mean?: number;
29
+ stddev?: number;
30
+ };
31
+ /** Map of 1-based band index to per-band stats. Empty when absent. */
32
+ export type GdalImageStats = Map<number, GdalBandStats>;
33
+ /** Snapshot of an in-progress histogram bake. */
34
+ export type HistogramSnapshot = {
35
+ /** Bin counts of length `HISTOGRAM_BINS`. */
36
+ bins: Uint32Array;
37
+ /** Lower bin-edge (inclusive). */
38
+ min: number;
39
+ /** Upper bin-edge (exclusive for inner bins, inclusive for the last). */
40
+ max: number;
41
+ /** Tiles binned so far. */
42
+ tilesProcessed: number;
43
+ /** Total tiles the streamer plans to consume, or null if unknown. */
44
+ tilesTotal: number | null;
45
+ };
46
+ /** Options for {@link streamHistogram}. */
47
+ export type StreamHistogramOptions = {
48
+ geotiff: GeoTIFFType;
49
+ /** 1-based band index. */
50
+ bandIndex: number;
51
+ signal: AbortSignal;
52
+ onProgress: (snap: HistogramSnapshot) => void;
53
+ /**
54
+ * 0-based overview index. When omitted, the lowest-resolution overview
55
+ * (i.e. the smallest available source, last entry of `geotiff.overviews`)
56
+ * is used. Falls back to the full-resolution image when `overviews` is
57
+ * empty.
58
+ */
59
+ overviewIndex?: number;
60
+ /** Hard upper bound on tiles consumed. Defaults to 32. */
61
+ maxTiles?: number;
62
+ };
63
+ /**
64
+ * Parse per-band stats out of the GDAL_METADATA tag. Tolerant of
65
+ * malformed XML, uses a regex pass over `<Item ...>...</Item>` rather
66
+ * than DOMParser so this can run inside a Web Worker.
67
+ *
68
+ * Prefers the library's pre-parsed `geotiff.storedStats` when present,
69
+ * falls back to scanning the raw `cachedTags.gdalMetadata` string when
70
+ * the library could not parse it but the tag is still present (defensive
71
+ * path, rarely fires with current @developmentseed/geotiff but cheap).
72
+ *
73
+ * Returns an empty Map when no usable per-band ranges are found.
74
+ */
75
+ export declare function readGdalStats(geotiff: GeoTIFFType): GdalImageStats;
76
+ /**
77
+ * Stream a 128-bin histogram for a single band. Fires `onProgress` after
78
+ * each tile so the UI can fill in the chart incrementally, the snapshots
79
+ * share no buffer references between calls (each `bins` is a fresh
80
+ * `Uint32Array`).
81
+ *
82
+ * Algorithm:
83
+ * - With GDAL priors, one pass, known bin edges, progressive snapshots.
84
+ * - Without priors, two passes, pass 1 finds min/max across all
85
+ * selected tiles, pass 2 bins into 128 buckets. Snapshots fire
86
+ * per-cached-tile in pass 2 so the UI still sees the histogram fill
87
+ * in on the no-priors path, bin edges aren't stable during pass 1.
88
+ *
89
+ * Honors `signal.aborted` between every tile fetch and after each bin
90
+ * pass. Throws no error on abort, returns silently with whatever
91
+ * partial snapshot the caller already received.
92
+ */
93
+ export declare function streamHistogram(opts: StreamHistogramOptions): Promise<void>;
94
+ /** Options for {@link seedRescaleFromGeotiff}. */
95
+ export type SeedRescaleOptions = {
96
+ signal?: AbortSignal;
97
+ /** 1-based band index. Defaults to 1. */
98
+ bandIndex?: number;
99
+ };
100
+ /**
101
+ * Seed a {@link RescaleConfig} for a freshly opened COG in the same
102
+ * normalized shader-space [0, 1] coordinate system the rescale slider
103
+ * operates on. The GPU's hardware normalization (`r8unorm` / `r16unorm`)
104
+ * and the `Colormap` CPU baker both divide raw integer samples by the
105
+ * format's max (255 for uint8, 65535 for uint16) before sampling, and
106
+ * `CogControls`'s `setRescaleMin/Max` clamps the slider via `clamp01`.
107
+ * Storing raw GeoTIFF stats (e.g. `{min: 0, max: 10000}` for uint16
108
+ * reflectance) here would snap back to `{0, 1}` on the user's first
109
+ * slider touch.
110
+ *
111
+ * Fallback chain:
112
+ * 1. GDAL `STATISTICS_MINIMUM` / `STATISTICS_MAXIMUM` for the band,
113
+ * normalized to shader space by dividing by the sample-format
114
+ * factor (255 for uint8, 65535 for uint ≥ 8 bps). Float bands are
115
+ * passed through unchanged because they already live near [0, 1].
116
+ * 2. `buildHistogramFromGeotiff` + p2 / p98 percentile lookup so a
117
+ * uint16 reflectance band with no STATISTICS tag still gets a
118
+ * contrasted preview from the first tile of the smallest overview.
119
+ * 3. {@link defaultRescaleForGeotiff} bit-depth-aware constants.
120
+ */
121
+ export declare function seedRescaleFromGeotiff(geotiff: GeoTIFFType, options?: SeedRescaleOptions): Promise<RescaleConfig>;