@jupytergis/base 0.4.1 → 0.4.3

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 (58) hide show
  1. package/lib/commands.js +120 -7
  2. package/lib/constants.d.ts +3 -0
  3. package/lib/constants.js +4 -0
  4. package/lib/dialogs/ProcessingFormDialog.d.ts +30 -0
  5. package/lib/dialogs/ProcessingFormDialog.js +109 -0
  6. package/lib/dialogs/layerBrowserDialog.js +1 -1
  7. package/lib/dialogs/{formdialog.d.ts → layerCreationFormDialog.d.ts} +1 -1
  8. package/lib/dialogs/{formdialog.js → layerCreationFormDialog.js} +1 -1
  9. package/lib/dialogs/symbology/hooks/useGetBandInfo.js +22 -3
  10. package/lib/formbuilder/formselectors.d.ts +3 -3
  11. package/lib/formbuilder/formselectors.js +3 -11
  12. package/lib/formbuilder/index.d.ts +2 -4
  13. package/lib/formbuilder/index.js +2 -4
  14. package/lib/formbuilder/objectform/baseform.d.ts +1 -10
  15. package/lib/formbuilder/objectform/baseform.js +1 -1
  16. package/lib/formbuilder/objectform/fileselectorwidget.js +3 -3
  17. package/lib/formbuilder/objectform/{heatmapLayerForm.js → layer/heatmapLayerForm.js} +1 -1
  18. package/lib/formbuilder/objectform/layer/index.d.ts +5 -0
  19. package/lib/formbuilder/objectform/layer/index.js +5 -0
  20. package/lib/formbuilder/objectform/{layerform.d.ts → layer/layerform.d.ts} +6 -1
  21. package/lib/formbuilder/objectform/{layerform.js → layer/layerform.js} +1 -1
  22. package/lib/formbuilder/objectform/process/dissolveProcessForm.d.ts +20 -0
  23. package/lib/formbuilder/objectform/process/dissolveProcessForm.js +62 -0
  24. package/lib/formbuilder/objectform/process/index.d.ts +1 -0
  25. package/lib/formbuilder/objectform/process/index.js +1 -0
  26. package/lib/formbuilder/objectform/{geojsonsource.d.ts → source/geojsonsource.d.ts} +2 -2
  27. package/lib/formbuilder/objectform/{geojsonsource.js → source/geojsonsource.js} +1 -1
  28. package/lib/formbuilder/objectform/{geotiffsource.d.ts → source/geotiffsource.d.ts} +3 -3
  29. package/lib/formbuilder/objectform/{geotiffsource.js → source/geotiffsource.js} +16 -3
  30. package/lib/formbuilder/objectform/source/index.d.ts +5 -0
  31. package/lib/formbuilder/objectform/source/index.js +5 -0
  32. package/lib/formbuilder/objectform/{pathbasedsource.d.ts → source/pathbasedsource.d.ts} +3 -3
  33. package/lib/formbuilder/objectform/{pathbasedsource.js → source/pathbasedsource.js} +4 -4
  34. package/lib/formbuilder/objectform/source/sourceform.d.ts +24 -0
  35. package/lib/formbuilder/objectform/source/sourceform.js +13 -0
  36. package/lib/formbuilder/objectform/{tilesourceform.d.ts → source/tilesourceform.d.ts} +2 -2
  37. package/lib/formbuilder/objectform/{tilesourceform.js → source/tilesourceform.js} +2 -2
  38. package/lib/index.d.ts +1 -1
  39. package/lib/index.js +1 -1
  40. package/lib/keybindings.json +5 -0
  41. package/lib/mainview/mainView.js +52 -15
  42. package/lib/panelview/components/layers.js +5 -5
  43. package/lib/panelview/leftpanel.d.ts +1 -0
  44. package/lib/panelview/leftpanel.js +8 -1
  45. package/lib/panelview/rightpanel.js +2 -0
  46. package/lib/processing.d.ts +25 -0
  47. package/lib/processing.js +177 -0
  48. package/lib/tools.d.ts +16 -0
  49. package/lib/tools.js +93 -0
  50. package/package.json +2 -2
  51. package/style/leftPanel.css +0 -1
  52. /package/lib/formbuilder/objectform/{heatmapLayerForm.d.ts → layer/heatmapLayerForm.d.ts} +0 -0
  53. /package/lib/formbuilder/objectform/{hillshadeLayerForm.d.ts → layer/hillshadeLayerForm.d.ts} +0 -0
  54. /package/lib/formbuilder/objectform/{hillshadeLayerForm.js → layer/hillshadeLayerForm.js} +0 -0
  55. /package/lib/formbuilder/objectform/{vectorlayerform.d.ts → layer/vectorlayerform.d.ts} +0 -0
  56. /package/lib/formbuilder/objectform/{vectorlayerform.js → layer/vectorlayerform.js} +0 -0
  57. /package/lib/formbuilder/objectform/{webGlLayerForm.d.ts → layer/webGlLayerForm.d.ts} +0 -0
  58. /package/lib/formbuilder/objectform/{webGlLayerForm.js → layer/webGlLayerForm.js} +0 -0
package/lib/commands.js CHANGED
@@ -1,10 +1,13 @@
1
1
  import { showErrorMessage } from '@jupyterlab/apputils';
2
2
  import { CommandIDs, icons } from './constants';
3
- import { CreationFormDialog } from './dialogs/formdialog';
3
+ import { LayerCreationFormDialog } from './dialogs/layerCreationFormDialog';
4
4
  import { LayerBrowserWidget } from './dialogs/layerBrowserDialog';
5
5
  import { SymbologyWidget } from './dialogs/symbology/symbologyDialog';
6
6
  import keybindings from './keybindings.json';
7
7
  import { JupyterGISDocumentWidget } from './widget';
8
+ import { getGeoJSONDataFromLayerSource, downloadFile } from './tools';
9
+ import { ProcessingFormDialog } from './dialogs/ProcessingFormDialog';
10
+ import { getSingleSelectedLayer, selectedLayerIsOfType, processSelectedLayer } from './processing';
8
11
  function loadKeybindings(commands, keybindings) {
9
12
  keybindings.forEach(binding => {
10
13
  commands.addKeyBinding({
@@ -67,12 +70,32 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
67
70
  }
68
71
  } }, icons.get(CommandIDs.undo)));
69
72
  commands.addCommand(CommandIDs.identify, Object.assign({ label: trans.__('Identify'), isToggled: () => {
70
- var _a;
71
- return ((_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model.isIdentifying) || false;
73
+ const current = tracker.currentWidget;
74
+ if (!current) {
75
+ return false;
76
+ }
77
+ const selectedLayer = getSingleSelectedLayer(tracker);
78
+ if (!selectedLayer) {
79
+ return false;
80
+ }
81
+ const canIdentify = [
82
+ 'VectorLayer',
83
+ 'ShapefileLayer',
84
+ 'WebGlLayer'
85
+ ].includes(selectedLayer.type);
86
+ const isIdentifying = current.model.isIdentifying;
87
+ if (isIdentifying && !canIdentify) {
88
+ current.model.isIdentifying = false;
89
+ current.node.classList.remove('jGIS-identify-tool');
90
+ return false;
91
+ }
92
+ return isIdentifying;
72
93
  }, isEnabled: () => {
73
- return tracker.currentWidget
74
- ? tracker.currentWidget.model.sharedModel.editable
75
- : false;
94
+ const selectedLayer = getSingleSelectedLayer(tracker);
95
+ if (!selectedLayer) {
96
+ return false;
97
+ }
98
+ return ['VectorLayer', 'ShapefileLayer', 'WebGlLayer'].includes(selectedLayer.type);
76
99
  }, execute: args => {
77
100
  const current = tracker.currentWidget;
78
101
  if (!current) {
@@ -172,6 +195,51 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
172
195
  sourceType: 'VectorTileSource',
173
196
  layerType: 'VectorTileLayer'
174
197
  }) }, icons.get(CommandIDs.newVectorTileEntry)));
198
+ commands.addCommand(CommandIDs.buffer, {
199
+ label: trans.__('Buffer'),
200
+ isEnabled: () => selectedLayerIsOfType(['VectorLayer'], tracker),
201
+ execute: async () => {
202
+ await processSelectedLayer(tracker, formSchemaRegistry, 'Buffer', {
203
+ sqlQueryFn: (layerName, bufferDistance) => `
204
+ SELECT ST_Union(ST_Buffer(geometry, ${bufferDistance})) AS geometry, *
205
+ FROM "${layerName}"
206
+ `,
207
+ gdalFunction: 'ogr2ogr',
208
+ options: (sqlQuery) => [
209
+ '-f',
210
+ 'GeoJSON',
211
+ '-dialect',
212
+ 'SQLITE',
213
+ '-sql',
214
+ sqlQuery,
215
+ 'output.geojson'
216
+ ]
217
+ }, app);
218
+ }
219
+ });
220
+ commands.addCommand(CommandIDs.dissolve, {
221
+ label: trans.__('Dissolve'),
222
+ isEnabled: () => selectedLayerIsOfType(['VectorLayer'], tracker),
223
+ execute: async () => {
224
+ await processSelectedLayer(tracker, formSchemaRegistry, 'Dissolve', {
225
+ sqlQueryFn: (layerName, dissolveField) => `
226
+ SELECT ST_Union(geometry) AS geometry, ${dissolveField}
227
+ FROM "${layerName}"
228
+ GROUP BY ${dissolveField}
229
+ `,
230
+ gdalFunction: 'ogr2ogr',
231
+ options: (sqlQuery) => [
232
+ '-f',
233
+ 'GeoJSON',
234
+ '-dialect',
235
+ 'SQLITE',
236
+ '-sql',
237
+ sqlQuery,
238
+ 'output.geojson'
239
+ ]
240
+ }, app);
241
+ }
242
+ });
175
243
  commands.addCommand(CommandIDs.newGeoJSONEntry, Object.assign({ label: trans.__('New GeoJSON layer'), isEnabled: () => {
176
244
  return tracker.currentWidget
177
245
  ? tracker.currentWidget.model.sharedModel.editable
@@ -729,6 +797,51 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
729
797
  model.centerOnPosition(layerId);
730
798
  }
731
799
  });
800
+ commands.addCommand(CommandIDs.downloadGeoJSON, {
801
+ label: trans.__('Download as GeoJSON'),
802
+ isEnabled: () => {
803
+ const selectedLayer = getSingleSelectedLayer(tracker);
804
+ return selectedLayer
805
+ ? ['VectorLayer', 'ShapefileLayer'].includes(selectedLayer.type)
806
+ : false;
807
+ },
808
+ execute: async () => {
809
+ var _a, _b;
810
+ const selectedLayer = getSingleSelectedLayer(tracker);
811
+ if (!selectedLayer) {
812
+ return;
813
+ }
814
+ const model = (_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model;
815
+ const sources = (_b = model.sharedModel.sources) !== null && _b !== void 0 ? _b : {};
816
+ const exportSchema = Object.assign({}, formSchemaRegistry.getSchemas().get('ExportGeoJSONSchema'));
817
+ const formValues = await new Promise(resolve => {
818
+ const dialog = new ProcessingFormDialog({
819
+ title: 'Download GeoJSON',
820
+ schema: exportSchema,
821
+ model,
822
+ sourceData: { exportFormat: 'GeoJSON' },
823
+ formContext: 'create',
824
+ processingType: 'Export',
825
+ syncData: (props) => {
826
+ resolve(props);
827
+ dialog.dispose();
828
+ }
829
+ });
830
+ dialog.launch();
831
+ });
832
+ if (!formValues || !selectedLayer.parameters) {
833
+ return;
834
+ }
835
+ const exportFileName = formValues.exportFileName;
836
+ const sourceId = selectedLayer.parameters.source;
837
+ const source = sources[sourceId];
838
+ const geojsonString = await getGeoJSONDataFromLayerSource(source, model);
839
+ if (!geojsonString) {
840
+ return;
841
+ }
842
+ downloadFile(geojsonString, `${exportFileName}.geojson`, 'application/geo+json');
843
+ }
844
+ });
732
845
  loadKeybindings(commands, keybindings);
733
846
  }
734
847
  var Private;
@@ -768,7 +881,7 @@ var Private;
768
881
  if (!current) {
769
882
  return;
770
883
  }
771
- const dialog = new CreationFormDialog({
884
+ const dialog = new LayerCreationFormDialog({
772
885
  model: current.model,
773
886
  title,
774
887
  createLayer,
@@ -17,6 +17,8 @@ export declare namespace CommandIDs {
17
17
  const newImageEntry = "jupytergis:newImageEntry";
18
18
  const newVideoEntry = "jupytergis:newVideoEntry";
19
19
  const newGeoTiffEntry = "jupytergis:newGeoTiffEntry";
20
+ const buffer = "jupytergis:buffer";
21
+ const dissolve = "jupytergis:dissolve";
20
22
  const newRasterSource = "jupytergis:newRasterSource";
21
23
  const newRasterDemSource = "jupytergis:newRasterDemSource";
22
24
  const newVectorSource = "jupytergis:newVectorSource";
@@ -48,6 +50,7 @@ export declare namespace CommandIDs {
48
50
  const selectCompleter = "jupytergis:selectConsoleCompleter";
49
51
  const addAnnotation = "jupytergis:addAnnotation";
50
52
  const zoomToLayer = "jupytergis:zoomToLayer";
53
+ const downloadGeoJSON = "jupytergis:downloadGeoJSON";
51
54
  }
52
55
  interface IRegisteredIcon {
53
56
  icon?: LabIcon;
package/lib/constants.js CHANGED
@@ -21,6 +21,9 @@ export var CommandIDs;
21
21
  CommandIDs.newImageEntry = 'jupytergis:newImageEntry';
22
22
  CommandIDs.newVideoEntry = 'jupytergis:newVideoEntry';
23
23
  CommandIDs.newGeoTiffEntry = 'jupytergis:newGeoTiffEntry';
24
+ // Processing commands
25
+ CommandIDs.buffer = 'jupytergis:buffer';
26
+ CommandIDs.dissolve = 'jupytergis:dissolve';
24
27
  // Sources only commands
25
28
  CommandIDs.newRasterSource = 'jupytergis:newRasterSource';
26
29
  CommandIDs.newRasterDemSource = 'jupytergis:newRasterDemSource';
@@ -58,6 +61,7 @@ export var CommandIDs;
58
61
  // Map Commands
59
62
  CommandIDs.addAnnotation = 'jupytergis:addAnnotation';
60
63
  CommandIDs.zoomToLayer = 'jupytergis:zoomToLayer';
64
+ CommandIDs.downloadGeoJSON = 'jupytergis:downloadGeoJSON';
61
65
  })(CommandIDs || (CommandIDs = {}));
62
66
  const iconObject = {
63
67
  RasterSource: { icon: rasterIcon },
@@ -0,0 +1,30 @@
1
+ import { IDict, IJupyterGISModel } from '@jupytergis/schema';
2
+ import { Dialog } from '@jupyterlab/apputils';
3
+ import { IBaseFormProps } from '../formbuilder/objectform/baseform';
4
+ import { Signal } from '@lumino/signaling';
5
+ import { PromiseDelegate } from '@lumino/coreutils';
6
+ export interface IProcessingFormDialogOptions extends IBaseFormProps {
7
+ formContext: 'update' | 'create';
8
+ schema: IDict;
9
+ sourceData: IDict;
10
+ title: string;
11
+ syncData: (props: IDict) => void;
12
+ syncSelectedPropField?: (id: string | null, value: any, parentType: 'dialog' | 'panel') => void;
13
+ model: IJupyterGISModel;
14
+ processingType: 'Buffer' | 'Dissolve' | 'Export';
15
+ }
16
+ /**
17
+ * Wrapper component to handle OK button state
18
+ */
19
+ export interface IProcessingFormWrapperProps extends IProcessingFormDialogOptions {
20
+ okSignalPromise: PromiseDelegate<Signal<Dialog<any>, number>>;
21
+ formErrorSignalPromise?: PromiseDelegate<Signal<Dialog<any>, boolean>>;
22
+ }
23
+ /**
24
+ * Dialog for processing operations
25
+ */
26
+ export declare class ProcessingFormDialog extends Dialog<IDict> {
27
+ constructor(options: IProcessingFormDialogOptions);
28
+ resolve(index?: number): void;
29
+ private okSignal;
30
+ }
@@ -0,0 +1,109 @@
1
+ import { Dialog } from '@jupyterlab/apputils';
2
+ import * as React from 'react';
3
+ import { BaseForm } from '../formbuilder/objectform/baseform';
4
+ import { DissolveForm } from '../formbuilder/objectform/process';
5
+ import { Signal } from '@lumino/signaling';
6
+ import { PromiseDelegate } from '@lumino/coreutils';
7
+ const ProcessingFormWrapper = (props) => {
8
+ var _a;
9
+ const [ready, setReady] = React.useState(false);
10
+ const okSignal = React.useRef();
11
+ const formErrorSignal = React.useRef();
12
+ Promise.all([
13
+ props.okSignalPromise.promise,
14
+ (_a = props.formErrorSignalPromise) === null || _a === void 0 ? void 0 : _a.promise
15
+ ]).then(([ok, formChanged]) => {
16
+ okSignal.current = ok;
17
+ formErrorSignal.current = formChanged;
18
+ setReady(true);
19
+ });
20
+ let FormComponent;
21
+ switch (props.processingType) {
22
+ case 'Dissolve':
23
+ FormComponent = DissolveForm;
24
+ break;
25
+ case 'Buffer':
26
+ case 'Export':
27
+ default:
28
+ FormComponent = BaseForm;
29
+ }
30
+ return (ready && (React.createElement(FormComponent, { formContext: props.formContext, filePath: props.model.filePath, model: props.model, ok: okSignal.current, cancel: props.cancel, sourceData: props.sourceData, schema: props.schema, syncData: props.syncData })));
31
+ };
32
+ /**
33
+ * Dialog for processing operations
34
+ */
35
+ export class ProcessingFormDialog extends Dialog {
36
+ constructor(options) {
37
+ var _a, _b, _c;
38
+ // Extract layers from the shared model
39
+ const layers = (_a = options.model.sharedModel.layers) !== null && _a !== void 0 ? _a : {};
40
+ const layerOptions = Object.keys(layers).map(layerId => ({
41
+ value: layerId,
42
+ label: layers[layerId].name
43
+ }));
44
+ // Modify schema to include layer options and layer name field
45
+ if (options.schema) {
46
+ if ((_b = options.schema.properties) === null || _b === void 0 ? void 0 : _b.inputLayer) {
47
+ options.schema.properties.inputLayer.enum = layerOptions.map(option => option.value);
48
+ options.schema.properties.inputLayer.enumNames = layerOptions.map(option => option.label);
49
+ }
50
+ // Ensure outputLayerName field exists in schema
51
+ if (!((_c = options.schema.properties) === null || _c === void 0 ? void 0 : _c.outputLayerName)) {
52
+ options.schema.properties.outputLayerName = {
53
+ type: 'string',
54
+ title: 'outputLayerName'
55
+ // default: ''
56
+ };
57
+ }
58
+ }
59
+ const filePath = options.model.filePath;
60
+ const jgisModel = options.model;
61
+ const okSignalPromise = new PromiseDelegate();
62
+ const formErrorSignalPromise = new PromiseDelegate();
63
+ // Custom syncData function to update layer name in the model
64
+ const syncData = (props) => {
65
+ if (props.outputLayerName &&
66
+ props.inputLayer &&
67
+ layers[props.inputLayer]) {
68
+ layers[props.inputLayer].name = props.outputLayerName;
69
+ }
70
+ options.syncData(props);
71
+ };
72
+ const body = (React.createElement("div", { style: { overflow: 'hidden' } },
73
+ React.createElement(ProcessingFormWrapper, Object.assign({}, options, { filePath: filePath, model: jgisModel, okSignalPromise: okSignalPromise, formErrorSignalPromise: formErrorSignalPromise, syncData: syncData }))));
74
+ super({
75
+ title: options.title,
76
+ body,
77
+ buttons: [Dialog.cancelButton(), Dialog.okButton()]
78
+ });
79
+ this.okSignal = new Signal(this);
80
+ const formErrorSignal = new Signal(this);
81
+ /**
82
+ * Disable the OK button if the form is invalid.
83
+ */
84
+ formErrorSignal.connect((_, extraErrors) => {
85
+ const invalid = extraErrors || !!this.node.querySelector(':invalid');
86
+ if (invalid) {
87
+ this.node
88
+ .getElementsByClassName('jp-mod-accept')[0]
89
+ .setAttribute('disabled', '');
90
+ }
91
+ else {
92
+ this.node
93
+ .getElementsByClassName('jp-mod-accept')[0]
94
+ .removeAttribute('disabled');
95
+ }
96
+ });
97
+ okSignalPromise.resolve(this.okSignal);
98
+ formErrorSignalPromise.resolve(formErrorSignal);
99
+ this.addClass('jGIS-processing-FormDialog');
100
+ }
101
+ resolve(index) {
102
+ if (index === 0) {
103
+ super.resolve(index);
104
+ }
105
+ if (index === 1) {
106
+ this.okSignal.emit(1);
107
+ }
108
+ }
109
+ }
@@ -5,7 +5,7 @@ import { PromiseDelegate, UUID } from '@lumino/coreutils';
5
5
  import { Signal } from '@lumino/signaling';
6
6
  import React, { useEffect, useState } from 'react';
7
7
  import CUSTOM_RASTER_IMAGE from '../../rasterlayer_gallery/custom_raster.png';
8
- import { CreationFormWrapper } from './formdialog';
8
+ import { CreationFormWrapper } from './layerCreationFormDialog';
9
9
  export const LayerBrowserComponent = ({ model, registry, formSchemaRegistry, okSignalPromise, cancel }) => {
10
10
  const [searchTerm, setSearchTerm] = useState('');
11
11
  const [activeLayers, setActiveLayers] = useState([]);
@@ -29,7 +29,7 @@ export declare const CreationFormWrapper: (props: ICreationFormWrapperProps) =>
29
29
  /**
30
30
  * Form for creating a source, a layer or both at the same time
31
31
  */
32
- export declare class CreationFormDialog extends Dialog<IDict> {
32
+ export declare class LayerCreationFormDialog extends Dialog<IDict> {
33
33
  constructor(options: ICreationFormDialogOptions);
34
34
  resolve(index?: number): void;
35
35
  private okSignal;
@@ -21,7 +21,7 @@ export const CreationFormWrapper = (props) => {
21
21
  /**
22
22
  * Form for creating a source, a layer or both at the same time
23
23
  */
24
- export class CreationFormDialog extends Dialog {
24
+ export class LayerCreationFormDialog extends Dialog {
25
25
  constructor(options) {
26
26
  const cancelCallback = () => {
27
27
  this.resolve(0);
@@ -1,5 +1,6 @@
1
1
  import { useEffect, useState } from 'react';
2
- import { fromUrl } from 'geotiff';
2
+ import { fromUrl, fromBlob } from 'geotiff';
3
+ import { loadFile } from '../../../tools';
3
4
  const useGetBandInfo = (model, layer) => {
4
5
  const [bandRows, setBandRows] = useState([]);
5
6
  const [loading, setLoading] = useState(false);
@@ -17,8 +18,26 @@ const useGetBandInfo = (model, layer) => {
17
18
  setLoading(false);
18
19
  return;
19
20
  }
20
- // TODO Get band names + get band stats
21
- const tiff = await fromUrl(sourceInfo.url);
21
+ let tiff;
22
+ if (sourceInfo.url.startsWith('http') ||
23
+ sourceInfo.url.startsWith('https')) {
24
+ // Handle remote GeoTIFF file
25
+ tiff = await fromUrl(sourceInfo.url);
26
+ }
27
+ else {
28
+ // Handle local GeoTIFF file
29
+ const preloadedFile = await loadFile({
30
+ filepath: sourceInfo.url,
31
+ type: 'GeoTiffSource',
32
+ model
33
+ });
34
+ if (!preloadedFile.file) {
35
+ setError('Failed to load local file.');
36
+ setLoading(false);
37
+ return;
38
+ }
39
+ tiff = await fromBlob(preloadedFile.file);
40
+ }
22
41
  const image = await tiff.getImage();
23
42
  const numberOfBands = image.getSamplesPerPixel();
24
43
  for (let i = 0; i < numberOfBands; i++) {
@@ -1,5 +1,5 @@
1
1
  import { LayerType, SourceType } from '@jupytergis/schema';
2
- import { BaseForm } from './objectform/baseform';
3
- import { LayerPropertiesForm } from './objectform/layerform';
2
+ import { SourcePropertiesForm } from './objectform/source';
3
+ import { LayerPropertiesForm } from './objectform/layer';
4
4
  export declare function getLayerTypeForm(layerType: LayerType): typeof LayerPropertiesForm;
5
- export declare function getSourceTypeForm(sourceType: SourceType): typeof BaseForm;
5
+ export declare function getSourceTypeForm(sourceType: SourceType): typeof SourcePropertiesForm;
@@ -1,13 +1,5 @@
1
- import { BaseForm } from './objectform/baseform';
2
- import { GeoJSONSourcePropertiesForm } from './objectform/geojsonsource';
3
- import { GeoTiffSourcePropertiesForm } from './objectform/geotiffsource';
4
- import { HeatmapLayerPropertiesForm } from './objectform/heatmapLayerForm';
5
- import { HillshadeLayerPropertiesForm } from './objectform/hillshadeLayerForm';
6
- import { LayerPropertiesForm } from './objectform/layerform';
7
- import { PathBasedSourcePropertiesForm } from './objectform/pathbasedsource';
8
- import { TileSourcePropertiesForm } from './objectform/tilesourceform';
9
- import { VectorLayerPropertiesForm } from './objectform/vectorlayerform';
10
- import { WebGlLayerPropertiesForm } from './objectform/webGlLayerForm';
1
+ import { GeoJSONSourcePropertiesForm, GeoTiffSourcePropertiesForm, PathBasedSourcePropertiesForm, TileSourcePropertiesForm, SourcePropertiesForm } from './objectform/source';
2
+ import { HeatmapLayerPropertiesForm, HillshadeLayerPropertiesForm, LayerPropertiesForm, VectorLayerPropertiesForm, WebGlLayerPropertiesForm } from './objectform/layer';
11
3
  export function getLayerTypeForm(layerType) {
12
4
  let LayerForm = LayerPropertiesForm;
13
5
  switch (layerType) {
@@ -28,7 +20,7 @@ export function getLayerTypeForm(layerType) {
28
20
  return LayerForm;
29
21
  }
30
22
  export function getSourceTypeForm(sourceType) {
31
- let SourceForm = BaseForm;
23
+ let SourceForm = SourcePropertiesForm;
32
24
  switch (sourceType) {
33
25
  case 'GeoJSONSource':
34
26
  SourceForm = GeoJSONSourcePropertiesForm;
@@ -1,6 +1,4 @@
1
1
  export * from './objectform/baseform';
2
- export * from './objectform/geojsonsource';
3
- export * from './objectform/layerform';
4
- export * from './objectform/tilesourceform';
5
- export * from './objectform/vectorlayerform';
2
+ export * from './objectform/source';
3
+ export * from './objectform/layer';
6
4
  export * from './creationform';
@@ -1,6 +1,4 @@
1
1
  export * from './objectform/baseform';
2
- export * from './objectform/geojsonsource';
3
- export * from './objectform/layerform';
4
- export * from './objectform/tilesourceform';
5
- export * from './objectform/vectorlayerform';
2
+ export * from './objectform/source';
3
+ export * from './objectform/layer';
6
4
  export * from './creationform';
@@ -1,4 +1,4 @@
1
- import { IJupyterGISModel, SourceType } from '@jupytergis/schema';
1
+ import { IJupyterGISModel } from '@jupytergis/schema';
2
2
  import { Dialog } from '@jupyterlab/apputils';
3
3
  import { Signal } from '@lumino/signaling';
4
4
  import { IChangeEvent, ISubmitEvent } from '@rjsf/core';
@@ -51,15 +51,6 @@ export interface IBaseFormProps {
51
51
  * extra errors or not.
52
52
  */
53
53
  formErrorSignal?: Signal<Dialog<any>, boolean>;
54
- /**
55
- * Configuration options for the dialog, including settings for layer data, source data,
56
- * and other form-related parameters.
57
- */
58
- dialogOptions?: any;
59
- /**
60
- * Source type property
61
- */
62
- sourceType: SourceType;
63
54
  }
64
55
  /**
65
56
  * Generate a form to edit a layer/source type. This class is meant to be sub-classed to create more refined forms for specific layers/sources.
@@ -186,7 +186,7 @@ export class BaseForm extends React.Component {
186
186
  (_a = submitRef.current) === null || _a === void 0 ? void 0 : _a.click();
187
187
  }
188
188
  } },
189
- React.createElement(WrappedFormComponent, { schema: schema, uiSchema: uiSchema, formData: formData, onSubmit: this.onFormSubmit.bind(this), onChange: this.onFormChange.bind(this), onBlur: this.onFormBlur.bind(this), liveValidate: true, children: React.createElement("button", { ref: submitRef, type: "submit", style: { display: 'none' } }), extraErrors: this.state.extraErrors })),
189
+ React.createElement(WrappedFormComponent, { schema: schema, uiSchema: uiSchema, formData: formData, onSubmit: this.onFormSubmit.bind(this), onChange: this.onFormChange.bind(this), onBlur: this.onFormBlur.bind(this), ok: this.props.ok, cancel: this.props.cancel, liveValidate: true, children: React.createElement("button", { ref: submitRef, type: "submit", style: { display: 'none' } }), extraErrors: this.state.extraErrors })),
190
190
  !this.props.ok && (React.createElement("div", { className: "jGIS-property-buttons" },
191
191
  React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: () => { var _a; return (_a = submitRef.current) === null || _a === void 0 ? void 0 : _a.click(); } },
192
192
  React.createElement("div", { className: "jp-Dialog-buttonLabel" }, "Ok"))))));
@@ -1,7 +1,7 @@
1
1
  import React, { useState, useEffect, useRef } from 'react';
2
2
  import { FileDialog } from '@jupyterlab/filebrowser';
3
3
  import { Dialog } from '@jupyterlab/apputils';
4
- import { CreationFormDialog } from '../../dialogs/formdialog';
4
+ import { LayerCreationFormDialog } from '../../dialogs/layerCreationFormDialog';
5
5
  import { PathExt } from '@jupyterlab/coreutils';
6
6
  export const FileSelectorWidget = (props) => {
7
7
  const { options } = props;
@@ -53,13 +53,13 @@ export const FileSelectorWidget = (props) => {
53
53
  else {
54
54
  formOptions.dialogOptions.sourceData = Object.assign(Object.assign({}, formOptions.sourceData), { path: relativePath });
55
55
  }
56
- const formDialog = new CreationFormDialog(Object.assign({}, formOptions.dialogOptions));
56
+ const formDialog = new LayerCreationFormDialog(Object.assign({}, formOptions.dialogOptions));
57
57
  await formDialog.launch();
58
58
  }
59
59
  }
60
60
  else {
61
61
  if (dialogElement) {
62
- const formDialog = new CreationFormDialog(Object.assign({}, formOptions.dialogOptions));
62
+ const formDialog = new LayerCreationFormDialog(Object.assign({}, formOptions.dialogOptions));
63
63
  await formDialog.launch();
64
64
  }
65
65
  }
@@ -1,4 +1,4 @@
1
- import { loadFile } from '../../tools';
1
+ import { loadFile } from '../../../tools';
2
2
  import { LayerPropertiesForm } from './layerform';
3
3
  export class HeatmapLayerPropertiesForm extends LayerPropertiesForm {
4
4
  constructor(props) {
@@ -0,0 +1,5 @@
1
+ export * from './heatmapLayerForm';
2
+ export * from './hillshadeLayerForm';
3
+ export * from './layerform';
4
+ export * from './vectorlayerform';
5
+ export * from './webGlLayerForm';
@@ -0,0 +1,5 @@
1
+ export * from './heatmapLayerForm';
2
+ export * from './hillshadeLayerForm';
3
+ export * from './layerform';
4
+ export * from './vectorlayerform';
5
+ export * from './webGlLayerForm';
@@ -1,5 +1,5 @@
1
1
  import { IDict, SourceType } from '@jupytergis/schema';
2
- import { BaseForm, IBaseFormProps } from './baseform';
2
+ import { BaseForm, IBaseFormProps } from '../baseform';
3
3
  import { Signal } from '@lumino/signaling';
4
4
  import { IChangeEvent } from '@rjsf/core';
5
5
  export interface ILayerProps extends IBaseFormProps {
@@ -11,6 +11,11 @@ export interface ILayerProps extends IBaseFormProps {
11
11
  * The signal emitted when the attached source form has changed, if it exists
12
12
  */
13
13
  sourceFormChangedSignal?: Signal<any, IDict<any>>;
14
+ /**
15
+ * Configuration options for the dialog, including settings for layer data, source data,
16
+ * and other form-related parameters.
17
+ */
18
+ dialogOptions?: any;
14
19
  }
15
20
  export declare class LayerPropertiesForm extends BaseForm {
16
21
  props: ILayerProps;
@@ -1,4 +1,4 @@
1
- import { BaseForm } from './baseform';
1
+ import { BaseForm } from '../baseform';
2
2
  export class LayerPropertiesForm extends BaseForm {
3
3
  constructor(props) {
4
4
  super(props);
@@ -0,0 +1,20 @@
1
+ import { BaseForm, IBaseFormProps } from '../baseform';
2
+ import { IDict, IJupyterGISModel } from '@jupytergis/schema';
3
+ import { IChangeEvent } from '@rjsf/core';
4
+ interface IDissolveFormOptions extends IBaseFormProps {
5
+ schema: IDict;
6
+ sourceData: IDict;
7
+ title: string;
8
+ cancelButton: (() => void) | boolean;
9
+ syncData: (props: IDict) => void;
10
+ model: IJupyterGISModel;
11
+ }
12
+ export declare class DissolveForm extends BaseForm {
13
+ private model;
14
+ private features;
15
+ constructor(options: IDissolveFormOptions);
16
+ private fetchFieldNames;
17
+ handleFormChange(e: IChangeEvent): void;
18
+ private updateSchema;
19
+ }
20
+ export {};
@@ -0,0 +1,62 @@
1
+ import { BaseForm } from '../baseform'; // Ensure BaseForm imports states
2
+ import { loadFile } from '../../../tools';
3
+ export class DissolveForm extends BaseForm {
4
+ constructor(options) {
5
+ var _a;
6
+ super(options);
7
+ this.features = [];
8
+ this.model = options.model;
9
+ // Ensure initial state matches IBaseFormStates
10
+ this.state = {
11
+ schema: (_a = options.schema) !== null && _a !== void 0 ? _a : {} // Ensure schema is never undefined
12
+ };
13
+ this.onFormChange = this.handleFormChange.bind(this);
14
+ this.fetchFieldNames(options.sourceData.inputLayer);
15
+ }
16
+ async fetchFieldNames(layerId) {
17
+ var _a, _b;
18
+ const layer = this.model.getLayer(layerId);
19
+ if (!((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.source)) {
20
+ return;
21
+ }
22
+ const source = this.model.getSource(layer.parameters.source);
23
+ if (!source || source.type !== 'GeoJSONSource') {
24
+ return;
25
+ }
26
+ const sourceData = source.parameters;
27
+ if (!(sourceData === null || sourceData === void 0 ? void 0 : sourceData.path)) {
28
+ return;
29
+ }
30
+ try {
31
+ const jsonData = await loadFile({
32
+ filepath: sourceData.path,
33
+ type: 'GeoJSONSource',
34
+ model: this.model
35
+ });
36
+ if (!((_b = jsonData === null || jsonData === void 0 ? void 0 : jsonData.features) === null || _b === void 0 ? void 0 : _b.length)) {
37
+ return;
38
+ }
39
+ this.features = Object.keys(jsonData.features[0].properties);
40
+ this.updateSchema();
41
+ }
42
+ catch (error) {
43
+ console.error('Error loading GeoJSON:', error);
44
+ }
45
+ }
46
+ handleFormChange(e) {
47
+ super.onFormChange(e);
48
+ if (e.formData.inputLayer) {
49
+ this.fetchFieldNames(e.formData.inputLayer);
50
+ }
51
+ }
52
+ updateSchema() {
53
+ this.setState((prevState) => {
54
+ var _a, _b, _c;
55
+ return ({
56
+ schema: Object.assign(Object.assign({}, prevState.schema), { properties: Object.assign(Object.assign({}, (_a = prevState.schema) === null || _a === void 0 ? void 0 : _a.properties), { dissolveField: Object.assign(Object.assign({}, (_c = (_b = prevState.schema) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.dissolveField), { enum: [...this.features] }) }) })
57
+ });
58
+ }, () => {
59
+ this.forceUpdate();
60
+ });
61
+ }
62
+ }