@jupytergis/base 0.4.2 → 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.
- package/lib/commands.js +120 -7
- package/lib/constants.d.ts +3 -0
- package/lib/constants.js +4 -0
- package/lib/dialogs/ProcessingFormDialog.d.ts +30 -0
- package/lib/dialogs/ProcessingFormDialog.js +109 -0
- package/lib/dialogs/layerBrowserDialog.js +1 -1
- package/lib/dialogs/{formdialog.d.ts → layerCreationFormDialog.d.ts} +1 -1
- package/lib/dialogs/{formdialog.js → layerCreationFormDialog.js} +1 -1
- package/lib/dialogs/symbology/hooks/useGetBandInfo.js +22 -3
- package/lib/formbuilder/formselectors.d.ts +3 -3
- package/lib/formbuilder/formselectors.js +3 -11
- package/lib/formbuilder/index.d.ts +2 -4
- package/lib/formbuilder/index.js +2 -4
- package/lib/formbuilder/objectform/baseform.d.ts +1 -10
- package/lib/formbuilder/objectform/baseform.js +1 -1
- package/lib/formbuilder/objectform/fileselectorwidget.js +3 -3
- package/lib/formbuilder/objectform/{heatmapLayerForm.js → layer/heatmapLayerForm.js} +1 -1
- package/lib/formbuilder/objectform/layer/index.d.ts +5 -0
- package/lib/formbuilder/objectform/layer/index.js +5 -0
- package/lib/formbuilder/objectform/{layerform.d.ts → layer/layerform.d.ts} +6 -1
- package/lib/formbuilder/objectform/{layerform.js → layer/layerform.js} +1 -1
- package/lib/formbuilder/objectform/process/dissolveProcessForm.d.ts +20 -0
- package/lib/formbuilder/objectform/process/dissolveProcessForm.js +62 -0
- package/lib/formbuilder/objectform/process/index.d.ts +1 -0
- package/lib/formbuilder/objectform/process/index.js +1 -0
- package/lib/formbuilder/objectform/{geojsonsource.d.ts → source/geojsonsource.d.ts} +2 -2
- package/lib/formbuilder/objectform/{geojsonsource.js → source/geojsonsource.js} +1 -1
- package/lib/formbuilder/objectform/{geotiffsource.d.ts → source/geotiffsource.d.ts} +3 -3
- package/lib/formbuilder/objectform/{geotiffsource.js → source/geotiffsource.js} +16 -3
- package/lib/formbuilder/objectform/source/index.d.ts +5 -0
- package/lib/formbuilder/objectform/source/index.js +5 -0
- package/lib/formbuilder/objectform/{pathbasedsource.d.ts → source/pathbasedsource.d.ts} +3 -3
- package/lib/formbuilder/objectform/{pathbasedsource.js → source/pathbasedsource.js} +4 -4
- package/lib/formbuilder/objectform/source/sourceform.d.ts +24 -0
- package/lib/formbuilder/objectform/source/sourceform.js +13 -0
- package/lib/formbuilder/objectform/{tilesourceform.d.ts → source/tilesourceform.d.ts} +2 -2
- package/lib/formbuilder/objectform/{tilesourceform.js → source/tilesourceform.js} +2 -2
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/keybindings.json +5 -0
- package/lib/mainview/mainView.js +52 -15
- package/lib/panelview/components/layers.js +3 -3
- package/lib/panelview/leftpanel.d.ts +1 -0
- package/lib/panelview/leftpanel.js +8 -1
- package/lib/panelview/rightpanel.js +2 -0
- package/lib/processing.d.ts +25 -0
- package/lib/processing.js +177 -0
- package/lib/tools.d.ts +16 -0
- package/lib/tools.js +93 -0
- package/package.json +2 -2
- /package/lib/formbuilder/objectform/{heatmapLayerForm.d.ts → layer/heatmapLayerForm.d.ts} +0 -0
- /package/lib/formbuilder/objectform/{hillshadeLayerForm.d.ts → layer/hillshadeLayerForm.d.ts} +0 -0
- /package/lib/formbuilder/objectform/{hillshadeLayerForm.js → layer/hillshadeLayerForm.js} +0 -0
- /package/lib/formbuilder/objectform/{vectorlayerform.d.ts → layer/vectorlayerform.d.ts} +0 -0
- /package/lib/formbuilder/objectform/{vectorlayerform.js → layer/vectorlayerform.js} +0 -0
- /package/lib/formbuilder/objectform/{webGlLayerForm.d.ts → layer/webGlLayerForm.d.ts} +0 -0
- /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 {
|
|
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
|
-
|
|
71
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
|
884
|
+
const dialog = new LayerCreationFormDialog({
|
|
772
885
|
model: current.model,
|
|
773
886
|
title,
|
|
774
887
|
createLayer,
|
package/lib/constants.d.ts
CHANGED
|
@@ -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 './
|
|
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
|
|
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
|
|
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
|
-
|
|
21
|
-
|
|
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 {
|
|
3
|
-
import { LayerPropertiesForm } from './objectform/
|
|
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
|
|
5
|
+
export declare function getSourceTypeForm(sourceType: SourceType): typeof SourcePropertiesForm;
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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 =
|
|
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/
|
|
3
|
-
export * from './objectform/
|
|
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';
|
package/lib/formbuilder/index.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
export * from './objectform/baseform';
|
|
2
|
-
export * from './objectform/
|
|
3
|
-
export * from './objectform/
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
62
|
+
const formDialog = new LayerCreationFormDialog(Object.assign({}, formOptions.dialogOptions));
|
|
63
63
|
await formDialog.launch();
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IDict, SourceType } from '@jupytergis/schema';
|
|
2
|
-
import { BaseForm, IBaseFormProps } from '
|
|
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;
|
|
@@ -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
|
+
}
|