@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.
- 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 +263 -112
- 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 +1 -1
- package/dist/components/viewers/StacMosaicViewer.svelte +1760 -408
- 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 +177 -20
- package/dist/utils/cog.js +361 -76
- 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/cog-pure.d.ts +0 -25
- package/dist/utils/cog-pure.js +0 -35
- 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
package/dist/index.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
// Core types
|
|
2
|
+
// Clipboard
|
|
3
|
+
// Cloud URL resolution
|
|
4
|
+
// Error handling
|
|
5
|
+
// Data export / serialization
|
|
6
|
+
// localStorage helpers
|
|
7
|
+
// Utilities
|
|
8
|
+
export { allChannelsBand0, applyFacets, applyPreset, applyStorageHintsToConnection, availablePresets, buildDataTypeLabel, buildFacets, buildGeoArrowTables, clampBounds, classifyType, compositeFromUrl, compositeToUrl, connectionIdentityKey, copyToClipboard, DATETIME_HISTOGRAM_BINS, describeParseResult, detectStorageExtensionVersion, emptyFacetState, emptyPushdown, emptyStorageHints, escapeCsvField, extractBounds, extractCogAssets, extractEpsgFromGeoMeta, extractGeometryTypes, extractItemView, extractStorageHints, findGeoColumn, findGeoColumnFromRows, flattenStacBbox, formatDate, formatFileSize, formatValue, generateHexDump, getFileExtension, getNativeScheme, handleLoadError, hasActiveFilters, interpolateTemplates, isAbortError, isSameConnectionIdentity, isSingleAssetComposite, isStacGeoparquetSchema, jsonReplacerBigInt, loadFromStorage, looksLikeUrl, markSqlBlocks, normalizeEndpoint, normalizeGeomType, normalizeProvider, PRESETS, parseMarkdownDocument, parseStorageUrl, parseWKB, persistToStorage, pickNaturalColorComposite, pickStacPrimaryAsset, presetMatchesComposite, readParquetMetadata, residualState, resolveCloudUrl, resolveStacAssetHref, SF_LABELS, STAC_GEOPARQUET_REQUIRED_COLUMNS, safeClamp, safeDecodeURIComponent, serializeToCsv, serializeToJson, sniffApiCapabilities, sortFileEntries, sortViews, stacRowToItem, syntheticSelfAsset, toBinary, toCql2Filter, toggleSortField, toNativeQuery, typeBadgeClass, typeColor, typeLabel, wireCodeCopyButtons } from '@walkthru-earth/objex-utils';
|
|
2
9
|
// Constants
|
|
3
10
|
export { COPY_FEEDBACK_MS, DEFAULT_TARGET_CRS, DUCKDB_INIT_TIMEOUT_MS, LAYER_HUE_MULTIPLIER, MAX_QUERY_HISTORY_ENTRIES, SQL_PREVIEW_LENGTH, STORAGE_KEYS, VIEWER_DIR_EXTENSIONS, WGS84_CODES } from './constants.js';
|
|
4
11
|
// File icons registry
|
|
@@ -6,26 +13,3 @@ export { buildDuckDbSource, getDuckDbReadFn, getFileTypeInfo, getMimeType, getVi
|
|
|
6
13
|
export { QueryCancelledError } from './query/engine.js';
|
|
7
14
|
export { buildEndpointFromTemplate, buildProviderBaseUrl, getProvider, isGcsProvider, PROVIDER_IDS, PROVIDERS } from './storage/providers.js';
|
|
8
15
|
export { UrlAdapter } from './storage/url-adapter.js';
|
|
9
|
-
// Clipboard
|
|
10
|
-
export { copyToClipboard, wireCodeCopyButtons } from './utils/clipboard.js';
|
|
11
|
-
// Cloud URL resolution
|
|
12
|
-
export { getNativeScheme, resolveCloudUrl, safeDecodeURIComponent } from './utils/cloud-url.js';
|
|
13
|
-
export { buildDataTypeLabel, clampBounds, SF_LABELS, safeClamp } from './utils/cog.js';
|
|
14
|
-
export { classifyType, typeBadgeClass, typeColor, typeLabel } from './utils/column-types.js';
|
|
15
|
-
export { connectionIdentityKey, isSameConnectionIdentity, normalizeEndpoint, normalizeProvider } from './utils/connection-identity.js';
|
|
16
|
-
// Error handling
|
|
17
|
-
export { handleLoadError } from './utils/error.js';
|
|
18
|
-
// Data export / serialization
|
|
19
|
-
export { escapeCsvField, serializeToCsv, serializeToJson } from './utils/export.js';
|
|
20
|
-
export { sortFileEntries, toggleSortField } from './utils/file-sort.js';
|
|
21
|
-
export { formatDate, formatFileSize, formatValue, getFileExtension, jsonReplacerBigInt } from './utils/format.js';
|
|
22
|
-
export { buildGeoArrowTables, normalizeGeomType } from './utils/geoarrow.js';
|
|
23
|
-
export { generateHexDump } from './utils/hex.js';
|
|
24
|
-
// localStorage helpers
|
|
25
|
-
export { loadFromStorage, persistToStorage } from './utils/local-storage.js';
|
|
26
|
-
export { interpolateTemplates, markSqlBlocks, parseMarkdownDocument } from './utils/markdown-sql.js';
|
|
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';
|
|
29
|
-
export { describeParseResult, looksLikeUrl, parseStorageUrl } from './utils/storage-url.js';
|
|
30
|
-
// Utilities
|
|
31
|
-
export { findGeoColumn, findGeoColumnFromRows, parseWKB, toBinary } from './utils/wkb.js';
|
package/dist/query/engine.d.ts
CHANGED
|
@@ -61,6 +61,16 @@ export interface QueryEngine {
|
|
|
61
61
|
crs: string | null;
|
|
62
62
|
}>;
|
|
63
63
|
queryCancellable?(connId: string, sql: string): QueryHandle;
|
|
64
|
+
/**
|
|
65
|
+
* Streaming variant of `queryCancellable`. Yields one `QueryResult`-shaped
|
|
66
|
+
* chunk per Arrow RecordBatch instead of materializing the full row set
|
|
67
|
+
* before returning. Peak memory tracks one batch (~64 KiB rows) rather
|
|
68
|
+
* than the full result, which avoids DuckDB-WASM OOMs on large parquet
|
|
69
|
+
* scans. The first chunk carries the schema (`columns` / `types`); later
|
|
70
|
+
* chunks repeat them so consumers don't need to track first-batch state.
|
|
71
|
+
* Cancellation aborts the inner DuckDB cursor.
|
|
72
|
+
*/
|
|
73
|
+
queryStream?(connId: string, sql: string, signal?: AbortSignal): AsyncIterable<QueryResult>;
|
|
64
74
|
queryForMapCancellable?(connId: string, sql: string, geomCol: string, geomColType: string, sourceCrs?: string | null): MapQueryHandle;
|
|
65
75
|
forceCancel?(): Promise<void>;
|
|
66
76
|
/** Register a file buffer in DuckDB-WASM's virtual filesystem for ATTACH. */
|
package/dist/query/source.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* stay free of ad-hoc branching.
|
|
10
10
|
*/
|
|
11
11
|
import { buildDuckDbSource } from '../file-icons/index.js';
|
|
12
|
-
import { buildDuckDbUrl, buildDuckDbUrlAsync } from '../utils/url.js';
|
|
12
|
+
import { buildDuckDbUrl, buildDuckDbUrlAsync } from '../utils/signed-url.js';
|
|
13
13
|
/**
|
|
14
14
|
* True when a source ref points at a self-authenticating HTTPS URL (e.g. a
|
|
15
15
|
* presigned `read_parquet('https://...?X-Amz-Signature=...')`). Used to decide
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch a `Tab` + classified STAC payload to the appropriate StacSource
|
|
3
|
+
* implementation. The only module allowed to import both `utils/stac-source-*`
|
|
4
|
+
* (API / static) and `query/stac-source-parquet` (DuckDB-bound) together.
|
|
5
|
+
*
|
|
6
|
+
* Keeping this in `query/` rather than `utils/` is deliberate: it imports the
|
|
7
|
+
* parquet source, which transitively pulls in DuckDB-WASM. `utils/` modules
|
|
8
|
+
* must stay DuckDB-free so the objex-utils sub-package doesn't accidentally
|
|
9
|
+
* pull DuckDB into its bundle (slice 6 promotion).
|
|
10
|
+
*/
|
|
11
|
+
import type { StacRoutableKind, StacSource } from '@walkthru-earth/objex-utils';
|
|
12
|
+
import type { StorageAdapter } from '../storage/adapter.js';
|
|
13
|
+
import type { Tab } from '../types.js';
|
|
14
|
+
export interface CreateStacSourceDeps {
|
|
15
|
+
adapter: StorageAdapter;
|
|
16
|
+
urlToKey?: (absoluteUrl: string) => string | null;
|
|
17
|
+
baseHref: string;
|
|
18
|
+
/**
|
|
19
|
+
* Connection id resolved at the dispatch site. Threaded into every source
|
|
20
|
+
* so the parquet impl does not have to read `tab.connectionId` itself
|
|
21
|
+
* (asymmetric vs. api/static, would silently break for callers passing a
|
|
22
|
+
* tab whose connectionId is missing).
|
|
23
|
+
*/
|
|
24
|
+
connectionId: string;
|
|
25
|
+
/**
|
|
26
|
+
* SDK opt-in: force the parquet source to treat `tab.path` as a
|
|
27
|
+
* hive-partitioned parquet directory and query it with
|
|
28
|
+
* `read_parquet('.../**\/*.parquet', hive_partitioning=true,
|
|
29
|
+
* union_by_name=true)`. Mirrors lazycogs'
|
|
30
|
+
* `DuckdbClient(use_hive_partitioning=True)`. When undefined, the factory
|
|
31
|
+
* also auto-detects directory layouts (see below).
|
|
32
|
+
*/
|
|
33
|
+
useHivePartitioning?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Debug flag. When true the parquet source runs `EXPLAIN <query>` once per
|
|
36
|
+
* `runQuery()` and logs the plan to the console so we can verify partition
|
|
37
|
+
* stats are pruning files. OFF by default — never enable in shipped UI.
|
|
38
|
+
*/
|
|
39
|
+
debugExplain?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* True when the tab looks like a hive-partitioned parquet directory rather
|
|
43
|
+
* than a single parquet file. Conservative: requires either an explicit SDK
|
|
44
|
+
* opt-in (`deps.useHivePartitioning`) OR a `tab.path` ending in `/` AND a
|
|
45
|
+
* `.parquet`/`.geoparquet` extension. We do NOT auto-promote arbitrary
|
|
46
|
+
* extensionless paths to parquet — that would silently mis-dispatch JSON
|
|
47
|
+
* catalogs whose URL happens to have no extension. The factory defers the
|
|
48
|
+
* actual `adapter.list()` probe to the parquet source so the dispatch stays
|
|
49
|
+
* synchronous.
|
|
50
|
+
*/
|
|
51
|
+
export declare function looksLikeHivePartitionedParquet(tab: Tab, deps: CreateStacSourceDeps): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* True when a tab.path looks like a STAC API endpoint (search / items /
|
|
54
|
+
* collections URL pattern). Used to pick the API source for FeatureCollection
|
|
55
|
+
* responses served from API URLs, where the response itself has no
|
|
56
|
+
* `rel="items"` link.
|
|
57
|
+
*/
|
|
58
|
+
export declare function tabLooksLikeStacApi(path: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Pick a `StacSource` for the tab. Synchronous: the orchestrator branches on
|
|
61
|
+
* `source.capabilities` immediately, before any I/O. The factory does NOT
|
|
62
|
+
* read the tab payload; it relies on the caller's `classified` argument
|
|
63
|
+
* (from `classifyStac`) plus extension and URL heuristics.
|
|
64
|
+
*/
|
|
65
|
+
export declare function createStacSourceForTab(tab: Tab, classified: StacRoutableKind, deps: CreateStacSourceDeps): StacSource;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch a `Tab` + classified STAC payload to the appropriate StacSource
|
|
3
|
+
* implementation. The only module allowed to import both `utils/stac-source-*`
|
|
4
|
+
* (API / static) and `query/stac-source-parquet` (DuckDB-bound) together.
|
|
5
|
+
*
|
|
6
|
+
* Keeping this in `query/` rather than `utils/` is deliberate: it imports the
|
|
7
|
+
* parquet source, which transitively pulls in DuckDB-WASM. `utils/` modules
|
|
8
|
+
* must stay DuckDB-free so the objex-utils sub-package doesn't accidentally
|
|
9
|
+
* pull DuckDB into its bundle (slice 6 promotion).
|
|
10
|
+
*/
|
|
11
|
+
import { createApiSource, createStaticSource, hasStacItemsEndpoint, STAC_API_PATH_RE } from '@walkthru-earth/objex-utils';
|
|
12
|
+
import { createParquetSource } from './stac-source-parquet.js';
|
|
13
|
+
/**
|
|
14
|
+
* True when the tab looks like a hive-partitioned parquet directory rather
|
|
15
|
+
* than a single parquet file. Conservative: requires either an explicit SDK
|
|
16
|
+
* opt-in (`deps.useHivePartitioning`) OR a `tab.path` ending in `/` AND a
|
|
17
|
+
* `.parquet`/`.geoparquet` extension. We do NOT auto-promote arbitrary
|
|
18
|
+
* extensionless paths to parquet — that would silently mis-dispatch JSON
|
|
19
|
+
* catalogs whose URL happens to have no extension. The factory defers the
|
|
20
|
+
* actual `adapter.list()` probe to the parquet source so the dispatch stays
|
|
21
|
+
* synchronous.
|
|
22
|
+
*/
|
|
23
|
+
export function looksLikeHivePartitionedParquet(tab, deps) {
|
|
24
|
+
if (deps.useHivePartitioning === true)
|
|
25
|
+
return true;
|
|
26
|
+
const path = tab.path ?? '';
|
|
27
|
+
const ext = (tab.extension ?? '').toLowerCase();
|
|
28
|
+
if (path.endsWith('/') && (ext === 'parquet' || ext === 'geoparquet'))
|
|
29
|
+
return true;
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* True when a tab.path looks like a STAC API endpoint (search / items /
|
|
34
|
+
* collections URL pattern). Used to pick the API source for FeatureCollection
|
|
35
|
+
* responses served from API URLs, where the response itself has no
|
|
36
|
+
* `rel="items"` link.
|
|
37
|
+
*/
|
|
38
|
+
export function tabLooksLikeStacApi(path) {
|
|
39
|
+
try {
|
|
40
|
+
return STAC_API_PATH_RE.test(new URL(path).pathname);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Pick a `StacSource` for the tab. Synchronous: the orchestrator branches on
|
|
48
|
+
* `source.capabilities` immediately, before any I/O. The factory does NOT
|
|
49
|
+
* read the tab payload; it relies on the caller's `classified` argument
|
|
50
|
+
* (from `classifyStac`) plus extension and URL heuristics.
|
|
51
|
+
*/
|
|
52
|
+
export function createStacSourceForTab(tab, classified, deps) {
|
|
53
|
+
const ext = (tab.extension ?? '').toLowerCase();
|
|
54
|
+
const hive = looksLikeHivePartitionedParquet(tab, deps);
|
|
55
|
+
if (ext === 'parquet' || ext === 'geoparquet' || hive) {
|
|
56
|
+
return createParquetSource(tab, deps.connectionId, {
|
|
57
|
+
adapter: deps.adapter,
|
|
58
|
+
useHivePartitioning: hive,
|
|
59
|
+
debugExplain: deps.debugExplain
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (classified.kind === 'collection' || classified.kind === 'catalog') {
|
|
63
|
+
if (hasStacItemsEndpoint(classified.payload)) {
|
|
64
|
+
return createApiSource(classified, deps);
|
|
65
|
+
}
|
|
66
|
+
return createStaticSource(classified, deps);
|
|
67
|
+
}
|
|
68
|
+
if (classified.kind === 'item-collection') {
|
|
69
|
+
if (tabLooksLikeStacApi(tab.path))
|
|
70
|
+
return createApiSource(classified, deps);
|
|
71
|
+
return createStaticSource(classified, deps);
|
|
72
|
+
}
|
|
73
|
+
if (classified.kind === 'item') {
|
|
74
|
+
return createStaticSource(classified, deps);
|
|
75
|
+
}
|
|
76
|
+
throw new Error(`Unsupported STAC kind: ${classified.kind}`);
|
|
77
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stac-geoparquet implementation of the StacSource contract.
|
|
3
|
+
*
|
|
4
|
+
* Reuses:
|
|
5
|
+
* - `getQueryEngine()` + `queryCancellable`/`query` for the single worker
|
|
6
|
+
* - `resolveTableSourceAsync(tab)` for presigned `signed-s3` URL handling
|
|
7
|
+
* - `stacRowToItem` from `@walkthru-earth/objex-utils` for the pure transform
|
|
8
|
+
* - `parseWKB` from `@walkthru-earth/objex-utils` for geometry decoding
|
|
9
|
+
*
|
|
10
|
+
* Push-down: `bbox` (`ST_Intersects` + `ST_MakeEnvelope`) and `datetime`
|
|
11
|
+
* (`datetime BETWEEN TIMESTAMPTZ ...`). Without the datetime push-down,
|
|
12
|
+
* `LIMIT + ORDER BY datetime DESC` silently drops older rows before the
|
|
13
|
+
* client-side filter ever runs, so any window outside the freshest N items
|
|
14
|
+
* returned zero matches. Cloud cover / GSD / platform / etc. still ride
|
|
15
|
+
* along on the residual until slice 3 plumbs them through DuckDB SQL.
|
|
16
|
+
*
|
|
17
|
+
* Hive partitioning: when the factory (or an SDK caller) sets
|
|
18
|
+
* `useHivePartitioning: true`, the FROM target switches to
|
|
19
|
+
* `read_parquet('.../**\/*.parquet', hive_partitioning=true,
|
|
20
|
+
* union_by_name=true)`. Mirrors lazycogs'
|
|
21
|
+
* `DuckdbClient(use_hive_partitioning=True)`. Partition columns appear as
|
|
22
|
+
* virtual columns on the schema, but `buildSelectList` only projects known
|
|
23
|
+
* STAC columns so they never leak into the rendered Items. `union_by_name`
|
|
24
|
+
* is required because partitioned writes can drift schemas across
|
|
25
|
+
* partitions (extra `proj:*` columns added later, etc.).
|
|
26
|
+
*
|
|
27
|
+
* Yields a single batch with `done: true`. Slice 3 turns this into a real
|
|
28
|
+
* stream via `conn.send()` so large catalogs can render progressively.
|
|
29
|
+
*/
|
|
30
|
+
import { type StacSource } from '@walkthru-earth/objex-utils';
|
|
31
|
+
import type { StorageAdapter } from '../storage/adapter.js';
|
|
32
|
+
import type { Tab } from '../types.js';
|
|
33
|
+
/**
|
|
34
|
+
* Options for `createParquetSource`. The factory threads adapter +
|
|
35
|
+
* `useHivePartitioning` + `debugExplain` from `CreateStacSourceDeps`, but
|
|
36
|
+
* library consumers can construct a `StacSource` directly.
|
|
37
|
+
*/
|
|
38
|
+
export interface CreateParquetSourceOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Storage adapter for the connection backing `tab`. Used solely to probe
|
|
41
|
+
* `tab.path` for `.parquet` children when `useHivePartitioning` is set —
|
|
42
|
+
* never consulted on the per-row read path (DuckDB httpfs handles I/O
|
|
43
|
+
* directly via the presigned / signed URL).
|
|
44
|
+
*/
|
|
45
|
+
adapter?: StorageAdapter;
|
|
46
|
+
/**
|
|
47
|
+
* When true, treat `tab.path` as a hive-partitioned parquet directory
|
|
48
|
+
* (e.g. `s3://bucket/year=2023/month=01/...`) and build SQL with
|
|
49
|
+
* `read_parquet('.../**\/*.parquet', hive_partitioning=true,
|
|
50
|
+
* union_by_name=true)` so DuckDB prunes partitions per `bbox` /
|
|
51
|
+
* `datetime` predicate. Mirrors lazycogs'
|
|
52
|
+
* `DuckdbClient(use_hive_partitioning=True)`.
|
|
53
|
+
*/
|
|
54
|
+
useHivePartitioning?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* When true, run `EXPLAIN <query>` once per `runQuery()` and log the plan
|
|
57
|
+
* to the console. Used to verify partition pruning hits parquet stats.
|
|
58
|
+
* Off by default — never enable in shipped UI.
|
|
59
|
+
*/
|
|
60
|
+
debugExplain?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Mobile / low-memory mode. When true the source caps the effective
|
|
63
|
+
* LIMIT (regardless of the caller's request) and skips
|
|
64
|
+
* `ORDER BY datetime DESC` so DuckDB can stop reading after the first
|
|
65
|
+
* N parquet rows instead of fully materializing every row's STRUCT
|
|
66
|
+
* `assets` column to compute a Top-N. The trade-off: items are
|
|
67
|
+
* returned in file order, not freshness order. Defaults to a
|
|
68
|
+
* mobile-UA detection at module load when undefined.
|
|
69
|
+
*/
|
|
70
|
+
lowMemoryMode?: boolean;
|
|
71
|
+
/** Hard cap on `req.limit` when `lowMemoryMode` is on. Default 200. */
|
|
72
|
+
lowMemoryLimit?: number;
|
|
73
|
+
}
|
|
74
|
+
export interface QueryStacGeoparquetOptions {
|
|
75
|
+
signal?: AbortSignal;
|
|
76
|
+
/** Hard cap on rows. Matches `hydrateStacItems` default. */
|
|
77
|
+
limit?: number;
|
|
78
|
+
/**
|
|
79
|
+
* When true, omit `ORDER BY datetime DESC` from the SQL. Sorting forces
|
|
80
|
+
* DuckDB's Top-N operator to read every row's heavy STRUCT `assets`
|
|
81
|
+
* column before the LIMIT engages, which OOMs the WASM heap on mobile
|
|
82
|
+
* Safari (~1.8 GiB cap, no OPFS spill). Without the sort the parquet
|
|
83
|
+
* scan can stop after the first N rows in file order. The trade-off:
|
|
84
|
+
* the user sees the first N items by file order, not the freshest N.
|
|
85
|
+
*/
|
|
86
|
+
skipOrderBy?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Hive-partitioning context resolved at source construction. When
|
|
89
|
+
* `enabled === true` the runtime FROM target is a `read_parquet` glob
|
|
90
|
+
* over `tab.path` and `union_by_name=true` is applied. The probe lives
|
|
91
|
+
* on the source (`createParquetSource` awaits it once before the first
|
|
92
|
+
* runQuery), not in `runQuery` itself, so the per-pan path stays cheap.
|
|
93
|
+
*/
|
|
94
|
+
hive?: {
|
|
95
|
+
enabled: boolean;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Debug flag, propagated from source options. See
|
|
99
|
+
* `CreateParquetSourceOptions.debugExplain`.
|
|
100
|
+
*/
|
|
101
|
+
debugExplain?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Optional WGS84 viewport bbox `[west, south, east, north]`. When set the
|
|
104
|
+
* query is filtered with `ST_Intersects(geometry, ST_MakeEnvelope(...))` so
|
|
105
|
+
* the parquet path mirrors the API's viewport-scoped behavior. Skipped when
|
|
106
|
+
* the geometry column is missing (already guarded above).
|
|
107
|
+
*/
|
|
108
|
+
bbox?: [number, number, number, number];
|
|
109
|
+
/**
|
|
110
|
+
* Optional ISO 8601 datetime range. When set and the parquet has a
|
|
111
|
+
* `datetime` column, the predicate is pushed into the WHERE clause as
|
|
112
|
+
* `datetime BETWEEN TIMESTAMPTZ ... AND TIMESTAMPTZ ...`. Without this
|
|
113
|
+
* push-down the LIMIT + `ORDER BY datetime DESC` would drop older rows
|
|
114
|
+
* before the client-side filter ever ran, so picking an older window
|
|
115
|
+
* returned zero items.
|
|
116
|
+
*/
|
|
117
|
+
datetime?: {
|
|
118
|
+
min?: string;
|
|
119
|
+
max?: string;
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* stac-geoparquet `StacSource`. Slice 1: bbox is the only push-down,
|
|
124
|
+
* single yield with `done: true`. Slice 3 widens push-down (cloud cover /
|
|
125
|
+
* gsd / platform via DuckDB SQL) and turns this into a streaming
|
|
126
|
+
* `conn.send()` cursor.
|
|
127
|
+
*
|
|
128
|
+
* `options.useHivePartitioning` switches the FROM target to a recursive
|
|
129
|
+
* `read_parquet` glob over `tab.path` so DuckDB prunes partitions per
|
|
130
|
+
* `bbox` / `datetime` predicate. The first `query()` call awaits a
|
|
131
|
+
* best-effort `adapter.list()` probe to confirm at least one `.parquet`
|
|
132
|
+
* child exists; if listing fails (e.g. UrlAdapter, AccessDenied) we still
|
|
133
|
+
* attempt the hive query and let DuckDB surface the real error.
|
|
134
|
+
*/
|
|
135
|
+
export declare function createParquetSource(tab: Tab, connectionId: string, options?: CreateParquetSourceOptions): StacSource;
|