@walkthru-earth/objex 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/dist/components/browser/FileTreeSidebar.svelte +1 -1
- package/dist/components/layout/ConnectionDialog.svelte +35 -3
- package/dist/components/layout/Sidebar.svelte +28 -2
- package/dist/components/viewers/ArchiveViewer.svelte +4 -4
- package/dist/components/viewers/CodeViewer.svelte +72 -19
- package/dist/components/viewers/CodeViewer.svelte.d.ts +11 -1
- package/dist/components/viewers/CogControls.svelte +151 -22
- package/dist/components/viewers/CogControls.svelte.d.ts +5 -1
- package/dist/components/viewers/CogViewer.svelte +45 -10
- package/dist/components/viewers/CopcViewer.svelte +20 -2
- package/dist/components/viewers/FlatGeobufViewer.svelte +15 -9
- package/dist/components/viewers/MultiCogViewer.svelte +416 -0
- package/dist/components/viewers/MultiCogViewer.svelte.d.ts +9 -0
- package/dist/components/viewers/PmtilesViewer.svelte +2 -2
- package/dist/components/viewers/StacMapViewer.svelte +34 -12
- package/dist/components/viewers/StacMapViewer.svelte.d.ts +1 -0
- package/dist/components/viewers/StacMosaicViewer.svelte +699 -0
- package/dist/components/viewers/StacMosaicViewer.svelte.d.ts +9 -0
- package/dist/components/viewers/StacTabViewer.svelte +254 -0
- package/dist/components/viewers/StacTabViewer.svelte.d.ts +13 -0
- package/dist/components/viewers/TableViewer.svelte +50 -21
- package/dist/components/viewers/ViewerRouter.svelte +155 -2
- package/dist/components/viewers/ViewerRouter.svelte.d.ts +1 -1
- package/dist/components/viewers/ZarrMapViewer.svelte +147 -8
- package/dist/components/viewers/ZarrMapViewer.svelte.d.ts +8 -2
- package/dist/components/viewers/ZarrViewer.svelte +3 -2
- package/dist/components/viewers/pmtiles/PmtilesMapView.svelte +0 -1
- package/dist/i18n/ar.js +28 -0
- package/dist/i18n/en.js +28 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/source.d.ts +12 -0
- package/dist/query/source.js +25 -8
- package/dist/query/stac-geoparquet.d.ts +31 -0
- package/dist/query/stac-geoparquet.js +136 -0
- package/dist/query/wasm.js +130 -23
- package/dist/storage/adapter.d.ts +9 -0
- package/dist/storage/adapter.js +13 -1
- package/dist/storage/browser-azure.d.ts +1 -1
- package/dist/storage/browser-azure.js +4 -0
- package/dist/storage/browser-cloud.d.ts +1 -1
- package/dist/storage/browser-cloud.js +7 -0
- package/dist/storage/presign.d.ts +13 -0
- package/dist/storage/presign.js +55 -0
- package/dist/storage/providers.d.ts +6 -0
- package/dist/storage/providers.js +13 -2
- package/dist/stores/browser.svelte.d.ts +2 -0
- package/dist/stores/browser.svelte.js +17 -1
- package/dist/stores/connections.svelte.d.ts +38 -23
- package/dist/stores/connections.svelte.js +105 -114
- package/dist/utils/cog.d.ts +80 -18
- package/dist/utils/cog.js +187 -125
- package/dist/utils/colormap-sprite.d.ts +39 -0
- package/dist/utils/colormap-sprite.js +77 -0
- package/dist/utils/connection-identity.d.ts +51 -0
- package/dist/utils/connection-identity.js +97 -0
- package/dist/utils/host-detection.js +48 -302
- package/dist/utils/parquet-metadata.d.ts +7 -1
- package/dist/utils/parquet-metadata.js +35 -1
- package/dist/utils/stac-geoparquet.d.ts +90 -0
- package/dist/utils/stac-geoparquet.js +223 -0
- package/dist/utils/stac-hydrate.d.ts +38 -0
- package/dist/utils/stac-hydrate.js +243 -0
- package/dist/utils/stac.d.ts +136 -0
- package/dist/utils/stac.js +176 -0
- package/dist/utils/storage-url.d.ts +26 -0
- package/dist/utils/storage-url.js +164 -28
- package/dist/utils/url.d.ts +13 -0
- package/dist/utils/url.js +36 -0
- package/dist/utils/wkb.js +22 -8
- package/dist/utils/zarr.d.ts +34 -0
- package/dist/utils/zarr.js +94 -0
- package/package.json +14 -13
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { MapboxOverlay } from '@deck.gl/mapbox';
|
|
2
3
|
import type maplibregl from 'maplibre-gl';
|
|
3
4
|
import maplibreModule from 'maplibre-gl';
|
|
4
5
|
import { onDestroy, untrack } from 'svelte';
|
|
5
6
|
import { t } from '../../i18n/index.svelte.js';
|
|
6
7
|
import { tabResources } from '../../stores/tab-resources.svelte.js';
|
|
7
|
-
import type { Tab } from '../../types';
|
|
8
|
-
import {
|
|
8
|
+
import type { Tab } from '../../types.js';
|
|
9
|
+
import { createEpsgResolver } from '../../utils/cog.js';
|
|
10
|
+
import { buildHttpsUrlAsync } from '../../utils/url.js';
|
|
9
11
|
import {
|
|
12
|
+
detectGeoZarr,
|
|
10
13
|
ensureCodecsRegistered,
|
|
11
14
|
extractZarrStoreUrl,
|
|
15
|
+
type GeoZarrInfo,
|
|
12
16
|
inferDims,
|
|
17
|
+
type ZarrHierarchy,
|
|
13
18
|
type ZarrNode
|
|
14
19
|
} from '../../utils/zarr.js';
|
|
15
20
|
import MapContainer from './map/MapContainer.svelte';
|
|
@@ -40,21 +45,40 @@ let {
|
|
|
40
45
|
variables,
|
|
41
46
|
coords = [],
|
|
42
47
|
spatialRefAttrs,
|
|
43
|
-
zarrVersion = null
|
|
48
|
+
zarrVersion = null,
|
|
49
|
+
hierarchy = null
|
|
44
50
|
}: {
|
|
45
51
|
tab: Tab;
|
|
46
52
|
variables: ZarrNode[];
|
|
47
53
|
coords?: ZarrNode[];
|
|
48
54
|
spatialRefAttrs: Record<string, any> | null;
|
|
49
55
|
zarrVersion?: number | null;
|
|
56
|
+
/**
|
|
57
|
+
* Full pre-loaded hierarchy. When present, `detectGeoZarr` can short-circuit
|
|
58
|
+
* to the `@developmentseed/deck.gl-zarr` path for GeoZarr-valid stores.
|
|
59
|
+
* Non-GeoZarr stores fall through to `@carbonplan/zarr-layer`.
|
|
60
|
+
*/
|
|
61
|
+
hierarchy?: ZarrHierarchy | null;
|
|
50
62
|
} = $props();
|
|
51
63
|
|
|
64
|
+
// GeoZarr detection runs once per hierarchy so the branch decision is stable
|
|
65
|
+
// across selector-slider tweaks. Returns null for non-GeoZarr stores, which
|
|
66
|
+
// sends everything through the existing carbonplan path.
|
|
67
|
+
const geoZarrInfo = $derived<GeoZarrInfo | null>(hierarchy ? detectGeoZarr(hierarchy) : null);
|
|
68
|
+
|
|
69
|
+
// MapboxOverlay holder for the deck.gl-zarr path. Separate from zarrLayer so
|
|
70
|
+
// the two paths can be cleaned up independently.
|
|
71
|
+
let dsZarrOverlay: MapboxOverlay | null = null;
|
|
72
|
+
const dsZarrEpsg = createEpsgResolver();
|
|
73
|
+
|
|
52
74
|
let loading = $state(true);
|
|
53
75
|
let error = $state<string | null>(null);
|
|
54
76
|
let selectedVar = $state('');
|
|
55
77
|
let zarrLayer: any = null;
|
|
56
78
|
let mapRef: maplibregl.Map | null = null;
|
|
57
79
|
let inspectPopup: maplibregl.Popup | null = null;
|
|
80
|
+
let loadGen = 0;
|
|
81
|
+
let addAbort = new AbortController();
|
|
58
82
|
|
|
59
83
|
// Extract proj4 from spatial_ref if available
|
|
60
84
|
const proj4String = $derived(extractProj4(spatialRefAttrs));
|
|
@@ -359,20 +383,39 @@ async function onMapReady(map: maplibregl.Map) {
|
|
|
359
383
|
}
|
|
360
384
|
|
|
361
385
|
async function addZarrLayer(map: maplibregl.Map) {
|
|
386
|
+
addAbort.abort();
|
|
387
|
+
addAbort = new AbortController();
|
|
388
|
+
const signal = addAbort.signal;
|
|
389
|
+
const gen = ++loadGen;
|
|
362
390
|
loading = true;
|
|
363
391
|
error = null;
|
|
364
392
|
|
|
365
393
|
try {
|
|
366
|
-
// Remove existing layer
|
|
367
394
|
if (zarrLayer && map.getLayer(zarrLayer.id)) {
|
|
368
395
|
map.removeLayer(zarrLayer.id);
|
|
369
396
|
}
|
|
397
|
+
if (dsZarrOverlay) {
|
|
398
|
+
try {
|
|
399
|
+
map.removeControl(dsZarrOverlay as unknown as maplibregl.IControl);
|
|
400
|
+
} catch {
|
|
401
|
+
/* already removed */
|
|
402
|
+
}
|
|
403
|
+
dsZarrOverlay = null;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (geoZarrInfo) {
|
|
407
|
+
const used = await tryAddGeoZarrLayer(map, gen, signal);
|
|
408
|
+
if (gen !== loadGen || signal.aborted) return;
|
|
409
|
+
if (used) return;
|
|
410
|
+
}
|
|
370
411
|
|
|
371
|
-
// Ensure numcodecs codecs (shuffle, zlib, etc.) are registered before zarr-layer uses zarrita
|
|
372
412
|
await ensureCodecsRegistered();
|
|
413
|
+
if (gen !== loadGen || signal.aborted) return;
|
|
373
414
|
const { ZarrLayer } = await import('@carbonplan/zarr-layer');
|
|
415
|
+
if (gen !== loadGen || signal.aborted) return;
|
|
374
416
|
|
|
375
|
-
const storeUrl = buildStoreUrl();
|
|
417
|
+
const storeUrl = await buildStoreUrl();
|
|
418
|
+
if (gen !== loadGen || signal.aborted) return;
|
|
376
419
|
const selector = buildSelector();
|
|
377
420
|
|
|
378
421
|
const opts: any = {
|
|
@@ -452,11 +495,102 @@ async function addZarrLayer(map: maplibregl.Map) {
|
|
|
452
495
|
}
|
|
453
496
|
}
|
|
454
497
|
|
|
455
|
-
function buildStoreUrl(): string {
|
|
456
|
-
const rawUrl =
|
|
498
|
+
async function buildStoreUrl(): Promise<string> {
|
|
499
|
+
const rawUrl = (await buildHttpsUrlAsync(tab)).replace(/\/+$/, '');
|
|
457
500
|
return extractZarrStoreUrl(rawUrl) ?? rawUrl;
|
|
458
501
|
}
|
|
459
502
|
|
|
503
|
+
/**
|
|
504
|
+
* Attempt to render via `@developmentseed/deck.gl-zarr`. Returns true on
|
|
505
|
+
* success (carbonplan fallback is skipped), false on any setup error so the
|
|
506
|
+
* caller can fall through to the legacy path. Errors thrown inside the layer
|
|
507
|
+
* after setup propagate through the overlay's `onError`.
|
|
508
|
+
*/
|
|
509
|
+
async function tryAddGeoZarrLayer(
|
|
510
|
+
map: maplibregl.Map,
|
|
511
|
+
gen: number,
|
|
512
|
+
signal: AbortSignal
|
|
513
|
+
): Promise<boolean> {
|
|
514
|
+
if (!geoZarrInfo) return false;
|
|
515
|
+
try {
|
|
516
|
+
const zarrita = await import('zarrita');
|
|
517
|
+
if (gen !== loadGen || signal.aborted) return false;
|
|
518
|
+
const { ZarrLayer } = await import('@developmentseed/deck.gl-zarr');
|
|
519
|
+
if (gen !== loadGen || signal.aborted) return false;
|
|
520
|
+
const storeUrl = await buildStoreUrl();
|
|
521
|
+
if (gen !== loadGen || signal.aborted) return false;
|
|
522
|
+
const store = new zarrita.FetchStore(storeUrl);
|
|
523
|
+
const group = await zarrita.open(store, { kind: 'group' });
|
|
524
|
+
if (gen !== loadGen || signal.aborted) return false;
|
|
525
|
+
|
|
526
|
+
const zarrInfoSnapshot = $state.snapshot(geoZarrInfo) as GeoZarrInfo;
|
|
527
|
+
const layer = new ZarrLayer({
|
|
528
|
+
id: `geozarr-${tab.id}`,
|
|
529
|
+
source: group,
|
|
530
|
+
variable: zarrInfoSnapshot.variantPath || undefined,
|
|
531
|
+
selection: {},
|
|
532
|
+
epsgResolver: dsZarrEpsg,
|
|
533
|
+
getTileData: async (arr, options) => {
|
|
534
|
+
const chunk = await zarrita.get(arr, options.sliceSpec);
|
|
535
|
+
if (gen !== loadGen || signal.aborted) {
|
|
536
|
+
throw new DOMException('Aborted', 'AbortError');
|
|
537
|
+
}
|
|
538
|
+
const data = chunk.data as unknown as ArrayLike<number> & { length: number };
|
|
539
|
+
return {
|
|
540
|
+
width: options.width,
|
|
541
|
+
height: options.height,
|
|
542
|
+
data,
|
|
543
|
+
byteLength: data.length
|
|
544
|
+
};
|
|
545
|
+
},
|
|
546
|
+
renderTile: (data) => {
|
|
547
|
+
const raw = (data as unknown as { data: ArrayLike<number> & { length: number } }).data;
|
|
548
|
+
if (!raw) return { image: undefined } as never;
|
|
549
|
+
let clamped: Uint8ClampedArray;
|
|
550
|
+
const asTyped = raw as unknown as {
|
|
551
|
+
buffer?: ArrayBufferLike;
|
|
552
|
+
byteOffset?: number;
|
|
553
|
+
byteLength?: number;
|
|
554
|
+
};
|
|
555
|
+
if (
|
|
556
|
+
asTyped.buffer &&
|
|
557
|
+
typeof asTyped.byteOffset === 'number' &&
|
|
558
|
+
typeof asTyped.byteLength === 'number'
|
|
559
|
+
) {
|
|
560
|
+
clamped = new Uint8ClampedArray(asTyped.buffer, asTyped.byteOffset, asTyped.byteLength);
|
|
561
|
+
} else {
|
|
562
|
+
clamped = new Uint8ClampedArray(raw as unknown as Uint8Array);
|
|
563
|
+
}
|
|
564
|
+
const img = new ImageData(
|
|
565
|
+
clamped as unknown as Uint8ClampedArray<ArrayBuffer>,
|
|
566
|
+
data.width,
|
|
567
|
+
data.height
|
|
568
|
+
);
|
|
569
|
+
return { image: img };
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
const overlay = new MapboxOverlay({
|
|
574
|
+
interleaved: false,
|
|
575
|
+
layers: [layer],
|
|
576
|
+
onError: (err) => {
|
|
577
|
+
error = err?.message || String(err);
|
|
578
|
+
loading = false;
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
dsZarrOverlay = overlay;
|
|
582
|
+
map.addControl(overlay as unknown as maplibregl.IControl);
|
|
583
|
+
loading = false;
|
|
584
|
+
return true;
|
|
585
|
+
} catch {
|
|
586
|
+
// Fall back to carbonplan path on any setup failure (e.g. the store
|
|
587
|
+
// looked like GeoZarr by shape but zarrita open failed, or the group
|
|
588
|
+
// attrs don't actually parse). Silent by design, the caller will mount
|
|
589
|
+
// carbonplan's ZarrLayer which surfaces its own errors.
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
460
594
|
// Re-render when selector changes
|
|
461
595
|
async function updateSelector() {
|
|
462
596
|
if (!zarrLayer) return;
|
|
@@ -476,6 +610,7 @@ async function changeVariable() {
|
|
|
476
610
|
}
|
|
477
611
|
|
|
478
612
|
function cleanup() {
|
|
613
|
+
addAbort.abort();
|
|
479
614
|
inspectPopup?.remove();
|
|
480
615
|
inspectPopup = null;
|
|
481
616
|
try {
|
|
@@ -483,10 +618,14 @@ function cleanup() {
|
|
|
483
618
|
if (zarrLayer && mapRef?.getLayer('zarr-data')) {
|
|
484
619
|
mapRef.removeLayer('zarr-data');
|
|
485
620
|
}
|
|
621
|
+
if (mapRef && dsZarrOverlay) {
|
|
622
|
+
mapRef.removeControl(dsZarrOverlay as unknown as maplibregl.IControl);
|
|
623
|
+
}
|
|
486
624
|
} catch {
|
|
487
625
|
// map may already be destroyed
|
|
488
626
|
}
|
|
489
627
|
zarrLayer = null;
|
|
628
|
+
dsZarrOverlay = null;
|
|
490
629
|
mapRef = null;
|
|
491
630
|
}
|
|
492
631
|
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import type { Tab } from '../../types';
|
|
2
|
-
import { type ZarrNode } from '../../utils/zarr.js';
|
|
1
|
+
import type { Tab } from '../../types.js';
|
|
2
|
+
import { type ZarrHierarchy, type ZarrNode } from '../../utils/zarr.js';
|
|
3
3
|
type $$ComponentProps = {
|
|
4
4
|
tab: Tab;
|
|
5
5
|
variables: ZarrNode[];
|
|
6
6
|
coords?: ZarrNode[];
|
|
7
7
|
spatialRefAttrs: Record<string, any> | null;
|
|
8
8
|
zarrVersion?: number | null;
|
|
9
|
+
/**
|
|
10
|
+
* Full pre-loaded hierarchy. When present, `detectGeoZarr` can short-circuit
|
|
11
|
+
* to the `@developmentseed/deck.gl-zarr` path for GeoZarr-valid stores.
|
|
12
|
+
* Non-GeoZarr stores fall through to `@carbonplan/zarr-layer`.
|
|
13
|
+
*/
|
|
14
|
+
hierarchy?: ZarrHierarchy | null;
|
|
9
15
|
};
|
|
10
16
|
declare const ZarrMapViewer: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
11
17
|
type ZarrMapViewer = ReturnType<typeof ZarrMapViewer>;
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from '../ui/resizable/index.js';
|
|
10
10
|
import { t } from '../../i18n/index.svelte.js';
|
|
11
11
|
import type { Tab } from '../../types';
|
|
12
|
-
import {
|
|
12
|
+
import { buildHttpsUrlAsync } from '../../utils/url.js';
|
|
13
13
|
import { getUrlView, updateUrlView } from '../../utils/url-state.js';
|
|
14
14
|
import {
|
|
15
15
|
computeChunkCount,
|
|
@@ -120,7 +120,7 @@ async function loadHierarchy() {
|
|
|
120
120
|
error = null;
|
|
121
121
|
|
|
122
122
|
try {
|
|
123
|
-
const rawUrl =
|
|
123
|
+
const rawUrl = (await buildHttpsUrlAsync(tab)).replace(/\/+$/, '');
|
|
124
124
|
const url = extractZarrStoreUrl(rawUrl) ?? rawUrl;
|
|
125
125
|
const storeName = tab.name.replace(/\.(zarr|zr3)$/, '');
|
|
126
126
|
|
|
@@ -474,6 +474,7 @@ function selectStoreAttrs() {
|
|
|
474
474
|
coords={coordArrays}
|
|
475
475
|
spatialRefAttrs={hierarchy?.spatialRefAttrs ?? null}
|
|
476
476
|
zarrVersion={hierarchy?.zarrVersion}
|
|
477
|
+
hierarchy={hierarchy ?? null}
|
|
477
478
|
/>
|
|
478
479
|
{/await}
|
|
479
480
|
{/key}
|
|
@@ -12,7 +12,6 @@ import type { Tab } from '../../../types';
|
|
|
12
12
|
import { setupSelectionLayer, updateSelection } from '../../../utils/map-selection.js';
|
|
13
13
|
import { buildPmtilesLayers, getPmtilesProtocol, type PmtilesMetadata } from '../../../utils/pmtiles';
|
|
14
14
|
import { layerHue } from '../../../utils/pmtiles-tile.js';
|
|
15
|
-
import { buildHttpsUrl } from '../../../utils/url.js';
|
|
16
15
|
import AttributeTable from '../map/AttributeTable.svelte';
|
|
17
16
|
import MapContainer from '../map/MapContainer.svelte';
|
|
18
17
|
|
package/dist/i18n/ar.js
CHANGED
|
@@ -40,6 +40,8 @@ export const ar = {
|
|
|
40
40
|
'connection.readOnlyCliTitle': 'تقييد عبر سطر الأوامر',
|
|
41
41
|
'connection.testSuccess': 'الاتصال ناجح',
|
|
42
42
|
'connection.testFail': 'فشل الاتصال. تحقق من الإعدادات وحاول مرة أخرى.',
|
|
43
|
+
'connection.duplicateMerged': 'تم العثور على اتصال موجود ("{name}") لهذه الحاوية. تم تحديث بيانات الاعتماد.',
|
|
44
|
+
'connection.duplicateBlocked': 'يوجد بالفعل اتصال آخر ("{name}") يستخدم هذه الحاوية. عدّله بدلاً من ذلك.',
|
|
43
45
|
'connection.testButton': 'اختبار الاتصال',
|
|
44
46
|
'connection.testing': 'جارٍ الاختبار...',
|
|
45
47
|
'connection.cancel': 'إلغاء',
|
|
@@ -300,6 +302,7 @@ export const ar = {
|
|
|
300
302
|
'code.stacCatalog': 'كتالوج STAC',
|
|
301
303
|
'code.stacCollection': 'مجموعة STAC',
|
|
302
304
|
'code.stacItem': 'عنصر STAC',
|
|
305
|
+
'code.stacGeoparquet': 'stac-geoparquet',
|
|
303
306
|
'code.browseStac': 'تصفح',
|
|
304
307
|
'code.keplerGl': 'Kepler.gl',
|
|
305
308
|
'code.openKepler': 'فتح الخريطة',
|
|
@@ -342,6 +345,7 @@ export const ar = {
|
|
|
342
345
|
'map.flatgeobufInfo': 'معلومات FlatGeobuf',
|
|
343
346
|
'map.cogInfo': 'معلومات COG',
|
|
344
347
|
'map.cogCorsError': 'تعذّر تحميل COG: الخادم لا يسمح بطلبات عبر النطاقات (CORS). يجب استضافة الملف مع تفعيل ترويسات CORS.',
|
|
348
|
+
'map.cogInvalidTiff': 'هذا الملف ليس ملف TIFF صالح. نوع المحتوى image/tiff لكن الأجزاء الأولى من الملف لا تطابق توقيع TIFF، قد يكون الملف تالفاً أو مشفّراً أو مُعنوناً بشكل خاطئ.',
|
|
345
349
|
'map.cogUnsupportedFormat': 'يستخدم هذا الملف صيغة {{type}} غير مدعومة لعرض الخريطة. يمكن عرض ملفات COG بصيغة RGB فقط.',
|
|
346
350
|
'map.noGeoColumn': 'لم يتم اكتشاف عمود هندسي في المخطط',
|
|
347
351
|
'map.noData': 'لا تتوفر بيانات لعرض الخريطة',
|
|
@@ -381,11 +385,35 @@ export const ar = {
|
|
|
381
385
|
'mapInfo.columns': 'الأعمدة',
|
|
382
386
|
'mapInfo.size': 'الحجم',
|
|
383
387
|
'mapInfo.bands': 'النطاقات',
|
|
388
|
+
'map.mosaicEmpty': 'كتالوج STAC لا يحتوي على عناصر بها أصول COG.',
|
|
389
|
+
'map.mosaicNoAssets': 'لا تحتوي عناصر STAC على عنوان URL صالح لأصل COG.',
|
|
390
|
+
'map.multiCogMissingBands': 'عنصر STAC هذا ينقصه نطاقات الأحمر والأخضر والأزرق اللازمة للتركيب.',
|
|
391
|
+
'map.multiCogPreset.label': 'الإعداد المسبق',
|
|
392
|
+
'map.multiCogPreset.trueColor': 'ألوان طبيعية',
|
|
393
|
+
'map.multiCogPreset.falseColorIR': 'أشعة تحت حمراء كاذبة اللون',
|
|
394
|
+
'map.multiCogPreset.swir': 'الأشعة تحت الحمراء قصيرة الموجة',
|
|
395
|
+
'map.multiCogPreset.vegetation': 'الغطاء النباتي',
|
|
396
|
+
'map.multiCogPreset.agriculture': 'الزراعة',
|
|
397
|
+
// تبديل عرض STAC (StacTabViewer)
|
|
398
|
+
'stac.viewMosaic': 'خريطة',
|
|
399
|
+
'stac.viewMultiCog': 'خريطة (نطاقات)',
|
|
400
|
+
'stac.viewStacMap': 'stac-map',
|
|
401
|
+
'stac.viewBrowser': 'متصفح STAC',
|
|
402
|
+
'stac.viewJson': 'JSON',
|
|
403
|
+
'stac.viewTable': 'جدول',
|
|
404
|
+
'stac.stacBrowserJsonOnly': 'متصفح STAC يدعم كتالوجات JSON فقط. استخدم stac-map لملفات parquet.',
|
|
405
|
+
'stac.iframeDisabledPrivate': 'معطل للبكتات الخاصة، الإطار الخارجي لا يستطيع توقيع طلباته. استخدم الخريطة أو JSON بدلاً من ذلك.',
|
|
406
|
+
'stac.iframePrivateBucketWarning': 'بكت خاص، الإطار الخارجي لا يستطيع توقيع طلباته، لذا ستفشل طلبات العناصر الفرعية بـ 403. لكن المانيفست الجذري سيعمل.',
|
|
407
|
+
'stac.mosaicSourcesOne': 'فسيفساء، {count} مصدر',
|
|
408
|
+
'stac.mosaicSourcesOther': 'فسيفساء، {count} مصادر',
|
|
409
|
+
'stac.mosaicInfo': 'معلومات الفسيفساء',
|
|
410
|
+
'stac.mosaicSourcesLabel': 'المصادر',
|
|
384
411
|
// COG Controls
|
|
385
412
|
'cog.style': 'النمط',
|
|
386
413
|
'cog.band': 'النطاق',
|
|
387
414
|
'cog.singleBand': 'نطاق واحد',
|
|
388
415
|
'cog.colorRamp': 'تدرج الألوان',
|
|
416
|
+
'cog.colorRampSearch': 'بحث في التدرجات…',
|
|
389
417
|
'cog.pixelValue': 'قيمة البكسل',
|
|
390
418
|
'cog.reading': 'قراءة البكسل...',
|
|
391
419
|
'cog.rescale': 'إعادة القياس',
|
package/dist/i18n/en.js
CHANGED
|
@@ -40,6 +40,8 @@ export const en = {
|
|
|
40
40
|
'connection.readOnlyCliTitle': 'Restrict via CLI',
|
|
41
41
|
'connection.testSuccess': 'Connection successful',
|
|
42
42
|
'connection.testFail': 'Connection failed. Check your settings and try again.',
|
|
43
|
+
'connection.duplicateMerged': 'Matched an existing connection ("{name}") for this bucket. Credentials were updated.',
|
|
44
|
+
'connection.duplicateBlocked': 'Another connection ("{name}") already uses this bucket. Edit that one instead.',
|
|
43
45
|
'connection.testButton': 'Test Connection',
|
|
44
46
|
'connection.testing': 'Testing...',
|
|
45
47
|
'connection.cancel': 'Cancel',
|
|
@@ -300,6 +302,7 @@ export const en = {
|
|
|
300
302
|
'code.stacCatalog': 'STAC Catalog',
|
|
301
303
|
'code.stacCollection': 'STAC Collection',
|
|
302
304
|
'code.stacItem': 'STAC Item',
|
|
305
|
+
'code.stacGeoparquet': 'stac-geoparquet',
|
|
303
306
|
'code.browseStac': 'Browse',
|
|
304
307
|
'code.keplerGl': 'Kepler.gl',
|
|
305
308
|
'code.openKepler': 'Open Map',
|
|
@@ -342,6 +345,7 @@ export const en = {
|
|
|
342
345
|
'map.flatgeobufInfo': 'FlatGeobuf Info',
|
|
343
346
|
'map.cogInfo': 'COG Info',
|
|
344
347
|
'map.cogCorsError': 'Cannot load COG: the server does not allow cross-origin requests (CORS). The file must be hosted with CORS headers enabled.',
|
|
348
|
+
'map.cogInvalidTiff': 'This file is not a valid TIFF. The server advertises image/tiff but the bytes do not match the TIFF signature, the file may be corrupt, encrypted, or mislabeled.',
|
|
345
349
|
'map.cogUnsupportedFormat': 'This COG uses {{type}} format which is not supported for map rendering. Only RGB COGs can be displayed.',
|
|
346
350
|
'map.noGeoColumn': 'No geometry column detected in schema',
|
|
347
351
|
'map.noData': 'No data available for map view',
|
|
@@ -381,11 +385,35 @@ export const en = {
|
|
|
381
385
|
'mapInfo.columns': 'Columns',
|
|
382
386
|
'mapInfo.size': 'Size',
|
|
383
387
|
'mapInfo.bands': 'Bands',
|
|
388
|
+
'map.mosaicEmpty': 'STAC catalog has no items with COG assets.',
|
|
389
|
+
'map.mosaicNoAssets': 'None of the STAC items expose a usable COG asset URL.',
|
|
390
|
+
'map.multiCogMissingBands': 'This STAC item is missing the red/green/blue bands required for a composite.',
|
|
391
|
+
'map.multiCogPreset.label': 'Preset',
|
|
392
|
+
'map.multiCogPreset.trueColor': 'True Color',
|
|
393
|
+
'map.multiCogPreset.falseColorIR': 'False-Color IR',
|
|
394
|
+
'map.multiCogPreset.swir': 'SWIR',
|
|
395
|
+
'map.multiCogPreset.vegetation': 'Vegetation',
|
|
396
|
+
'map.multiCogPreset.agriculture': 'Agriculture',
|
|
397
|
+
// STAC tab toggle (StacTabViewer)
|
|
398
|
+
'stac.viewMosaic': 'Map',
|
|
399
|
+
'stac.viewMultiCog': 'Map (Bands)',
|
|
400
|
+
'stac.viewStacMap': 'stac-map',
|
|
401
|
+
'stac.viewBrowser': 'STAC Browser',
|
|
402
|
+
'stac.viewJson': 'JSON',
|
|
403
|
+
'stac.viewTable': 'Table',
|
|
404
|
+
'stac.stacBrowserJsonOnly': 'STAC Browser supports JSON catalogs only. Use stac-map for parquet.',
|
|
405
|
+
'stac.iframeDisabledPrivate': 'Disabled for private buckets, the external iframe cannot sign its own crawl. Use Map or JSON instead.',
|
|
406
|
+
'stac.iframePrivateBucketWarning': 'Private bucket, the external iframe cannot sign its own crawl, so child items will 403. The top manifest still renders.',
|
|
407
|
+
'stac.mosaicSourcesOne': 'Mosaic, {count} source',
|
|
408
|
+
'stac.mosaicSourcesOther': 'Mosaic, {count} sources',
|
|
409
|
+
'stac.mosaicInfo': 'Mosaic info',
|
|
410
|
+
'stac.mosaicSourcesLabel': 'Sources',
|
|
384
411
|
// COG Controls
|
|
385
412
|
'cog.style': 'Style',
|
|
386
413
|
'cog.band': 'Band',
|
|
387
414
|
'cog.singleBand': 'Single',
|
|
388
415
|
'cog.colorRamp': 'Color ramp',
|
|
416
|
+
'cog.colorRampSearch': 'Search ramps…',
|
|
389
417
|
'cog.pixelValue': 'Pixel Value',
|
|
390
418
|
'cog.reading': 'Reading pixel...',
|
|
391
419
|
'cog.rescale': 'Rescale',
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type { CogInfo, GeoBounds } from './utils/cog.js';
|
|
|
14
14
|
export { buildDataTypeLabel, clampBounds, SF_LABELS, safeClamp } from './utils/cog.js';
|
|
15
15
|
export type { TypeCategory } from './utils/column-types.js';
|
|
16
16
|
export { classifyType, typeBadgeClass, typeColor, typeLabel } from './utils/column-types.js';
|
|
17
|
+
export type { ConnectionIdentityInput } from './utils/connection-identity.js';
|
|
18
|
+
export { connectionIdentityKey, isSameConnectionIdentity, normalizeEndpoint, normalizeProvider } from './utils/connection-identity.js';
|
|
17
19
|
export { handleLoadError } from './utils/error.js';
|
|
18
20
|
export { escapeCsvField, serializeToCsv, serializeToJson } from './utils/export.js';
|
|
19
21
|
export type { SortConfig, SortDirection, SortField } from './utils/file-sort.js';
|
|
@@ -28,6 +30,8 @@ export type { ParsedMarkdownDocument, SqlBlock } from './utils/markdown-sql.js';
|
|
|
28
30
|
export { interpolateTemplates, markSqlBlocks, parseMarkdownDocument } from './utils/markdown-sql.js';
|
|
29
31
|
export type { GeoColumnMeta, GeoParquetMeta, ParquetFileMetadata } from './utils/parquet-metadata.js';
|
|
30
32
|
export { extractBounds, extractEpsgFromGeoMeta, extractGeometryTypes, readParquetMetadata } from './utils/parquet-metadata.js';
|
|
33
|
+
export type { StacBboxStruct, StacGeoparquetRow, StacGeoparquetSchemaColumn, StacRowToItemOptions } from './utils/stac-geoparquet.js';
|
|
34
|
+
export { flattenStacBbox, isStacGeoparquetSchema, pickStacPrimaryAsset, resolveStacAssetHref, STAC_GEOPARQUET_REQUIRED_COLUMNS, stacRowToItem } from './utils/stac-geoparquet.js';
|
|
31
35
|
export type { Defaults, ParsedStorageUrl, StorageProvider } from './utils/storage-url.js';
|
|
32
36
|
export { describeParseResult, looksLikeUrl, parseStorageUrl } from './utils/storage-url.js';
|
|
33
37
|
export type { GeoType, ParsedGeometry } from './utils/wkb.js';
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export { copyToClipboard, wireCodeCopyButtons } from './utils/clipboard.js';
|
|
|
12
12
|
export { getNativeScheme, resolveCloudUrl, safeDecodeURIComponent } from './utils/cloud-url.js';
|
|
13
13
|
export { buildDataTypeLabel, clampBounds, SF_LABELS, safeClamp } from './utils/cog.js';
|
|
14
14
|
export { classifyType, typeBadgeClass, typeColor, typeLabel } from './utils/column-types.js';
|
|
15
|
+
export { connectionIdentityKey, isSameConnectionIdentity, normalizeEndpoint, normalizeProvider } from './utils/connection-identity.js';
|
|
15
16
|
// Error handling
|
|
16
17
|
export { handleLoadError } from './utils/error.js';
|
|
17
18
|
// Data export / serialization
|
|
@@ -24,6 +25,7 @@ export { generateHexDump } from './utils/hex.js';
|
|
|
24
25
|
export { loadFromStorage, persistToStorage } from './utils/local-storage.js';
|
|
25
26
|
export { interpolateTemplates, markSqlBlocks, parseMarkdownDocument } from './utils/markdown-sql.js';
|
|
26
27
|
export { extractBounds, extractEpsgFromGeoMeta, extractGeometryTypes, readParquetMetadata } from './utils/parquet-metadata.js';
|
|
28
|
+
export { flattenStacBbox, isStacGeoparquetSchema, pickStacPrimaryAsset, resolveStacAssetHref, STAC_GEOPARQUET_REQUIRED_COLUMNS, stacRowToItem } from './utils/stac-geoparquet.js';
|
|
27
29
|
export { describeParseResult, looksLikeUrl, parseStorageUrl } from './utils/storage-url.js';
|
|
28
30
|
// Utilities
|
|
29
31
|
export { findGeoColumn, findGeoColumnFromRows, parseWKB, toBinary } from './utils/wkb.js';
|
package/dist/query/index.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ import type { QueryEngine } from './engine';
|
|
|
2
2
|
export declare function getQueryEngine(): Promise<QueryEngine>;
|
|
3
3
|
export type { MapQueryHandle, MapQueryResult, QueryEngine, QueryHandle, QueryResult, QuerySource, SchemaField } from './engine';
|
|
4
4
|
export { QueryCancelledError } from './engine';
|
|
5
|
-
export { type ResolvedTableSource, resolveTableSource } from './source.js';
|
|
5
|
+
export { type ResolvedTableSource, resolveTableSource, resolveTableSourceAsync } from './source.js';
|
package/dist/query/index.js
CHANGED
package/dist/query/source.d.ts
CHANGED
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { Tab } from '../types.js';
|
|
12
12
|
import type { QuerySource } from './engine.js';
|
|
13
|
+
/**
|
|
14
|
+
* True when a source ref points at a self-authenticating HTTPS URL (e.g. a
|
|
15
|
+
* presigned `read_parquet('https://...?X-Amz-Signature=...')`). Used to decide
|
|
16
|
+
* whether DuckDB needs S3 credential config — presigned URLs don't.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isHttpsSourceRef(ref: string): boolean;
|
|
13
19
|
export interface ResolvedTableSource extends QuerySource {
|
|
14
20
|
/**
|
|
15
21
|
* True when the tab is file-backed and hyparquet / parquet metadata
|
|
@@ -28,3 +34,9 @@ export interface ResolvedTableSource extends QuerySource {
|
|
|
28
34
|
* over a tab's lifetime.
|
|
29
35
|
*/
|
|
30
36
|
export declare function resolveTableSource(tab: Tab): ResolvedTableSource;
|
|
37
|
+
/**
|
|
38
|
+
* Async counterpart of `resolveTableSource`. Returns a presigned HTTPS URL
|
|
39
|
+
* for `signed-s3` connections so DuckDB httpfs can fetch without the
|
|
40
|
+
* `Authorization` header preflight.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveTableSourceAsync(tab: Tab): Promise<ResolvedTableSource>;
|
package/dist/query/source.js
CHANGED
|
@@ -9,13 +9,16 @@
|
|
|
9
9
|
* stay free of ad-hoc branching.
|
|
10
10
|
*/
|
|
11
11
|
import { buildDuckDbSource } from '../file-icons/index.js';
|
|
12
|
-
import { buildDuckDbUrl } from '../utils/url.js';
|
|
12
|
+
import { buildDuckDbUrl, buildDuckDbUrlAsync } from '../utils/url.js';
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* True when a source ref points at a self-authenticating HTTPS URL (e.g. a
|
|
15
|
+
* presigned `read_parquet('https://...?X-Amz-Signature=...')`). Used to decide
|
|
16
|
+
* whether DuckDB needs S3 credential config — presigned URLs don't.
|
|
17
17
|
*/
|
|
18
|
-
export function
|
|
18
|
+
export function isHttpsSourceRef(ref) {
|
|
19
|
+
return /(?:^|\(\s*['"])https:\/\//.test(ref);
|
|
20
|
+
}
|
|
21
|
+
function toResolved(tab, fileUrl) {
|
|
19
22
|
if (tab.sourceRef) {
|
|
20
23
|
return {
|
|
21
24
|
ref: tab.sourceRef,
|
|
@@ -25,13 +28,27 @@ export function resolveTableSource(tab) {
|
|
|
25
28
|
label: tab.name
|
|
26
29
|
};
|
|
27
30
|
}
|
|
28
|
-
const fileUrl = buildDuckDbUrl(tab);
|
|
29
|
-
const ref = buildDuckDbSource(tab.path, fileUrl);
|
|
30
31
|
return {
|
|
31
|
-
ref,
|
|
32
|
+
ref: buildDuckDbSource(tab.path, fileUrl ?? ''),
|
|
32
33
|
filePath: tab.path,
|
|
33
34
|
isFileSource: true,
|
|
34
35
|
fileUrl,
|
|
35
36
|
label: tab.name
|
|
36
37
|
};
|
|
37
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve a tab to its QuerySource. Must be called lazily (inside reactive
|
|
41
|
+
* expressions or functions) because `tab.sourceRef` and `tab.path` can change
|
|
42
|
+
* over a tab's lifetime.
|
|
43
|
+
*/
|
|
44
|
+
export function resolveTableSource(tab) {
|
|
45
|
+
return toResolved(tab, tab.sourceRef ? null : buildDuckDbUrl(tab));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Async counterpart of `resolveTableSource`. Returns a presigned HTTPS URL
|
|
49
|
+
* for `signed-s3` connections so DuckDB httpfs can fetch without the
|
|
50
|
+
* `Authorization` header preflight.
|
|
51
|
+
*/
|
|
52
|
+
export async function resolveTableSourceAsync(tab) {
|
|
53
|
+
return toResolved(tab, tab.sourceRef ? null : await buildDuckDbUrlAsync(tab));
|
|
54
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a stac-geoparquet file through the existing DuckDB-WASM engine and
|
|
3
|
+
* materialize a standard STAC FeatureCollection in memory.
|
|
4
|
+
*
|
|
5
|
+
* Reuses:
|
|
6
|
+
* - `getQueryEngine()` + `queryCancellable`/`query` for the single worker
|
|
7
|
+
* - `resolveTableSourceAsync(tab)` for presigned `signed-s3` URL handling
|
|
8
|
+
* - `stacRowToItem` from `utils/stac-geoparquet.js` for the pure transform
|
|
9
|
+
* - `parseWKB` from `utils/wkb.js` for geometry decoding
|
|
10
|
+
*
|
|
11
|
+
* The returned `FeatureCollection` is the same shape `classifyStac()` returns
|
|
12
|
+
* as `{ kind: 'item-collection', fc }`, so downstream viewers
|
|
13
|
+
* (`StacMosaicViewer`, `MultiCogViewer`) consume it unchanged.
|
|
14
|
+
*/
|
|
15
|
+
import type { Tab } from '../types.js';
|
|
16
|
+
import type { StacFeatureCollection } from '../utils/stac.js';
|
|
17
|
+
export interface QueryStacGeoparquetOptions {
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
/** Hard cap on rows. Matches `hydrateStacItems` default. */
|
|
20
|
+
limit?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Query a stac-geoparquet tab and return a STAC FeatureCollection whose
|
|
24
|
+
* features are proper STAC Items (assets absolutized, WKB decoded, bbox
|
|
25
|
+
* flattened).
|
|
26
|
+
*
|
|
27
|
+
* @param tab - the tab pointing at the `.parquet` file
|
|
28
|
+
* @param connId - connection id used for DuckDB's httpfs S3 config; pass
|
|
29
|
+
* an empty string for URL-source tabs (DuckDB will use anonymous httpfs)
|
|
30
|
+
*/
|
|
31
|
+
export declare function queryStacGeoparquetFeatureCollection(tab: Tab, connId: string, opts?: QueryStacGeoparquetOptions): Promise<StacFeatureCollection>;
|