kepler.gl 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -132
- package/dist/src/common-utils/src/data-type.d.ts +9 -1
- package/dist/src/components/src/common/data-table/header-cell.d.ts +1 -0
- package/dist/src/components/src/common/data-table/index.d.ts +1 -0
- package/dist/src/components/src/common/file-uploader/file-drop.d.ts +1 -32
- package/dist/src/components/src/common/item-selector/dropdown-list.d.ts +2 -2
- package/dist/src/components/src/common/item-selector/item-selector.d.ts +1 -1
- package/dist/src/components/src/common/item-selector/typeahead.d.ts +1 -1
- package/dist/src/components/src/geocoder/geocoder.d.ts +7 -0
- package/dist/src/components/src/geocoder-panel.d.ts +2 -2
- package/dist/src/components/src/hooks/use-fetch-vector-tile-metadata.d.ts +4 -3
- package/dist/src/constants/src/default-settings.d.ts +14 -1
- package/dist/src/deckgl-arrow-layers/src/constants.d.ts +0 -12
- package/dist/src/deckgl-arrow-layers/src/index.d.ts +0 -1
- package/dist/src/deckgl-arrow-layers/src/utils/utils.d.ts +12 -1
- package/dist/src/duckdb/src/components/preview-data-panel.d.ts +7 -2
- package/dist/src/duckdb/src/components/schema-panel.d.ts +12 -3
- package/dist/src/duckdb/src/processors/data-processor.d.ts +1 -0
- package/dist/src/duckdb/src/table/duckdb-table-utils.d.ts +106 -0
- package/dist/src/duckdb/src/table/duckdb-table.d.ts +5 -3
- package/dist/src/processors/src/data-processor.d.ts +15 -1
- package/dist/src/reducers/src/provider-state-updaters.d.ts +1 -0
- package/dist/src/reducers/src/vis-state-updaters.d.ts +1 -1
- package/dist/src/table/src/dataset-utils.d.ts +1 -1
- package/dist/src/table/src/tileset/vector-tile-utils.d.ts +17 -1
- package/dist/src/utils/src/application-config.d.ts +4 -3
- package/dist/src/utils/src/data-utils.d.ts +9 -3
- package/dist/src/utils/src/indexed-data-container.d.ts +2 -2
- package/dist/src/utils/src/map-style-utils/mapbox-utils.d.ts +1 -1
- package/dist/src/utils/src/map-utils.d.ts +1 -1
- package/dist/src/utils/src/plot.d.ts +2 -2
- package/package.json +5 -5
- package/src/actions/package.json +8 -8
- package/src/ai-assistant/dist/components/ai-assistant-component.js +2 -1
- package/src/ai-assistant/package.json +7 -7
- package/src/ai-assistant/src/components/ai-assistant-component.tsx +1 -0
- package/src/cloud-providers/package.json +2 -2
- package/src/common-utils/dist/data-type.d.ts +9 -1
- package/src/common-utils/dist/data-type.js +50 -1
- package/src/common-utils/package.json +3 -3
- package/src/common-utils/src/data-type.ts +49 -1
- package/src/components/dist/common/data-table/header-cell.d.ts +1 -0
- package/src/components/dist/common/data-table/header-cell.js +2 -2
- package/src/components/dist/common/data-table/index.d.ts +1 -0
- package/src/components/dist/common/data-table/index.js +5 -1
- package/src/components/dist/common/file-uploader/file-drop.d.ts +1 -32
- package/src/components/dist/common/file-uploader/file-drop.js +152 -167
- package/src/components/dist/common/item-selector/dropdown-list.d.ts +2 -2
- package/src/components/dist/common/item-selector/dropdown-list.js +2 -3
- package/src/components/dist/common/item-selector/item-selector.d.ts +1 -1
- package/src/components/dist/common/item-selector/typeahead.d.ts +1 -1
- package/src/components/dist/common/range-brush.js +3 -3
- package/src/components/dist/geocoder/geocoder.d.ts +7 -0
- package/src/components/dist/geocoder/geocoder.js +19 -11
- package/src/components/dist/geocoder-panel.d.ts +2 -2
- package/src/components/dist/geocoder-panel.js +64 -91
- package/src/components/dist/hooks/use-fetch-vector-tile-metadata.d.ts +4 -3
- package/src/components/dist/hooks/use-fetch-vector-tile-metadata.js +35 -26
- package/src/components/dist/hooks/use-legend-position.js +8 -4
- package/src/components/dist/map/map-legend-panel.js +2 -3
- package/src/components/dist/modal-container.js +8 -8
- package/src/components/dist/modals/tilesets-modals/tileset-vector-form.js +31 -12
- package/src/components/dist/side-panel/layer-panel/color-palette-preset.js +2 -2
- package/src/components/dist/side-panel/layer-panel/color-range-selector.js +10 -11
- package/src/components/dist/side-panel/layer-panel/color-selector.js +3 -3
- package/src/components/dist/side-panel/layer-panel/custom-palette.js +1 -2
- package/src/components/dist/side-panel/layer-panel/layer-configurator.js +6 -8
- package/src/components/dist/side-panel/layer-panel/vector-tile-layer-configurator.js +1 -2
- package/src/components/package.json +16 -16
- package/src/components/src/common/data-table/header-cell.tsx +2 -1
- package/src/components/src/common/data-table/index.tsx +4 -0
- package/src/components/src/common/file-uploader/file-drop.tsx +186 -161
- package/src/components/src/common/item-selector/dropdown-list.tsx +1 -6
- package/src/components/src/common/range-brush.tsx +2 -2
- package/src/components/src/geocoder/geocoder.tsx +16 -8
- package/src/components/src/geocoder-panel.tsx +95 -85
- package/src/components/src/hooks/use-fetch-vector-tile-metadata.ts +27 -25
- package/src/components/src/hooks/use-legend-position.ts +5 -2
- package/src/components/src/map/map-legend-panel.tsx +1 -2
- package/src/components/src/modal-container.tsx +7 -7
- package/src/components/src/modals/tilesets-modals/tileset-vector-form.tsx +12 -3
- package/src/components/src/side-panel/layer-panel/color-palette-preset.tsx +1 -1
- package/src/components/src/side-panel/layer-panel/color-range-selector.tsx +6 -11
- package/src/components/src/side-panel/layer-panel/color-selector.tsx +2 -2
- package/src/components/src/side-panel/layer-panel/custom-palette.tsx +0 -1
- package/src/components/src/side-panel/layer-panel/layer-configurator.tsx +23 -33
- package/src/components/src/side-panel/layer-panel/vector-tile-layer-configurator.tsx +0 -1
- package/src/constants/dist/default-settings.d.ts +14 -1
- package/src/constants/dist/default-settings.js +20 -5
- package/src/constants/node_modules/.cache/terser-webpack-plugin/content-v2/sha512/91/4f/6c65f3a1eba584ffd2d892cabc25ab5804d4a9ef53694c98707372a83e56343a274bec04f15927a6fe7602615f0f23f73ccf599f524ef97ad6c77c8832ea +1 -0
- package/src/constants/node_modules/.cache/terser-webpack-plugin/index-v5/a6/b2/5424aa4477192fb44cc90fd80892efdc96731f2ff0f48e8f28abf64243cc +2 -0
- package/src/constants/package.json +2 -2
- package/src/constants/src/default-settings.ts +16 -1
- package/src/constants/umd/keplergl.min.js +2 -2
- package/src/deckgl-arrow-layers/dist/constants.d.ts +0 -12
- package/src/deckgl-arrow-layers/dist/constants.js +4 -15
- package/src/deckgl-arrow-layers/dist/index.d.ts +0 -1
- package/src/deckgl-arrow-layers/dist/index.js +1 -8
- package/src/deckgl-arrow-layers/dist/layers/geo-arrow-scatterplot-layer.js +4 -4
- package/src/deckgl-arrow-layers/dist/layers/geo-arrow-text-layer.js +5 -5
- package/src/deckgl-arrow-layers/dist/utils/utils.d.ts +12 -1
- package/src/deckgl-arrow-layers/dist/utils/utils.js +7 -5
- package/src/deckgl-arrow-layers/package.json +2 -1
- package/src/deckgl-arrow-layers/src/constants.ts +0 -13
- package/src/deckgl-arrow-layers/src/index.ts +0 -2
- package/src/deckgl-arrow-layers/src/layers/geo-arrow-scatterplot-layer.ts +5 -3
- package/src/deckgl-arrow-layers/src/layers/geo-arrow-text-layer.ts +8 -4
- package/src/deckgl-arrow-layers/src/utils/utils.ts +7 -5
- package/src/deckgl-layers/dist/deckgl-extensions/filter-arrow-layer.d.ts +1 -1
- package/src/deckgl-layers/dist/deckgl-extensions/filter-shader-module.d.ts +1 -1
- package/src/deckgl-layers/dist/deckgl-extensions/filter-shader-module.js +3 -3
- package/src/deckgl-layers/package.json +5 -5
- package/src/deckgl-layers/src/deckgl-extensions/filter-shader-module.ts +4 -3
- package/src/duckdb/dist/components/monaco-editor.js +4 -5
- package/src/duckdb/dist/components/preview-data-panel.d.ts +7 -2
- package/src/duckdb/dist/components/preview-data-panel.js +7 -8
- package/src/duckdb/dist/components/schema-panel.d.ts +12 -3
- package/src/duckdb/dist/components/schema-panel.js +41 -24
- package/src/duckdb/dist/components/sql-panel.js +245 -56
- package/src/duckdb/dist/init.js +20 -14
- package/src/duckdb/dist/processors/data-processor.d.ts +1 -0
- package/src/duckdb/dist/processors/data-processor.js +4 -4
- package/src/duckdb/dist/table/duckdb-table-utils.d.ts +106 -0
- package/src/duckdb/dist/table/duckdb-table-utils.js +493 -2
- package/src/duckdb/dist/table/duckdb-table.d.ts +5 -3
- package/src/duckdb/dist/table/duckdb-table.js +153 -261
- package/src/duckdb/package.json +6 -6
- package/src/duckdb/src/components/monaco-editor.tsx +3 -3
- package/src/duckdb/src/components/preview-data-panel.tsx +15 -8
- package/src/duckdb/src/components/schema-panel.tsx +92 -21
- package/src/duckdb/src/components/sql-panel.tsx +160 -22
- package/src/duckdb/src/init.ts +4 -1
- package/src/duckdb/src/processors/data-processor.ts +2 -2
- package/src/duckdb/src/table/duckdb-table-utils.ts +407 -1
- package/src/duckdb/src/table/duckdb-table.ts +61 -92
- package/src/effects/package.json +5 -5
- package/src/layers/dist/arc-layer/arc-layer.d.ts +1 -1
- package/src/layers/dist/arc-layer/arc-layer.js +2 -3
- package/src/layers/dist/base-layer.d.ts +7 -6
- package/src/layers/dist/base-layer.js +36 -19
- package/src/layers/dist/geojson-layer/geojson-layer.js +4 -2
- package/src/layers/dist/geojson-layer/geojson-utils.js +10 -3
- package/src/layers/dist/heatmap-layer/heatmap-layer.d.ts +2 -0
- package/src/layers/dist/heatmap-layer/heatmap-layer.js +16 -9
- package/src/layers/dist/icon-layer/icon-layer.d.ts +6 -3
- package/src/layers/dist/icon-layer/icon-layer.js +19 -11
- package/src/layers/dist/index.d.ts +1 -1
- package/src/layers/dist/index.js +1 -1
- package/src/layers/dist/layer-text-label.d.ts +9 -0
- package/src/layers/dist/layer-text-label.js +51 -6
- package/src/layers/dist/layer-utils.d.ts +39 -30
- package/src/layers/dist/layer-utils.js +69 -9
- package/src/layers/dist/line-layer/line-layer.d.ts +1 -1
- package/src/layers/dist/line-layer/line-layer.js +2 -3
- package/src/layers/dist/point-layer/point-layer.d.ts +10 -7
- package/src/layers/dist/point-layer/point-layer.js +16 -8
- package/src/layers/dist/trip-layer/trip-layer.d.ts +2 -2
- package/src/layers/dist/trip-layer/trip-layer.js +4 -2
- package/src/layers/dist/vector-tile/abstract-tile-layer.d.ts +3 -7
- package/src/layers/dist/vector-tile/abstract-tile-layer.js +7 -6
- package/src/layers/dist/vector-tile/vector-tile-layer.d.ts +5 -7
- package/src/layers/dist/vector-tile/vector-tile-layer.js +23 -2
- package/src/layers/package.json +11 -10
- package/src/layers/src/arc-layer/arc-layer.ts +1 -1
- package/src/layers/src/base-layer.ts +50 -9
- package/src/layers/src/geojson-layer/geojson-layer.ts +3 -1
- package/src/layers/src/geojson-layer/geojson-utils.ts +4 -1
- package/src/layers/src/heatmap-layer/heatmap-layer.ts +17 -12
- package/src/layers/src/icon-layer/icon-layer.ts +7 -3
- package/src/layers/src/index.ts +1 -2
- package/src/layers/src/layer-text-label.ts +53 -4
- package/src/layers/src/layer-utils.ts +88 -9
- package/src/layers/src/line-layer/line-layer.ts +1 -1
- package/src/layers/src/point-layer/point-layer.ts +15 -11
- package/src/layers/src/trip-layer/trip-layer.ts +4 -2
- package/src/layers/src/vector-tile/abstract-tile-layer.ts +11 -5
- package/src/layers/src/vector-tile/vector-tile-layer.ts +33 -4
- package/src/localization/package.json +1 -1
- package/src/processors/dist/data-processor.d.ts +15 -1
- package/src/processors/dist/data-processor.js +104 -11
- package/src/processors/package.json +8 -7
- package/src/processors/src/data-processor.ts +116 -13
- package/src/reducers/dist/export-utils.js +2 -2
- package/src/reducers/dist/layer-utils.js +1 -1
- package/src/reducers/dist/provider-state-updaters.d.ts +1 -0
- package/src/reducers/dist/provider-state-updaters.js +5 -2
- package/src/reducers/dist/vis-state-updaters.d.ts +1 -1
- package/src/reducers/dist/vis-state-updaters.js +23 -12
- package/src/reducers/package.json +16 -16
- package/src/reducers/src/export-utils.ts +1 -1
- package/src/reducers/src/layer-utils.ts +2 -1
- package/src/reducers/src/provider-state-updaters.ts +4 -1
- package/src/reducers/src/vis-state-updaters.ts +45 -10
- package/src/schemas/dist/dataset-schema.js +35 -6
- package/src/schemas/package.json +7 -7
- package/src/schemas/src/dataset-schema.ts +36 -8
- package/src/styles/node_modules/.cache/terser-webpack-plugin/content-v2/sha512/a3/a9/7f26bbc52905b0e21e9366a30ef0ba98f4ccf9aa8dd8326d90ba90191ba2e305323f505bf134c825356156c793bf9386ed2cef2aeb48eb327c26d106f69a +1 -0
- package/src/styles/node_modules/.cache/terser-webpack-plugin/index-v5/7e/51/b8756555a0e2e696d944445ac23f7febf769006013fbc8419f39a701649d +2 -0
- package/src/styles/package.json +2 -2
- package/src/styles/umd/keplergl.min.js +2 -2
- package/src/table/dist/dataset-utils.js +23 -14
- package/src/table/dist/src/table/src/dataset-utils.d.ts +1 -1
- package/src/table/dist/src/table/src/tileset/vector-tile-utils.d.ts +17 -1
- package/src/table/dist/tileset/vector-tile-utils.js +122 -2
- package/src/table/package.json +5 -5
- package/src/table/src/dataset-utils.ts +16 -6
- package/src/table/src/tileset/vector-tile-utils.ts +131 -8
- package/src/tasks/package.json +2 -2
- package/src/types/actions.d.ts +1 -0
- package/src/types/layers.d.ts +1 -0
- package/src/types/package.json +1 -1
- package/src/types/types.d.ts +17 -0
- package/src/utils/dist/application-config.d.ts +4 -3
- package/src/utils/dist/application-config.js +3 -2
- package/src/utils/dist/data-scale-utils.js +4 -4
- package/src/utils/dist/data-utils.d.ts +9 -3
- package/src/utils/dist/data-utils.js +40 -7
- package/src/utils/dist/dataset-utils.js +23 -8
- package/src/utils/dist/indexed-data-container.d.ts +2 -2
- package/src/utils/dist/indexed-data-container.js +1 -1
- package/src/utils/dist/map-style-utils/mapbox-utils.d.ts +1 -1
- package/src/utils/dist/map-style-utils/mapbox-utils.js +3 -3
- package/src/utils/dist/plot.d.ts +2 -2
- package/src/utils/dist/plot.js +6 -6
- package/src/utils/map-utils.spec.js +1 -2
- package/src/utils/package.json +5 -4
- package/src/utils/src/application-config.ts +11 -8
- package/src/utils/src/data-scale-utils.ts +3 -3
- package/src/utils/src/data-utils.ts +36 -7
- package/src/utils/src/dataset-utils.ts +41 -9
- package/src/utils/src/indexed-data-container.ts +1 -1
- package/src/utils/src/map-style-utils/mapbox-utils.ts +2 -2
- package/src/utils/src/plot.ts +5 -5
- package/umd/keplergl.min.js +1279 -1295
|
@@ -7,9 +7,153 @@
|
|
|
7
7
|
|
|
8
8
|
// Copied from loaders.gl/geoarrow
|
|
9
9
|
|
|
10
|
-
// TODO: Remove once Kepler.gl is upgraded to loaders.gl 4.4+
|
|
10
|
+
// TODO: Remove isGeoArrow* once Kepler.gl is upgraded to loaders.gl 4.4+
|
|
11
11
|
|
|
12
|
+
import * as arrow from 'apache-arrow';
|
|
12
13
|
import {DataType} from 'apache-arrow/type';
|
|
14
|
+
import {AsyncDuckDBConnection, DuckDBDataProtocol} from '@duckdb/duckdb-wasm';
|
|
15
|
+
|
|
16
|
+
import {GEOARROW_EXTENSIONS, GEOARROW_METADATA_KEY} from '@kepler.gl/constants';
|
|
17
|
+
import {ProtoDatasetField} from '@kepler.gl/types';
|
|
18
|
+
|
|
19
|
+
import {getDuckDB} from '../init';
|
|
20
|
+
|
|
21
|
+
export const SUPPORTED_DUCKDB_DROP_EXTENSIONS = ['arrow', 'csv', 'geojson', 'json', 'parquet'];
|
|
22
|
+
|
|
23
|
+
export type DuckDBColumnDesc = {name: string; type: string};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Queries a DuckDB table for the schema description.
|
|
27
|
+
* @param connection An active DuckDB connection.
|
|
28
|
+
* @param tableName A name of DuckDB table to query.
|
|
29
|
+
* @returns An array of column names and DuckDB types.
|
|
30
|
+
*/
|
|
31
|
+
export async function getDuckDBColumnTypes(
|
|
32
|
+
connection: AsyncDuckDBConnection,
|
|
33
|
+
tableName: string
|
|
34
|
+
): Promise<DuckDBColumnDesc[]> {
|
|
35
|
+
const resDescribe = await connection.query(`DESCRIBE "${tableName}";`);
|
|
36
|
+
|
|
37
|
+
const duckDbTypes: DuckDBColumnDesc[] = [];
|
|
38
|
+
const numRows = resDescribe.numRows;
|
|
39
|
+
for (let i = 0; i < numRows; ++i) {
|
|
40
|
+
const columnName = resDescribe.getChildAt(0)?.get(i);
|
|
41
|
+
const columnType = resDescribe.getChildAt(1)?.get(i);
|
|
42
|
+
|
|
43
|
+
duckDbTypes.push({
|
|
44
|
+
name: columnName,
|
|
45
|
+
type: columnType
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return duckDbTypes;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generates a mapping of column names to their corresponding DuckDB data types.
|
|
54
|
+
* @param columns An array of column descriptions from DuckDB. Check getDuckDBColumnTypes.
|
|
55
|
+
* @returns A record where keys are column names and values are their data types.
|
|
56
|
+
*/
|
|
57
|
+
export function getDuckDBColumnTypesMap(columns: DuckDBColumnDesc[]) {
|
|
58
|
+
return columns.reduce((acc, value) => {
|
|
59
|
+
acc[value.name] = value.type;
|
|
60
|
+
return acc;
|
|
61
|
+
}, {} as Record<string, string>);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Constructs an SQL query to select all columns from a given table,
|
|
66
|
+
* converting specified columns to Well-Known Binary (WKB) format using ST_AsWKB.
|
|
67
|
+
* @param tableName The name of the table from which to select data.
|
|
68
|
+
* @param columnsToConvertToWKB An array of column names that should be converted to WKB format.
|
|
69
|
+
* @returns The constructed SQL query.
|
|
70
|
+
*/
|
|
71
|
+
export function constructST_asWKBQuery(tableName: string, columnsToConvertToWKB: string[]): string {
|
|
72
|
+
const exclude =
|
|
73
|
+
columnsToConvertToWKB.length > 0 ? `EXCLUDE ${columnsToConvertToWKB.join(', ')}` : '';
|
|
74
|
+
const asWKB =
|
|
75
|
+
columnsToConvertToWKB.length > 0
|
|
76
|
+
? `, ${columnsToConvertToWKB.map(column => `ST_AsWKB(${column}) as ${column}`).join(', ')}`
|
|
77
|
+
: '';
|
|
78
|
+
return `SELECT * ${exclude} ${asWKB} FROM '${tableName}';`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Finds the names of columns that have a GEOMETRY type.
|
|
83
|
+
* @param columns An array of column descriptors from a DuckDB table.
|
|
84
|
+
* @returns An array of column names that are of type GEOMETRY.
|
|
85
|
+
*/
|
|
86
|
+
export function getGeometryColumns(columns: DuckDBColumnDesc[]): string[] {
|
|
87
|
+
return columns.filter(column => column.type === 'GEOMETRY').map(column => column.name);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Sets the GeoArrow WKB extension metadata for columns of type GEOMETRY in an Arrow table.
|
|
92
|
+
* @param table The Apache Arrow table whose schema fields will be modified.
|
|
93
|
+
* @param columns An array of column descriptors from a DuckDB table.
|
|
94
|
+
*/
|
|
95
|
+
export function setGeoArrowWKBExtension(table: arrow.Table, columns: DuckDBColumnDesc[]) {
|
|
96
|
+
table.schema.fields.forEach(field => {
|
|
97
|
+
const info = columns.find(t => t.name === field.name);
|
|
98
|
+
if (info?.type === 'GEOMETRY') {
|
|
99
|
+
field.metadata.set(GEOARROW_METADATA_KEY, GEOARROW_EXTENSIONS.WKB);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Creates an arrow table from an array of arrow vectors and fields.
|
|
106
|
+
* @param columns An array of arrow vectors.
|
|
107
|
+
* @param fields An array of fields per arrow vector.
|
|
108
|
+
* @param arrowSchema Optional arrow table schema when available.
|
|
109
|
+
* @returns An arrow table.
|
|
110
|
+
*/
|
|
111
|
+
export const restoreArrowTable = (
|
|
112
|
+
columns: arrow.Vector[],
|
|
113
|
+
fields: ProtoDatasetField[],
|
|
114
|
+
arrowSchema?: arrow.Schema
|
|
115
|
+
) => {
|
|
116
|
+
const creaOpts = {};
|
|
117
|
+
fields.map((field, index) => {
|
|
118
|
+
creaOpts[field.name] = columns[index];
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return arrowSchema ? new arrow.Table(arrowSchema, creaOpts) : new arrow.Table(creaOpts);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* DuckDb throws when geoarrow extensions are present in metadata.
|
|
126
|
+
* @param table An arrow table to clear from extensions.
|
|
127
|
+
* @returns A map of removed per field geoarrow extensions.
|
|
128
|
+
*/
|
|
129
|
+
export const removeUnsupportedExtensions = (table: arrow.Table): Record<string, string> => {
|
|
130
|
+
const removedMetadata: Record<string, string> = {};
|
|
131
|
+
table.schema.fields.forEach(field => {
|
|
132
|
+
const extension = field.metadata.get(GEOARROW_METADATA_KEY);
|
|
133
|
+
if (extension?.startsWith('geoarrow')) {
|
|
134
|
+
removedMetadata[field.name] = extension;
|
|
135
|
+
field.metadata.delete(GEOARROW_METADATA_KEY);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
return removedMetadata;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Restore removed metadata extensions after a call to removeUnsupportedExtensions.
|
|
143
|
+
* @param table An arrow table to restore geoarrow extensions.
|
|
144
|
+
* @param removedExtensions A map of per field geoarrow extensions to restore.
|
|
145
|
+
*/
|
|
146
|
+
export const restoreUnsupportedExtensions = (
|
|
147
|
+
table: arrow.Table,
|
|
148
|
+
removedExtensions: Record<string, string>
|
|
149
|
+
) => {
|
|
150
|
+
table.schema.fields.forEach(field => {
|
|
151
|
+
const extension = removedExtensions[field.name];
|
|
152
|
+
if (extension) {
|
|
153
|
+
field.metadata.set(GEOARROW_METADATA_KEY, extension);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
};
|
|
13
157
|
|
|
14
158
|
/** Checks whether the given Apache Arrow JS type is a Point data type */
|
|
15
159
|
export function isGeoArrowPoint(type: DataType) {
|
|
@@ -104,3 +248,265 @@ export function isGeoArrowMultiPolygon(type: DataType) {
|
|
|
104
248
|
|
|
105
249
|
return true;
|
|
106
250
|
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Checks if the given SQL query is a SELECT query by using the EXPLAIN command.
|
|
254
|
+
* @param connection The DuckDB connection instance.
|
|
255
|
+
* @param query The SQL query to check.
|
|
256
|
+
* @returns Resolves to `true` if the query is a SELECT statement, otherwise `false`.
|
|
257
|
+
*/
|
|
258
|
+
export async function checkIsSelectQuery(
|
|
259
|
+
connection: AsyncDuckDBConnection,
|
|
260
|
+
query: string
|
|
261
|
+
): Promise<boolean> {
|
|
262
|
+
try {
|
|
263
|
+
const result = await connection.query(`EXPLAIN (${query})`);
|
|
264
|
+
return result.numRows > 0;
|
|
265
|
+
} catch (error) {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Split a string with potentially multiple SQL queries (separated as usual by ';') into an array of queries.
|
|
272
|
+
* This implementation:
|
|
273
|
+
* - Handles single and double quoted strings with proper escaping
|
|
274
|
+
* - Ignores semicolons in line comments (--) and block comments (slash asterisk)
|
|
275
|
+
* - Trims whitespace from queries
|
|
276
|
+
* - Handles SQL-style escaped quotes ('' inside strings)
|
|
277
|
+
* - Returns only non-empty queries
|
|
278
|
+
* @param input A string with potentially multiple SQL queries.
|
|
279
|
+
* @returns An array of queries.
|
|
280
|
+
*/
|
|
281
|
+
export function splitSqlStatements(input: string): string[] {
|
|
282
|
+
const queries: string[] = [];
|
|
283
|
+
let currentQuery = '';
|
|
284
|
+
let inSingleQuote = false;
|
|
285
|
+
let inDoubleQuote = false;
|
|
286
|
+
let inLineComment = false;
|
|
287
|
+
let inBlockComment = false;
|
|
288
|
+
|
|
289
|
+
for (let i = 0; i < input.length; i++) {
|
|
290
|
+
const char = input[i];
|
|
291
|
+
|
|
292
|
+
if (inLineComment) {
|
|
293
|
+
currentQuery += char;
|
|
294
|
+
if (char === '\n') {
|
|
295
|
+
inLineComment = false;
|
|
296
|
+
}
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (inBlockComment) {
|
|
301
|
+
currentQuery += char;
|
|
302
|
+
if (char === '*' && input[i + 1] === '/') {
|
|
303
|
+
inBlockComment = false;
|
|
304
|
+
currentQuery += input[++i]; // Consume '/'
|
|
305
|
+
}
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (inSingleQuote) {
|
|
310
|
+
currentQuery += char;
|
|
311
|
+
if (char === "'") {
|
|
312
|
+
// Handle escaped single quotes in SQL
|
|
313
|
+
if (i + 1 < input.length && input[i + 1] === "'") {
|
|
314
|
+
currentQuery += input[++i];
|
|
315
|
+
} else {
|
|
316
|
+
inSingleQuote = false;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (inDoubleQuote) {
|
|
323
|
+
currentQuery += char;
|
|
324
|
+
if (char === '"') {
|
|
325
|
+
// Handle escaped double quotes
|
|
326
|
+
if (i + 1 < input.length && input[i + 1] === '"') {
|
|
327
|
+
currentQuery += input[++i];
|
|
328
|
+
} else {
|
|
329
|
+
inDoubleQuote = false;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Check for comment starts
|
|
336
|
+
if (char === '-' && input[i + 1] === '-') {
|
|
337
|
+
inLineComment = true;
|
|
338
|
+
currentQuery += char + input[++i];
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (char === '/' && input[i + 1] === '*') {
|
|
343
|
+
inBlockComment = true;
|
|
344
|
+
currentQuery += char + input[++i];
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Check for quote starts
|
|
349
|
+
if (char === "'") {
|
|
350
|
+
inSingleQuote = true;
|
|
351
|
+
currentQuery += char;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (char === '"') {
|
|
356
|
+
inDoubleQuote = true;
|
|
357
|
+
currentQuery += char;
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Handle query separator
|
|
362
|
+
if (char === ';') {
|
|
363
|
+
const trimmed = currentQuery.trim();
|
|
364
|
+
if (trimmed.length > 0) {
|
|
365
|
+
queries.push(trimmed);
|
|
366
|
+
}
|
|
367
|
+
currentQuery = '';
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
currentQuery += char;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Add the final query
|
|
375
|
+
const trimmed = currentQuery.trim();
|
|
376
|
+
if (trimmed.length > 0) {
|
|
377
|
+
queries.push(trimmed);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return queries;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Removes SQL comments from a given SQL string.
|
|
385
|
+
* @param sql The SQL query string from which comments should be removed.
|
|
386
|
+
* @returns The cleaned SQL string without comments.
|
|
387
|
+
*/
|
|
388
|
+
export function removeSQLComments(sql: string): string {
|
|
389
|
+
// Remove multi-line comments (/* ... */)
|
|
390
|
+
sql = sql.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
391
|
+
// Remove single-line comments (-- ...)
|
|
392
|
+
sql = sql.replace(/--.*$/gm, '');
|
|
393
|
+
return sql.trim();
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Drops a table if it exists in the DuckDB database.
|
|
398
|
+
* @param connection The DuckDB connection instance.
|
|
399
|
+
* @param tableName The name of the table to drop.
|
|
400
|
+
* @returns A promise that resolves when the operation is complete.
|
|
401
|
+
* @throws Logs an error if the table drop operation fails.
|
|
402
|
+
*/
|
|
403
|
+
export const dropTableIfExists = async (connection: AsyncDuckDBConnection, tableName: string) => {
|
|
404
|
+
try {
|
|
405
|
+
await connection.query(`DROP TABLE IF EXISTS "${tableName}";`);
|
|
406
|
+
} catch (error) {
|
|
407
|
+
console.error('Dropping table failed', tableName, error);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Imports a file into DuckDB as a table, supporting multiple formats from SUPPORTED_DUCKDB_DROP_EXTENSIONS.
|
|
413
|
+
* @param file The file to be imported.
|
|
414
|
+
* @returns A promise that resolves when the file has been processed into a DuckDB table.
|
|
415
|
+
*/
|
|
416
|
+
export async function tableFromFile(file: File | null): Promise<null | Error> {
|
|
417
|
+
if (!file) return new Error('File Drag & Drop: No file');
|
|
418
|
+
|
|
419
|
+
const fileExt = SUPPORTED_DUCKDB_DROP_EXTENSIONS.find(ext => file.name.endsWith(ext));
|
|
420
|
+
if (!fileExt) {
|
|
421
|
+
return new Error("File Drag & Drop: File extension isn't supported");
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const db = await getDuckDB();
|
|
425
|
+
const c = await db.connect();
|
|
426
|
+
|
|
427
|
+
let error: Error | null = null;
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
const tableName = sanitizeDuckDBTableName(file.name);
|
|
431
|
+
const sourceName = 'temp_file_handle';
|
|
432
|
+
|
|
433
|
+
c.query(`install spatial;
|
|
434
|
+
load spatial;`);
|
|
435
|
+
|
|
436
|
+
if (fileExt === 'arrow') {
|
|
437
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
438
|
+
const uint8Array = new Uint8Array(arrayBuffer);
|
|
439
|
+
const arrowTable = arrow.tableFromIPC(uint8Array);
|
|
440
|
+
|
|
441
|
+
await c.insertArrowTable(arrowTable, {name: tableName});
|
|
442
|
+
} else {
|
|
443
|
+
await db.registerFileHandle(sourceName, file, DuckDBDataProtocol.BROWSER_FILEREADER, true);
|
|
444
|
+
|
|
445
|
+
if (fileExt === 'csv') {
|
|
446
|
+
await c.query(`
|
|
447
|
+
CREATE TABLE '${tableName}' AS
|
|
448
|
+
SELECT *
|
|
449
|
+
FROM read_csv('${sourceName}', header = true, auto_detect = true, sample_size = -1);
|
|
450
|
+
`);
|
|
451
|
+
} else if (fileExt === 'json') {
|
|
452
|
+
await c.query(`
|
|
453
|
+
CREATE TABLE '${tableName}' AS
|
|
454
|
+
SELECT *
|
|
455
|
+
FROM read_json_auto('${sourceName}');
|
|
456
|
+
`);
|
|
457
|
+
} else if (fileExt === 'geojson') {
|
|
458
|
+
await c.query(`
|
|
459
|
+
CREATE TABLE '${tableName}' AS
|
|
460
|
+
SELECT *
|
|
461
|
+
FROM ST_READ('${sourceName}', keep_wkb = TRUE);
|
|
462
|
+
`);
|
|
463
|
+
} else if (fileExt === 'parquet') {
|
|
464
|
+
await c.query(`
|
|
465
|
+
CREATE TABLE '${tableName}' AS
|
|
466
|
+
SELECT *
|
|
467
|
+
FROM read_parquet('${sourceName}')
|
|
468
|
+
`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
} catch (errorData) {
|
|
472
|
+
if (errorData instanceof Error) {
|
|
473
|
+
const message = errorData.message || '';
|
|
474
|
+
// output more readable errors for known issues
|
|
475
|
+
if (message.includes('Arrow Type with extension name: geoarrow')) {
|
|
476
|
+
error = new Error(
|
|
477
|
+
'The GeoArrow extensions are not implemented in the connected DuckDB version.'
|
|
478
|
+
);
|
|
479
|
+
} else if (message.includes("Geoparquet column 'geometry' does not have geometry types")) {
|
|
480
|
+
error = new Error(
|
|
481
|
+
`Invalid Input Error: Geoparquet column 'geometry' does not have geometry types.
|
|
482
|
+
Possible reasons:
|
|
483
|
+
- Old .parquet files that don't match the Parquet format specification.
|
|
484
|
+
- Unsupported compression.`
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (!error) {
|
|
490
|
+
error = errorData as Error;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
await c.close();
|
|
495
|
+
|
|
496
|
+
return error;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Sanitizes a file name to be a valid DuckDB table name.
|
|
501
|
+
* @param fileName The input file name to be sanitized.
|
|
502
|
+
* @returns A valid DuckDB table name.
|
|
503
|
+
*/
|
|
504
|
+
export function sanitizeDuckDBTableName(fileName: string): string {
|
|
505
|
+
// Replace invalid characters with underscores
|
|
506
|
+
let name = fileName.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
507
|
+
// Ensure it doesn't start with a digit
|
|
508
|
+
if (/^\d/.test(name)) {
|
|
509
|
+
name = `t_${name}`;
|
|
510
|
+
}
|
|
511
|
+
return name || 'default_table';
|
|
512
|
+
}
|
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
import * as arrow from 'apache-arrow';
|
|
5
5
|
import {AsyncDuckDB, AsyncDuckDBConnection} from '@duckdb/duckdb-wasm';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import {
|
|
8
|
+
DatasetType,
|
|
9
|
+
DATASET_FORMATS,
|
|
10
|
+
GEOARROW_EXTENSIONS,
|
|
11
|
+
GEOARROW_METADATA_KEY
|
|
12
|
+
} from '@kepler.gl/constants';
|
|
10
13
|
import {
|
|
11
14
|
arrowSchemaToFields,
|
|
12
15
|
isArrowData,
|
|
@@ -15,6 +18,8 @@ import {
|
|
|
15
18
|
isRowObject,
|
|
16
19
|
processArrowBatches
|
|
17
20
|
} from '@kepler.gl/processors';
|
|
21
|
+
import {KeplerTable} from '@kepler.gl/table';
|
|
22
|
+
import {Field} from '@kepler.gl/types';
|
|
18
23
|
|
|
19
24
|
import {getDuckDB} from '../init';
|
|
20
25
|
import {
|
|
@@ -33,6 +38,17 @@ import {
|
|
|
33
38
|
isGeoArrowMultiPolygon
|
|
34
39
|
} from './duckdb-table-utils';
|
|
35
40
|
|
|
41
|
+
import {
|
|
42
|
+
constructST_asWKBQuery,
|
|
43
|
+
dropTableIfExists,
|
|
44
|
+
getDuckDBColumnTypes,
|
|
45
|
+
getDuckDBColumnTypesMap,
|
|
46
|
+
getGeometryColumns,
|
|
47
|
+
removeUnsupportedExtensions,
|
|
48
|
+
restoreArrowTable,
|
|
49
|
+
restoreUnsupportedExtensions
|
|
50
|
+
} from '../table/duckdb-table-utils';
|
|
51
|
+
|
|
36
52
|
// TODO use files from disk or url directly, without parsing by loaders and then ingection into DeckDb
|
|
37
53
|
|
|
38
54
|
/**
|
|
@@ -52,47 +68,24 @@ const KEPLER_GEOM_FROM_GEOJSON_COLUMN = '_geojson';
|
|
|
52
68
|
* Names of columns that most likely contain binary wkb geometry
|
|
53
69
|
*/
|
|
54
70
|
const SUGGESTED_GEOM_COLUMNS = {
|
|
55
|
-
[KEPLER_GEOM_FROM_GEOJSON_COLUMN]:
|
|
56
|
-
[DUCKDB_GEOM_COLUMN]:
|
|
57
|
-
geometry:
|
|
71
|
+
[KEPLER_GEOM_FROM_GEOJSON_COLUMN]: GEOARROW_EXTENSIONS.WKB,
|
|
72
|
+
[DUCKDB_GEOM_COLUMN]: GEOARROW_EXTENSIONS.WKB,
|
|
73
|
+
geometry: GEOARROW_EXTENSIONS.WKB
|
|
58
74
|
};
|
|
59
75
|
|
|
60
76
|
type ImportDataToDuckProps = {
|
|
61
|
-
data: ProcessorResult;
|
|
77
|
+
data: ProcessorResult & {arrowSchema: arrow.Schema};
|
|
62
78
|
db: AsyncDuckDB;
|
|
63
79
|
c: AsyncDuckDBConnection;
|
|
64
80
|
};
|
|
65
81
|
|
|
66
82
|
type ImportDataToDuckResult = {
|
|
67
|
-
// DuckDb drops geoarrow metadata, so try to preserve
|
|
68
|
-
|
|
83
|
+
// DuckDb drops geoarrow metadata, so try to preserve and then restore the extension name
|
|
84
|
+
geoarrowMetadata?: Record<string, string>;
|
|
69
85
|
// Use fields from arrow table even if fields are provided
|
|
70
86
|
useNewFields?: boolean;
|
|
71
87
|
};
|
|
72
88
|
|
|
73
|
-
/**
|
|
74
|
-
* Creates an arrow table from arrow vectors and fields.
|
|
75
|
-
* @param columns An array of arrow vectors.
|
|
76
|
-
* @param fields An array of fields per arrow vector.
|
|
77
|
-
* @returns An arrow table.
|
|
78
|
-
*/
|
|
79
|
-
// TODO: consider using original drag&dropped arrow table, as it could contain extra metadata, not passed to the fields.
|
|
80
|
-
const restoreArrowTable = (columns: arrow.Vector[], fields: ProtoDatasetField[]) => {
|
|
81
|
-
const creaOpts = {};
|
|
82
|
-
fields.map((field, index) => {
|
|
83
|
-
creaOpts[field.name] = columns[index];
|
|
84
|
-
});
|
|
85
|
-
return new arrow.Table(creaOpts);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const dropIfTableExists = async (c: AsyncDuckDBConnection, tableName: string) => {
|
|
89
|
-
try {
|
|
90
|
-
await c.query(`DROP TABLE IF EXISTS "${tableName}";`);
|
|
91
|
-
} catch (error) {
|
|
92
|
-
console.error('Dropping table failed', tableName, error);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
89
|
export class KeplerGlDuckDbTable extends KeplerTable {
|
|
97
90
|
declare readonly id: string;
|
|
98
91
|
declare label: string;
|
|
@@ -157,18 +150,23 @@ export class KeplerGlDuckDbTable extends KeplerTable {
|
|
|
157
150
|
}
|
|
158
151
|
|
|
159
152
|
return {
|
|
160
|
-
|
|
153
|
+
// _geojson column is created from geometry with keep_wkb flag and contains valid WKB data.
|
|
154
|
+
geoarrowMetadata: {[KEPLER_GEOM_FROM_GEOJSON_COLUMN]: GEOARROW_EXTENSIONS.WKB}
|
|
161
155
|
};
|
|
162
156
|
}
|
|
163
157
|
|
|
164
|
-
async importArrowData({data,
|
|
158
|
+
async importArrowData({data, c}: ImportDataToDuckProps): Promise<ImportDataToDuckResult> {
|
|
159
|
+
let adjustedMetadata = {};
|
|
165
160
|
try {
|
|
166
161
|
// 1) data.rows contains an arrow table created by Add to Map data from DuckDb query.
|
|
167
162
|
// 2) arrow table is in cols & fields when a file is dragged & dropped into Add Data To Map dialog.
|
|
168
163
|
const arrowTable =
|
|
169
164
|
data.rows instanceof arrow.Table
|
|
170
165
|
? data.rows
|
|
171
|
-
: restoreArrowTable(data.cols || [], data.fields);
|
|
166
|
+
: restoreArrowTable(data.cols || [], data.fields, data.arrowSchema);
|
|
167
|
+
|
|
168
|
+
// remove unsupported extensions from an arrow table that throw exceptions in DuckDB.
|
|
169
|
+
adjustedMetadata = removeUnsupportedExtensions(arrowTable);
|
|
172
170
|
|
|
173
171
|
const setupSql = `
|
|
174
172
|
install spatial;
|
|
@@ -176,6 +174,9 @@ export class KeplerGlDuckDbTable extends KeplerTable {
|
|
|
176
174
|
`;
|
|
177
175
|
await c.query(setupSql);
|
|
178
176
|
await c.insertArrowTable(arrowTable, {name: this.label});
|
|
177
|
+
|
|
178
|
+
// restore unsupported extensions that throw exceptions in DuckDb
|
|
179
|
+
restoreUnsupportedExtensions(arrowTable, adjustedMetadata);
|
|
179
180
|
} catch (error) {
|
|
180
181
|
// Known issues:
|
|
181
182
|
// 1) Arrow Type with extension name: geoarrow.point and format: +w:2 is not currently supported in DuckDB.
|
|
@@ -183,8 +184,8 @@ export class KeplerGlDuckDbTable extends KeplerTable {
|
|
|
183
184
|
}
|
|
184
185
|
|
|
185
186
|
return {
|
|
186
|
-
|
|
187
|
-
// use
|
|
187
|
+
geoarrowMetadata: {...SUGGESTED_GEOM_COLUMNS, ...adjustedMetadata},
|
|
188
|
+
// use fields generated from the created arrow table
|
|
188
189
|
useNewFields: true
|
|
189
190
|
};
|
|
190
191
|
}
|
|
@@ -199,7 +200,7 @@ export class KeplerGlDuckDbTable extends KeplerTable {
|
|
|
199
200
|
const c = await db.connect();
|
|
200
201
|
|
|
201
202
|
const tableName = this.label;
|
|
202
|
-
await
|
|
203
|
+
await dropTableIfExists(c, tableName);
|
|
203
204
|
|
|
204
205
|
let format = this.metadata.format;
|
|
205
206
|
if (!format) {
|
|
@@ -228,27 +229,26 @@ export class KeplerGlDuckDbTable extends KeplerTable {
|
|
|
228
229
|
let cols: arrow.Vector[] = [];
|
|
229
230
|
|
|
230
231
|
try {
|
|
231
|
-
const {
|
|
232
|
+
const {geoarrowMetadata = {}, useNewFields = false} = importDetails || {};
|
|
232
233
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const
|
|
234
|
+
const duckDbColumns = await getDuckDBColumnTypes(c, tableName);
|
|
235
|
+
const tableDuckDBTypes = getDuckDBColumnTypesMap(duckDbColumns);
|
|
236
|
+
const columnsToConvertToWKB = getGeometryColumns(duckDbColumns);
|
|
237
|
+
const adjustedQuery = constructST_asWKBQuery(tableName, columnsToConvertToWKB);
|
|
238
|
+
const arrowResult = await c.query(adjustedQuery);
|
|
236
239
|
|
|
237
|
-
|
|
238
|
-
const arrowResult = await c.query(getArrowQuery);
|
|
240
|
+
// TODO if format is an arrow table then just use the original one, instead of the new table from the query?
|
|
239
241
|
|
|
240
|
-
restoreGeoarrowMetadata(arrowResult,
|
|
242
|
+
restoreGeoarrowMetadata(arrowResult, geoarrowMetadata);
|
|
241
243
|
|
|
242
244
|
fields = useNewFields
|
|
243
|
-
? arrowSchemaToFields(arrowResult
|
|
244
|
-
: data.fields ?? arrowSchemaToFields(arrowResult
|
|
245
|
+
? arrowSchemaToFields(arrowResult, tableDuckDBTypes)
|
|
246
|
+
: data.fields ?? arrowSchemaToFields(arrowResult, tableDuckDBTypes);
|
|
245
247
|
cols = [...Array(arrowResult.numCols).keys()]
|
|
246
248
|
.map(i => arrowResult.getChildAt(i))
|
|
247
249
|
.filter(col => col) as arrow.Vector[];
|
|
248
250
|
} catch (error) {
|
|
249
|
-
|
|
250
|
-
// 1) ST_AsWKB binder error, no type match
|
|
251
|
-
console.error('DuckDb table: binary geometry column to wkb', error);
|
|
251
|
+
console.error('DuckDB table: createTableAndGetArrow', error);
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
await c.close();
|
|
@@ -285,7 +285,6 @@ export class KeplerGlDuckDbTable extends KeplerTable {
|
|
|
285
285
|
let format;
|
|
286
286
|
if (inputFormat === DATASET_FORMATS.arrow || isArrowData(data)) {
|
|
287
287
|
format = DATASET_FORMATS.arrow;
|
|
288
|
-
// TODO injest arrow files from disk without loaders
|
|
289
288
|
processor = processArrowBatches;
|
|
290
289
|
} else if (inputFormat === DATASET_FORMATS.keplergl || isKeplerGlMap(data)) {
|
|
291
290
|
format = DATASET_FORMATS.keplergl;
|
|
@@ -302,6 +301,7 @@ export class KeplerGlDuckDbTable extends KeplerTable {
|
|
|
302
301
|
};
|
|
303
302
|
|
|
304
303
|
static getInputDataValidator = function () {
|
|
304
|
+
// In DuckDB mode data is validated later during import.
|
|
305
305
|
return d => d;
|
|
306
306
|
};
|
|
307
307
|
}
|
|
@@ -310,58 +310,27 @@ export class KeplerGlDuckDbTable extends KeplerTable {
|
|
|
310
310
|
* Try to restore geoarrow metadata lost during DuckDb ingestion.
|
|
311
311
|
* Note that this function can generate wrong geometry types.
|
|
312
312
|
* @param arrowTable Arrow table to update.
|
|
313
|
-
* @param
|
|
314
|
-
* @param addWKBMetadata A map with field names that usually used to store WKB geometry.
|
|
313
|
+
* @param geoarrowMetadata A map with field names that usually used to store geoarrow geometry.
|
|
315
314
|
*/
|
|
316
315
|
const restoreGeoarrowMetadata = async (
|
|
317
316
|
arrowTable: arrow.Table,
|
|
318
|
-
|
|
319
|
-
addWKBMetadata: Record<string, boolean>
|
|
317
|
+
geoarrowMetadata: Record<string, string>
|
|
320
318
|
) => {
|
|
321
319
|
arrowTable.schema.fields.forEach(f => {
|
|
322
|
-
if (arrow.DataType.isBinary(f.type) &&
|
|
323
|
-
f.metadata.set(
|
|
320
|
+
if (arrow.DataType.isBinary(f.type) && geoarrowMetadata[f.name]) {
|
|
321
|
+
f.metadata.set(GEOARROW_METADATA_KEY, geoarrowMetadata[f.name]);
|
|
324
322
|
} else if (isGeoArrowPoint(f.type)) {
|
|
325
|
-
f.metadata.set(
|
|
323
|
+
f.metadata.set(GEOARROW_METADATA_KEY, GEOARROW_EXTENSIONS.POINT);
|
|
326
324
|
} else if (isGeoArrowLineString(f.type)) {
|
|
327
|
-
f.metadata.set(
|
|
325
|
+
f.metadata.set(GEOARROW_METADATA_KEY, GEOARROW_EXTENSIONS.LINESTRING);
|
|
328
326
|
} else if (isGeoArrowPolygon(f.type)) {
|
|
329
|
-
f.metadata.set(
|
|
327
|
+
f.metadata.set(GEOARROW_METADATA_KEY, GEOARROW_EXTENSIONS.POLYGON);
|
|
330
328
|
} else if (isGeoArrowMultiPoint(f.type)) {
|
|
331
|
-
f.metadata.set(
|
|
329
|
+
f.metadata.set(GEOARROW_METADATA_KEY, GEOARROW_EXTENSIONS.MULTIPOINT);
|
|
332
330
|
} else if (isGeoArrowMultiLineString(f.type)) {
|
|
333
|
-
f.metadata.set(
|
|
331
|
+
f.metadata.set(GEOARROW_METADATA_KEY, GEOARROW_EXTENSIONS.MULTILINESTRING);
|
|
334
332
|
} else if (isGeoArrowMultiPolygon(f.type)) {
|
|
335
|
-
f.metadata.set(
|
|
333
|
+
f.metadata.set(GEOARROW_METADATA_KEY, GEOARROW_EXTENSIONS.MULTIPOLYGON);
|
|
336
334
|
}
|
|
337
335
|
});
|
|
338
336
|
};
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* For all binary columns tries to transform geometry from DuckDB's internal format into proper WKB.
|
|
342
|
-
* @param props
|
|
343
|
-
* @returns A map of fields that should be transformed to valid WKB.
|
|
344
|
-
*/
|
|
345
|
-
const tryST_AsWKB = async (props: {tableName: string; c: AsyncDuckDBConnection}) => {
|
|
346
|
-
const {c, tableName} = props;
|
|
347
|
-
|
|
348
|
-
const resultWithSchema = await c.query(`
|
|
349
|
-
SELECT * FROM '${tableName}' LIMIT 0;
|
|
350
|
-
`);
|
|
351
|
-
const binaryColumns = resultWithSchema.schema.fields.filter(f => arrow.DataType.isBinary(f.type));
|
|
352
|
-
|
|
353
|
-
const canCastST_asWKB: Record<string, boolean> = {};
|
|
354
|
-
for (const column of binaryColumns) {
|
|
355
|
-
try {
|
|
356
|
-
const columnName = column.name;
|
|
357
|
-
const query = `SELECT * EXCLUDE ${columnName}, ST_AsWKB(${columnName}) as ${columnName} FROM '${tableName}' LIMIT 1;`;
|
|
358
|
-
await c.query(query);
|
|
359
|
-
|
|
360
|
-
canCastST_asWKB[column.name] = true;
|
|
361
|
-
} catch (error) {
|
|
362
|
-
canCastST_asWKB[column.name] = false;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return canCastST_asWKB;
|
|
367
|
-
};
|