@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/utils/notebook.js
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lightweight browser-native Jupyter notebook renderer.
|
|
3
|
-
* Replaces `notebookjs` which depends on jsdom/Buffer (Node.js only).
|
|
4
|
-
* Handles nbformat 2, 3, 4, and 5.
|
|
5
|
-
*/
|
|
6
|
-
const PREFIX = 'nb-';
|
|
7
|
-
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
8
|
-
function el(tag, classNames = []) {
|
|
9
|
-
const e = document.createElement(tag);
|
|
10
|
-
if (classNames.length)
|
|
11
|
-
e.className = classNames.map((c) => PREFIX + c).join(' ');
|
|
12
|
-
return e;
|
|
13
|
-
}
|
|
14
|
-
function escapeHTML(raw) {
|
|
15
|
-
return raw.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
16
|
-
}
|
|
17
|
-
function joinText(text) {
|
|
18
|
-
if (Array.isArray(text))
|
|
19
|
-
return text.join('');
|
|
20
|
-
return text ?? '';
|
|
21
|
-
}
|
|
22
|
-
// ── Display renderers ────────────────────────────────────────────────────────
|
|
23
|
-
const IMAGE_FORMATS = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'];
|
|
24
|
-
function renderImage(format, data) {
|
|
25
|
-
const img = el('img', ['image-output']);
|
|
26
|
-
img.src = `data:${format};base64,${joinText(data).replace(/\n/g, '')}`;
|
|
27
|
-
return img;
|
|
28
|
-
}
|
|
29
|
-
function renderDisplayData(output, config) {
|
|
30
|
-
// Resolve data from v4 `data` field or v3 flat fields
|
|
31
|
-
const getData = (mime) => output.data?.[mime] ?? output[mime];
|
|
32
|
-
// Priority order (richest first)
|
|
33
|
-
for (const fmt of IMAGE_FORMATS) {
|
|
34
|
-
const d = getData(fmt);
|
|
35
|
-
if (d)
|
|
36
|
-
return renderImage(fmt, d);
|
|
37
|
-
}
|
|
38
|
-
const svg = getData('image/svg+xml') ?? getData('text/svg+xml');
|
|
39
|
-
if (svg) {
|
|
40
|
-
const wrapper = el('div', ['svg-output']);
|
|
41
|
-
wrapper.innerHTML = joinText(svg);
|
|
42
|
-
return wrapper;
|
|
43
|
-
}
|
|
44
|
-
const html = getData('text/html');
|
|
45
|
-
if (html) {
|
|
46
|
-
const wrapper = el('div', ['html-output']);
|
|
47
|
-
wrapper.innerHTML = joinText(html);
|
|
48
|
-
return wrapper;
|
|
49
|
-
}
|
|
50
|
-
const md = getData('text/markdown');
|
|
51
|
-
if (md) {
|
|
52
|
-
const wrapper = el('div', ['html-output']);
|
|
53
|
-
wrapper.innerHTML = config.markdown(joinText(md));
|
|
54
|
-
return wrapper;
|
|
55
|
-
}
|
|
56
|
-
const latex = getData('text/latex');
|
|
57
|
-
if (latex) {
|
|
58
|
-
const wrapper = el('div', ['latex-output']);
|
|
59
|
-
wrapper.textContent = joinText(latex);
|
|
60
|
-
return wrapper;
|
|
61
|
-
}
|
|
62
|
-
const plain = getData('text/plain');
|
|
63
|
-
if (plain) {
|
|
64
|
-
const pre = el('pre', ['text-output']);
|
|
65
|
-
pre.innerHTML = config.ansi(escapeHTML(joinText(plain)));
|
|
66
|
-
return pre;
|
|
67
|
-
}
|
|
68
|
-
return el('div', ['empty-output']);
|
|
69
|
-
}
|
|
70
|
-
function renderStream(output, config) {
|
|
71
|
-
const streamName = output.stream ?? output.name ?? 'stdout';
|
|
72
|
-
const pre = el('pre', [streamName]);
|
|
73
|
-
pre.innerHTML = config.ansi(escapeHTML(joinText(output.text ?? '')));
|
|
74
|
-
return pre;
|
|
75
|
-
}
|
|
76
|
-
function renderError(output, config) {
|
|
77
|
-
const pre = el('pre', ['pyerr']);
|
|
78
|
-
const raw = (output.traceback ?? []).join('\n');
|
|
79
|
-
pre.innerHTML = config.ansi(escapeHTML(raw));
|
|
80
|
-
return pre;
|
|
81
|
-
}
|
|
82
|
-
// ── Output coalescing (merge consecutive same-name streams) ──────────────────
|
|
83
|
-
function coalesceStreams(outputs) {
|
|
84
|
-
if (!outputs.length)
|
|
85
|
-
return outputs;
|
|
86
|
-
const result = [{ ...outputs[0] }];
|
|
87
|
-
for (let i = 1; i < outputs.length; i++) {
|
|
88
|
-
const o = outputs[i];
|
|
89
|
-
const last = result[result.length - 1];
|
|
90
|
-
if (o.output_type === 'stream' &&
|
|
91
|
-
last.output_type === 'stream' &&
|
|
92
|
-
(o.stream ?? o.name) === (last.stream ?? last.name)) {
|
|
93
|
-
// Merge text
|
|
94
|
-
const lastText = Array.isArray(last.text) ? last.text : [last.text ?? ''];
|
|
95
|
-
const oText = Array.isArray(o.text) ? o.text : [o.text ?? ''];
|
|
96
|
-
last.text = lastText.concat(oText);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
result.push({ ...o });
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
104
|
-
// ── Cell renderers ───────────────────────────────────────────────────────────
|
|
105
|
-
function renderCodeCell(cell, lang, config) {
|
|
106
|
-
const cellEl = el('div', ['cell', 'code-cell']);
|
|
107
|
-
// Input
|
|
108
|
-
const source = joinText(cell.source ?? cell.input ?? '');
|
|
109
|
-
if (source) {
|
|
110
|
-
const holder = el('div', ['input']);
|
|
111
|
-
const num = cell.prompt_number ?? cell.execution_count;
|
|
112
|
-
if (typeof num === 'number' && num > -1) {
|
|
113
|
-
holder.setAttribute('data-prompt-number', String(num));
|
|
114
|
-
}
|
|
115
|
-
const pre = el('pre');
|
|
116
|
-
const code = document.createElement('code');
|
|
117
|
-
code.setAttribute('data-language', lang);
|
|
118
|
-
code.className = `lang-${lang}`;
|
|
119
|
-
code.innerHTML = config.highlighter(escapeHTML(source), lang);
|
|
120
|
-
pre.appendChild(code);
|
|
121
|
-
holder.appendChild(pre);
|
|
122
|
-
cellEl.appendChild(holder);
|
|
123
|
-
}
|
|
124
|
-
// Outputs
|
|
125
|
-
const rawOutputs = coalesceStreams(cell.outputs ?? []);
|
|
126
|
-
for (const output of rawOutputs) {
|
|
127
|
-
const outer = el('div', ['output']);
|
|
128
|
-
let inner;
|
|
129
|
-
switch (output.output_type) {
|
|
130
|
-
case 'display_data':
|
|
131
|
-
case 'execute_result':
|
|
132
|
-
case 'pyout':
|
|
133
|
-
inner = renderDisplayData(output, config);
|
|
134
|
-
break;
|
|
135
|
-
case 'stream':
|
|
136
|
-
inner = renderStream(output, config);
|
|
137
|
-
break;
|
|
138
|
-
case 'error':
|
|
139
|
-
case 'pyerr':
|
|
140
|
-
inner = renderError(output, config);
|
|
141
|
-
break;
|
|
142
|
-
default:
|
|
143
|
-
inner = el('div', ['empty-output']);
|
|
144
|
-
}
|
|
145
|
-
outer.appendChild(inner);
|
|
146
|
-
cellEl.appendChild(outer);
|
|
147
|
-
}
|
|
148
|
-
return cellEl;
|
|
149
|
-
}
|
|
150
|
-
function renderMarkdownCell(cell, config) {
|
|
151
|
-
const cellEl = el('div', ['cell', 'markdown-cell']);
|
|
152
|
-
const source = joinText(cell.source ?? cell.input ?? '');
|
|
153
|
-
cellEl.innerHTML = config.markdown(source);
|
|
154
|
-
return cellEl;
|
|
155
|
-
}
|
|
156
|
-
function renderHeadingCell(cell) {
|
|
157
|
-
const level = Math.min(Math.max(cell.level ?? 1, 1), 6);
|
|
158
|
-
const heading = el(`h${level}`, ['cell', 'heading-cell']);
|
|
159
|
-
heading.innerHTML = escapeHTML(joinText(cell.source ?? cell.input ?? ''));
|
|
160
|
-
return heading;
|
|
161
|
-
}
|
|
162
|
-
function renderRawCell(cell) {
|
|
163
|
-
const cellEl = el('div', ['cell', 'raw-cell']);
|
|
164
|
-
cellEl.innerHTML = escapeHTML(joinText(cell.source ?? cell.input ?? ''));
|
|
165
|
-
return cellEl;
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Parse and render a Jupyter notebook JSON to a DOM element.
|
|
169
|
-
* Returns the rendered element and metadata.
|
|
170
|
-
*/
|
|
171
|
-
export function renderNotebook(raw, config) {
|
|
172
|
-
const meta = raw.metadata ?? {};
|
|
173
|
-
const lang = meta.kernelspec?.language ?? meta.language_info?.name ?? meta.language ?? 'python';
|
|
174
|
-
const kernelName = meta.kernelspec?.display_name ?? meta.language_info?.name ?? '';
|
|
175
|
-
// Normalize cells: v4+ has top-level `cells`, v2/v3 uses `worksheets`
|
|
176
|
-
let allCells = [];
|
|
177
|
-
if (Array.isArray(raw.cells)) {
|
|
178
|
-
allCells = raw.cells;
|
|
179
|
-
}
|
|
180
|
-
else if (Array.isArray(raw.worksheets)) {
|
|
181
|
-
for (const ws of raw.worksheets) {
|
|
182
|
-
if (Array.isArray(ws.cells))
|
|
183
|
-
allCells.push(...ws.cells);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
const notebook = el('div', ['notebook']);
|
|
187
|
-
for (const cell of allCells) {
|
|
188
|
-
let rendered;
|
|
189
|
-
switch (cell.cell_type) {
|
|
190
|
-
case 'code':
|
|
191
|
-
rendered = renderCodeCell(cell, cell.language ?? lang, config);
|
|
192
|
-
break;
|
|
193
|
-
case 'markdown':
|
|
194
|
-
rendered = renderMarkdownCell(cell, config);
|
|
195
|
-
break;
|
|
196
|
-
case 'heading':
|
|
197
|
-
rendered = renderHeadingCell(cell);
|
|
198
|
-
break;
|
|
199
|
-
case 'raw':
|
|
200
|
-
rendered = renderRawCell(cell);
|
|
201
|
-
break;
|
|
202
|
-
default:
|
|
203
|
-
rendered = renderRawCell(cell);
|
|
204
|
-
}
|
|
205
|
-
notebook.appendChild(rendered);
|
|
206
|
-
}
|
|
207
|
-
return {
|
|
208
|
-
element: notebook,
|
|
209
|
-
meta: { kernelName, language: lang, cellCount: allCells.length }
|
|
210
|
-
};
|
|
211
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lightweight Parquet metadata reader using hyparquet.
|
|
3
|
-
*
|
|
4
|
-
* Reads schema, row count, CRS, and geometry types from the Parquet footer
|
|
5
|
-
* via a single HTTP range request (~512KB). No DuckDB boot needed.
|
|
6
|
-
*
|
|
7
|
-
* This provides instant metadata display before DuckDB-WASM finishes loading.
|
|
8
|
-
*/
|
|
9
|
-
import type { GeoArrowGeomType } from './geoarrow.js';
|
|
10
|
-
export interface GeoColumnMeta {
|
|
11
|
-
encoding: string;
|
|
12
|
-
geometryTypes: string[];
|
|
13
|
-
crs: any | null;
|
|
14
|
-
bbox?: number[];
|
|
15
|
-
}
|
|
16
|
-
export interface GeoParquetMeta {
|
|
17
|
-
primaryColumn: string;
|
|
18
|
-
columns: Record<string, GeoColumnMeta>;
|
|
19
|
-
}
|
|
20
|
-
export interface ParquetFileMetadata {
|
|
21
|
-
/** Total number of rows across all row groups. */
|
|
22
|
-
rowCount: number;
|
|
23
|
-
/** Column schema (name + type). Leaf columns only (structs flattened to children). */
|
|
24
|
-
schema: {
|
|
25
|
-
name: string;
|
|
26
|
-
type: string;
|
|
27
|
-
}[];
|
|
28
|
-
/**
|
|
29
|
-
* Top-level column names as written. Includes struct/group parents (e.g.
|
|
30
|
-
* `assets`, `bbox`) that `schema` flattens away. Required for stac-geoparquet
|
|
31
|
-
* detection, which looks up struct columns by their parent name.
|
|
32
|
-
*/
|
|
33
|
-
topLevelColumns: string[];
|
|
34
|
-
/** GeoParquet "geo" metadata if present. */
|
|
35
|
-
geo: GeoParquetMeta | null;
|
|
36
|
-
/** True when file has legacy GeoParquet metadata (schema_version 0.x without "version" field). */
|
|
37
|
-
legacyGeoParquet: boolean;
|
|
38
|
-
/** Tool that created the file (e.g. "pyarrow 15.0.0"). */
|
|
39
|
-
createdBy: string | null;
|
|
40
|
-
/** Number of row groups. */
|
|
41
|
-
numRowGroups: number;
|
|
42
|
-
/** Primary compression codec (e.g. "SNAPPY", "ZSTD"). */
|
|
43
|
-
compression: string | null;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Read Parquet footer metadata from a remote URL using hyparquet.
|
|
47
|
-
* Uses HTTP range requests — typically a single ~512KB fetch.
|
|
48
|
-
*/
|
|
49
|
-
export declare function readParquetMetadata(url: string): Promise<ParquetFileMetadata>;
|
|
50
|
-
/**
|
|
51
|
-
* Extract EPSG code from GeoParquet CRS metadata.
|
|
52
|
-
* Returns null for WGS84/CRS84 (no reprojection needed).
|
|
53
|
-
*/
|
|
54
|
-
export declare function extractEpsgFromGeoMeta(geo: GeoParquetMeta): string | null;
|
|
55
|
-
/**
|
|
56
|
-
* Extract normalized geometry types from GeoParquet metadata.
|
|
57
|
-
* Returns the set of geometry types for the primary column.
|
|
58
|
-
*/
|
|
59
|
-
export declare function extractGeometryTypes(geo: GeoParquetMeta): GeoArrowGeomType[];
|
|
60
|
-
/**
|
|
61
|
-
* Extract bounds from GeoParquet metadata (bbox field).
|
|
62
|
-
* Returns [minX, minY, maxX, maxY] or null.
|
|
63
|
-
*/
|
|
64
|
-
export declare function extractBounds(geo: GeoParquetMeta): [number, number, number, number] | null;
|
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lightweight Parquet metadata reader using hyparquet.
|
|
3
|
-
*
|
|
4
|
-
* Reads schema, row count, CRS, and geometry types from the Parquet footer
|
|
5
|
-
* via a single HTTP range request (~512KB). No DuckDB boot needed.
|
|
6
|
-
*
|
|
7
|
-
* This provides instant metadata display before DuckDB-WASM finishes loading.
|
|
8
|
-
*/
|
|
9
|
-
import { WGS84_CODES } from '../constants.js';
|
|
10
|
-
/** Map hyparquet schema element types to DuckDB-like type strings. */
|
|
11
|
-
function mapParquetType(col) {
|
|
12
|
-
const lt = col.logical_type;
|
|
13
|
-
if (lt) {
|
|
14
|
-
// Parquet Format 2.11+ native geometry/geography types
|
|
15
|
-
if (lt.type === 'GEOMETRY' || lt.type === 'GEOGRAPHY')
|
|
16
|
-
return 'GEOMETRY';
|
|
17
|
-
// String types
|
|
18
|
-
if (lt.type === 'STRING' || lt.type === 'UTF8')
|
|
19
|
-
return 'VARCHAR';
|
|
20
|
-
if (lt.type === 'JSON')
|
|
21
|
-
return 'JSON';
|
|
22
|
-
if (lt.type === 'UUID')
|
|
23
|
-
return 'UUID';
|
|
24
|
-
if (lt.type === 'ENUM')
|
|
25
|
-
return 'VARCHAR';
|
|
26
|
-
// Integer types
|
|
27
|
-
if (lt.type === 'INT' || lt.type === 'INTEGER') {
|
|
28
|
-
const bits = lt.bitWidth ?? 32;
|
|
29
|
-
const signed = lt.isSigned !== false;
|
|
30
|
-
if (bits <= 8)
|
|
31
|
-
return signed ? 'TINYINT' : 'UTINYINT';
|
|
32
|
-
if (bits <= 16)
|
|
33
|
-
return signed ? 'SMALLINT' : 'USMALLINT';
|
|
34
|
-
if (bits <= 32)
|
|
35
|
-
return signed ? 'INTEGER' : 'UINTEGER';
|
|
36
|
-
return signed ? 'BIGINT' : 'UBIGINT';
|
|
37
|
-
}
|
|
38
|
-
// Decimal
|
|
39
|
-
if (lt.type === 'DECIMAL')
|
|
40
|
-
return `DECIMAL(${lt.precision ?? 18},${lt.scale ?? 0})`;
|
|
41
|
-
// Date/Time
|
|
42
|
-
if (lt.type === 'DATE')
|
|
43
|
-
return 'DATE';
|
|
44
|
-
if (lt.type === 'TIME')
|
|
45
|
-
return 'TIME';
|
|
46
|
-
if (lt.type === 'TIMESTAMP')
|
|
47
|
-
return 'TIMESTAMP';
|
|
48
|
-
// Binary
|
|
49
|
-
if (lt.type === 'BSON')
|
|
50
|
-
return 'BLOB';
|
|
51
|
-
}
|
|
52
|
-
// Fallback to physical type
|
|
53
|
-
const ct = col.converted_type;
|
|
54
|
-
if (ct === 'UTF8')
|
|
55
|
-
return 'VARCHAR';
|
|
56
|
-
if (ct === 'JSON')
|
|
57
|
-
return 'JSON';
|
|
58
|
-
if (ct === 'DATE')
|
|
59
|
-
return 'DATE';
|
|
60
|
-
if (ct === 'TIMESTAMP_MILLIS' || ct === 'TIMESTAMP_MICROS')
|
|
61
|
-
return 'TIMESTAMP';
|
|
62
|
-
if (ct === 'DECIMAL')
|
|
63
|
-
return `DECIMAL(${col.precision ?? 18},${col.scale ?? 0})`;
|
|
64
|
-
if (ct === 'INT_8')
|
|
65
|
-
return 'TINYINT';
|
|
66
|
-
if (ct === 'INT_16')
|
|
67
|
-
return 'SMALLINT';
|
|
68
|
-
if (ct === 'INT_32')
|
|
69
|
-
return 'INTEGER';
|
|
70
|
-
if (ct === 'INT_64')
|
|
71
|
-
return 'BIGINT';
|
|
72
|
-
if (ct === 'UINT_8')
|
|
73
|
-
return 'UTINYINT';
|
|
74
|
-
if (ct === 'UINT_16')
|
|
75
|
-
return 'USMALLINT';
|
|
76
|
-
if (ct === 'UINT_32')
|
|
77
|
-
return 'UINTEGER';
|
|
78
|
-
if (ct === 'UINT_64')
|
|
79
|
-
return 'UBIGINT';
|
|
80
|
-
const pt = col.type;
|
|
81
|
-
if (pt === 'BOOLEAN')
|
|
82
|
-
return 'BOOLEAN';
|
|
83
|
-
if (pt === 'INT32')
|
|
84
|
-
return 'INTEGER';
|
|
85
|
-
if (pt === 'INT64')
|
|
86
|
-
return 'BIGINT';
|
|
87
|
-
if (pt === 'INT96')
|
|
88
|
-
return 'TIMESTAMP';
|
|
89
|
-
if (pt === 'FLOAT')
|
|
90
|
-
return 'FLOAT';
|
|
91
|
-
if (pt === 'DOUBLE')
|
|
92
|
-
return 'DOUBLE';
|
|
93
|
-
if (pt === 'BYTE_ARRAY')
|
|
94
|
-
return 'BLOB';
|
|
95
|
-
if (pt === 'FIXED_LEN_BYTE_ARRAY')
|
|
96
|
-
return 'BLOB';
|
|
97
|
-
return 'VARCHAR';
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Read Parquet footer metadata from a remote URL using hyparquet.
|
|
101
|
-
* Uses HTTP range requests — typically a single ~512KB fetch.
|
|
102
|
-
*/
|
|
103
|
-
export async function readParquetMetadata(url) {
|
|
104
|
-
const { parquetMetadataAsync, asyncBufferFromUrl } = await import('hyparquet');
|
|
105
|
-
const file = await asyncBufferFromUrl({ url });
|
|
106
|
-
const metadata = await parquetMetadataAsync(file);
|
|
107
|
-
// Row count from row group metadata (no data scan needed)
|
|
108
|
-
const rowCount = metadata.row_groups.reduce((sum, rg) => sum + Number(rg.num_rows), 0);
|
|
109
|
-
// Schema from metadata (skip root element at index 0)
|
|
110
|
-
const schema = metadata.schema
|
|
111
|
-
.slice(1)
|
|
112
|
-
.filter((col) => col.num_children === undefined) // skip group elements
|
|
113
|
-
.map((col) => ({
|
|
114
|
-
name: col.name,
|
|
115
|
-
type: mapParquetType(col)
|
|
116
|
-
}));
|
|
117
|
-
// Walk depth-first to collect top-level column names (including struct parents).
|
|
118
|
-
// hyparquet serializes the schema as a flat depth-first array where the root
|
|
119
|
-
// carries `num_children` and each group element claims the next N siblings.
|
|
120
|
-
const topLevelColumns = [];
|
|
121
|
-
const root = metadata.schema[0];
|
|
122
|
-
const rootChildren = root?.num_children ?? 0;
|
|
123
|
-
let cursor = 1;
|
|
124
|
-
for (let i = 0; i < rootChildren && cursor < metadata.schema.length; i++) {
|
|
125
|
-
const el = metadata.schema[cursor];
|
|
126
|
-
if (el?.name)
|
|
127
|
-
topLevelColumns.push(el.name);
|
|
128
|
-
cursor += countSubtree(metadata.schema, cursor);
|
|
129
|
-
}
|
|
130
|
-
// GeoParquet "geo" key-value metadata
|
|
131
|
-
let geo = null;
|
|
132
|
-
let legacyGeoParquet = false;
|
|
133
|
-
const geoKv = metadata.key_value_metadata?.find((kv) => kv.key === 'geo');
|
|
134
|
-
if (geoKv) {
|
|
135
|
-
try {
|
|
136
|
-
const geoJson = JSON.parse(geoKv.value ?? '');
|
|
137
|
-
// Detect legacy GeoParquet (schema_version 0.x without "version" field).
|
|
138
|
-
// Early geopandas (<0.12) wrote schema_version instead of version.
|
|
139
|
-
// DuckDB's spatial extension rejects these files with:
|
|
140
|
-
// "Geoparquet metadata does not have a version"
|
|
141
|
-
if (geoJson.schema_version && !geoJson.version) {
|
|
142
|
-
legacyGeoParquet = true;
|
|
143
|
-
}
|
|
144
|
-
geo = {
|
|
145
|
-
primaryColumn: geoJson.primary_column ?? 'geometry',
|
|
146
|
-
columns: {}
|
|
147
|
-
};
|
|
148
|
-
if (geoJson.columns) {
|
|
149
|
-
for (const [colName, colMeta] of Object.entries(geoJson.columns)) {
|
|
150
|
-
geo.columns[colName] = {
|
|
151
|
-
encoding: colMeta.encoding ?? 'WKB',
|
|
152
|
-
geometryTypes: colMeta.geometry_types ?? [],
|
|
153
|
-
crs: colMeta.crs ?? null,
|
|
154
|
-
bbox: colMeta.bbox
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
catch {
|
|
160
|
-
// malformed geo metadata — ignore
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// Created-by tool
|
|
164
|
-
const createdBy = metadata.created_by ?? null;
|
|
165
|
-
// Row group count
|
|
166
|
-
const numRowGroups = metadata.row_groups.length;
|
|
167
|
-
// Compression: collect unique codecs from first row group's columns
|
|
168
|
-
let compression = null;
|
|
169
|
-
if (numRowGroups > 0 && metadata.row_groups[0].columns) {
|
|
170
|
-
const codecs = new Set();
|
|
171
|
-
for (const col of metadata.row_groups[0].columns) {
|
|
172
|
-
const codec = col.meta_data?.codec;
|
|
173
|
-
if (codec)
|
|
174
|
-
codecs.add(codec);
|
|
175
|
-
}
|
|
176
|
-
if (codecs.size === 1) {
|
|
177
|
-
compression = [...codecs][0];
|
|
178
|
-
}
|
|
179
|
-
else if (codecs.size > 1) {
|
|
180
|
-
compression = [...codecs].join(', ');
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return {
|
|
184
|
-
rowCount,
|
|
185
|
-
schema,
|
|
186
|
-
topLevelColumns,
|
|
187
|
-
geo,
|
|
188
|
-
legacyGeoParquet,
|
|
189
|
-
createdBy,
|
|
190
|
-
numRowGroups,
|
|
191
|
-
compression
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
/** Count how many schema elements belong to the subtree rooted at `start` (inclusive). */
|
|
195
|
-
function countSubtree(schema, start) {
|
|
196
|
-
const el = schema[start];
|
|
197
|
-
if (!el)
|
|
198
|
-
return 0;
|
|
199
|
-
const n = el.num_children ?? 0;
|
|
200
|
-
let cursor = start + 1;
|
|
201
|
-
for (let i = 0; i < n; i++) {
|
|
202
|
-
cursor += countSubtree(schema, cursor);
|
|
203
|
-
}
|
|
204
|
-
return cursor - start;
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Extract EPSG code from GeoParquet CRS metadata.
|
|
208
|
-
* Returns null for WGS84/CRS84 (no reprojection needed).
|
|
209
|
-
*/
|
|
210
|
-
export function extractEpsgFromGeoMeta(geo) {
|
|
211
|
-
const primaryCol = geo.columns[geo.primaryColumn];
|
|
212
|
-
if (!primaryCol?.crs)
|
|
213
|
-
return null;
|
|
214
|
-
const crs = primaryCol.crs;
|
|
215
|
-
// OGC CRS84 is lon/lat WGS84
|
|
216
|
-
if (crs.type === 'name' && crs.properties?.name?.includes('CRS84'))
|
|
217
|
-
return null;
|
|
218
|
-
// PROJJSON: { "id": { "authority": "EPSG", "code": 27700 } }
|
|
219
|
-
if (crs.id?.authority === 'EPSG') {
|
|
220
|
-
const code = crs.id.code;
|
|
221
|
-
if (WGS84_CODES.has(code))
|
|
222
|
-
return null;
|
|
223
|
-
return `EPSG:${code}`;
|
|
224
|
-
}
|
|
225
|
-
return null;
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Extract normalized geometry types from GeoParquet metadata.
|
|
229
|
-
* Returns the set of geometry types for the primary column.
|
|
230
|
-
*/
|
|
231
|
-
export function extractGeometryTypes(geo) {
|
|
232
|
-
const primaryCol = geo.columns[geo.primaryColumn];
|
|
233
|
-
if (!primaryCol?.geometryTypes?.length)
|
|
234
|
-
return [];
|
|
235
|
-
const typeMap = {
|
|
236
|
-
Point: 'point',
|
|
237
|
-
LineString: 'linestring',
|
|
238
|
-
Polygon: 'polygon',
|
|
239
|
-
MultiPoint: 'multipoint',
|
|
240
|
-
MultiLineString: 'multilinestring',
|
|
241
|
-
MultiPolygon: 'multipolygon'
|
|
242
|
-
};
|
|
243
|
-
const types = [];
|
|
244
|
-
for (const raw of primaryCol.geometryTypes) {
|
|
245
|
-
// GeoParquet geometry_types may include Z/M suffixes like "Polygon Z"
|
|
246
|
-
const base = raw.split(' ')[0];
|
|
247
|
-
const mapped = typeMap[base];
|
|
248
|
-
if (mapped && !types.includes(mapped))
|
|
249
|
-
types.push(mapped);
|
|
250
|
-
}
|
|
251
|
-
return types;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Extract bounds from GeoParquet metadata (bbox field).
|
|
255
|
-
* Returns [minX, minY, maxX, maxY] or null.
|
|
256
|
-
*/
|
|
257
|
-
export function extractBounds(geo) {
|
|
258
|
-
const primaryCol = geo.columns[geo.primaryColumn];
|
|
259
|
-
if (!primaryCol?.bbox || primaryCol.bbox.length < 4)
|
|
260
|
-
return null;
|
|
261
|
-
return [primaryCol.bbox[0], primaryCol.bbox[1], primaryCol.bbox[2], primaryCol.bbox[3]];
|
|
262
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* stac-geoparquet helpers.
|
|
3
|
-
*
|
|
4
|
-
* Pure TypeScript, zero Svelte / DuckDB / deck.gl dependencies. Re-exported
|
|
5
|
-
* through `@walkthru-earth/objex-utils` so other projects can consume the
|
|
6
|
-
* detection + row-to-Item transforms without pulling the Svelte lib.
|
|
7
|
-
*
|
|
8
|
-
* The module is decoupled from WKB decoding: callers pass a `wkbParser`
|
|
9
|
-
* callback (the objex Svelte lib threads its `parseWKB`; other consumers
|
|
10
|
-
* can plug in `geoarrow-wasm`, `wkx`, or any other library).
|
|
11
|
-
*/
|
|
12
|
-
import type { StacAsset, StacItem } from './stac.js';
|
|
13
|
-
/** Minimal schema column shape. Works with hyparquet, DuckDB, Arrow. */
|
|
14
|
-
export interface StacGeoparquetSchemaColumn {
|
|
15
|
-
name: string;
|
|
16
|
-
type?: string;
|
|
17
|
-
}
|
|
18
|
-
/** Bbox in struct shape as produced by DuckDB's `bbox struct(xmin,ymin,xmax,ymax)`. */
|
|
19
|
-
export interface StacBboxStruct {
|
|
20
|
-
xmin: number;
|
|
21
|
-
ymin: number;
|
|
22
|
-
xmax: number;
|
|
23
|
-
ymax: number;
|
|
24
|
-
}
|
|
25
|
-
/** Generic Record shape representing a single stac-geoparquet row after DuckDB decoding. */
|
|
26
|
-
export type StacGeoparquetRow = Record<string, unknown>;
|
|
27
|
-
export interface StacRowToItemOptions {
|
|
28
|
-
/**
|
|
29
|
-
* Decoder for the geometry column. Accepts a Uint8Array of WKB bytes and
|
|
30
|
-
* returns a GeoJSON geometry object (or null on failure).
|
|
31
|
-
*
|
|
32
|
-
* Consumers in the objex Svelte lib should pass `parseWKB` from
|
|
33
|
-
* `@walkthru-earth/objex-utils`. Other projects can use any WKB library.
|
|
34
|
-
*/
|
|
35
|
-
wkbParser?: (bytes: Uint8Array) => unknown;
|
|
36
|
-
/**
|
|
37
|
-
* Column name holding the WKB bytes. Defaults to `'geom_wkb'` because the
|
|
38
|
-
* recommended SQL projection is `ST_AsWKB(geometry) AS geom_wkb`.
|
|
39
|
-
*/
|
|
40
|
-
wkbColumn?: string;
|
|
41
|
-
/** Override the column holding the pre-decoded GeoJSON geometry, when available. */
|
|
42
|
-
geometryColumn?: string;
|
|
43
|
-
}
|
|
44
|
-
/** Columns every stac-geoparquet file MUST carry per the stac-geoparquet spec. */
|
|
45
|
-
export declare const STAC_GEOPARQUET_REQUIRED_COLUMNS: readonly ["stac_version", "type", "geometry", "assets"];
|
|
46
|
-
/**
|
|
47
|
-
* Detect stac-geoparquet by presence of the required STAC columns.
|
|
48
|
-
*
|
|
49
|
-
* Deliberately type-agnostic: some pipelines know the type (DuckDB DESCRIBE,
|
|
50
|
-
* Arrow Field), others only have the name list (hyparquet schema walk). The
|
|
51
|
-
* set of names is sufficient for routing.
|
|
52
|
-
*/
|
|
53
|
-
export declare function isStacGeoparquetSchema(schema: StacGeoparquetSchemaColumn[]): boolean;
|
|
54
|
-
/**
|
|
55
|
-
* Flatten a DuckDB `struct(xmin,ymin,xmax,ymax)` bbox to the `[minX, minY, maxX, maxY]`
|
|
56
|
-
* array shape that STAC Items and deck.gl-geotiff MosaicLayer expect.
|
|
57
|
-
*
|
|
58
|
-
* Pass-through for arrays so callers that already have `[minX,minY,maxX,maxY]`
|
|
59
|
-
* shape (e.g. from a Feature's `bbox` field) don't need a separate path.
|
|
60
|
-
*/
|
|
61
|
-
export declare function flattenStacBbox(bbox: StacBboxStruct | number[] | null | undefined): [number, number, number, number] | null;
|
|
62
|
-
/**
|
|
63
|
-
* Resolve a possibly-relative STAC asset href against the parquet file URL.
|
|
64
|
-
*
|
|
65
|
-
* `./foo.tif` or `foo.tif` → absolute against `baseUrl`. Absolute URLs
|
|
66
|
-
* (`http(s)://`, `s3://`, `gs://`, etc.) are returned unchanged.
|
|
67
|
-
*/
|
|
68
|
-
export declare function resolveStacAssetHref(href: string, baseUrl: string): string;
|
|
69
|
-
/**
|
|
70
|
-
* Pick the "primary" asset from a STAC Item's `assets` map.
|
|
71
|
-
*
|
|
72
|
-
* Priority: caller-specified `preferredKeys` → `data` key → first asset with
|
|
73
|
-
* `roles` containing `'data'` → first asset. Returns `null` if the map is
|
|
74
|
-
* empty or not an object.
|
|
75
|
-
*/
|
|
76
|
-
export declare function pickStacPrimaryAsset(assets: Record<string, StacAsset> | null | undefined, preferredKeys?: readonly string[]): {
|
|
77
|
-
key: string;
|
|
78
|
-
asset: StacAsset;
|
|
79
|
-
} | null;
|
|
80
|
-
/**
|
|
81
|
-
* Convert one stac-geoparquet row into a standard STAC Item JSON object.
|
|
82
|
-
*
|
|
83
|
-
* Handles:
|
|
84
|
-
* - `assets` named-struct flattening + relative href resolution
|
|
85
|
-
* - `bbox` struct → `[minX, minY, maxX, maxY]` array
|
|
86
|
-
* - Optional WKB geometry → GeoJSON via `opts.wkbParser`
|
|
87
|
-
* - `datetime` → ISO string (passes through already-string values)
|
|
88
|
-
* - Promotes `properties.*` columns (`proj:*`, `datetime`) onto `item.properties`
|
|
89
|
-
*/
|
|
90
|
-
export declare function stacRowToItem(row: StacGeoparquetRow, baseUrl: string, opts?: StacRowToItemOptions): StacItem;
|