@@ -24,10 +24,10 @@ import keybindings from './keybindings.json';
24
24
import { JupyterGISTracker } from './types' ;
25
25
import { JupyterGISDocumentWidget } from './widget' ;
26
26
import { getGdal } from './gdal' ;
27
- import { loadFile } from './tools' ;
27
+ import { getGeoJSONDataFromLayerSource , downloadFile } from './tools' ;
28
28
import { IJGISLayer , IJGISSource } from '@jupytergis/schema' ;
29
29
import { UUID } from '@lumino/coreutils' ;
30
- import { ProcessingFormDialog } from './formbuilder/processingformdialog ' ;
30
+ import { FormDialog } from './formbuilder/formdialog ' ;
31
31
32
32
interface ICreateEntry {
33
33
tracker : JupyterGISTracker ;
@@ -51,6 +51,34 @@ function loadKeybindings(commands: CommandRegistry, keybindings: any[]) {
51
51
} ) ;
52
52
}
53
53
54
+ /**
55
+ * Get the currently selected layer from the shared model. Returns null if there is no selection or multiple layer is selected.
56
+ */
57
+ function getSingleSelectedLayer ( tracker : JupyterGISTracker ) : IJGISLayer | null {
58
+ const model = tracker . currentWidget ?. model as IJupyterGISModel ;
59
+ if ( ! model ) {
60
+ return null ;
61
+ }
62
+
63
+ const localState = model . sharedModel . awareness . getLocalState ( ) ;
64
+ if ( ! localState || ! localState [ 'selected' ] ?. value ) {
65
+ return null ;
66
+ }
67
+
68
+ const selectedLayers = Object . keys ( localState [ 'selected' ] . value ) ;
69
+
70
+ // Ensure only one layer is selected
71
+ if ( selectedLayers . length !== 1 ) {
72
+ return null ;
73
+ }
74
+
75
+ const selectedLayerId = selectedLayers [ 0 ] ;
76
+ const layers = model . sharedModel . layers ?? { } ;
77
+ const selectedLayer = layers [ selectedLayerId ] ;
78
+
79
+ return selectedLayer && selectedLayer . parameters ? selectedLayer : null ;
80
+ }
81
+
54
82
/**
55
83
* Add the commands to the application's command registry.
56
84
*/
@@ -293,33 +321,18 @@ export function addCommands(
293
321
commands . addCommand ( CommandIDs . buffer , {
294
322
label : trans . __ ( 'Buffer' ) ,
295
323
isEnabled : ( ) => {
296
- const model = tracker . currentWidget ?. model ;
297
- const localState = model ?. sharedModel . awareness . getLocalState ( ) ;
298
-
299
- if ( ! model || ! localState || ! localState [ 'selected' ] ?. value ) {
300
- return false ;
301
- }
302
-
303
- const selectedLayers = localState [ 'selected' ] . value ;
304
-
305
- if ( Object . keys ( selectedLayers ) . length > 1 ) {
306
- return false ;
307
- }
308
-
309
- const layerId = Object . keys ( selectedLayers ) [ 0 ] ;
310
- const layer = model . getLayer ( layerId ) ;
311
-
312
- if ( ! layer ) {
324
+ const selectedLayer = getSingleSelectedLayer ( tracker ) ;
325
+ if ( ! selectedLayer ) {
313
326
return false ;
314
327
}
315
-
316
- const isValidLayer = [ 'VectorLayer' , 'ShapefileLayer' ] . includes (
317
- layer . type
318
- ) ;
319
-
320
- return isValidLayer ;
328
+ return [ 'VectorLayer' , 'ShapefileLayer' ] . includes ( selectedLayer . type ) ;
321
329
} ,
322
330
execute : async ( ) => {
331
+ const selected = getSingleSelectedLayer ( tracker ) ;
332
+ if ( ! selected ) {
333
+ console . error ( 'No valid selected layer.' ) ;
334
+ return ;
335
+ }
323
336
const layers = tracker . currentWidget ?. model . sharedModel . layers ?? { } ;
324
337
const sources = tracker . currentWidget ?. model . sharedModel . sources ?? { } ;
325
338
@@ -337,10 +350,10 @@ export function addCommands(
337
350
338
351
// Open form and get user input
339
352
const formValues = await new Promise < IDict > ( resolve => {
340
- const dialog = new ProcessingFormDialog ( {
353
+ const dialog = new FormDialog ( {
341
354
title : 'Buffer' ,
342
355
schema : schema ,
343
- model : tracker . currentWidget ?. model as IJupyterGISModel ,
356
+ model : model ,
344
357
sourceData : {
345
358
inputLayer : selectedLayerId ,
346
359
bufferDistance : 10 ,
@@ -372,30 +385,14 @@ export function addCommands(
372
385
const sourceId = inputLayer . parameters . source ;
373
386
const source = sources [ sourceId ] ;
374
387
375
- if ( ! source . parameters ) {
388
+ if ( ! source || ! source . parameters ) {
376
389
console . error ( `Source with ID ${ sourceId } not found or missing path.` ) ;
377
390
return ;
378
391
}
379
392
380
- let geojsonString : string ;
381
-
382
- if ( source . parameters . path ) {
383
- const fileContent = await loadFile ( {
384
- filepath : source . parameters . path ,
385
- type : source . type ,
386
- model : tracker . currentWidget ?. model as IJupyterGISModel
387
- } ) ;
388
-
389
- geojsonString =
390
- typeof fileContent === 'object'
391
- ? JSON . stringify ( fileContent )
392
- : fileContent ;
393
- } else if ( source . parameters . data ) {
394
- geojsonString = JSON . stringify ( source . parameters . data ) ;
395
- } else {
396
- throw new Error (
397
- `Source ${ sourceId } is missing both 'path' and 'data' parameters.`
398
- ) ;
393
+ const geojsonString = await getGeoJSONDataFromLayerSource ( source , model ) ;
394
+ if ( ! geojsonString ) {
395
+ return ;
399
396
}
400
397
401
398
const fileBlob = new Blob ( [ geojsonString ] , {
@@ -446,18 +443,13 @@ export function addCommands(
446
443
447
444
const layerModel : IJGISLayer = {
448
445
type : 'VectorLayer' ,
449
- parameters : {
450
- source : newSourceId
451
- } ,
446
+ parameters : { source : newSourceId } ,
452
447
visible : true ,
453
448
name : inputLayer . name + ' Buffer'
454
449
} ;
455
450
456
- tracker . currentWidget ?. model . sharedModel . addSource (
457
- newSourceId ,
458
- sourceModel
459
- ) ;
460
- tracker . currentWidget ?. model . addLayer ( UUID . uuid4 ( ) , layerModel ) ;
451
+ model . sharedModel . addSource ( newSourceId , sourceModel ) ;
452
+ model . addLayer ( UUID . uuid4 ( ) , layerModel ) ;
461
453
}
462
454
}
463
455
} ) ;
@@ -1167,6 +1159,63 @@ export function addCommands(
1167
1159
}
1168
1160
} ) ;
1169
1161
1162
+ commands . addCommand ( CommandIDs . downloadGeoJSON , {
1163
+ label : trans . __ ( 'Download as GeoJSON' ) ,
1164
+ isEnabled : ( ) => {
1165
+ const selectedLayer = getSingleSelectedLayer ( tracker ) ;
1166
+ return selectedLayer
1167
+ ? [ 'VectorLayer' , 'ShapefileLayer' ] . includes ( selectedLayer . type )
1168
+ : false ;
1169
+ } ,
1170
+ execute : async ( ) => {
1171
+ const selectedLayer = getSingleSelectedLayer ( tracker ) ;
1172
+ if ( ! selectedLayer ) {
1173
+ return ;
1174
+ }
1175
+ const model = tracker . currentWidget ?. model as IJupyterGISModel ;
1176
+ const sources = model . sharedModel . sources ?? { } ;
1177
+
1178
+ const exportSchema = {
1179
+ ...( formSchemaRegistry . getSchemas ( ) . get ( 'ExportGeoJSONSchema' ) as IDict )
1180
+ } ;
1181
+
1182
+ const formValues = await new Promise < IDict > ( resolve => {
1183
+ const dialog = new FormDialog ( {
1184
+ title : 'Download GeoJSON' ,
1185
+ schema : exportSchema ,
1186
+ model,
1187
+ sourceData : { exportFormat : 'GeoJSON' } ,
1188
+ cancelButton : false ,
1189
+ syncData : ( props : IDict ) => {
1190
+ resolve ( props ) ;
1191
+ dialog . dispose ( ) ;
1192
+ }
1193
+ } ) ;
1194
+
1195
+ dialog . launch ( ) ;
1196
+ } ) ;
1197
+
1198
+ if ( ! formValues || ! selectedLayer . parameters ) {
1199
+ return ;
1200
+ }
1201
+
1202
+ const exportFileName = formValues . exportFileName ;
1203
+ const sourceId = selectedLayer . parameters . source ;
1204
+ const source = sources [ sourceId ] ;
1205
+
1206
+ const geojsonString = await getGeoJSONDataFromLayerSource ( source , model ) ;
1207
+ if ( ! geojsonString ) {
1208
+ return ;
1209
+ }
1210
+
1211
+ downloadFile (
1212
+ geojsonString ,
1213
+ `${ exportFileName } .geojson` ,
1214
+ 'application/geo+json'
1215
+ ) ;
1216
+ }
1217
+ } ) ;
1218
+
1170
1219
loadKeybindings ( commands , keybindings ) ;
1171
1220
}
1172
1221
0 commit comments