@walkthru-earth/objex 1.3.0 → 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.
- package/LICENSE +5 -0
- package/README.md +20 -12
- package/dist/components/browser/FileTreeSidebar.svelte +32 -17
- package/dist/components/layout/AboutSheet.svelte +5 -2
- package/dist/components/layout/ConnectionDialog.svelte +1 -1
- package/dist/components/layout/SettingsSheet.svelte +237 -0
- package/dist/components/layout/SettingsSheet.svelte.d.ts +6 -0
- package/dist/components/layout/Sidebar.svelte +73 -6
- package/dist/components/layout/Sidebar.svelte.d.ts +4 -1
- package/dist/components/layout/StatusBar.svelte +1 -1
- package/dist/components/layout/TabBar.svelte +2 -2
- package/dist/components/ui/context-menu/context-menu-radio-group.svelte.d.ts +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +1 -1
- package/dist/components/ui/input/input.svelte.d.ts +1 -1
- package/dist/components/ui/resizable/index.d.ts +1 -1
- package/dist/components/ui/resizable/index.js +2 -2
- package/dist/components/ui/slider/index.d.ts +3 -0
- package/dist/components/ui/slider/index.js +5 -0
- package/dist/components/ui/slider/range-slider.svelte +94 -0
- package/dist/components/ui/slider/range-slider.svelte.d.ts +21 -0
- package/dist/components/ui/slider/slider.svelte +83 -0
- package/dist/components/ui/slider/slider.svelte.d.ts +7 -0
- package/dist/components/viewers/ArchiveViewer.svelte +2 -2
- package/dist/components/viewers/CodeViewer.svelte +31 -22
- package/dist/components/viewers/CogControls.svelte +338 -184
- package/dist/components/viewers/CogControls.svelte.d.ts +33 -10
- package/dist/components/viewers/CogViewer.svelte +320 -119
- package/dist/components/viewers/CopcViewer.svelte +1 -1
- package/dist/components/viewers/FlatGeobufViewer.svelte +1 -1
- package/dist/components/viewers/GeoParquetMapViewer.svelte +6 -6
- package/dist/components/viewers/GeoParquetMapViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/ImageViewer.svelte +2 -2
- package/dist/components/viewers/MarkdownViewer.svelte +12 -9
- package/dist/components/viewers/MediaViewer.svelte +2 -2
- package/dist/components/viewers/ModelViewer.svelte +1 -1
- package/dist/components/viewers/MultiCogViewer.svelte +467 -102
- package/dist/components/viewers/MultiCogViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/NotebookViewer.svelte +6 -3
- package/dist/components/viewers/PdfViewer.svelte +2 -2
- package/dist/components/viewers/PmtilesViewer.svelte +3 -6
- package/dist/components/viewers/RawViewer.svelte +6 -3
- package/dist/components/viewers/StacMapViewer.svelte +10 -2
- package/dist/components/viewers/StacMosaicViewer.svelte +1800 -362
- package/dist/components/viewers/StacMosaicViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/StacTabViewer.svelte +24 -13
- package/dist/components/viewers/StacTabViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/TableGrid.svelte +4 -4
- package/dist/components/viewers/TableStatusBar.svelte +1 -1
- package/dist/components/viewers/TableToolbar.svelte +1 -1
- package/dist/components/viewers/TableViewer.svelte +25 -17
- package/dist/components/viewers/TableViewer.svelte.d.ts +1 -0
- package/dist/components/viewers/ViewerRouter.svelte +16 -8
- package/dist/components/viewers/ZarrMapViewer.svelte +11 -9
- package/dist/components/viewers/ZarrViewer.svelte +4 -4
- package/dist/components/viewers/cog/ChannelPicker.svelte +83 -0
- package/dist/components/viewers/cog/ChannelPicker.svelte.d.ts +13 -0
- package/dist/components/viewers/cog/PixelInspectorPanel.svelte +87 -0
- package/dist/components/viewers/cog/PixelInspectorPanel.svelte.d.ts +17 -0
- package/dist/components/viewers/cog/buildRgbLayer.d.ts +78 -0
- package/dist/components/viewers/cog/buildRgbLayer.js +176 -0
- package/dist/components/viewers/map/AttributeTable.svelte +1 -1
- package/dist/components/viewers/map/MapContainer.svelte +37 -11
- package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +1 -1
- package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +1 -1
- package/dist/components/viewers/stac/StacDatetimeBar.svelte +175 -0
- package/dist/components/viewers/stac/StacDatetimeBar.svelte.d.ts +10 -0
- package/dist/components/viewers/stac/StacFilterPanel.svelte +243 -0
- package/dist/components/viewers/stac/StacFilterPanel.svelte.d.ts +14 -0
- package/dist/components/viewers/stac/StacItemInspector.svelte +223 -0
- package/dist/components/viewers/stac/StacItemInspector.svelte.d.ts +10 -0
- package/dist/components/viewers/stac/StacItemStrip.svelte +228 -0
- package/dist/components/viewers/stac/StacItemStrip.svelte.d.ts +12 -0
- package/dist/file-icons/index.d.ts +1 -1
- package/dist/file-icons/index.js +1 -1
- package/dist/i18n/ar.js +110 -2
- package/dist/i18n/en.js +110 -2
- package/dist/index.d.ts +2 -28
- package/dist/index.js +7 -23
- package/dist/query/engine.d.ts +10 -0
- package/dist/query/source.js +1 -1
- package/dist/query/stac-source-factory.d.ts +65 -0
- package/dist/query/stac-source-factory.js +77 -0
- package/dist/query/stac-source-parquet.d.ts +135 -0
- package/dist/query/stac-source-parquet.js +465 -0
- package/dist/query/wasm.d.ts +8 -0
- package/dist/query/wasm.js +304 -2
- package/dist/storage/presign.js +1 -1
- package/dist/storage/providers.js +5 -5
- package/dist/stores/config.svelte.d.ts +15 -0
- package/dist/stores/config.svelte.js +46 -0
- package/dist/stores/connections.svelte.d.ts +2 -2
- package/dist/stores/connections.svelte.js +1 -2
- package/dist/stores/files.svelte.d.ts +1 -1
- package/dist/stores/files.svelte.js +1 -1
- package/dist/stores/query-history.svelte.js +1 -1
- package/dist/stores/settings.svelte.d.ts +16 -1
- package/dist/stores/settings.svelte.js +104 -48
- package/dist/stores/tabs.svelte.d.ts +3 -0
- package/dist/stores/tabs.svelte.js +17 -0
- package/dist/utils/cog-histogram.d.ts +121 -0
- package/dist/utils/cog-histogram.js +424 -0
- package/dist/utils/cog.d.ts +200 -60
- package/dist/utils/cog.js +377 -114
- package/dist/utils/colormap-sprite.d.ts +0 -9
- package/dist/utils/colormap-sprite.js +0 -21
- package/dist/utils/deck.d.ts +16 -12
- package/dist/utils/deck.js +10 -4
- package/dist/utils/pmtiles-tile.js +2 -2
- package/dist/utils/{url.d.ts → signed-url.d.ts} +15 -1
- package/dist/utils/{url.js → signed-url.js} +32 -10
- package/dist/utils/url-state.d.ts +36 -0
- package/dist/utils/url-state.js +72 -2
- package/dist/utils/zarr-tab.d.ts +1 -2
- package/dist/utils/zarr-tab.js +1 -2
- package/dist/utils/zarr.d.ts +0 -17
- package/dist/utils/zarr.js +1 -45
- package/package.json +55 -84
- package/dist/components/browser/Breadcrumb.svelte +0 -50
- package/dist/components/browser/Breadcrumb.svelte.d.ts +0 -7
- package/dist/components/browser/CreateFolderDialog.svelte +0 -98
- package/dist/components/browser/CreateFolderDialog.svelte.d.ts +0 -6
- package/dist/components/browser/DeleteConfirmDialog.svelte +0 -90
- package/dist/components/browser/DeleteConfirmDialog.svelte.d.ts +0 -8
- package/dist/components/browser/DropZone.svelte +0 -83
- package/dist/components/browser/DropZone.svelte.d.ts +0 -7
- package/dist/components/browser/FileBrowser.svelte +0 -252
- package/dist/components/browser/FileBrowser.svelte.d.ts +0 -3
- package/dist/components/browser/FileRow.svelte +0 -117
- package/dist/components/browser/FileRow.svelte.d.ts +0 -9
- package/dist/components/browser/RenameDialog.svelte +0 -101
- package/dist/components/browser/RenameDialog.svelte.d.ts +0 -8
- package/dist/components/browser/SearchBar.svelte +0 -40
- package/dist/components/browser/SearchBar.svelte.d.ts +0 -6
- package/dist/components/browser/UploadButton.svelte +0 -65
- package/dist/components/browser/UploadButton.svelte.d.ts +0 -3
- package/dist/query/stac-geoparquet.d.ts +0 -31
- package/dist/query/stac-geoparquet.js +0 -136
- package/dist/utils/clipboard.d.ts +0 -13
- package/dist/utils/clipboard.js +0 -38
- package/dist/utils/cloud-url.d.ts +0 -27
- package/dist/utils/cloud-url.js +0 -61
- package/dist/utils/column-types.d.ts +0 -5
- package/dist/utils/column-types.js +0 -137
- package/dist/utils/connection-identity.d.ts +0 -51
- package/dist/utils/connection-identity.js +0 -97
- package/dist/utils/error.d.ts +0 -8
- package/dist/utils/error.js +0 -12
- package/dist/utils/evidence-context.d.ts +0 -22
- package/dist/utils/evidence-context.js +0 -56
- package/dist/utils/export.d.ts +0 -22
- package/dist/utils/export.js +0 -76
- package/dist/utils/file-sort.d.ts +0 -20
- package/dist/utils/file-sort.js +0 -41
- package/dist/utils/format.d.ts +0 -24
- package/dist/utils/format.js +0 -78
- package/dist/utils/geoarrow.d.ts +0 -32
- package/dist/utils/geoarrow.js +0 -672
- package/dist/utils/geometry-type.d.ts +0 -52
- package/dist/utils/geometry-type.js +0 -76
- package/dist/utils/hex.d.ts +0 -10
- package/dist/utils/hex.js +0 -27
- package/dist/utils/host-detection.d.ts +0 -23
- package/dist/utils/host-detection.js +0 -95
- package/dist/utils/local-storage.d.ts +0 -16
- package/dist/utils/local-storage.js +0 -37
- package/dist/utils/markdown-sql.d.ts +0 -30
- package/dist/utils/markdown-sql.js +0 -72
- package/dist/utils/notebook.d.ts +0 -59
- package/dist/utils/notebook.js +0 -211
- package/dist/utils/parquet-metadata.d.ts +0 -64
- package/dist/utils/parquet-metadata.js +0 -262
- package/dist/utils/stac-geoparquet.d.ts +0 -90
- package/dist/utils/stac-geoparquet.js +0 -223
- package/dist/utils/stac-hydrate.d.ts +0 -38
- package/dist/utils/stac-hydrate.js +0 -243
- package/dist/utils/stac.d.ts +0 -136
- package/dist/utils/stac.js +0 -176
- package/dist/utils/storage-url.d.ts +0 -90
- package/dist/utils/storage-url.js +0 -568
- package/dist/utils/wkb.d.ts +0 -43
- package/dist/utils/wkb.js +0 -359
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
import { MapboxOverlay } from '@deck.gl/mapbox';
|
|
3
3
|
import { COGLayer } from '@developmentseed/deck.gl-geotiff';
|
|
4
4
|
import { DecoderPool, GeoTIFF } from '@developmentseed/geotiff';
|
|
5
|
+
import {
|
|
6
|
+
attachPixelInspector,
|
|
7
|
+
type ChannelComposite,
|
|
8
|
+
type CogAsset,
|
|
9
|
+
smokeTestHref,
|
|
10
|
+
syntheticSelfAsset
|
|
11
|
+
} from '@walkthru-earth/objex-utils';
|
|
5
12
|
import type maplibregl from 'maplibre-gl';
|
|
6
13
|
import { onDestroy, untrack } from 'svelte';
|
|
7
14
|
import { t } from '../../i18n/index.svelte.js';
|
|
@@ -9,26 +16,39 @@ import { tabResources } from '../../stores/tab-resources.svelte.js';
|
|
|
9
16
|
import type { Tab } from '../../types.js';
|
|
10
17
|
import {
|
|
11
18
|
type BandConfig,
|
|
19
|
+
buildCustomRenderTile,
|
|
12
20
|
buildDataTypeLabel,
|
|
13
21
|
type CogInfo,
|
|
22
|
+
type ConfigurableTileLoader,
|
|
23
|
+
type CustomTileData,
|
|
14
24
|
clampBounds,
|
|
15
25
|
cleanupNativeBitmap,
|
|
26
|
+
createConfigurableGetTileData,
|
|
16
27
|
createEpsgResolver,
|
|
28
|
+
DEFAULT_NODATA_CONFIG,
|
|
17
29
|
DEFAULT_RESCALE,
|
|
18
30
|
defaultBandConfig,
|
|
19
31
|
fitCogBounds,
|
|
32
|
+
HISTOGRAM_BIN_COUNT,
|
|
20
33
|
inspectCogTags,
|
|
34
|
+
loadGeoTIFF,
|
|
35
|
+
mapResolutionMetersPerPixel,
|
|
36
|
+
type NodataConfig,
|
|
21
37
|
needsCustomPipelineForConfig,
|
|
22
38
|
normalizeCogGeotiff,
|
|
23
39
|
type PixelValue,
|
|
24
40
|
type RescaleConfig,
|
|
41
|
+
readGdalNodata,
|
|
25
42
|
readPixelAtLngLat,
|
|
26
43
|
renderNonTiledBitmap,
|
|
27
44
|
resolveProj4Def,
|
|
28
|
-
selectCogPipeline
|
|
45
|
+
selectCogPipeline,
|
|
46
|
+
selectOverviewForResolution
|
|
29
47
|
} from '../../utils/cog.js';
|
|
30
|
-
import {
|
|
48
|
+
import { seedRescaleFromGeotiff } from '../../utils/cog-histogram.js';
|
|
49
|
+
import { buildHttpsUrlAsync } from '../../utils/signed-url.js';
|
|
31
50
|
import CogControls from './CogControls.svelte';
|
|
51
|
+
import PixelInspectorPanel, { type PixelInspectorRow } from './cog/PixelInspectorPanel.svelte';
|
|
32
52
|
import MapContainer from './map/MapContainer.svelte';
|
|
33
53
|
|
|
34
54
|
// ─── State ───────────────────────────────────────────────────────
|
|
@@ -41,24 +61,69 @@ let showControls = $state(false);
|
|
|
41
61
|
let bounds = $state<[number, number, number, number] | undefined>();
|
|
42
62
|
let cogInfo = $state<CogInfo | null>(null);
|
|
43
63
|
let bandConfig = $state<BandConfig | null>(null);
|
|
64
|
+
let resolvedHrefForControls = $state<string | null>(null);
|
|
65
|
+
let probedBandCount = $state<number | null>(null);
|
|
66
|
+
|
|
67
|
+
const cogControlsAssets = $derived.by<CogAsset[]>(() => {
|
|
68
|
+
const href = resolvedHrefForControls;
|
|
69
|
+
if (!href) return [];
|
|
70
|
+
return [syntheticSelfAsset(href, probedBandCount ?? undefined)];
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const cogControlsComposite = $derived.by<ChannelComposite>(() => {
|
|
74
|
+
const bc = bandConfig;
|
|
75
|
+
if (!bc) {
|
|
76
|
+
return {
|
|
77
|
+
r: { assetKey: 'self', bandIndex: 0 },
|
|
78
|
+
g: { assetKey: 'self', bandIndex: 0 },
|
|
79
|
+
b: { assetKey: 'self', bandIndex: 0 }
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (bc.mode === 'rgb') {
|
|
83
|
+
return {
|
|
84
|
+
r: { assetKey: 'self', bandIndex: bc.rBand ?? 0 },
|
|
85
|
+
g: { assetKey: 'self', bandIndex: bc.gBand ?? 0 },
|
|
86
|
+
b: { assetKey: 'self', bandIndex: bc.bBand ?? 0 }
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const i = bc.band ?? 0;
|
|
90
|
+
return {
|
|
91
|
+
r: { assetKey: 'self', bandIndex: i },
|
|
92
|
+
g: { assetKey: 'self', bandIndex: i },
|
|
93
|
+
b: { assetKey: 'self', bandIndex: i }
|
|
94
|
+
};
|
|
95
|
+
});
|
|
44
96
|
let histogram = $state.raw<Uint32Array | null>(null);
|
|
45
97
|
let histogramTick = $state(0);
|
|
46
98
|
let rescale = $state<RescaleConfig>({ ...DEFAULT_RESCALE });
|
|
99
|
+
// User-facing nodata override (Auto/Value/Off). Auto resolves at read time
|
|
100
|
+
// from the GeoTIFF's GDAL_NODATA tag, surfaced as a hint pill in CogControls.
|
|
101
|
+
let nodataConfig = $state<NodataConfig>({ ...DEFAULT_NODATA_CONFIG });
|
|
102
|
+
let autoNodata = $state<number | null>(null);
|
|
47
103
|
// Palette-indexed COGs render through the library's Colormap module; a GPU
|
|
48
104
|
// rescale at that stage is cosmetic and would confuse the legend. Keep the
|
|
49
105
|
// slider hidden when a ColorMap tag is present.
|
|
50
106
|
let isPaletteIndexed = $state(false);
|
|
51
107
|
let pixelValue = $state<PixelValue | null>(null);
|
|
52
108
|
let inspecting = $state(false);
|
|
109
|
+
// Storage smoke-test result for the primary asset href. Inspired by
|
|
110
|
+
// lazycogs `_smoketest_store`, surfaces auth / CORS / bucket failures at
|
|
111
|
+
// open time as a small amber pill, never blocks the layer mount.
|
|
112
|
+
let smokeWarning = $state<string | null>(null);
|
|
113
|
+
let smokeProbed = false;
|
|
53
114
|
|
|
54
115
|
let abortController = new AbortController();
|
|
55
116
|
let mapRef: maplibregl.Map | null = null;
|
|
56
117
|
let overlayRef: MapboxOverlay | null = null;
|
|
57
118
|
let geotiffRef: GeoTIFF | null = null;
|
|
119
|
+
// Identity-stable tile loader for the configurable CPU path. Lives for the
|
|
120
|
+
// duration of the current GeoTIFF identity, so deck.gl's TileLayer cache
|
|
121
|
+
// survives band/ramp swaps (a fresh getTileData reference would invalidate it).
|
|
122
|
+
let tileLoaderRef: ConfigurableTileLoader | null = null;
|
|
58
123
|
let proj4DefRef: string | null = null;
|
|
59
124
|
let sampleFormatRef = 1;
|
|
60
125
|
let isTiledRef = true;
|
|
61
|
-
let
|
|
126
|
+
let detachInspector: (() => void) | null = null;
|
|
62
127
|
let resolvedHttpsUrl: string | null = null;
|
|
63
128
|
// LinearRescale operates on a 0..1 scalar. Two cases expose a meaningful
|
|
64
129
|
// slider: (1) the library-default uint RGB pipeline (scales `color.rgb`
|
|
@@ -109,8 +174,11 @@ $effect(() => {
|
|
|
109
174
|
}
|
|
110
175
|
overlayRef = null;
|
|
111
176
|
geotiffRef = null;
|
|
177
|
+
tileLoaderRef = null;
|
|
112
178
|
proj4DefRef = null;
|
|
113
179
|
resolvedHttpsUrl = null;
|
|
180
|
+
resolvedHrefForControls = null;
|
|
181
|
+
probedBandCount = null;
|
|
114
182
|
loading = true;
|
|
115
183
|
error = null;
|
|
116
184
|
cogInfo = null;
|
|
@@ -118,8 +186,12 @@ $effect(() => {
|
|
|
118
186
|
histogram = null;
|
|
119
187
|
histogramTick = 0;
|
|
120
188
|
rescale = { ...DEFAULT_RESCALE };
|
|
189
|
+
nodataConfig = { ...DEFAULT_NODATA_CONFIG };
|
|
190
|
+
autoNodata = null;
|
|
121
191
|
isPaletteIndexed = false;
|
|
122
192
|
pixelValue = null;
|
|
193
|
+
smokeWarning = null;
|
|
194
|
+
smokeProbed = false;
|
|
123
195
|
bounds = undefined;
|
|
124
196
|
hasFittedOnce = false;
|
|
125
197
|
showControls = false;
|
|
@@ -138,34 +210,32 @@ function onMapReady(map: maplibregl.Map) {
|
|
|
138
210
|
// ─── Click handler for pixel inspection ──────────────────────────
|
|
139
211
|
|
|
140
212
|
function removeClickHandler() {
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
|
|
213
|
+
if (detachInspector) {
|
|
214
|
+
detachInspector();
|
|
215
|
+
detachInspector = null;
|
|
144
216
|
}
|
|
145
217
|
}
|
|
146
218
|
|
|
147
219
|
function setupClickHandler(map: maplibregl.Map) {
|
|
148
220
|
removeClickHandler();
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
221
|
+
detachInspector = attachPixelInspector<PixelValue>(map, {
|
|
222
|
+
probe: async ({ lng, lat, signal }) => {
|
|
223
|
+
if (!geotiffRef) return null;
|
|
224
|
+
// matches overview shown on screen
|
|
225
|
+
const targetRes = mapResolutionMetersPerPixel(map.getZoom(), lat);
|
|
226
|
+
const overview = selectOverviewForResolution(geotiffRef, targetRes);
|
|
227
|
+
return readPixelAtLngLat(geotiffRef, lng, lat, proj4DefRef, pool, signal, {
|
|
228
|
+
overview
|
|
229
|
+
});
|
|
230
|
+
},
|
|
231
|
+
onStart: () => {
|
|
232
|
+
inspecting = true;
|
|
233
|
+
},
|
|
234
|
+
onResult: (result) => {
|
|
161
235
|
pixelValue = result;
|
|
162
|
-
} catch {
|
|
163
|
-
pixelValue = null;
|
|
164
|
-
} finally {
|
|
165
236
|
inspecting = false;
|
|
166
237
|
}
|
|
167
|
-
};
|
|
168
|
-
map.on('click', clickHandlerRef);
|
|
238
|
+
});
|
|
169
239
|
}
|
|
170
240
|
|
|
171
241
|
// ─── Core load function ──────────────────────────────────────────
|
|
@@ -177,12 +247,30 @@ async function loadCog(map: maplibregl.Map) {
|
|
|
177
247
|
const url = await buildHttpsUrlAsync(tab);
|
|
178
248
|
if (signal.aborted) return;
|
|
179
249
|
resolvedHttpsUrl = url;
|
|
250
|
+
resolvedHrefForControls = url;
|
|
251
|
+
|
|
252
|
+
// One-shot storage smoke-test. lazycogs-style probe surfaces auth /
|
|
253
|
+
// CORS / bucket failures at open time as an amber pill, never blocks
|
|
254
|
+
// the layer mount. Aborts via the viewer's existing controller.
|
|
255
|
+
if (!smokeProbed) {
|
|
256
|
+
smokeProbed = true;
|
|
257
|
+
void (async () => {
|
|
258
|
+
try {
|
|
259
|
+
const result = await smokeTestHref(url, signal);
|
|
260
|
+
if (signal.aborted) return;
|
|
261
|
+
if (!result.ok) smokeWarning = result.reason;
|
|
262
|
+
} catch (err) {
|
|
263
|
+
if (err instanceof DOMException && err.name === 'AbortError') return;
|
|
264
|
+
smokeWarning = err instanceof Error ? err.message : String(err);
|
|
265
|
+
}
|
|
266
|
+
})();
|
|
267
|
+
}
|
|
180
268
|
|
|
181
269
|
// Pre-flight: read first IFD to check if tiled (single range request).
|
|
182
270
|
let isTiled = true;
|
|
183
271
|
let preflightGeotiff: GeoTIFF | undefined;
|
|
184
272
|
try {
|
|
185
|
-
preflightGeotiff = await
|
|
273
|
+
preflightGeotiff = await loadGeoTIFF(url);
|
|
186
274
|
if (signal.aborted) return;
|
|
187
275
|
isTiled = preflightGeotiff.isTiled;
|
|
188
276
|
|
|
@@ -231,6 +319,22 @@ async function loadCog(map: maplibregl.Map) {
|
|
|
231
319
|
|
|
232
320
|
// Set default band config
|
|
233
321
|
bandConfig = defaultBandConfig(preflightGeotiff.count, sampleFormatRef);
|
|
322
|
+
probedBandCount = preflightGeotiff.count;
|
|
323
|
+
|
|
324
|
+
// Surface GDAL_NODATA + a shader-space rescale seed (when present) so
|
|
325
|
+
// the nodata hint pill and rescale slider have meaningful defaults
|
|
326
|
+
// before the first tile decodes — matches source-cooperative/cog-viewer
|
|
327
|
+
// UX. The slider operates in normalized [0, 1] shader space, so
|
|
328
|
+
// `seedRescaleFromGeotiff` divides GDAL STATISTICS_MIN/MAX by the
|
|
329
|
+
// sample-format factor and falls back to a p2/p98 histogram + the
|
|
330
|
+
// bit-depth-aware default.
|
|
331
|
+
autoNodata = readGdalNodata(preflightGeotiff);
|
|
332
|
+
try {
|
|
333
|
+
rescale = await seedRescaleFromGeotiff(preflightGeotiff, { signal });
|
|
334
|
+
} catch {
|
|
335
|
+
// fall through, defaults remain
|
|
336
|
+
}
|
|
337
|
+
if (signal.aborted) return;
|
|
234
338
|
}
|
|
235
339
|
|
|
236
340
|
if (!isTiled && preflightGeotiff) {
|
|
@@ -265,32 +369,49 @@ async function loadCog(map: maplibregl.Map) {
|
|
|
265
369
|
|
|
266
370
|
// ─── Build & add COGLayer ────────────────────────────────────────
|
|
267
371
|
|
|
268
|
-
|
|
372
|
+
// Build the pipeline props (getTileData/renderTile/etc) for the current state.
|
|
373
|
+
// When the configurable CPU path applies, the tile loader is created once per
|
|
374
|
+
// GeoTIFF identity and its `getTileData` reference is reused across rebuilds so
|
|
375
|
+
// deck.gl's TileLayer cache survives band/ramp swaps. Only `renderTile` and
|
|
376
|
+
// downstream uniforms vary across style changes.
|
|
377
|
+
function buildPipelineProps(geotiff: GeoTIFF | undefined): Record<string, unknown> {
|
|
378
|
+
if (!geotiff || !bandConfig) {
|
|
379
|
+
return geotiff ? selectCogPipeline(geotiff, { bandConfig, rescale }) : {};
|
|
380
|
+
}
|
|
381
|
+
if (needsCustomPipelineForConfig(geotiff, bandConfig)) {
|
|
382
|
+
if (!tileLoaderRef) {
|
|
383
|
+
tileLoaderRef = createConfigurableGetTileData(geotiff, bandConfig);
|
|
384
|
+
} else {
|
|
385
|
+
tileLoaderRef.updateConfig(bandConfig);
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
getTileData: tileLoaderRef.getTileData,
|
|
389
|
+
renderTile: buildCustomRenderTile(bandConfig, rescale)
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
// Library-default or rescaled-only path. The loader (if previously seeded)
|
|
393
|
+
// is harmless to keep, but the upcoming rebuild won't reference it.
|
|
394
|
+
return selectCogPipeline(geotiff, { bandConfig, rescale });
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function buildCogLayer(
|
|
269
398
|
map: maplibregl.Map,
|
|
270
399
|
preflightGeotiff: GeoTIFF | undefined,
|
|
271
400
|
signal: AbortSignal
|
|
272
|
-
) {
|
|
401
|
+
): COGLayer {
|
|
273
402
|
// Pick the library-default or one of three custom pipelines. Empty when the
|
|
274
403
|
// library-default uint path runs unchanged.
|
|
275
|
-
const customProps = preflightGeotiff
|
|
276
|
-
? selectCogPipeline(preflightGeotiff, {
|
|
277
|
-
bandConfig,
|
|
278
|
-
rescale,
|
|
279
|
-
onHistogram: (bins) => {
|
|
280
|
-
// Copy once so the derived UI sees an immutable snapshot
|
|
281
|
-
// and the accumulating worker buffer is not observed mid-mutation.
|
|
282
|
-
histogram = new Uint32Array(bins);
|
|
283
|
-
histogramTick++;
|
|
284
|
-
}
|
|
285
|
-
})
|
|
286
|
-
: {};
|
|
404
|
+
const customProps = buildPipelineProps(preflightGeotiff);
|
|
287
405
|
|
|
288
406
|
// Apply upstream-bug workarounds in place (overview filter, 4326 bbox clamp).
|
|
289
407
|
if (preflightGeotiff) normalizeCogGeotiff(preflightGeotiff);
|
|
290
408
|
|
|
291
409
|
const cogInput = preflightGeotiff ?? resolvedHttpsUrl ?? '';
|
|
292
410
|
|
|
293
|
-
|
|
411
|
+
// Cast: `onViewportLoad` is forwarded natively by COGLayer's RasterTileLayer
|
|
412
|
+
// base in 0.7.0 (deck.gl-raster PR #546), but COGLayer's generated .d.ts does
|
|
413
|
+
// not surface it.
|
|
414
|
+
const cogProps: any = {
|
|
294
415
|
// Stable id per tab so rebuilds on band/style change don't force deck.gl
|
|
295
416
|
// to treat this as a brand-new layer and drop cached tile state.
|
|
296
417
|
id: `cog-layer-${tab.id}`,
|
|
@@ -299,6 +420,15 @@ function buildAndAddLayer(
|
|
|
299
420
|
epsgResolver,
|
|
300
421
|
signal,
|
|
301
422
|
...customProps,
|
|
423
|
+
// COG-native histogram: sum `content.histogram` over tiles currently
|
|
424
|
+
// visible in the viewport. Fires after every pan/zoom settles and
|
|
425
|
+
// reuses deck.gl's tile cache for free, cached tiles still carry
|
|
426
|
+
// their per-tile histogram so no rebake is needed on revisit.
|
|
427
|
+
onViewportLoad: (visibleTiles: unknown) => {
|
|
428
|
+
aggregateVisibleHistogram(
|
|
429
|
+
visibleTiles as ReadonlyArray<{ content?: unknown } | null | undefined>
|
|
430
|
+
);
|
|
431
|
+
},
|
|
302
432
|
onGeoTIFFLoad: (
|
|
303
433
|
loadedTiff: GeoTIFF,
|
|
304
434
|
{
|
|
@@ -345,7 +475,19 @@ function buildAndAddLayer(
|
|
|
345
475
|
}
|
|
346
476
|
loading = false;
|
|
347
477
|
}
|
|
348
|
-
}
|
|
478
|
+
};
|
|
479
|
+
return new COGLayer(cogProps);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// First-mount: create the MapboxOverlay once and attach via addControl.
|
|
483
|
+
// Subsequent style changes go through pushLayer() which only calls setProps,
|
|
484
|
+
// preserving deck.gl's WebGL context and tile cache.
|
|
485
|
+
function buildAndAddLayer(
|
|
486
|
+
map: maplibregl.Map,
|
|
487
|
+
preflightGeotiff: GeoTIFF | undefined,
|
|
488
|
+
signal: AbortSignal
|
|
489
|
+
) {
|
|
490
|
+
const layer = buildCogLayer(map, preflightGeotiff, signal);
|
|
349
491
|
|
|
350
492
|
const overlay = new MapboxOverlay({
|
|
351
493
|
interleaved: false,
|
|
@@ -362,41 +504,100 @@ function buildAndAddLayer(
|
|
|
362
504
|
map.addControl(overlay as unknown as maplibregl.IControl);
|
|
363
505
|
}
|
|
364
506
|
|
|
507
|
+
// Style-change update path: swap layers in place via setProps. Identity of the
|
|
508
|
+
// COGLayer's `id` and `getTileData` is preserved so deck.gl reconciles the
|
|
509
|
+
// existing layer instance and keeps its tile cache.
|
|
510
|
+
function pushLayer() {
|
|
511
|
+
if (!mapRef || !geotiffRef || !overlayRef) return;
|
|
512
|
+
const layer = buildCogLayer(mapRef, geotiffRef, abortController.signal);
|
|
513
|
+
overlayRef.setProps({ layers: [layer] });
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// ─── Viewport-scoped histogram aggregation ───────────────────────
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Sum per-tile histograms from tiles currently visible in the viewport. COG
|
|
520
|
+
* pyramid semantics map cleanly: zoomed out → a handful of low-res overview
|
|
521
|
+
* tiles cover the whole scene; zoomed in → only the tiles intersecting the
|
|
522
|
+
* AOI are decoded. deck.gl reuses its tile cache on revisits so each cached
|
|
523
|
+
* tile still carries `content.histogram`, no rebake needed.
|
|
524
|
+
*/
|
|
525
|
+
function aggregateVisibleHistogram(
|
|
526
|
+
visibleTiles: ReadonlyArray<{ content?: unknown } | null | undefined>
|
|
527
|
+
): void {
|
|
528
|
+
if (!visibleTiles || visibleTiles.length === 0) {
|
|
529
|
+
histogram = null;
|
|
530
|
+
histogramTick++;
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const summed = new Uint32Array(HISTOGRAM_BIN_COUNT);
|
|
534
|
+
let found = false;
|
|
535
|
+
for (const tile of visibleTiles) {
|
|
536
|
+
// COGLayer wraps our baker's return as `{data, forwardTransform,
|
|
537
|
+
// inverseTransform}` in `_getTileData`, so the histogram lives at
|
|
538
|
+
// `content.data.histogram`. MosaicLayer's sub-COGs follow the same
|
|
539
|
+
// shape. Fall back to `content.histogram` for future-proofing if
|
|
540
|
+
// upstream ever stops wrapping.
|
|
541
|
+
const content = tile?.content as
|
|
542
|
+
| { data?: CustomTileData; histogram?: Uint32Array }
|
|
543
|
+
| null
|
|
544
|
+
| undefined;
|
|
545
|
+
const bins = content?.data?.histogram ?? content?.histogram;
|
|
546
|
+
if (!bins || bins.length !== HISTOGRAM_BIN_COUNT) continue;
|
|
547
|
+
for (let i = 0; i < HISTOGRAM_BIN_COUNT; i++) summed[i] += bins[i];
|
|
548
|
+
found = true;
|
|
549
|
+
}
|
|
550
|
+
histogram = found ? summed : null;
|
|
551
|
+
histogramTick++;
|
|
552
|
+
}
|
|
553
|
+
|
|
365
554
|
// ─── Rebuild layer on band config change ─────────────────────────
|
|
366
555
|
|
|
367
556
|
function handleConfigChange(newConfig: BandConfig) {
|
|
368
557
|
bandConfig = newConfig;
|
|
558
|
+
// Only the single-band CPU baker emits `onHistogram`. Clear the buffer on
|
|
559
|
+
// every mode/band change so (a) switching back to RGB hides stale bars
|
|
560
|
+
// that the rescale slider would otherwise draw on top of, and (b) picking
|
|
561
|
+
// a different single band starts a fresh distribution.
|
|
562
|
+
histogram = null;
|
|
563
|
+
histogramTick = 0;
|
|
369
564
|
if (!mapRef || !geotiffRef || !isTiledRef) return;
|
|
370
565
|
|
|
371
|
-
//
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
} catch {
|
|
376
|
-
/* already removed */
|
|
377
|
-
}
|
|
378
|
-
overlayRef = null;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Rebuild with new config
|
|
382
|
-
buildAndAddLayer(mapRef, geotiffRef, abortController.signal);
|
|
566
|
+
// Swap layers in place: deck.gl diffs on layer id and reuses the stable
|
|
567
|
+
// `getTileData` reference held by `tileLoaderRef`, so the tile cache and
|
|
568
|
+
// in-flight fetches survive band/style changes.
|
|
569
|
+
pushLayer();
|
|
383
570
|
}
|
|
384
571
|
|
|
385
572
|
function handleRescaleChange(next: RescaleConfig) {
|
|
386
573
|
rescale = next;
|
|
387
574
|
if (!mapRef || !geotiffRef || !isTiledRef) return;
|
|
575
|
+
pushLayer();
|
|
576
|
+
}
|
|
388
577
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
578
|
+
// ─── Unified picker change handlers ──────────────────────────────
|
|
579
|
+
|
|
580
|
+
function handleCompositeChange(next: ChannelComposite): void {
|
|
581
|
+
if (!bandConfig) return;
|
|
582
|
+
if (bandConfig.mode === 'rgb') {
|
|
583
|
+
handleConfigChange({
|
|
584
|
+
...bandConfig,
|
|
585
|
+
rBand: next.r.bandIndex,
|
|
586
|
+
gBand: next.g.bandIndex,
|
|
587
|
+
bBand: next.b.bandIndex
|
|
588
|
+
});
|
|
589
|
+
} else {
|
|
590
|
+
handleConfigChange({ ...bandConfig, band: next.r.bandIndex });
|
|
398
591
|
}
|
|
399
|
-
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function handleModeChange(m: 'rgb' | 'single'): void {
|
|
595
|
+
if (!bandConfig) return;
|
|
596
|
+
handleConfigChange({ ...bandConfig, mode: m });
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function handleBandConfigChange(next: BandConfig): void {
|
|
600
|
+
handleConfigChange(next);
|
|
400
601
|
}
|
|
401
602
|
|
|
402
603
|
// ─── Cleanup ─────────────────────────────────────────────────────
|
|
@@ -415,9 +616,12 @@ function cleanup() {
|
|
|
415
616
|
mapRef = null;
|
|
416
617
|
overlayRef = null;
|
|
417
618
|
geotiffRef = null;
|
|
619
|
+
tileLoaderRef = null;
|
|
418
620
|
proj4DefRef = null;
|
|
419
621
|
pixelValue = null;
|
|
420
622
|
resolvedHttpsUrl = null;
|
|
623
|
+
resolvedHrefForControls = null;
|
|
624
|
+
probedBandCount = null;
|
|
421
625
|
}
|
|
422
626
|
|
|
423
627
|
$effect(() => {
|
|
@@ -434,7 +638,7 @@ onDestroy(cleanup);
|
|
|
434
638
|
</div>
|
|
435
639
|
|
|
436
640
|
<!-- Top-left: Loading + metadata badges -->
|
|
437
|
-
<div class="pointer-events-none absolute left-2 top-2 z-10 flex flex-col gap-1">
|
|
641
|
+
<div class="pointer-events-none absolute left-2 top-2 z-10 flex max-w-[calc(100vw-7rem)] flex-col gap-1 sm:max-w-none">
|
|
438
642
|
{#if loading}
|
|
439
643
|
<div
|
|
440
644
|
class="rounded bg-card/80 px-2 py-1 text-xs text-card-foreground backdrop-blur-sm"
|
|
@@ -462,14 +666,23 @@ onDestroy(cleanup);
|
|
|
462
666
|
{error}
|
|
463
667
|
</div>
|
|
464
668
|
{/if}
|
|
669
|
+
|
|
670
|
+
{#if smokeWarning && !error}
|
|
671
|
+
<div
|
|
672
|
+
class="pointer-events-auto max-w-sm rounded bg-amber-900/80 px-2 py-1 text-xs text-amber-100"
|
|
673
|
+
title={t('stac.smokeWarningHint')}
|
|
674
|
+
>
|
|
675
|
+
{t('stac.smokeWarning', { reason: smokeWarning })}
|
|
676
|
+
</div>
|
|
677
|
+
{/if}
|
|
465
678
|
</div>
|
|
466
679
|
|
|
467
680
|
<!-- Top-right: Info + Style buttons -->
|
|
468
681
|
{#if cogInfo}
|
|
469
|
-
<div class="absolute right-2 top-2 z-10 flex gap-1">
|
|
682
|
+
<div class="absolute right-2 top-2 z-10 flex gap-1" style="touch-action: manipulation;">
|
|
470
683
|
{#if bandConfig}
|
|
471
684
|
<button
|
|
472
|
-
class="rounded bg-card/80 px-
|
|
685
|
+
class="inline-flex min-h-11 min-w-11 items-center justify-center rounded bg-card/80 px-3 py-1.5 text-xs text-card-foreground backdrop-blur-sm hover:bg-card sm:min-h-0 sm:min-w-0 sm:px-2 sm:py-1"
|
|
473
686
|
class:ring-1={showControls}
|
|
474
687
|
class:ring-primary={showControls}
|
|
475
688
|
onclick={() => {
|
|
@@ -481,7 +694,7 @@ onDestroy(cleanup);
|
|
|
481
694
|
</button>
|
|
482
695
|
{/if}
|
|
483
696
|
<button
|
|
484
|
-
class="rounded bg-card/80 px-
|
|
697
|
+
class="inline-flex min-h-11 min-w-11 items-center justify-center rounded bg-card/80 px-3 py-1.5 text-xs text-card-foreground backdrop-blur-sm hover:bg-card sm:min-h-0 sm:min-w-0 sm:px-2 sm:py-1"
|
|
485
698
|
class:ring-1={showInfo}
|
|
486
699
|
class:ring-primary={showInfo}
|
|
487
700
|
onclick={() => {
|
|
@@ -493,23 +706,39 @@ onDestroy(cleanup);
|
|
|
493
706
|
</button>
|
|
494
707
|
</div>
|
|
495
708
|
|
|
496
|
-
<!-- Band/Color controls panel
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
709
|
+
<!-- Band/Color controls panel. Kept mounted so slider drag state and focus
|
|
710
|
+
survive every visibility toggle; only the `hidden` class is flipped. -->
|
|
711
|
+
{#if bandConfig}
|
|
712
|
+
<div class={showControls ? 'contents' : 'hidden'}>
|
|
713
|
+
<CogControls
|
|
714
|
+
assets={cogControlsAssets}
|
|
715
|
+
composite={cogControlsComposite}
|
|
716
|
+
onCompositeChange={handleCompositeChange}
|
|
717
|
+
presets={[]}
|
|
718
|
+
activePresetId=""
|
|
719
|
+
onPresetChange={() => {}}
|
|
720
|
+
mode={bandConfig?.mode ?? 'rgb'}
|
|
721
|
+
onModeChange={handleModeChange}
|
|
722
|
+
{bandConfig}
|
|
723
|
+
bandCount={probedBandCount ?? cogInfo.bandCount}
|
|
724
|
+
onBandConfigChange={handleBandConfigChange}
|
|
725
|
+
{rescale}
|
|
726
|
+
rescaleApplicable={rescaleApplicable}
|
|
727
|
+
onRescaleChange={handleRescaleChange}
|
|
728
|
+
{histogram}
|
|
729
|
+
nodata={nodataConfig}
|
|
730
|
+
{autoNodata}
|
|
731
|
+
onNodataChange={(next) => {
|
|
732
|
+
nodataConfig = next;
|
|
733
|
+
}}
|
|
734
|
+
/>
|
|
735
|
+
</div>
|
|
507
736
|
{/if}
|
|
508
737
|
|
|
509
738
|
<!-- Info panel -->
|
|
510
739
|
{#if showInfo}
|
|
511
740
|
<div
|
|
512
|
-
class="absolute
|
|
741
|
+
class="absolute inset-x-2 top-16 z-10 max-h-[60vh] overflow-auto rounded bg-card/90 p-3 text-xs text-card-foreground backdrop-blur-sm sm:inset-x-auto sm:right-2 sm:top-10 sm:max-h-[70vh] sm:w-64"
|
|
513
742
|
>
|
|
514
743
|
<h3 class="mb-2 font-medium">{t('map.cogInfo')}</h3>
|
|
515
744
|
<dl class="space-y-1.5">
|
|
@@ -529,45 +758,17 @@ onDestroy(cleanup);
|
|
|
529
758
|
{/if}
|
|
530
759
|
|
|
531
760
|
<!-- Bottom-left: Pixel value on click -->
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
<div class="space-y-0.5 text-muted-foreground">
|
|
546
|
-
<div>
|
|
547
|
-
{pixelValue.lat.toFixed(6)}°, {pixelValue.lng.toFixed(6)}°
|
|
548
|
-
</div>
|
|
549
|
-
<div class="text-[10px]">
|
|
550
|
-
px ({pixelValue.col}, {pixelValue.row})
|
|
551
|
-
</div>
|
|
552
|
-
</div>
|
|
553
|
-
<div class="mt-1.5 space-y-0.5">
|
|
554
|
-
{#each pixelValue.values as val, i}
|
|
555
|
-
<div class="flex justify-between gap-2">
|
|
556
|
-
<span class="text-muted-foreground">{t('cog.band')} {i + 1}</span>
|
|
557
|
-
<span class="font-mono tabular-nums">
|
|
558
|
-
{Number.isInteger(val) ? val : val.toFixed(4)}
|
|
559
|
-
</span>
|
|
560
|
-
</div>
|
|
561
|
-
{/each}
|
|
562
|
-
</div>
|
|
563
|
-
</div>
|
|
564
|
-
{/if}
|
|
565
|
-
|
|
566
|
-
{#if inspecting}
|
|
567
|
-
<div
|
|
568
|
-
class="pointer-events-none absolute bottom-2 left-2 z-10 rounded bg-card/80 px-2 py-1 text-xs text-card-foreground backdrop-blur-sm"
|
|
569
|
-
>
|
|
570
|
-
{t('cog.reading')}
|
|
571
|
-
</div>
|
|
572
|
-
{/if}
|
|
761
|
+
<PixelInspectorPanel
|
|
762
|
+
lng={pixelValue?.lng ?? null}
|
|
763
|
+
lat={pixelValue?.lat ?? null}
|
|
764
|
+
rows={pixelValue
|
|
765
|
+
? (pixelValue.values.map((v, i) => ({
|
|
766
|
+
label: `${t('cog.band')} ${i + 1}`,
|
|
767
|
+
value: v
|
|
768
|
+
})) satisfies PixelInspectorRow[])
|
|
769
|
+
: null}
|
|
770
|
+
footnote={pixelValue ? `px (${pixelValue.col}, ${pixelValue.row})` : undefined}
|
|
771
|
+
onClose={() => (pixelValue = null)}
|
|
772
|
+
{inspecting}
|
|
773
|
+
/>
|
|
573
774
|
</div>
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
hoverCursor,
|
|
20
20
|
loadDeckModules
|
|
21
21
|
} from '../../utils/deck.js';
|
|
22
|
-
import { buildHttpsUrlAsync } from '../../utils/url.js';
|
|
22
|
+
import { buildHttpsUrlAsync } from '../../utils/signed-url.js';
|
|
23
23
|
import AttributeTable from './map/AttributeTable.svelte';
|
|
24
24
|
import MapContainer from './map/MapContainer.svelte';
|
|
25
25
|
|