@walkthru-earth/objex 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +20 -12
  3. package/dist/components/browser/FileTreeSidebar.svelte +32 -17
  4. package/dist/components/layout/AboutSheet.svelte +5 -2
  5. package/dist/components/layout/ConnectionDialog.svelte +1 -1
  6. package/dist/components/layout/SettingsSheet.svelte +237 -0
  7. package/dist/components/layout/SettingsSheet.svelte.d.ts +6 -0
  8. package/dist/components/layout/Sidebar.svelte +73 -6
  9. package/dist/components/layout/Sidebar.svelte.d.ts +4 -1
  10. package/dist/components/layout/StatusBar.svelte +1 -1
  11. package/dist/components/layout/TabBar.svelte +2 -2
  12. package/dist/components/ui/context-menu/context-menu-radio-group.svelte.d.ts +1 -1
  13. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +1 -1
  14. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +1 -1
  15. package/dist/components/ui/input/input.svelte.d.ts +1 -1
  16. package/dist/components/ui/resizable/index.d.ts +1 -1
  17. package/dist/components/ui/resizable/index.js +2 -2
  18. package/dist/components/ui/slider/index.d.ts +3 -0
  19. package/dist/components/ui/slider/index.js +5 -0
  20. package/dist/components/ui/slider/range-slider.svelte +94 -0
  21. package/dist/components/ui/slider/range-slider.svelte.d.ts +21 -0
  22. package/dist/components/ui/slider/slider.svelte +83 -0
  23. package/dist/components/ui/slider/slider.svelte.d.ts +7 -0
  24. package/dist/components/viewers/ArchiveViewer.svelte +2 -2
  25. package/dist/components/viewers/CodeViewer.svelte +31 -22
  26. package/dist/components/viewers/CogControls.svelte +338 -184
  27. package/dist/components/viewers/CogControls.svelte.d.ts +33 -10
  28. package/dist/components/viewers/CogViewer.svelte +320 -119
  29. package/dist/components/viewers/CopcViewer.svelte +1 -1
  30. package/dist/components/viewers/FlatGeobufViewer.svelte +1 -1
  31. package/dist/components/viewers/GeoParquetMapViewer.svelte +6 -6
  32. package/dist/components/viewers/GeoParquetMapViewer.svelte.d.ts +1 -1
  33. package/dist/components/viewers/ImageViewer.svelte +2 -2
  34. package/dist/components/viewers/MarkdownViewer.svelte +12 -9
  35. package/dist/components/viewers/MediaViewer.svelte +2 -2
  36. package/dist/components/viewers/ModelViewer.svelte +1 -1
  37. package/dist/components/viewers/MultiCogViewer.svelte +467 -102
  38. package/dist/components/viewers/MultiCogViewer.svelte.d.ts +1 -1
  39. package/dist/components/viewers/NotebookViewer.svelte +6 -3
  40. package/dist/components/viewers/PdfViewer.svelte +2 -2
  41. package/dist/components/viewers/PmtilesViewer.svelte +3 -6
  42. package/dist/components/viewers/RawViewer.svelte +6 -3
  43. package/dist/components/viewers/StacMapViewer.svelte +10 -2
  44. package/dist/components/viewers/StacMosaicViewer.svelte +1800 -362
  45. package/dist/components/viewers/StacMosaicViewer.svelte.d.ts +1 -1
  46. package/dist/components/viewers/StacTabViewer.svelte +24 -13
  47. package/dist/components/viewers/StacTabViewer.svelte.d.ts +1 -1
  48. package/dist/components/viewers/TableGrid.svelte +4 -4
  49. package/dist/components/viewers/TableStatusBar.svelte +1 -1
  50. package/dist/components/viewers/TableToolbar.svelte +1 -1
  51. package/dist/components/viewers/TableViewer.svelte +25 -17
  52. package/dist/components/viewers/TableViewer.svelte.d.ts +1 -0
  53. package/dist/components/viewers/ViewerRouter.svelte +16 -8
  54. package/dist/components/viewers/ZarrMapViewer.svelte +11 -9
  55. package/dist/components/viewers/ZarrViewer.svelte +4 -4
  56. package/dist/components/viewers/cog/ChannelPicker.svelte +83 -0
  57. package/dist/components/viewers/cog/ChannelPicker.svelte.d.ts +13 -0
  58. package/dist/components/viewers/cog/PixelInspectorPanel.svelte +87 -0
  59. package/dist/components/viewers/cog/PixelInspectorPanel.svelte.d.ts +17 -0
  60. package/dist/components/viewers/cog/buildRgbLayer.d.ts +78 -0
  61. package/dist/components/viewers/cog/buildRgbLayer.js +176 -0
  62. package/dist/components/viewers/map/AttributeTable.svelte +1 -1
  63. package/dist/components/viewers/map/MapContainer.svelte +37 -11
  64. package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +1 -1
  65. package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +1 -1
  66. package/dist/components/viewers/stac/StacDatetimeBar.svelte +175 -0
  67. package/dist/components/viewers/stac/StacDatetimeBar.svelte.d.ts +10 -0
  68. package/dist/components/viewers/stac/StacFilterPanel.svelte +243 -0
  69. package/dist/components/viewers/stac/StacFilterPanel.svelte.d.ts +14 -0
  70. package/dist/components/viewers/stac/StacItemInspector.svelte +223 -0
  71. package/dist/components/viewers/stac/StacItemInspector.svelte.d.ts +10 -0
  72. package/dist/components/viewers/stac/StacItemStrip.svelte +228 -0
  73. package/dist/components/viewers/stac/StacItemStrip.svelte.d.ts +12 -0
  74. package/dist/file-icons/index.d.ts +1 -1
  75. package/dist/file-icons/index.js +1 -1
  76. package/dist/i18n/ar.js +110 -2
  77. package/dist/i18n/en.js +110 -2
  78. package/dist/index.d.ts +2 -28
  79. package/dist/index.js +7 -23
  80. package/dist/query/engine.d.ts +10 -0
  81. package/dist/query/source.js +1 -1
  82. package/dist/query/stac-source-factory.d.ts +65 -0
  83. package/dist/query/stac-source-factory.js +77 -0
  84. package/dist/query/stac-source-parquet.d.ts +135 -0
  85. package/dist/query/stac-source-parquet.js +465 -0
  86. package/dist/query/wasm.d.ts +8 -0
  87. package/dist/query/wasm.js +304 -2
  88. package/dist/storage/presign.js +1 -1
  89. package/dist/storage/providers.js +5 -5
  90. package/dist/stores/config.svelte.d.ts +15 -0
  91. package/dist/stores/config.svelte.js +46 -0
  92. package/dist/stores/connections.svelte.d.ts +2 -2
  93. package/dist/stores/connections.svelte.js +1 -2
  94. package/dist/stores/files.svelte.d.ts +1 -1
  95. package/dist/stores/files.svelte.js +1 -1
  96. package/dist/stores/query-history.svelte.js +1 -1
  97. package/dist/stores/settings.svelte.d.ts +16 -1
  98. package/dist/stores/settings.svelte.js +104 -48
  99. package/dist/stores/tabs.svelte.d.ts +3 -0
  100. package/dist/stores/tabs.svelte.js +17 -0
  101. package/dist/utils/cog-histogram.d.ts +121 -0
  102. package/dist/utils/cog-histogram.js +424 -0
  103. package/dist/utils/cog.d.ts +200 -60
  104. package/dist/utils/cog.js +377 -114
  105. package/dist/utils/colormap-sprite.d.ts +0 -9
  106. package/dist/utils/colormap-sprite.js +0 -21
  107. package/dist/utils/deck.d.ts +16 -12
  108. package/dist/utils/deck.js +10 -4
  109. package/dist/utils/pmtiles-tile.js +2 -2
  110. package/dist/utils/{url.d.ts → signed-url.d.ts} +15 -1
  111. package/dist/utils/{url.js → signed-url.js} +32 -10
  112. package/dist/utils/url-state.d.ts +36 -0
  113. package/dist/utils/url-state.js +72 -2
  114. package/dist/utils/zarr-tab.d.ts +1 -2
  115. package/dist/utils/zarr-tab.js +1 -2
  116. package/dist/utils/zarr.d.ts +0 -17
  117. package/dist/utils/zarr.js +1 -45
  118. package/package.json +55 -84
  119. package/dist/components/browser/Breadcrumb.svelte +0 -50
  120. package/dist/components/browser/Breadcrumb.svelte.d.ts +0 -7
  121. package/dist/components/browser/CreateFolderDialog.svelte +0 -98
  122. package/dist/components/browser/CreateFolderDialog.svelte.d.ts +0 -6
  123. package/dist/components/browser/DeleteConfirmDialog.svelte +0 -90
  124. package/dist/components/browser/DeleteConfirmDialog.svelte.d.ts +0 -8
  125. package/dist/components/browser/DropZone.svelte +0 -83
  126. package/dist/components/browser/DropZone.svelte.d.ts +0 -7
  127. package/dist/components/browser/FileBrowser.svelte +0 -252
  128. package/dist/components/browser/FileBrowser.svelte.d.ts +0 -3
  129. package/dist/components/browser/FileRow.svelte +0 -117
  130. package/dist/components/browser/FileRow.svelte.d.ts +0 -9
  131. package/dist/components/browser/RenameDialog.svelte +0 -101
  132. package/dist/components/browser/RenameDialog.svelte.d.ts +0 -8
  133. package/dist/components/browser/SearchBar.svelte +0 -40
  134. package/dist/components/browser/SearchBar.svelte.d.ts +0 -6
  135. package/dist/components/browser/UploadButton.svelte +0 -65
  136. package/dist/components/browser/UploadButton.svelte.d.ts +0 -3
  137. package/dist/query/stac-geoparquet.d.ts +0 -31
  138. package/dist/query/stac-geoparquet.js +0 -136
  139. package/dist/utils/clipboard.d.ts +0 -13
  140. package/dist/utils/clipboard.js +0 -38
  141. package/dist/utils/cloud-url.d.ts +0 -27
  142. package/dist/utils/cloud-url.js +0 -61
  143. package/dist/utils/column-types.d.ts +0 -5
  144. package/dist/utils/column-types.js +0 -137
  145. package/dist/utils/connection-identity.d.ts +0 -51
  146. package/dist/utils/connection-identity.js +0 -97
  147. package/dist/utils/error.d.ts +0 -8
  148. package/dist/utils/error.js +0 -12
  149. package/dist/utils/evidence-context.d.ts +0 -22
  150. package/dist/utils/evidence-context.js +0 -56
  151. package/dist/utils/export.d.ts +0 -22
  152. package/dist/utils/export.js +0 -76
  153. package/dist/utils/file-sort.d.ts +0 -20
  154. package/dist/utils/file-sort.js +0 -41
  155. package/dist/utils/format.d.ts +0 -24
  156. package/dist/utils/format.js +0 -78
  157. package/dist/utils/geoarrow.d.ts +0 -32
  158. package/dist/utils/geoarrow.js +0 -672
  159. package/dist/utils/geometry-type.d.ts +0 -52
  160. package/dist/utils/geometry-type.js +0 -76
  161. package/dist/utils/hex.d.ts +0 -10
  162. package/dist/utils/hex.js +0 -27
  163. package/dist/utils/host-detection.d.ts +0 -23
  164. package/dist/utils/host-detection.js +0 -95
  165. package/dist/utils/local-storage.d.ts +0 -16
  166. package/dist/utils/local-storage.js +0 -37
  167. package/dist/utils/markdown-sql.d.ts +0 -30
  168. package/dist/utils/markdown-sql.js +0 -72
  169. package/dist/utils/notebook.d.ts +0 -59
  170. package/dist/utils/notebook.js +0 -211
  171. package/dist/utils/parquet-metadata.d.ts +0 -64
  172. package/dist/utils/parquet-metadata.js +0 -262
  173. package/dist/utils/stac-geoparquet.d.ts +0 -90
  174. package/dist/utils/stac-geoparquet.js +0 -223
  175. package/dist/utils/stac-hydrate.d.ts +0 -38
  176. package/dist/utils/stac-hydrate.js +0 -243
  177. package/dist/utils/stac.d.ts +0 -136
  178. package/dist/utils/stac.js +0 -176
  179. package/dist/utils/storage-url.d.ts +0 -90
  180. package/dist/utils/storage-url.js +0 -568
  181. package/dist/utils/wkb.d.ts +0 -43
  182. 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
- }