@walkthru-earth/objex 1.3.1 → 1.5.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 +28 -20
- package/dist/components/browser/FileTreeSidebar.svelte +32 -17
- package/dist/components/layout/AboutSheet.svelte +5 -2
- package/dist/components/layout/ConnectionDialog.svelte +7 -2
- package/dist/components/layout/SettingsSheet.svelte +238 -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 +17 -14
- package/dist/components/layout/TabBar.svelte +4 -4
- 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 +140 -113
- package/dist/components/viewers/CodeViewer.svelte +45 -48
- package/dist/components/viewers/CodeViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/CogControls.svelte +338 -184
- package/dist/components/viewers/CogControls.svelte.d.ts +33 -10
- package/dist/components/viewers/CogViewer.svelte +269 -116
- package/dist/components/viewers/CopcViewer.svelte +8 -15
- package/dist/components/viewers/DatabaseViewer.svelte +22 -21
- package/dist/components/viewers/FileInfo.svelte +16 -16
- package/dist/components/viewers/FlatGeobufViewer.svelte +16 -46
- package/dist/components/viewers/GeoParquetMapViewer.svelte +11 -9
- package/dist/components/viewers/GeoParquetMapViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/ImageViewer.svelte +12 -14
- package/dist/components/viewers/LoadProgress.svelte +6 -6
- package/dist/components/viewers/MarkdownViewer.svelte +29 -30
- package/dist/components/viewers/MediaViewer.svelte +13 -14
- package/dist/components/viewers/ModelViewer.svelte +18 -21
- package/dist/components/viewers/MultiCogViewer.svelte +474 -106
- package/dist/components/viewers/MultiCogViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/NotebookViewer.svelte +28 -29
- package/dist/components/viewers/PdfViewer.svelte +24 -33
- package/dist/components/viewers/PmtilesViewer.svelte +13 -15
- package/dist/components/viewers/QueryHistoryPanel.svelte +18 -18
- package/dist/components/viewers/RawViewer.svelte +27 -21
- package/dist/components/viewers/StacMapViewer.svelte +6 -13
- package/dist/components/viewers/StacMosaicViewer.svelte +1764 -410
- package/dist/components/viewers/StacMosaicViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/StacTabViewer.svelte +26 -15
- package/dist/components/viewers/StacTabViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/TableGrid.svelte +38 -34
- package/dist/components/viewers/TableStatusBar.svelte +7 -7
- package/dist/components/viewers/TableToolbar.svelte +10 -9
- package/dist/components/viewers/TableViewer.svelte +47 -30
- package/dist/components/viewers/TableViewer.svelte.d.ts +1 -0
- package/dist/components/viewers/ViewerHeader.svelte +18 -0
- package/dist/components/viewers/ViewerHeader.svelte.d.ts +10 -0
- package/dist/components/viewers/ViewerRouter.svelte +16 -8
- package/dist/components/viewers/ViewerStatus.svelte +19 -0
- package/dist/components/viewers/ViewerStatus.svelte.d.ts +7 -0
- package/dist/components/viewers/ZarrMapViewer.svelte +24 -21
- package/dist/components/viewers/ZarrViewer.svelte +98 -65
- 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 +7 -7
- package/dist/components/viewers/map/MapContainer.svelte +38 -12
- package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +109 -83
- package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +16 -16
- 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/constants.d.ts +6 -0
- package/dist/constants.js +8 -0
- package/dist/file-icons/index.d.ts +1 -1
- package/dist/file-icons/index.js +1 -1
- package/dist/i18n/ar.js +113 -2
- package/dist/i18n/en.js +113 -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 +468 -0
- package/dist/query/wasm.d.ts +8 -0
- package/dist/query/wasm.js +310 -65
- package/dist/storage/presign.js +3 -2
- package/dist/storage/providers.js +7 -6
- 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 +18 -12
- package/dist/utils/deck.js +15 -7
- package/dist/utils/media-query.svelte.d.ts +14 -0
- package/dist/utils/media-query.svelte.js +29 -0
- package/dist/utils/pmtiles-tile.js +2 -2
- package/dist/utils/signed-url-effect.d.ts +7 -0
- package/dist/utils/signed-url-effect.js +19 -0
- 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/wkb.js
DELETED
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lightweight WKB (Well-Known Binary) parser for extracting coordinates.
|
|
3
|
-
* Supports Point, LineString, Polygon, and Multi* variants.
|
|
4
|
-
* Handles standard WKB, ISO WKB (with Z/M), and EWKB (PostGIS SRID).
|
|
5
|
-
*/
|
|
6
|
-
// WKB type codes
|
|
7
|
-
const WKB_POINT = 1;
|
|
8
|
-
const WKB_LINESTRING = 2;
|
|
9
|
-
const WKB_POLYGON = 3;
|
|
10
|
-
const WKB_MULTIPOINT = 4;
|
|
11
|
-
const WKB_MULTILINESTRING = 5;
|
|
12
|
-
const WKB_MULTIPOLYGON = 6;
|
|
13
|
-
// EWKB flags
|
|
14
|
-
const EWKB_Z_FLAG = 0x80000000;
|
|
15
|
-
const EWKB_M_FLAG = 0x40000000;
|
|
16
|
-
const EWKB_SRID_FLAG = 0x20000000;
|
|
17
|
-
/**
|
|
18
|
-
* Normalize binary data from various formats to Uint8Array.
|
|
19
|
-
* Handles Uint8Array, ArrayBuffer, number arrays, hex strings,
|
|
20
|
-
* and DuckDB toJSON() blob objects ({0: byte, 1: byte, ...}).
|
|
21
|
-
*/
|
|
22
|
-
export function toBinary(value) {
|
|
23
|
-
if (value === null || value === undefined)
|
|
24
|
-
return null;
|
|
25
|
-
if (value instanceof Uint8Array)
|
|
26
|
-
return value;
|
|
27
|
-
if (value instanceof ArrayBuffer)
|
|
28
|
-
return new Uint8Array(value);
|
|
29
|
-
if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'number') {
|
|
30
|
-
return new Uint8Array(value);
|
|
31
|
-
}
|
|
32
|
-
if (typeof value === 'string' && /^[0-9a-fA-F]+$/.test(value) && value.length % 2 === 0) {
|
|
33
|
-
const bytes = new Uint8Array(value.length / 2);
|
|
34
|
-
for (let i = 0; i < value.length; i += 2) {
|
|
35
|
-
bytes[i / 2] = parseInt(value.substring(i, i + 2), 16);
|
|
36
|
-
}
|
|
37
|
-
return bytes;
|
|
38
|
-
}
|
|
39
|
-
// DuckDB toJSON() serializes BLOB/GEOMETRY as {"0": byte, "1": byte, ...}
|
|
40
|
-
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
41
|
-
const obj = value;
|
|
42
|
-
const keys = Object.keys(obj);
|
|
43
|
-
if (keys.length > 4 && typeof obj['0'] === 'number') {
|
|
44
|
-
const arr = new Uint8Array(keys.length);
|
|
45
|
-
for (let i = 0; i < keys.length; i++)
|
|
46
|
-
arr[i] = obj[i];
|
|
47
|
-
return arr;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Parse a WKB binary blob into coordinates and geometry type.
|
|
54
|
-
*/
|
|
55
|
-
export function parseWKB(data) {
|
|
56
|
-
if (data.length < 5)
|
|
57
|
-
return null;
|
|
58
|
-
try {
|
|
59
|
-
return readGeometry(new DataView(data.buffer, data.byteOffset, data.byteLength), 0).geometry;
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function readGeometry(view, offset) {
|
|
66
|
-
const le = view.getUint8(offset) === 1;
|
|
67
|
-
offset += 1;
|
|
68
|
-
let typeInt = view.getUint32(offset, le);
|
|
69
|
-
offset += 4;
|
|
70
|
-
// Detect coordinate dimensions from EWKB flags or ISO type ranges
|
|
71
|
-
const hasZ = (typeInt & EWKB_Z_FLAG) !== 0 ||
|
|
72
|
-
(typeInt % 10000 >= 1000 && typeInt % 10000 < 2000) ||
|
|
73
|
-
(typeInt % 10000 >= 3000 && typeInt % 10000 < 4000);
|
|
74
|
-
const hasM = (typeInt & EWKB_M_FLAG) !== 0 ||
|
|
75
|
-
(typeInt % 10000 >= 2000 && typeInt % 10000 < 3000) ||
|
|
76
|
-
(typeInt % 10000 >= 3000 && typeInt % 10000 < 4000);
|
|
77
|
-
// Skip SRID if present (EWKB)
|
|
78
|
-
if (typeInt & EWKB_SRID_FLAG)
|
|
79
|
-
offset += 4;
|
|
80
|
-
// Strip all flags to get base geometry type
|
|
81
|
-
typeInt = (typeInt & 0x0000ffff) % 1000;
|
|
82
|
-
const extraDims = (hasZ ? 1 : 0) + (hasM ? 1 : 0);
|
|
83
|
-
const rd = (o) => view.getFloat64(o, le);
|
|
84
|
-
const ru = (o) => view.getUint32(o, le);
|
|
85
|
-
function readPoint(off) {
|
|
86
|
-
const x = rd(off);
|
|
87
|
-
const y = rd(off + 8);
|
|
88
|
-
return { coords: [x, y], offset: off + 16 + extraDims * 8 };
|
|
89
|
-
}
|
|
90
|
-
function readLineString(off) {
|
|
91
|
-
const n = ru(off);
|
|
92
|
-
off += 4;
|
|
93
|
-
const coords = [];
|
|
94
|
-
for (let i = 0; i < n; i++) {
|
|
95
|
-
const pt = readPoint(off);
|
|
96
|
-
coords.push(pt.coords);
|
|
97
|
-
off = pt.offset;
|
|
98
|
-
}
|
|
99
|
-
return { coords, offset: off };
|
|
100
|
-
}
|
|
101
|
-
function readPolygon(off) {
|
|
102
|
-
const n = ru(off);
|
|
103
|
-
off += 4;
|
|
104
|
-
const coords = [];
|
|
105
|
-
for (let i = 0; i < n; i++) {
|
|
106
|
-
const ring = readLineString(off);
|
|
107
|
-
coords.push(ring.coords);
|
|
108
|
-
off = ring.offset;
|
|
109
|
-
}
|
|
110
|
-
return { coords, offset: off };
|
|
111
|
-
}
|
|
112
|
-
switch (typeInt) {
|
|
113
|
-
case WKB_POINT: {
|
|
114
|
-
const pt = readPoint(offset);
|
|
115
|
-
return { geometry: { type: 'Point', coordinates: pt.coords }, offset: pt.offset };
|
|
116
|
-
}
|
|
117
|
-
case WKB_LINESTRING: {
|
|
118
|
-
const ls = readLineString(offset);
|
|
119
|
-
return { geometry: { type: 'LineString', coordinates: ls.coords }, offset: ls.offset };
|
|
120
|
-
}
|
|
121
|
-
case WKB_POLYGON: {
|
|
122
|
-
const pg = readPolygon(offset);
|
|
123
|
-
return { geometry: { type: 'Polygon', coordinates: pg.coords }, offset: pg.offset };
|
|
124
|
-
}
|
|
125
|
-
case WKB_MULTIPOINT: {
|
|
126
|
-
const n = ru(offset);
|
|
127
|
-
offset += 4;
|
|
128
|
-
const coords = [];
|
|
129
|
-
for (let i = 0; i < n; i++) {
|
|
130
|
-
const r = readGeometry(view, offset);
|
|
131
|
-
coords.push(r.geometry.coordinates);
|
|
132
|
-
offset = r.offset;
|
|
133
|
-
}
|
|
134
|
-
return { geometry: { type: 'MultiPoint', coordinates: coords }, offset };
|
|
135
|
-
}
|
|
136
|
-
case WKB_MULTILINESTRING: {
|
|
137
|
-
const n = ru(offset);
|
|
138
|
-
offset += 4;
|
|
139
|
-
const coords = [];
|
|
140
|
-
for (let i = 0; i < n; i++) {
|
|
141
|
-
const r = readGeometry(view, offset);
|
|
142
|
-
coords.push(r.geometry.coordinates);
|
|
143
|
-
offset = r.offset;
|
|
144
|
-
}
|
|
145
|
-
return { geometry: { type: 'MultiLineString', coordinates: coords }, offset };
|
|
146
|
-
}
|
|
147
|
-
case WKB_MULTIPOLYGON: {
|
|
148
|
-
const n = ru(offset);
|
|
149
|
-
offset += 4;
|
|
150
|
-
const coords = [];
|
|
151
|
-
for (let i = 0; i < n; i++) {
|
|
152
|
-
const r = readGeometry(view, offset);
|
|
153
|
-
coords.push(r.geometry.coordinates);
|
|
154
|
-
offset = r.offset;
|
|
155
|
-
}
|
|
156
|
-
return { geometry: { type: 'MultiPolygon', coordinates: coords }, offset };
|
|
157
|
-
}
|
|
158
|
-
default:
|
|
159
|
-
return { geometry: { type: 'Unknown', coordinates: [] }, offset };
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
// ---------------------------------------------------------------------------
|
|
163
|
-
// Geometry column detection
|
|
164
|
-
// ---------------------------------------------------------------------------
|
|
165
|
-
/** Exact well-known geometry column names (lowercase for comparison). */
|
|
166
|
-
const GEO_NAMES = [
|
|
167
|
-
'geometry',
|
|
168
|
-
'geom',
|
|
169
|
-
'wkb_geometry',
|
|
170
|
-
'the_geom',
|
|
171
|
-
'shape',
|
|
172
|
-
'geo',
|
|
173
|
-
'wkt_geometry',
|
|
174
|
-
'the_geog',
|
|
175
|
-
'geog',
|
|
176
|
-
'way',
|
|
177
|
-
'ora_geometry'
|
|
178
|
-
];
|
|
179
|
-
/** Type keywords that indicate a geometry column. */
|
|
180
|
-
const GEO_TYPE_KEYWORDS = [
|
|
181
|
-
'geometry',
|
|
182
|
-
'geography',
|
|
183
|
-
'wkb',
|
|
184
|
-
'point',
|
|
185
|
-
'linestring',
|
|
186
|
-
'polygon',
|
|
187
|
-
'multipoint',
|
|
188
|
-
'multilinestring',
|
|
189
|
-
'multipolygon',
|
|
190
|
-
'geometrycollection',
|
|
191
|
-
'sdo_geometry'
|
|
192
|
-
];
|
|
193
|
-
/**
|
|
194
|
-
* Tokens in column names that hint at geometry content. Matched against
|
|
195
|
-
* snake_case / kebab-case / camelCase tokens only — never as loose substrings
|
|
196
|
-
* (e.g. `_geographic_` must not match `geo` via the `_geo` substring, because
|
|
197
|
-
* count columns like `n_geographic_entities` are INT, not geometry).
|
|
198
|
-
*/
|
|
199
|
-
const GEO_NAME_HINTS = ['geom', 'geometry', 'wkb', 'wkt', 'shape', 'spatial', 'geo'];
|
|
200
|
-
/** Split a column name into lowercase tokens for hint matching. */
|
|
201
|
-
function tokenizeColumnName(name) {
|
|
202
|
-
return name
|
|
203
|
-
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
204
|
-
.replace(/([a-z])([0-9])/g, '$1_$2')
|
|
205
|
-
.toLowerCase()
|
|
206
|
-
.split(/[^a-z0-9]+/)
|
|
207
|
-
.filter(Boolean);
|
|
208
|
-
}
|
|
209
|
-
/** Valid GeoJSON geometry type names. */
|
|
210
|
-
const GEOJSON_TYPES = [
|
|
211
|
-
'Point',
|
|
212
|
-
'LineString',
|
|
213
|
-
'Polygon',
|
|
214
|
-
'MultiPoint',
|
|
215
|
-
'MultiLineString',
|
|
216
|
-
'MultiPolygon'
|
|
217
|
-
];
|
|
218
|
-
/** Check if a value is a GeoJSON geometry object ({type, coordinates}). */
|
|
219
|
-
function isGeoJSONGeometry(value) {
|
|
220
|
-
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
221
|
-
return false;
|
|
222
|
-
const obj = value;
|
|
223
|
-
return (typeof obj.type === 'string' && GEOJSON_TYPES.includes(obj.type) && obj.coordinates != null);
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Find the geometry column name from a schema.
|
|
227
|
-
*
|
|
228
|
-
* Detection priority:
|
|
229
|
-
* 1. Column type contains a geometry keyword (GEOMETRY, POINT, WKB, etc.)
|
|
230
|
-
* 2. Exact well-known column name with BLOB/BINARY type
|
|
231
|
-
* 3. Exact well-known column name regardless of type
|
|
232
|
-
* 4. Column name contains a geo-related substring with BLOB/BINARY type
|
|
233
|
-
* 5. Column name contains a geo-related substring regardless of type
|
|
234
|
-
*/
|
|
235
|
-
export function findGeoColumn(schema) {
|
|
236
|
-
// Priority 1: column type contains a geometry keyword
|
|
237
|
-
// Skip STRUCT/MAP/LIST types — their field names can false-positive
|
|
238
|
-
// (e.g. STRUCT(... Geometry VARCHAR ...) matches 'geometry')
|
|
239
|
-
for (const f of schema) {
|
|
240
|
-
const t = f.type.toLowerCase();
|
|
241
|
-
if (t.startsWith('struct') || t.startsWith('map') || t.startsWith('list'))
|
|
242
|
-
continue;
|
|
243
|
-
if (GEO_TYPE_KEYWORDS.some((kw) => t.includes(kw)))
|
|
244
|
-
return f.name;
|
|
245
|
-
}
|
|
246
|
-
// Priority 2: exact well-known name with binary type
|
|
247
|
-
for (const f of schema) {
|
|
248
|
-
const n = f.name.toLowerCase();
|
|
249
|
-
const t = f.type.toLowerCase();
|
|
250
|
-
const isBinary = t.includes('blob') || t.includes('binary') || t.includes('bytea');
|
|
251
|
-
if (GEO_NAMES.includes(n) && isBinary)
|
|
252
|
-
return f.name;
|
|
253
|
-
}
|
|
254
|
-
// Priority 3: exact well-known name, any type
|
|
255
|
-
for (const f of schema) {
|
|
256
|
-
if (GEO_NAMES.includes(f.name.toLowerCase()))
|
|
257
|
-
return f.name;
|
|
258
|
-
}
|
|
259
|
-
// Priority 4: name token matches geo hint with binary type
|
|
260
|
-
for (const f of schema) {
|
|
261
|
-
const tokens = tokenizeColumnName(f.name);
|
|
262
|
-
const t = f.type.toLowerCase();
|
|
263
|
-
const isBinary = t.includes('blob') || t.includes('binary') || t.includes('bytea');
|
|
264
|
-
if (isBinary && tokens.some((tok) => GEO_NAME_HINTS.includes(tok)))
|
|
265
|
-
return f.name;
|
|
266
|
-
}
|
|
267
|
-
// Priority 5: name token matches geo hint, any type
|
|
268
|
-
for (const f of schema) {
|
|
269
|
-
const tokens = tokenizeColumnName(f.name);
|
|
270
|
-
if (tokens.some((tok) => GEO_NAME_HINTS.includes(tok)))
|
|
271
|
-
return f.name;
|
|
272
|
-
}
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* Check if a value looks like valid WKB by inspecting its first bytes.
|
|
277
|
-
* WKB starts with a byte-order marker (0x00 big-endian, 0x01 little-endian)
|
|
278
|
-
* followed by a uint32 geometry type code.
|
|
279
|
-
*/
|
|
280
|
-
function looksLikeWKB(value) {
|
|
281
|
-
const bytes = toBinary(value);
|
|
282
|
-
if (!bytes || bytes.length < 5)
|
|
283
|
-
return false;
|
|
284
|
-
const bom = bytes[0];
|
|
285
|
-
if (bom !== 0x00 && bom !== 0x01)
|
|
286
|
-
return false;
|
|
287
|
-
const le = bom === 0x01;
|
|
288
|
-
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
289
|
-
let typeInt = view.getUint32(1, le);
|
|
290
|
-
// Strip EWKB flags
|
|
291
|
-
typeInt = (typeInt & 0x0000ffff) % 1000;
|
|
292
|
-
// Valid base geometry types are 1–7
|
|
293
|
-
return typeInt >= 1 && typeInt <= 7;
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Fallback: probe actual row data to find a column containing WKB geometry.
|
|
297
|
-
* Use this when schema-only detection via `findGeoColumn()` returns null.
|
|
298
|
-
* Samples the first row and checks each BLOB/binary column for WKB magic bytes.
|
|
299
|
-
*/
|
|
300
|
-
export function findGeoColumnFromRows(rows, schema) {
|
|
301
|
-
if (rows.length === 0)
|
|
302
|
-
return null;
|
|
303
|
-
const sample = rows[0];
|
|
304
|
-
// First pass: check binary-typed columns
|
|
305
|
-
for (const f of schema) {
|
|
306
|
-
const t = f.type.toLowerCase();
|
|
307
|
-
const isBinary = t.includes('blob') || t.includes('binary') || t.includes('bytea');
|
|
308
|
-
if (isBinary && looksLikeWKB(sample[f.name]))
|
|
309
|
-
return f.name;
|
|
310
|
-
}
|
|
311
|
-
// Second pass: check any column that holds binary-like data or WKT strings
|
|
312
|
-
for (const [key, value] of Object.entries(sample)) {
|
|
313
|
-
if (value instanceof Uint8Array || value instanceof ArrayBuffer) {
|
|
314
|
-
if (looksLikeWKB(value))
|
|
315
|
-
return key;
|
|
316
|
-
}
|
|
317
|
-
if (typeof value === 'string') {
|
|
318
|
-
// Hex-encoded WKB
|
|
319
|
-
if (/^[0-9a-fA-F]+$/.test(value) && value.length >= 10 && looksLikeWKB(value)) {
|
|
320
|
-
return key;
|
|
321
|
-
}
|
|
322
|
-
// WKT string (from DuckDB spatial GEOMETRY type)
|
|
323
|
-
if (isWKT(value))
|
|
324
|
-
return key;
|
|
325
|
-
}
|
|
326
|
-
// GeoJSON geometry object (from DuckDB read_json_auto struct serialization)
|
|
327
|
-
if (isGeoJSONGeometry(value))
|
|
328
|
-
return key;
|
|
329
|
-
// GeoJSON JSON string (from DuckDB to_json()::VARCHAR)
|
|
330
|
-
if (typeof value === 'string' && value.startsWith('{')) {
|
|
331
|
-
try {
|
|
332
|
-
if (isGeoJSONGeometry(JSON.parse(value)))
|
|
333
|
-
return key;
|
|
334
|
-
}
|
|
335
|
-
catch {
|
|
336
|
-
// not JSON
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
return null;
|
|
341
|
-
}
|
|
342
|
-
// ---------------------------------------------------------------------------
|
|
343
|
-
// WKT detection (used by findGeoColumnFromRows for column sniffing)
|
|
344
|
-
// ---------------------------------------------------------------------------
|
|
345
|
-
const WKT_TYPES = [
|
|
346
|
-
'POINT',
|
|
347
|
-
'LINESTRING',
|
|
348
|
-
'POLYGON',
|
|
349
|
-
'MULTIPOINT',
|
|
350
|
-
'MULTILINESTRING',
|
|
351
|
-
'MULTIPOLYGON'
|
|
352
|
-
];
|
|
353
|
-
/** Check if a string looks like WKT. */
|
|
354
|
-
function isWKT(value) {
|
|
355
|
-
if (typeof value !== 'string')
|
|
356
|
-
return false;
|
|
357
|
-
const s = value.trimStart().toUpperCase();
|
|
358
|
-
return WKT_TYPES.some((t) => s.startsWith(t) || s.startsWith(`MULTI${t}`));
|
|
359
|
-
}
|