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.
Files changed (234) hide show
  1. package/README.md +27 -132
  2. package/dist/src/common-utils/src/data-type.d.ts +9 -1
  3. package/dist/src/components/src/common/data-table/header-cell.d.ts +1 -0
  4. package/dist/src/components/src/common/data-table/index.d.ts +1 -0
  5. package/dist/src/components/src/common/file-uploader/file-drop.d.ts +1 -32
  6. package/dist/src/components/src/common/item-selector/dropdown-list.d.ts +2 -2
  7. package/dist/src/components/src/common/item-selector/item-selector.d.ts +1 -1
  8. package/dist/src/components/src/common/item-selector/typeahead.d.ts +1 -1
  9. package/dist/src/components/src/geocoder/geocoder.d.ts +7 -0
  10. package/dist/src/components/src/geocoder-panel.d.ts +2 -2
  11. package/dist/src/components/src/hooks/use-fetch-vector-tile-metadata.d.ts +4 -3
  12. package/dist/src/constants/src/default-settings.d.ts +14 -1
  13. package/dist/src/deckgl-arrow-layers/src/constants.d.ts +0 -12
  14. package/dist/src/deckgl-arrow-layers/src/index.d.ts +0 -1
  15. package/dist/src/deckgl-arrow-layers/src/utils/utils.d.ts +12 -1
  16. package/dist/src/duckdb/src/components/preview-data-panel.d.ts +7 -2
  17. package/dist/src/duckdb/src/components/schema-panel.d.ts +12 -3
  18. package/dist/src/duckdb/src/processors/data-processor.d.ts +1 -0
  19. package/dist/src/duckdb/src/table/duckdb-table-utils.d.ts +106 -0
  20. package/dist/src/duckdb/src/table/duckdb-table.d.ts +5 -3
  21. package/dist/src/processors/src/data-processor.d.ts +15 -1
  22. package/dist/src/reducers/src/provider-state-updaters.d.ts +1 -0
  23. package/dist/src/reducers/src/vis-state-updaters.d.ts +1 -1
  24. package/dist/src/table/src/dataset-utils.d.ts +1 -1
  25. package/dist/src/table/src/tileset/vector-tile-utils.d.ts +17 -1
  26. package/dist/src/utils/src/application-config.d.ts +4 -3
  27. package/dist/src/utils/src/data-utils.d.ts +9 -3
  28. package/dist/src/utils/src/indexed-data-container.d.ts +2 -2
  29. package/dist/src/utils/src/map-style-utils/mapbox-utils.d.ts +1 -1
  30. package/dist/src/utils/src/map-utils.d.ts +1 -1
  31. package/dist/src/utils/src/plot.d.ts +2 -2
  32. package/package.json +5 -5
  33. package/src/actions/package.json +8 -8
  34. package/src/ai-assistant/dist/components/ai-assistant-component.js +2 -1
  35. package/src/ai-assistant/package.json +7 -7
  36. package/src/ai-assistant/src/components/ai-assistant-component.tsx +1 -0
  37. package/src/cloud-providers/package.json +2 -2
  38. package/src/common-utils/dist/data-type.d.ts +9 -1
  39. package/src/common-utils/dist/data-type.js +50 -1
  40. package/src/common-utils/package.json +3 -3
  41. package/src/common-utils/src/data-type.ts +49 -1
  42. package/src/components/dist/common/data-table/header-cell.d.ts +1 -0
  43. package/src/components/dist/common/data-table/header-cell.js +2 -2
  44. package/src/components/dist/common/data-table/index.d.ts +1 -0
  45. package/src/components/dist/common/data-table/index.js +5 -1
  46. package/src/components/dist/common/file-uploader/file-drop.d.ts +1 -32
  47. package/src/components/dist/common/file-uploader/file-drop.js +152 -167
  48. package/src/components/dist/common/item-selector/dropdown-list.d.ts +2 -2
  49. package/src/components/dist/common/item-selector/dropdown-list.js +2 -3
  50. package/src/components/dist/common/item-selector/item-selector.d.ts +1 -1
  51. package/src/components/dist/common/item-selector/typeahead.d.ts +1 -1
  52. package/src/components/dist/common/range-brush.js +3 -3
  53. package/src/components/dist/geocoder/geocoder.d.ts +7 -0
  54. package/src/components/dist/geocoder/geocoder.js +19 -11
  55. package/src/components/dist/geocoder-panel.d.ts +2 -2
  56. package/src/components/dist/geocoder-panel.js +64 -91
  57. package/src/components/dist/hooks/use-fetch-vector-tile-metadata.d.ts +4 -3
  58. package/src/components/dist/hooks/use-fetch-vector-tile-metadata.js +35 -26
  59. package/src/components/dist/hooks/use-legend-position.js +8 -4
  60. package/src/components/dist/map/map-legend-panel.js +2 -3
  61. package/src/components/dist/modal-container.js +8 -8
  62. package/src/components/dist/modals/tilesets-modals/tileset-vector-form.js +31 -12
  63. package/src/components/dist/side-panel/layer-panel/color-palette-preset.js +2 -2
  64. package/src/components/dist/side-panel/layer-panel/color-range-selector.js +10 -11
  65. package/src/components/dist/side-panel/layer-panel/color-selector.js +3 -3
  66. package/src/components/dist/side-panel/layer-panel/custom-palette.js +1 -2
  67. package/src/components/dist/side-panel/layer-panel/layer-configurator.js +6 -8
  68. package/src/components/dist/side-panel/layer-panel/vector-tile-layer-configurator.js +1 -2
  69. package/src/components/package.json +16 -16
  70. package/src/components/src/common/data-table/header-cell.tsx +2 -1
  71. package/src/components/src/common/data-table/index.tsx +4 -0
  72. package/src/components/src/common/file-uploader/file-drop.tsx +186 -161
  73. package/src/components/src/common/item-selector/dropdown-list.tsx +1 -6
  74. package/src/components/src/common/range-brush.tsx +2 -2
  75. package/src/components/src/geocoder/geocoder.tsx +16 -8
  76. package/src/components/src/geocoder-panel.tsx +95 -85
  77. package/src/components/src/hooks/use-fetch-vector-tile-metadata.ts +27 -25
  78. package/src/components/src/hooks/use-legend-position.ts +5 -2
  79. package/src/components/src/map/map-legend-panel.tsx +1 -2
  80. package/src/components/src/modal-container.tsx +7 -7
  81. package/src/components/src/modals/tilesets-modals/tileset-vector-form.tsx +12 -3
  82. package/src/components/src/side-panel/layer-panel/color-palette-preset.tsx +1 -1
  83. package/src/components/src/side-panel/layer-panel/color-range-selector.tsx +6 -11
  84. package/src/components/src/side-panel/layer-panel/color-selector.tsx +2 -2
  85. package/src/components/src/side-panel/layer-panel/custom-palette.tsx +0 -1
  86. package/src/components/src/side-panel/layer-panel/layer-configurator.tsx +23 -33
  87. package/src/components/src/side-panel/layer-panel/vector-tile-layer-configurator.tsx +0 -1
  88. package/src/constants/dist/default-settings.d.ts +14 -1
  89. package/src/constants/dist/default-settings.js +20 -5
  90. package/src/constants/node_modules/.cache/terser-webpack-plugin/content-v2/sha512/91/4f/6c65f3a1eba584ffd2d892cabc25ab5804d4a9ef53694c98707372a83e56343a274bec04f15927a6fe7602615f0f23f73ccf599f524ef97ad6c77c8832ea +1 -0
  91. package/src/constants/node_modules/.cache/terser-webpack-plugin/index-v5/a6/b2/5424aa4477192fb44cc90fd80892efdc96731f2ff0f48e8f28abf64243cc +2 -0
  92. package/src/constants/package.json +2 -2
  93. package/src/constants/src/default-settings.ts +16 -1
  94. package/src/constants/umd/keplergl.min.js +2 -2
  95. package/src/deckgl-arrow-layers/dist/constants.d.ts +0 -12
  96. package/src/deckgl-arrow-layers/dist/constants.js +4 -15
  97. package/src/deckgl-arrow-layers/dist/index.d.ts +0 -1
  98. package/src/deckgl-arrow-layers/dist/index.js +1 -8
  99. package/src/deckgl-arrow-layers/dist/layers/geo-arrow-scatterplot-layer.js +4 -4
  100. package/src/deckgl-arrow-layers/dist/layers/geo-arrow-text-layer.js +5 -5
  101. package/src/deckgl-arrow-layers/dist/utils/utils.d.ts +12 -1
  102. package/src/deckgl-arrow-layers/dist/utils/utils.js +7 -5
  103. package/src/deckgl-arrow-layers/package.json +2 -1
  104. package/src/deckgl-arrow-layers/src/constants.ts +0 -13
  105. package/src/deckgl-arrow-layers/src/index.ts +0 -2
  106. package/src/deckgl-arrow-layers/src/layers/geo-arrow-scatterplot-layer.ts +5 -3
  107. package/src/deckgl-arrow-layers/src/layers/geo-arrow-text-layer.ts +8 -4
  108. package/src/deckgl-arrow-layers/src/utils/utils.ts +7 -5
  109. package/src/deckgl-layers/dist/deckgl-extensions/filter-arrow-layer.d.ts +1 -1
  110. package/src/deckgl-layers/dist/deckgl-extensions/filter-shader-module.d.ts +1 -1
  111. package/src/deckgl-layers/dist/deckgl-extensions/filter-shader-module.js +3 -3
  112. package/src/deckgl-layers/package.json +5 -5
  113. package/src/deckgl-layers/src/deckgl-extensions/filter-shader-module.ts +4 -3
  114. package/src/duckdb/dist/components/monaco-editor.js +4 -5
  115. package/src/duckdb/dist/components/preview-data-panel.d.ts +7 -2
  116. package/src/duckdb/dist/components/preview-data-panel.js +7 -8
  117. package/src/duckdb/dist/components/schema-panel.d.ts +12 -3
  118. package/src/duckdb/dist/components/schema-panel.js +41 -24
  119. package/src/duckdb/dist/components/sql-panel.js +245 -56
  120. package/src/duckdb/dist/init.js +20 -14
  121. package/src/duckdb/dist/processors/data-processor.d.ts +1 -0
  122. package/src/duckdb/dist/processors/data-processor.js +4 -4
  123. package/src/duckdb/dist/table/duckdb-table-utils.d.ts +106 -0
  124. package/src/duckdb/dist/table/duckdb-table-utils.js +493 -2
  125. package/src/duckdb/dist/table/duckdb-table.d.ts +5 -3
  126. package/src/duckdb/dist/table/duckdb-table.js +153 -261
  127. package/src/duckdb/package.json +6 -6
  128. package/src/duckdb/src/components/monaco-editor.tsx +3 -3
  129. package/src/duckdb/src/components/preview-data-panel.tsx +15 -8
  130. package/src/duckdb/src/components/schema-panel.tsx +92 -21
  131. package/src/duckdb/src/components/sql-panel.tsx +160 -22
  132. package/src/duckdb/src/init.ts +4 -1
  133. package/src/duckdb/src/processors/data-processor.ts +2 -2
  134. package/src/duckdb/src/table/duckdb-table-utils.ts +407 -1
  135. package/src/duckdb/src/table/duckdb-table.ts +61 -92
  136. package/src/effects/package.json +5 -5
  137. package/src/layers/dist/arc-layer/arc-layer.d.ts +1 -1
  138. package/src/layers/dist/arc-layer/arc-layer.js +2 -3
  139. package/src/layers/dist/base-layer.d.ts +7 -6
  140. package/src/layers/dist/base-layer.js +36 -19
  141. package/src/layers/dist/geojson-layer/geojson-layer.js +4 -2
  142. package/src/layers/dist/geojson-layer/geojson-utils.js +10 -3
  143. package/src/layers/dist/heatmap-layer/heatmap-layer.d.ts +2 -0
  144. package/src/layers/dist/heatmap-layer/heatmap-layer.js +16 -9
  145. package/src/layers/dist/icon-layer/icon-layer.d.ts +6 -3
  146. package/src/layers/dist/icon-layer/icon-layer.js +19 -11
  147. package/src/layers/dist/index.d.ts +1 -1
  148. package/src/layers/dist/index.js +1 -1
  149. package/src/layers/dist/layer-text-label.d.ts +9 -0
  150. package/src/layers/dist/layer-text-label.js +51 -6
  151. package/src/layers/dist/layer-utils.d.ts +39 -30
  152. package/src/layers/dist/layer-utils.js +69 -9
  153. package/src/layers/dist/line-layer/line-layer.d.ts +1 -1
  154. package/src/layers/dist/line-layer/line-layer.js +2 -3
  155. package/src/layers/dist/point-layer/point-layer.d.ts +10 -7
  156. package/src/layers/dist/point-layer/point-layer.js +16 -8
  157. package/src/layers/dist/trip-layer/trip-layer.d.ts +2 -2
  158. package/src/layers/dist/trip-layer/trip-layer.js +4 -2
  159. package/src/layers/dist/vector-tile/abstract-tile-layer.d.ts +3 -7
  160. package/src/layers/dist/vector-tile/abstract-tile-layer.js +7 -6
  161. package/src/layers/dist/vector-tile/vector-tile-layer.d.ts +5 -7
  162. package/src/layers/dist/vector-tile/vector-tile-layer.js +23 -2
  163. package/src/layers/package.json +11 -10
  164. package/src/layers/src/arc-layer/arc-layer.ts +1 -1
  165. package/src/layers/src/base-layer.ts +50 -9
  166. package/src/layers/src/geojson-layer/geojson-layer.ts +3 -1
  167. package/src/layers/src/geojson-layer/geojson-utils.ts +4 -1
  168. package/src/layers/src/heatmap-layer/heatmap-layer.ts +17 -12
  169. package/src/layers/src/icon-layer/icon-layer.ts +7 -3
  170. package/src/layers/src/index.ts +1 -2
  171. package/src/layers/src/layer-text-label.ts +53 -4
  172. package/src/layers/src/layer-utils.ts +88 -9
  173. package/src/layers/src/line-layer/line-layer.ts +1 -1
  174. package/src/layers/src/point-layer/point-layer.ts +15 -11
  175. package/src/layers/src/trip-layer/trip-layer.ts +4 -2
  176. package/src/layers/src/vector-tile/abstract-tile-layer.ts +11 -5
  177. package/src/layers/src/vector-tile/vector-tile-layer.ts +33 -4
  178. package/src/localization/package.json +1 -1
  179. package/src/processors/dist/data-processor.d.ts +15 -1
  180. package/src/processors/dist/data-processor.js +104 -11
  181. package/src/processors/package.json +8 -7
  182. package/src/processors/src/data-processor.ts +116 -13
  183. package/src/reducers/dist/export-utils.js +2 -2
  184. package/src/reducers/dist/layer-utils.js +1 -1
  185. package/src/reducers/dist/provider-state-updaters.d.ts +1 -0
  186. package/src/reducers/dist/provider-state-updaters.js +5 -2
  187. package/src/reducers/dist/vis-state-updaters.d.ts +1 -1
  188. package/src/reducers/dist/vis-state-updaters.js +23 -12
  189. package/src/reducers/package.json +16 -16
  190. package/src/reducers/src/export-utils.ts +1 -1
  191. package/src/reducers/src/layer-utils.ts +2 -1
  192. package/src/reducers/src/provider-state-updaters.ts +4 -1
  193. package/src/reducers/src/vis-state-updaters.ts +45 -10
  194. package/src/schemas/dist/dataset-schema.js +35 -6
  195. package/src/schemas/package.json +7 -7
  196. package/src/schemas/src/dataset-schema.ts +36 -8
  197. package/src/styles/node_modules/.cache/terser-webpack-plugin/content-v2/sha512/a3/a9/7f26bbc52905b0e21e9366a30ef0ba98f4ccf9aa8dd8326d90ba90191ba2e305323f505bf134c825356156c793bf9386ed2cef2aeb48eb327c26d106f69a +1 -0
  198. package/src/styles/node_modules/.cache/terser-webpack-plugin/index-v5/7e/51/b8756555a0e2e696d944445ac23f7febf769006013fbc8419f39a701649d +2 -0
  199. package/src/styles/package.json +2 -2
  200. package/src/styles/umd/keplergl.min.js +2 -2
  201. package/src/table/dist/dataset-utils.js +23 -14
  202. package/src/table/dist/src/table/src/dataset-utils.d.ts +1 -1
  203. package/src/table/dist/src/table/src/tileset/vector-tile-utils.d.ts +17 -1
  204. package/src/table/dist/tileset/vector-tile-utils.js +122 -2
  205. package/src/table/package.json +5 -5
  206. package/src/table/src/dataset-utils.ts +16 -6
  207. package/src/table/src/tileset/vector-tile-utils.ts +131 -8
  208. package/src/tasks/package.json +2 -2
  209. package/src/types/actions.d.ts +1 -0
  210. package/src/types/layers.d.ts +1 -0
  211. package/src/types/package.json +1 -1
  212. package/src/types/types.d.ts +17 -0
  213. package/src/utils/dist/application-config.d.ts +4 -3
  214. package/src/utils/dist/application-config.js +3 -2
  215. package/src/utils/dist/data-scale-utils.js +4 -4
  216. package/src/utils/dist/data-utils.d.ts +9 -3
  217. package/src/utils/dist/data-utils.js +40 -7
  218. package/src/utils/dist/dataset-utils.js +23 -8
  219. package/src/utils/dist/indexed-data-container.d.ts +2 -2
  220. package/src/utils/dist/indexed-data-container.js +1 -1
  221. package/src/utils/dist/map-style-utils/mapbox-utils.d.ts +1 -1
  222. package/src/utils/dist/map-style-utils/mapbox-utils.js +3 -3
  223. package/src/utils/dist/plot.d.ts +2 -2
  224. package/src/utils/dist/plot.js +6 -6
  225. package/src/utils/map-utils.spec.js +1 -2
  226. package/src/utils/package.json +5 -4
  227. package/src/utils/src/application-config.ts +11 -8
  228. package/src/utils/src/data-scale-utils.ts +3 -3
  229. package/src/utils/src/data-utils.ts +36 -7
  230. package/src/utils/src/dataset-utils.ts +41 -9
  231. package/src/utils/src/indexed-data-container.ts +1 -1
  232. package/src/utils/src/map-style-utils/mapbox-utils.ts +2 -2
  233. package/src/utils/src/plot.ts +5 -5
  234. 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 {DatasetType, DATASET_FORMATS} from '@kepler.gl/constants';
8
- import {KeplerTable} from '@kepler.gl/table';
9
- import {Field, ProtoDatasetField} from '@kepler.gl/types';
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]: true,
56
- [DUCKDB_GEOM_COLUMN]: true,
57
- geometry: true
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 someting
68
- addWKBMetadata?: Record<string, boolean>;
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
- addWKBMetadata: {[KEPLER_GEOM_FROM_GEOJSON_COLUMN]: true}
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, db, c}: ImportDataToDuckProps): Promise<ImportDataToDuckResult> {
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
- addWKBMetadata: {...SUGGESTED_GEOM_COLUMNS},
187
- // use metadata from arrow table
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 dropIfTableExists(c, tableName);
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 {addWKBMetadata = {}, useNewFields = false} = importDetails || {};
232
+ const {geoarrowMetadata = {}, useNewFields = false} = importDetails || {};
232
233
 
233
- // Try to figure out whether geometry should be transformed by ST_AsWKB
234
- // TODO if canCastST_asWKB contains any keys then add ST_AsWKB to the following query
235
- const canCastST_asWKB = await tryST_AsWKB({tableName, c});
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
- const getArrowQuery = `SELECT * FROM '${tableName}';`;
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, canCastST_asWKB, addWKBMetadata);
242
+ restoreGeoarrowMetadata(arrowResult, geoarrowMetadata);
241
243
 
242
244
  fields = useNewFields
243
- ? arrowSchemaToFields(arrowResult.schema)
244
- : data.fields ?? arrowSchemaToFields(arrowResult.schema);
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
- // Known issues:
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 canCastST_asWKB A map with fields that are likely in DeckDB's internal WBK format.
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
- canCastST_asWKB: Record<string, boolean>,
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) && (addWKBMetadata[f.name] || canCastST_asWKB[f.name])) {
323
- f.metadata.set('ARROW:extension:name', 'geoarrow.wkb');
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('ARROW:extension:name', 'geoarrow.point');
323
+ f.metadata.set(GEOARROW_METADATA_KEY, GEOARROW_EXTENSIONS.POINT);
326
324
  } else if (isGeoArrowLineString(f.type)) {
327
- f.metadata.set('ARROW:extension:name', 'geoarrow.linestring');
325
+ f.metadata.set(GEOARROW_METADATA_KEY, GEOARROW_EXTENSIONS.LINESTRING);
328
326
  } else if (isGeoArrowPolygon(f.type)) {
329
- f.metadata.set('ARROW:extension:name', 'geoarrow.polygon');
327
+ f.metadata.set(GEOARROW_METADATA_KEY, GEOARROW_EXTENSIONS.POLYGON);
330
328
  } else if (isGeoArrowMultiPoint(f.type)) {
331
- f.metadata.set('ARROW:extension:name', 'geoarrow.multipoint');
329
+ f.metadata.set(GEOARROW_METADATA_KEY, GEOARROW_EXTENSIONS.MULTIPOINT);
332
330
  } else if (isGeoArrowMultiLineString(f.type)) {
333
- f.metadata.set('ARROW:extension:name', 'geoarrow.multilinestring');
331
+ f.metadata.set(GEOARROW_METADATA_KEY, GEOARROW_EXTENSIONS.MULTILINESTRING);
334
332
  } else if (isGeoArrowMultiPolygon(f.type)) {
335
- f.metadata.set('ARROW:extension:name', 'geoarrow.multipolygon');
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
- };