@@ -5,7 +5,17 @@ import interpolate from 'color-interpolate';
5
5
6
6
import { Layer } from '@kepler.gl/layers' ;
7
7
import { Datasets , KeplerTable } from '@kepler.gl/table' ;
8
+ import { SpatialJoinGeometries } from '@openassistant/geoda' ;
9
+ import { ALL_FIELD_TYPES } from '@kepler.gl/constants' ;
10
+ import { AddDataToMapPayload , ProtoDataset , ProtoDatasetField } from '@kepler.gl/types' ;
8
11
12
+ /**
13
+ * Check if the dataset exists
14
+ * @param datasets The kepler.gl datasets
15
+ * @param datasetName The name of the dataset
16
+ * @param functionName The name of the function
17
+ * @returns The result of the check
18
+ */
9
19
export function checkDatasetNotExists (
10
20
datasets : Datasets ,
11
21
datasetName : string ,
@@ -26,6 +36,13 @@ export function checkDatasetNotExists(
26
36
return null ;
27
37
}
28
38
39
+ /**
40
+ * Check if the field exists
41
+ * @param dataset The kepler.gl dataset
42
+ * @param fieldName The name of the field
43
+ * @param functionName The name of the function
44
+ * @returns The result of the check
45
+ */
29
46
export function checkFieldNotExists ( dataset : KeplerTable , fieldName : string , functionName : string ) {
30
47
const field = dataset . fields . find ( f => f . name === fieldName ) ;
31
48
if ( ! field ) {
@@ -43,6 +60,12 @@ export function checkFieldNotExists(dataset: KeplerTable, fieldName: string, fun
43
60
return null ;
44
61
}
45
62
63
+ /**
64
+ * Interpolate the colors from the original colors with the given number of colors
65
+ * @param originalColors The original colors
66
+ * @param numberOfColors The number of colors
67
+ * @returns The interpolated colors
68
+ */
46
69
export function interpolateColor ( originalColors : string [ ] , numberOfColors : number ) {
47
70
if ( originalColors . length === numberOfColors ) {
48
71
return originalColors ;
@@ -98,6 +121,14 @@ export function getScatterplotValuesFromDataset(
98
121
return { x : xValues , y : yValues } ;
99
122
}
100
123
124
+ /**
125
+ * Highlight the rows in a dataset
126
+ * @param datasets The kepler.gl datasets
127
+ * @param layers The kepler.gl layers
128
+ * @param datasetName The name of the dataset
129
+ * @param selectedRowIndices The indices of the rows to highlight
130
+ * @param layerSetIsValid The function to set the layer validity
131
+ */
101
132
export function highlightRows (
102
133
datasets : Datasets ,
103
134
layers : Layer [ ] ,
@@ -117,11 +148,17 @@ export function highlightRows(
117
148
selectLayers . forEach ( layer => {
118
149
layer . formatLayerData ( datasets ) ;
119
150
// trigger a re-render using layerSetIsValid() to update the top layer
120
- layerSetIsValid ( selectLayers [ 0 ] , true ) ;
151
+ layerSetIsValid ( layer , true ) ;
121
152
} ) ;
122
153
}
123
154
}
124
155
156
+ /**
157
+ * Get the dataset context, which is used to provide the dataset information to the AI assistant
158
+ * @param datasets The kepler.gl datasets
159
+ * @param layers The kepler.gl layers
160
+ * @returns The dataset context
161
+ */
125
162
export function getDatasetContext ( datasets : Datasets , layers : Layer [ ] ) {
126
163
const context = 'Please remember the following dataset context:' ;
127
164
const dataMeta = Object . values ( datasets ) . map ( dataset => ( {
@@ -130,7 +167,128 @@ export function getDatasetContext(datasets: Datasets, layers: Layer[]) {
130
167
fields : dataset . fields . map ( field => ( { [ field . name ] : field . type } ) ) ,
131
168
layers : layers
132
169
. filter ( layer => layer . config . dataId === dataset . id )
133
- . map ( layer => ( { id : layer . id , label : layer . config . label , type : layer . type } ) )
170
+ . map ( layer => ( {
171
+ id : layer . id ,
172
+ label : layer . config . label ,
173
+ type : layer . type ,
174
+ geometryMode : layer . config . columnMode ,
175
+ // get the valid geometry columns as string
176
+ geometryColumns : Object . fromEntries (
177
+ Object . entries ( layer . config . columns )
178
+ . filter ( ( [ _ , value ] ) => value !== null )
179
+ . map ( ( [ key , value ] ) => [
180
+ key ,
181
+ typeof value === 'object' && value !== null
182
+ ? Object . fromEntries ( Object . entries ( value ) . filter ( ( [ _ , v ] ) => v !== null ) )
183
+ : value
184
+ ] )
185
+ )
186
+ } ) )
134
187
} ) ) ;
135
188
return `${ context } \n${ JSON . stringify ( dataMeta ) } ` ;
136
189
}
190
+
191
+ /**
192
+ * Get the geometries from a dataset
193
+ * @param datasets The kepler.gl datasets
194
+ * @param layers The kepler.gl layers
195
+ * @param layerData The layer data
196
+ * @param datasetName The name of the dataset
197
+ * @returns The geometries
198
+ */
199
+ export function getGeometriesFromDataset (
200
+ datasets : Datasets ,
201
+ layers : Layer [ ] ,
202
+ layerData : any [ ] ,
203
+ datasetName : string
204
+ ) : SpatialJoinGeometries {
205
+ const datasetId = Object . keys ( datasets ) . find ( dataId => datasets [ dataId ] . label === datasetName ) ;
206
+ if ( ! datasetId ) return [ ] ;
207
+ const dataset = datasets [ datasetId ] ;
208
+
209
+ // get the index of the layer
210
+ const layerIndex = layers . findIndex ( layer => layer . config . dataId === dataset . id ) ;
211
+ if ( layerIndex === - 1 ) return [ ] ;
212
+
213
+ const geometries = layerData [ layerIndex ] ;
214
+
215
+ return geometries ?. data ;
216
+ }
217
+
218
+ /**
219
+ * Save the data as a new dataset by joining it with the left dataset
220
+ * @param datasets The kepler.gl datasets
221
+ * @param datasetName The name of the left dataset
222
+ * @param data The data to save
223
+ * @param addDataToMap The function to add the data to the map
224
+ */
225
+ export function saveAsDataset (
226
+ datasets : Datasets ,
227
+ datasetName : string ,
228
+ data : Record < string , number [ ] > ,
229
+ addDataToMap : ( data : AddDataToMapPayload ) => void
230
+ ) {
231
+ // find datasetId from datasets
232
+ const datasetId = Object . keys ( datasets ) . find ( dataId => datasets [ dataId ] . label === datasetName ) ;
233
+ if ( ! datasetId ) return ;
234
+
235
+ const leftDataset = datasets [ datasetId ] ;
236
+ const numRows = leftDataset . length ;
237
+
238
+ const fields : ProtoDatasetField [ ] = [
239
+ // New fields from data
240
+ ...Object . keys ( data ) . map ( ( fieldName , index ) => ( {
241
+ name : fieldName ,
242
+ id : `${ fieldName } _${ index } ` ,
243
+ displayName : fieldName ,
244
+ type : determineFieldType ( data [ fieldName ] [ 0 ] )
245
+ } ) ) ,
246
+ // Existing fields from leftDataset
247
+ ...leftDataset . fields . map ( ( field , index ) => ( {
248
+ name : field . name ,
249
+ id : field . id || `${ field . name } _${ index } ` ,
250
+ displayName : field . displayName ,
251
+ type : field . type
252
+ } ) )
253
+ ] ;
254
+
255
+ // Pre-calculate data values array
256
+ const dataValues = Object . values ( data ) ;
257
+
258
+ const rows = Array ( numRows )
259
+ . fill ( null )
260
+ . map ( ( _ , rowIdx ) => [
261
+ // New data values
262
+ ...dataValues . map ( col => col [ rowIdx ] ) ,
263
+ // Existing dataset values
264
+ ...leftDataset . fields . map ( field => leftDataset . getValue ( field . name , rowIdx ) )
265
+ ] ) ;
266
+
267
+ // create new dataset
268
+ const newDatasetName = `${ datasetName } _joined` ;
269
+ const newDataset : ProtoDataset = {
270
+ info : {
271
+ id : newDatasetName ,
272
+ label : newDatasetName
273
+ } ,
274
+ data : {
275
+ fields,
276
+ rows
277
+ }
278
+ } ;
279
+
280
+ addDataToMap ( { datasets : [ newDataset ] , options : { autoCreateLayers : true , centerMap : true } } ) ;
281
+ }
282
+
283
+ /**
284
+ * Helper function to determine field type
285
+ * @param value The value to determine the field type
286
+ * @returns The field type
287
+ */
288
+ function determineFieldType ( value : unknown ) : keyof typeof ALL_FIELD_TYPES {
289
+ return typeof value === 'number'
290
+ ? Number . isInteger ( value )
291
+ ? ALL_FIELD_TYPES . integer
292
+ : ALL_FIELD_TYPES . real
293
+ : ALL_FIELD_TYPES . string ;
294
+ }
0 commit comments