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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kepler.gl/duckdb",
3
3
  "author": "Shan He <heshan0131@gmail.com>",
4
- "version": "3.1.0",
4
+ "version": "3.1.1",
5
5
  "description": "DuckDB plugin for Kepler.gl",
6
6
  "license": "MIT",
7
7
  "main": "dist/index.js",
@@ -26,11 +26,11 @@
26
26
  ],
27
27
  "dependencies": {
28
28
  "@duckdb/duckdb-wasm": "^1.28.0",
29
- "@kepler.gl/common-utils": "3.1.0",
30
- "@kepler.gl/constants": "3.1.0",
31
- "@kepler.gl/processors": "3.1.0",
32
- "@kepler.gl/table": "3.1.0",
33
- "@kepler.gl/types": "3.1.0",
29
+ "@kepler.gl/common-utils": "3.1.1",
30
+ "@kepler.gl/constants": "3.1.1",
31
+ "@kepler.gl/processors": "3.1.1",
32
+ "@kepler.gl/table": "3.1.1",
33
+ "@kepler.gl/types": "3.1.1",
34
34
  "@monaco-editor/react": "^4.6.0",
35
35
  "@radix-ui/react-collapsible": "^1.1.0",
36
36
  "apache-arrow": ">=15.0.0",
@@ -92,7 +92,7 @@ const MonacoEditor: React.FC<MonacoEditorProps> = ({
92
92
  });
93
93
 
94
94
  monaco.languages.registerCompletionItemProvider('*', {
95
- provideCompletionItems: (model, position, context, cancelationToken) => {
95
+ provideCompletionItems: (model, position, _context, _cancelationToken) => {
96
96
  const suggestions: monaco.languages.CompletionItem[] = [
97
97
  {
98
98
  label: 'myCustomSnippet',
@@ -152,7 +152,7 @@ const MonacoEditor: React.FC<MonacoEditorProps> = ({
152
152
  ...tableSchema
153
153
  .filter(d => d.table_name === table_name)
154
154
  .map(
155
- ({table_name, column_name}) =>
155
+ ({column_name}) =>
156
156
  ({
157
157
  label: column_name,
158
158
  kind: monaco.languages.CompletionItemKind.Field,
@@ -170,7 +170,7 @@ const MonacoEditor: React.FC<MonacoEditorProps> = ({
170
170
  }
171
171
  });
172
172
  },
173
- [tableSchema]
173
+ [tableSchema, schemaTableNames, schemaTableNamesSet]
174
174
  );
175
175
 
176
176
  return (
@@ -1,13 +1,13 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  // Copyright contributors to the kepler.gl project
3
3
 
4
+ import * as arrow from 'apache-arrow';
4
5
  import React, {useCallback, useMemo, useState, CSSProperties} from 'react';
6
+ import {withTheme} from 'styled-components';
5
7
 
6
8
  import {DataTable, renderedSize} from '@kepler.gl/components';
7
- import {parseFieldValue, createDataContainer} from '@kepler.gl/utils';
8
9
  import {arrowSchemaToFields} from '@kepler.gl/processors';
9
- import {DataForm} from '@kepler.gl/utils';
10
- import {withTheme} from 'styled-components';
10
+ import {parseFieldValue, createDataContainer, DataForm} from '@kepler.gl/utils';
11
11
 
12
12
  type BaseComponentProps = {
13
13
  className?: string;
@@ -39,27 +39,34 @@ export type DataTableStyle = {
39
39
  optionsButton?: number;
40
40
  };
41
41
 
42
+ export type QueryResult = {
43
+ table: arrow.Table;
44
+ tableDuckDBTypes: Record<string, string>;
45
+ };
46
+
42
47
  export type PreviewDataPanelProps = BaseComponentProps & {
43
- result: any;
48
+ result: QueryResult;
44
49
  rowsToCalculatePreview?: number;
45
50
  theme?: any;
46
51
  setColumnDisplayFormat?: (formats: {[key: string]: string}) => void;
47
52
  defaultPinnedColumns?: string[];
48
53
  dataTableStyle: DataTableStyle;
49
- onAddResultToMap: (result: any) => void;
54
+ onAddResultToMap: (result: QueryResult) => void;
50
55
  };
51
56
 
52
57
  const PreviewDataPanelWOTheme: React.FC<PreviewDataPanelProps> = ({
53
58
  result,
54
59
  rowsToCalculatePreview = DEFAULT_ROWS_TO_CALCULATE_PREVIEW,
55
- setColumnDisplayFormat,
56
60
  defaultPinnedColumns = [],
57
61
  theme
58
62
  }) => {
59
63
  const [pinnedColumns, setPinnedColumns] = useState<string[]>(defaultPinnedColumns);
60
- const fields = useMemo(() => arrowSchemaToFields(result.schema), [result.schema]);
64
+ const fields = useMemo(
65
+ () => arrowSchemaToFields(result.table, result.tableDuckDBTypes),
66
+ [result]
67
+ );
61
68
  const dataContainer = useMemo(() => {
62
- const cols = [...Array(result.numCols).keys()].map(i => result.getChildAt(i));
69
+ const cols = [...Array(result.table.numCols).keys()].map(i => result.table.getChildAt(i));
63
70
 
64
71
  const dataContainer = createDataContainer(cols, {
65
72
  fields,
@@ -4,11 +4,15 @@
4
4
  import React, {useCallback, useEffect, useState} from 'react';
5
5
  import {useSelector} from 'react-redux';
6
6
  import styled from 'styled-components';
7
- import {arrowDataTypeToFieldType} from '@kepler.gl/utils';
8
- import {ALL_FIELD_TYPES} from '@kepler.gl/constants';
7
+ import {AsyncDuckDBConnection} from '@duckdb/duckdb-wasm';
8
+
9
+ import {LoadingSpinner, Icons} from '@kepler.gl/components';
10
+ import {arrowSchemaToFields} from '@kepler.gl/processors';
9
11
  import {VisState} from '@kepler.gl/schemas';
12
+
10
13
  import {Tree, DatasetNode, ColumnNode, TreeNodeData} from './tree';
11
14
  import {getDuckDB} from '../init';
15
+ import {getDuckDBColumnTypes, getDuckDBColumnTypesMap} from '../table/duckdb-table-utils';
12
16
 
13
17
  // TODO note that demo state is available in demo-app, but not when add modules to dependencies in a custom map
14
18
  type State = {
@@ -26,64 +30,127 @@ const StyledSchemaPanel = styled.div`
26
30
  font-size: 12px;
27
31
  padding: 12px;
28
32
  font-family: ${props => props.theme.fontFamily};
33
+ height: 100%;
34
+ `;
35
+
36
+ const StyledLoadingSpinnerWrapper = styled.div`
37
+ display: flex;
38
+ justify-content: center;
39
+ align-items: center;
40
+ height: 100%;
29
41
  `;
30
42
 
31
- async function getColumnSchema(c, name) {
32
- const columnResult = await c.query(`Select * from '${name}' LIMIT 1;`);
43
+ async function getColumnSchema(connection: AsyncDuckDBConnection, tableName: string) {
44
+ const columnResult = await connection.query(`Select * from '${tableName}' LIMIT 1;`);
45
+
46
+ const columnDescribe = await getDuckDBColumnTypes(connection, tableName);
47
+ const keplerFields = arrowSchemaToFields(columnResult, getDuckDBColumnTypesMap(columnDescribe));
33
48
 
34
49
  return {
35
- key: name,
50
+ key: tableName,
36
51
  object: {
37
52
  type: 'dataset',
38
- tableName: name
53
+ tableName: tableName
39
54
  },
40
- children: columnResult.schema.fields.map(field => {
41
- const isGeoArrowColumn = field.metadata.get('ARROW:extension:name')?.startsWith('geoarrow');
55
+ children: columnResult.schema.fields.map((field, fieldIndex) => {
42
56
  return {
43
57
  key: field.name,
44
58
  object: {
45
59
  type: 'column',
46
60
  name: field.name,
47
61
  arrowType: field.type,
48
- fieldType: isGeoArrowColumn
49
- ? ALL_FIELD_TYPES.geoarrow
50
- : arrowDataTypeToFieldType(field.type)
62
+ fieldType: keplerFields[fieldIndex].type
51
63
  }
52
64
  };
53
65
  })
54
66
  };
55
67
  }
56
68
 
57
- function getSchemaSuggestion(result) {
69
+ export type SchemaSuggestion = {column_name: string; table_name: string};
70
+
71
+ function getSchemaSuggestion(result: {key: string; children: {key: string}[]}[]) {
58
72
  return result.reduce((accu, data) => {
59
73
  const columns = data.children.map(child => ({
60
74
  column_name: child.key,
61
75
  table_name: data.key
62
76
  }));
63
77
  return accu.concat(columns);
64
- }, []);
78
+ }, [] as SchemaSuggestion[]);
65
79
  }
66
- export const SchemaPanel = ({setTableSchema}) => {
80
+
81
+ type SchemaPanelProps = {
82
+ setTableSchema: (tableSchema: SchemaSuggestion[]) => void;
83
+ droppedFile: File | null;
84
+ schemaUpdateTrigger: number;
85
+ };
86
+
87
+ const StyledSchemaPanelDropMessage = styled.div`
88
+ display: flex;
89
+ justify-content: center;
90
+ align-items: center;
91
+ height: 100%;
92
+ flex-direction: column;
93
+ text-align: center;
94
+
95
+ div {
96
+ margin: 5px;
97
+ }
98
+ .header {
99
+ font-size: 15px;
100
+ }
101
+ .bold {
102
+ font-weight: 700;
103
+ }
104
+ `;
105
+
106
+ const StyledAddIcon = styled(Icons.Add)`
107
+ display: inline;
108
+ margin-top: -3px;
109
+ `;
110
+
111
+ export const SchemaPanelDropMessage = () => {
112
+ return (
113
+ <StyledSchemaPanelDropMessage>
114
+ <div className="header">
115
+ <StyledAddIcon /> Add files to DuckDB
116
+ </div>
117
+ <div className="bold">Supported formats: </div>
118
+ <div>.csv, .json, .geojson, .parquet, .arrow</div>
119
+ <div>Files you add will stay local to your browser.</div>
120
+ </StyledSchemaPanelDropMessage>
121
+ );
122
+ };
123
+
124
+ export const SchemaPanel = ({
125
+ setTableSchema,
126
+ droppedFile,
127
+ schemaUpdateTrigger
128
+ }: SchemaPanelProps) => {
67
129
  const [columnSchemas, setColumnSchemas] = useState<TreeNodeData<{type: string}>[]>([]);
68
130
  const datasets = useSelector((state: State) => state?.demo?.keplerGl?.map?.visState.datasets);
69
- useEffect(() => {
70
- getTableSchema();
71
- }, [datasets]);
131
+
72
132
  const getTableSchema = useCallback(async () => {
73
133
  const db = await getDuckDB();
74
134
  const c = await db.connect();
75
135
 
76
136
  const tableResult = await c.query('SHOW TABLES;');
77
137
 
78
- const tableNames = tableResult.getChildAt(0)?.toJSON();
138
+ const tableNames: string[] | undefined = tableResult.getChildAt(0)?.toJSON();
79
139
 
80
140
  const result = await Promise.all((tableNames || [])?.map(name => getColumnSchema(c, name)));
81
141
  const tableSchema = getSchemaSuggestion(result);
82
142
 
83
143
  setColumnSchemas(result);
84
144
  setTableSchema(tableSchema);
85
- c.close();
86
- }, [setColumnSchemas, setTableSchema]);
145
+ await c.close();
146
+
147
+ // schemaUpdateTrigger indicates possible change in DuckDB
148
+ // eslint-disable-next-line react-hooks/exhaustive-deps
149
+ }, [setColumnSchemas, setTableSchema, schemaUpdateTrigger]);
150
+
151
+ useEffect(() => {
152
+ getTableSchema();
153
+ }, [datasets, droppedFile, getTableSchema]);
87
154
 
88
155
  return (
89
156
  <StyledSchemaPanel>
@@ -102,8 +169,12 @@ export const SchemaPanel = ({setTableSchema}) => {
102
169
  }}
103
170
  />
104
171
  ))
172
+ ) : droppedFile ? (
173
+ <StyledLoadingSpinnerWrapper>
174
+ <LoadingSpinner />
175
+ </StyledLoadingSpinnerWrapper>
105
176
  ) : (
106
- <div>No tables found</div>
177
+ <SchemaPanelDropMessage />
107
178
  )}
108
179
  </StyledSchemaPanel>
109
180
  );
@@ -2,21 +2,34 @@
2
2
  // Copyright contributors to the kepler.gl project
3
3
 
4
4
  import * as arrow from 'apache-arrow';
5
- import React, {useCallback, useState, useEffect} from 'react';
5
+ import React, {useCallback, useState, useEffect, useRef} from 'react';
6
6
  import {useDispatch} from 'react-redux';
7
7
  import styled from 'styled-components';
8
8
  import {Panel, PanelGroup, PanelResizeHandle} from 'react-resizable-panels';
9
- import MonacoEditor from './monaco-editor';
10
- import {SchemaPanel} from './schema-panel';
11
- import {PreviewDataPanel} from './preview-data-panel';
12
- import {getDuckDB} from '../init';
13
- import {Button, IconButton, Tooltip} from '@kepler.gl/components';
14
- import {generateHashId} from '@kepler.gl/common-utils';
9
+
15
10
  import {addDataToMap} from '@kepler.gl/actions';
16
- import {Icons, LoadingSpinner} from '@kepler.gl/components';
11
+ import {generateHashId} from '@kepler.gl/common-utils';
12
+ import {Button, FileDrop, IconButton, Icons, LoadingSpinner, Tooltip} from '@kepler.gl/components';
13
+ import {arrowSchemaToFields} from '@kepler.gl/processors';
17
14
  import {sidePanelBg, panelBorderColor} from '@kepler.gl/styles';
18
15
  import {isAppleDevice} from '@kepler.gl/utils';
19
- import {arrowSchemaToFields} from '@kepler.gl/processors';
16
+
17
+ import MonacoEditor from './monaco-editor';
18
+ import {SchemaPanel, SchemaSuggestion} from './schema-panel';
19
+ import {PreviewDataPanel, QueryResult} from './preview-data-panel';
20
+ import {getDuckDB} from '../init';
21
+ import {
22
+ constructST_asWKBQuery,
23
+ getDuckDBColumnTypes,
24
+ getDuckDBColumnTypesMap,
25
+ getGeometryColumns,
26
+ setGeoArrowWKBExtension,
27
+ splitSqlStatements,
28
+ checkIsSelectQuery,
29
+ removeSQLComments,
30
+ tableFromFile,
31
+ SUPPORTED_DUCKDB_DROP_EXTENSIONS
32
+ } from '../table/duckdb-table-utils';
20
33
 
21
34
  const StyledSqlPanel = styled.div`
22
35
  display: flex;
@@ -114,6 +127,20 @@ const StyledErrorContainer = styled.pre`
114
127
  overflow: auto;
115
128
  `;
116
129
 
130
+ interface StyledDragPanelProps {
131
+ dragOver?: boolean;
132
+ }
133
+
134
+ const StyledFileDropArea = styled(FileDrop)<StyledDragPanelProps>`
135
+ height: 100%;
136
+ border-width: 1px;
137
+ border: 1px ${props => (props.dragOver ? 'solid' : 'dashed')}
138
+ ${props => (props.dragOver ? props.theme.subtextColorLT : 'transparent')};
139
+ .file-drop-target {
140
+ height: 100%;
141
+ }
142
+ `;
143
+
117
144
  type SqlPanelProps = {
118
145
  initialSql?: string;
119
146
  };
@@ -125,14 +152,19 @@ export const SqlPanel: React.FC<SqlPanelProps> = ({initialSql = ''}) => {
125
152
  const params = new URLSearchParams(window.location.search);
126
153
  return params.get('sql') || initialSql;
127
154
  });
128
- const [result, setResult] = useState<null | arrow.Table>(null);
155
+ const [droppedFile, setDroppedFile] = useState<File | null>(null);
156
+ const [dragState, setDragState] = useState(false);
157
+ const [result, setResult] = useState<null | QueryResult>(null);
158
+ const [schemaUpdateTrigger, setSchemaUpdateTrigger] = useState<number>(0);
129
159
  const [error, setError] = useState<Error | null>(null);
130
160
  const [counter, setCounter] = useState(0);
131
- const [tableSchema, setTableSchema] = useState([]);
161
+ const [tableSchema, setTableSchema] = useState<SchemaSuggestion[]>([]);
132
162
  const [isRunning, setIsRunning] = useState(false);
133
163
  const [isMac] = useState(() => isAppleDevice());
134
164
  const dispatch = useDispatch();
135
165
 
166
+ const droppedFileAreaRef = useRef(null);
167
+
136
168
  useEffect(() => {
137
169
  const currentUrl = new URL(window.location.href);
138
170
  if (sql) {
@@ -146,7 +178,8 @@ export const SqlPanel: React.FC<SqlPanelProps> = ({initialSql = ''}) => {
146
178
  const runQuery = useCallback(async () => {
147
179
  setIsRunning(true);
148
180
  try {
149
- if (!sql?.trim()) {
181
+ const adjustedQuery = sql ? removeSQLComments(sql) : null;
182
+ if (!adjustedQuery) {
150
183
  setError(new Error('Query is empty'));
151
184
  return;
152
185
  }
@@ -154,16 +187,55 @@ export const SqlPanel: React.FC<SqlPanelProps> = ({initialSql = ''}) => {
154
187
  const db = await getDuckDB();
155
188
  const connection = await db.connect();
156
189
 
157
- const arrowResult = await connection.query(sql);
158
- setResult(arrowResult);
159
- setError(null);
190
+ // TODO find a cheap way to get DuckDb types with a single query to a remote resource - temp table? cte?
191
+ const tempTableName = 'temp_keplergl_table';
192
+
193
+ // remove comments
194
+ const sqlStatements = splitSqlStatements(adjustedQuery);
195
+
196
+ let arrowResult: arrow.Table | null = null;
197
+ let tableDuckDBTypes = {};
198
+
199
+ for (const statement of sqlStatements) {
200
+ const isLastQuery = statement === sqlStatements[sqlStatements.length - 1];
201
+ if (isLastQuery && (await checkIsSelectQuery(connection, statement))) {
202
+ // 1) create temp table from the original query.
203
+ await connection.query(`CREATE OR REPLACE TABLE '${tempTableName}' AS ${statement}`);
204
+
205
+ // 2) query duckdb types and detect candidate columns for ST_asWKB transform.
206
+ const duckDbColumns = await getDuckDBColumnTypes(connection, tempTableName);
207
+ tableDuckDBTypes = getDuckDBColumnTypesMap(duckDbColumns);
208
+ const columnsToConvertToWKB = getGeometryColumns(duckDbColumns);
209
+
210
+ // 3) query GEOMETRY columns as WKB.
211
+ const adjustedQuery = constructST_asWKBQuery(tempTableName, columnsToConvertToWKB);
212
+ arrowResult = await connection.query(adjustedQuery);
213
+
214
+ // 4) set geoarrow extension for the arrow table as DuckDB doesn't support the geoarrow extension.
215
+ setGeoArrowWKBExtension(arrowResult, duckDbColumns);
160
216
 
161
- connection.close();
217
+ // 5) remove temp table
218
+ await connection.query(`DROP TABLE ${tempTableName};`);
219
+ } else {
220
+ await connection.query(statement);
221
+ }
222
+ }
223
+
224
+ // Show preview only for the result of the last query
225
+ if (arrowResult) {
226
+ setResult({table: arrowResult, tableDuckDBTypes});
227
+ setError(null);
228
+ }
229
+
230
+ await connection.close();
162
231
  } catch (e) {
163
232
  setError(e as Error);
164
233
  } finally {
165
234
  setIsRunning(false);
166
235
  }
236
+
237
+ // Indicate that there may be a possible change in DuckDB.
238
+ setSchemaUpdateTrigger(Date.now());
167
239
  }, [sql]);
168
240
 
169
241
  const onChange = useCallback(
@@ -176,13 +248,13 @@ export const SqlPanel: React.FC<SqlPanelProps> = ({initialSql = ''}) => {
176
248
  const onAddResultToMap = useCallback(() => {
177
249
  if (!result) return;
178
250
 
179
- const keplerFields = arrowSchemaToFields(result.schema);
251
+ const keplerFields = arrowSchemaToFields(result.table, result.tableDuckDBTypes);
180
252
 
181
253
  const datasetToAdd = {
182
254
  data: {
183
255
  fields: keplerFields,
184
256
  // TODO type AddDataToMapPayload -> rows -> + arrow.Table
185
- rows: result as any
257
+ rows: result.table as any
186
258
  },
187
259
  info: {
188
260
  id: generateHashId(),
@@ -195,11 +267,77 @@ export const SqlPanel: React.FC<SqlPanelProps> = ({initialSql = ''}) => {
195
267
  setCounter(counter + 1);
196
268
  }, [result, counter, dispatch]);
197
269
 
270
+ const isValidFileType = useCallback(filename => {
271
+ const fileExt = SUPPORTED_DUCKDB_DROP_EXTENSIONS.find(ext => filename.endsWith(ext));
272
+ return Boolean(fileExt);
273
+ }, []);
274
+
275
+ const createTableFromDroppedFile = useCallback(async (droppedFile: File | null) => {
276
+ if (droppedFile) {
277
+ const error = await tableFromFile(droppedFile);
278
+ if (error) {
279
+ setError(error);
280
+ } else {
281
+ setError(null);
282
+ }
283
+ }
284
+
285
+ setDroppedFile(null);
286
+ setDragState(false);
287
+ }, []);
288
+
289
+ useEffect(() => {
290
+ createTableFromDroppedFile(droppedFile);
291
+ }, [droppedFile, createTableFromDroppedFile]);
292
+
293
+ const handleFileInput = useCallback(
294
+ (fileList: FileList, event: DragEvent) => {
295
+ if (event) {
296
+ event.preventDefault();
297
+ event.stopPropagation();
298
+ }
299
+
300
+ const files = [...fileList].filter(Boolean);
301
+
302
+ const disableExtensionFilter = false;
303
+
304
+ const filesToLoad: File[] = [];
305
+ const errorFiles: string[] = [];
306
+ for (const file of files) {
307
+ if (disableExtensionFilter || isValidFileType(file.name)) {
308
+ filesToLoad.push(file);
309
+ } else {
310
+ errorFiles.push(file.name);
311
+ }
312
+ }
313
+
314
+ if (filesToLoad.length > 0) {
315
+ setDroppedFile(filesToLoad[0]);
316
+ } else if (errorFiles.length > 0) {
317
+ setError(new Error(`Unsupported file formats: ${errorFiles.join(', ')}`));
318
+ }
319
+ },
320
+ [isValidFileType]
321
+ );
322
+
198
323
  return (
199
324
  <StyledSqlPanel>
200
325
  <PanelGroup direction="horizontal">
201
- <Panel defaultSize={20} minSize={15} style={SCHEMA_PANEL_STYLE}>
202
- <SchemaPanel setTableSchema={setTableSchema} />
326
+ <Panel defaultSize={20} minSize={15} style={SCHEMA_PANEL_STYLE} className="schema-panel">
327
+ <StyledFileDropArea
328
+ dragOver={dragState}
329
+ onDragOver={() => setDragState(true)}
330
+ onDragLeave={() => setDragState(false)}
331
+ frame={droppedFileAreaRef.current || document}
332
+ onDrop={handleFileInput}
333
+ className="file-uploader__file-drop"
334
+ >
335
+ <SchemaPanel
336
+ setTableSchema={setTableSchema}
337
+ droppedFile={droppedFile}
338
+ schemaUpdateTrigger={schemaUpdateTrigger}
339
+ />
340
+ </StyledFileDropArea>
203
341
  </Panel>
204
342
 
205
343
  <StyledResizeHandle />
@@ -235,7 +373,7 @@ export const SqlPanel: React.FC<SqlPanelProps> = ({initialSql = ''}) => {
235
373
  </Panel>
236
374
 
237
375
  <StyledVerticalResizeHandle />
238
- <Panel>
376
+ <Panel className="preview-panel">
239
377
  {isRunning ? (
240
378
  <StyledLoadingContainer>
241
379
  <LoadingSpinner />
@@ -250,7 +388,7 @@ export const SqlPanel: React.FC<SqlPanelProps> = ({initialSql = ''}) => {
250
388
  <Button secondary onClick={onAddResultToMap}>
251
389
  Add to Map
252
390
  </Button>
253
- <div>{result.numRows} rows</div>
391
+ <div>{result.table.numRows} rows</div>
254
392
  </StyledResultActions>
255
393
  <PreviewDataPanel
256
394
  result={result}
@@ -38,9 +38,12 @@ const _initializeDuckDb = async (config?: DuckDBConfig): Promise<AsyncDuckDB> =>
38
38
  // Select a bundle based on browser checks
39
39
  const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
40
40
  const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
41
+ if (!bundle.mainWorker) {
42
+ throw new Error('Failed to initialize DuckDB');
43
+ }
41
44
 
42
45
  const worker_url = URL.createObjectURL(
43
- new Blob([`importScripts("${bundle.mainWorker!}");`], {
46
+ new Blob([`importScripts("${bundle.mainWorker}");`], {
44
47
  type: 'text/javascript'
45
48
  })
46
49
  );
@@ -38,8 +38,8 @@ enum COLUMN_TYPES {
38
38
  TIMESTAMP = 'TIMESTAMP',
39
39
  VARCHAR = 'VARCHAR'
40
40
  }
41
- // @ts-expect-error https://duckdb.org/docs/data/csv/auto_detection.html#type-detection
42
- const COLUMN_TYPES_PRIORITIES = [
41
+ // https://duckdb.org/docs/data/csv/auto_detection.html#type-detection
42
+ export const COLUMN_TYPES_PRIORITIES = [
43
43
  'GEOMETRY',
44
44
  'BOOLEAN',
45
45
  'INTEGER',