@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.
- 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 +5 -5
- 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/style/leftPanel.css +0 -1
- /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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dissolveProcessForm';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dissolveProcessForm';
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { IDict } from '@jupytergis/schema';
|
|
2
|
-
import { IBaseFormProps } from './baseform';
|
|
3
2
|
import { PathBasedSourcePropertiesForm } from './pathbasedsource';
|
|
3
|
+
import { ISourceFormProps } from './sourceform';
|
|
4
4
|
/**
|
|
5
5
|
* The form to modify a GeoJSON source.
|
|
6
6
|
*/
|
|
7
7
|
export declare class GeoJSONSourcePropertiesForm extends PathBasedSourcePropertiesForm {
|
|
8
8
|
private _validate;
|
|
9
|
-
constructor(props:
|
|
9
|
+
constructor(props: ISourceFormProps);
|
|
10
10
|
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
11
11
|
/**
|
|
12
12
|
* Validate the path, to avoid invalid path or invalid GeoJSON.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Ajv } from 'ajv';
|
|
2
2
|
import * as geojson from '@jupytergis/schema/src/schema/geojson.json';
|
|
3
3
|
import { PathBasedSourcePropertiesForm } from './pathbasedsource';
|
|
4
|
-
import { loadFile } from '
|
|
4
|
+
import { loadFile } from '../../../tools';
|
|
5
5
|
/**
|
|
6
6
|
* The form to modify a GeoJSON source.
|
|
7
7
|
*/
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { IDict } from '@jupytergis/schema';
|
|
2
2
|
import { IChangeEvent, ISubmitEvent } from '@rjsf/core';
|
|
3
|
-
import {
|
|
3
|
+
import { ISourceFormProps, SourcePropertiesForm } from './sourceform';
|
|
4
4
|
/**
|
|
5
5
|
* The form to modify a GeoTiff source.
|
|
6
6
|
*/
|
|
7
|
-
export declare class GeoTiffSourcePropertiesForm extends
|
|
7
|
+
export declare class GeoTiffSourcePropertiesForm extends SourcePropertiesForm {
|
|
8
8
|
private _isSubmitted;
|
|
9
|
-
constructor(props:
|
|
9
|
+
constructor(props: ISourceFormProps);
|
|
10
10
|
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
11
11
|
protected onFormChange(e: IChangeEvent): void;
|
|
12
12
|
protected onFormBlur(id: string, value: any): void;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { showErrorMessage } from '@jupyterlab/apputils';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { getMimeType } from '../../../tools';
|
|
3
|
+
import { SourcePropertiesForm } from './sourceform';
|
|
4
|
+
import { FileSelectorWidget } from '../fileselectorwidget';
|
|
4
5
|
/**
|
|
5
6
|
* The form to modify a GeoTiff source.
|
|
6
7
|
*/
|
|
7
|
-
export class GeoTiffSourcePropertiesForm extends
|
|
8
|
+
export class GeoTiffSourcePropertiesForm extends SourcePropertiesForm {
|
|
8
9
|
constructor(props) {
|
|
9
10
|
var _a, _b;
|
|
10
11
|
super(props);
|
|
@@ -12,10 +13,22 @@ export class GeoTiffSourcePropertiesForm extends BaseForm {
|
|
|
12
13
|
this._validateUrls((_b = (_a = props.sourceData) === null || _a === void 0 ? void 0 : _a.urls) !== null && _b !== void 0 ? _b : []);
|
|
13
14
|
}
|
|
14
15
|
processSchema(data, schema, uiSchema) {
|
|
16
|
+
var _a;
|
|
15
17
|
super.processSchema(data, schema, uiSchema);
|
|
16
18
|
if (!schema.properties || !data) {
|
|
17
19
|
return;
|
|
18
20
|
}
|
|
21
|
+
// Customize the widget for urls
|
|
22
|
+
if (schema.properties && schema.properties.urls) {
|
|
23
|
+
const docManager = (_a = this.props.formChangedSignal) === null || _a === void 0 ? void 0 : _a.sender.props.formSchemaRegistry.getDocManager();
|
|
24
|
+
uiSchema.urls = Object.assign(Object.assign({}, uiSchema.urls), { items: Object.assign(Object.assign({}, uiSchema.urls.items), { url: {
|
|
25
|
+
'ui:widget': FileSelectorWidget,
|
|
26
|
+
'ui:options': {
|
|
27
|
+
docManager,
|
|
28
|
+
formOptions: this.props
|
|
29
|
+
}
|
|
30
|
+
} }) });
|
|
31
|
+
}
|
|
19
32
|
// This is not user-editable
|
|
20
33
|
delete schema.properties.valid;
|
|
21
34
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { IDict } from '@jupytergis/schema';
|
|
2
2
|
import { IChangeEvent, ISubmitEvent } from '@rjsf/core';
|
|
3
|
-
import {
|
|
3
|
+
import { ISourceFormProps, SourcePropertiesForm } from './sourceform';
|
|
4
4
|
/**
|
|
5
5
|
* The form to modify a PathBasedSource source.
|
|
6
6
|
*/
|
|
7
|
-
export declare class PathBasedSourcePropertiesForm extends
|
|
8
|
-
constructor(props:
|
|
7
|
+
export declare class PathBasedSourcePropertiesForm extends SourcePropertiesForm {
|
|
8
|
+
constructor(props: ISourceFormProps);
|
|
9
9
|
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
10
10
|
protected onFormBlur(id: string, value: any): void;
|
|
11
11
|
protected onFormChange(e: IChangeEvent): void;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { showErrorMessage } from '@jupyterlab/apputils';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { loadFile } from '../../../tools';
|
|
3
|
+
import { FileSelectorWidget } from '../fileselectorwidget';
|
|
4
|
+
import { SourcePropertiesForm } from './sourceform';
|
|
5
5
|
/**
|
|
6
6
|
* The form to modify a PathBasedSource source.
|
|
7
7
|
*/
|
|
8
|
-
export class PathBasedSourcePropertiesForm extends
|
|
8
|
+
export class PathBasedSourcePropertiesForm extends SourcePropertiesForm {
|
|
9
9
|
constructor(props) {
|
|
10
10
|
var _a, _b;
|
|
11
11
|
super(props);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { IDict, SourceType } from '@jupytergis/schema';
|
|
2
|
+
import { BaseForm, IBaseFormProps } from '../baseform';
|
|
3
|
+
import { Signal } from '@lumino/signaling';
|
|
4
|
+
import { IChangeEvent } from '@rjsf/core';
|
|
5
|
+
export interface ISourceFormProps extends IBaseFormProps {
|
|
6
|
+
/**
|
|
7
|
+
* The source type for this form.
|
|
8
|
+
*/
|
|
9
|
+
sourceType: SourceType;
|
|
10
|
+
/**
|
|
11
|
+
* The signal emitted when the source form has changed.
|
|
12
|
+
*/
|
|
13
|
+
sourceFormChangedSignal?: Signal<any, IDict<any>>;
|
|
14
|
+
/**
|
|
15
|
+
* Configuration options for the dialog, including settings for source data and other parameters.
|
|
16
|
+
*/
|
|
17
|
+
dialogOptions?: any;
|
|
18
|
+
}
|
|
19
|
+
export declare class SourcePropertiesForm extends BaseForm {
|
|
20
|
+
props: ISourceFormProps;
|
|
21
|
+
protected sourceFormChangedSignal: Signal<any, IDict<any>> | undefined;
|
|
22
|
+
constructor(props: ISourceFormProps);
|
|
23
|
+
protected onFormChange(e: IChangeEvent): void;
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseForm } from '../baseform';
|
|
2
|
+
export class SourcePropertiesForm extends BaseForm {
|
|
3
|
+
constructor(props) {
|
|
4
|
+
super(props);
|
|
5
|
+
this.sourceFormChangedSignal = props.sourceFormChangedSignal;
|
|
6
|
+
}
|
|
7
|
+
onFormChange(e) {
|
|
8
|
+
super.onFormChange(e);
|
|
9
|
+
if (this.props.dialogOptions) {
|
|
10
|
+
this.props.dialogOptions.sourceData = Object.assign({}, e.formData);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IDict } from '@jupytergis/schema';
|
|
2
|
-
import {
|
|
3
|
-
export declare class TileSourcePropertiesForm extends
|
|
2
|
+
import { SourcePropertiesForm } from './sourceform';
|
|
3
|
+
export declare class TileSourcePropertiesForm extends SourcePropertiesForm {
|
|
4
4
|
private _urlParameters;
|
|
5
5
|
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
6
6
|
protected onFormBlur(id: string, value: any): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export class TileSourcePropertiesForm extends
|
|
1
|
+
import { SourcePropertiesForm } from './sourceform';
|
|
2
|
+
export class TileSourcePropertiesForm extends SourcePropertiesForm {
|
|
3
3
|
constructor() {
|
|
4
4
|
super(...arguments);
|
|
5
5
|
this._urlParameters = [];
|
package/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './classificationModes';
|
|
2
2
|
export * from './commands';
|
|
3
3
|
export * from './constants';
|
|
4
|
-
export * from './dialogs/
|
|
4
|
+
export * from './dialogs/layerCreationFormDialog';
|
|
5
5
|
export * from './formbuilder/objectform/baseform';
|
|
6
6
|
export * from './icons';
|
|
7
7
|
export * from './mainview';
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './classificationModes';
|
|
2
2
|
export * from './commands';
|
|
3
3
|
export * from './constants';
|
|
4
|
-
export * from './dialogs/
|
|
4
|
+
export * from './dialogs/layerCreationFormDialog';
|
|
5
5
|
export * from './formbuilder/objectform/baseform';
|
|
6
6
|
export * from './icons';
|
|
7
7
|
export * from './mainview';
|
package/lib/keybindings.json
CHANGED
package/lib/mainview/mainView.js
CHANGED
|
@@ -311,7 +311,7 @@ export class MainView extends React.Component {
|
|
|
311
311
|
if (this._model.getClientId() === clientId) {
|
|
312
312
|
return;
|
|
313
313
|
}
|
|
314
|
-
const clientPointers = this.state.clientPointers;
|
|
314
|
+
const clientPointers = Object.assign({}, this.state.clientPointers);
|
|
315
315
|
let currentClientPointer = clientPointers[clientId];
|
|
316
316
|
if (pointer) {
|
|
317
317
|
const pixel = this._Map.getPixelFromCoordinate([
|
|
@@ -320,7 +320,7 @@ export class MainView extends React.Component {
|
|
|
320
320
|
]);
|
|
321
321
|
const lonLat = toLonLat([pointer.coordinates.x, pointer.coordinates.y]);
|
|
322
322
|
if (!currentClientPointer) {
|
|
323
|
-
currentClientPointer =
|
|
323
|
+
currentClientPointer = {
|
|
324
324
|
username: client.user.username,
|
|
325
325
|
displayName: client.user.display_name,
|
|
326
326
|
color: client.user.color,
|
|
@@ -328,14 +328,15 @@ export class MainView extends React.Component {
|
|
|
328
328
|
lonLat: { longitude: lonLat[0], latitude: lonLat[1] }
|
|
329
329
|
};
|
|
330
330
|
}
|
|
331
|
-
|
|
332
|
-
|
|
331
|
+
else {
|
|
332
|
+
currentClientPointer = Object.assign(Object.assign({}, currentClientPointer), { coordinates: { x: pixel[0], y: pixel[1] }, lonLat: { longitude: lonLat[0], latitude: lonLat[1] } });
|
|
333
|
+
}
|
|
333
334
|
clientPointers[clientId] = currentClientPointer;
|
|
334
335
|
}
|
|
335
336
|
else {
|
|
336
337
|
delete clientPointers[clientId];
|
|
337
338
|
}
|
|
338
|
-
this.setState(old => (Object.assign(Object.assign({}, old), { clientPointers
|
|
339
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { clientPointers })));
|
|
339
340
|
});
|
|
340
341
|
// Temporal controller bit
|
|
341
342
|
// ? There's probably a better way to get changes in the model to trigger react rerenders
|
|
@@ -575,9 +576,6 @@ export class MainView extends React.Component {
|
|
|
575
576
|
*/
|
|
576
577
|
async addSource(id, source) {
|
|
577
578
|
var _a, _b;
|
|
578
|
-
const rasterSourceCommon = {
|
|
579
|
-
interpolate: false
|
|
580
|
-
};
|
|
581
579
|
let newSource;
|
|
582
580
|
switch (source.type) {
|
|
583
581
|
case 'RasterSource': {
|
|
@@ -585,16 +583,32 @@ export class MainView extends React.Component {
|
|
|
585
583
|
const pmTiles = sourceParameters.url.endsWith('.pmtiles');
|
|
586
584
|
const url = this.computeSourceUrl(source);
|
|
587
585
|
if (!pmTiles) {
|
|
588
|
-
newSource = new XYZSource(
|
|
586
|
+
newSource = new XYZSource({
|
|
587
|
+
interpolate: sourceParameters.interpolate,
|
|
588
|
+
attributions: sourceParameters.attribution,
|
|
589
|
+
minZoom: sourceParameters.minZoom,
|
|
590
|
+
maxZoom: sourceParameters.maxZoom,
|
|
591
|
+
tileSize: 256,
|
|
592
|
+
url: url
|
|
593
|
+
});
|
|
589
594
|
}
|
|
590
595
|
else {
|
|
591
|
-
newSource = new PMTilesRasterSource(
|
|
596
|
+
newSource = new PMTilesRasterSource({
|
|
597
|
+
interpolate: sourceParameters.interpolate,
|
|
598
|
+
attributions: sourceParameters.attribution,
|
|
599
|
+
tileSize: 256,
|
|
600
|
+
url: url
|
|
601
|
+
});
|
|
592
602
|
}
|
|
593
603
|
break;
|
|
594
604
|
}
|
|
595
605
|
case 'RasterDemSource': {
|
|
596
606
|
const sourceParameters = source.parameters;
|
|
597
|
-
newSource = new ImageTileSource(
|
|
607
|
+
newSource = new ImageTileSource({
|
|
608
|
+
interpolate: sourceParameters.interpolate,
|
|
609
|
+
url: this.computeSourceUrl(source),
|
|
610
|
+
attributions: sourceParameters.attribution
|
|
611
|
+
});
|
|
598
612
|
break;
|
|
599
613
|
}
|
|
600
614
|
case 'VectorTileSource': {
|
|
@@ -681,7 +695,12 @@ export class MainView extends React.Component {
|
|
|
681
695
|
type: 'ImageSource',
|
|
682
696
|
model: this._model
|
|
683
697
|
});
|
|
684
|
-
newSource = new Static(
|
|
698
|
+
newSource = new Static({
|
|
699
|
+
interpolate: sourceParameters.interpolate,
|
|
700
|
+
imageExtent: extent,
|
|
701
|
+
url: imageUrl,
|
|
702
|
+
crossOrigin: ''
|
|
703
|
+
});
|
|
685
704
|
break;
|
|
686
705
|
}
|
|
687
706
|
case 'VideoSource': {
|
|
@@ -694,9 +713,27 @@ export class MainView extends React.Component {
|
|
|
694
713
|
return Object.assign(Object.assign({}, url), { nodata: 0 });
|
|
695
714
|
};
|
|
696
715
|
const sources = await Promise.all(sourceParameters.urls.map(async (sourceInfo) => {
|
|
697
|
-
|
|
716
|
+
var _a, _b, _c;
|
|
717
|
+
const isRemote = ((_a = sourceInfo.url) === null || _a === void 0 ? void 0 : _a.startsWith('http://')) ||
|
|
718
|
+
((_b = sourceInfo.url) === null || _b === void 0 ? void 0 : _b.startsWith('https://'));
|
|
719
|
+
if (isRemote) {
|
|
720
|
+
return Object.assign(Object.assign({}, addNoData(sourceInfo)), { min: sourceInfo.min, max: sourceInfo.max, url: sourceInfo.url });
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
const geotiff = await loadFile({
|
|
724
|
+
filepath: (_c = sourceInfo.url) !== null && _c !== void 0 ? _c : '',
|
|
725
|
+
type: 'GeoTiffSource',
|
|
726
|
+
model: this._model
|
|
727
|
+
});
|
|
728
|
+
return Object.assign(Object.assign({}, addNoData(sourceInfo)), { min: sourceInfo.min, max: sourceInfo.max, geotiff, url: URL.createObjectURL(geotiff.file) });
|
|
729
|
+
}
|
|
698
730
|
}));
|
|
699
|
-
newSource = new GeoTIFFSource(
|
|
731
|
+
newSource = new GeoTIFFSource({
|
|
732
|
+
interpolate: sourceParameters.interpolate,
|
|
733
|
+
sources,
|
|
734
|
+
normalize: sourceParameters.normalize,
|
|
735
|
+
wrapX: sourceParameters.wrapX
|
|
736
|
+
});
|
|
700
737
|
break;
|
|
701
738
|
}
|
|
702
739
|
}
|
|
@@ -1382,7 +1419,7 @@ export class MainView extends React.Component {
|
|
|
1382
1419
|
}),
|
|
1383
1420
|
React.createElement("div", { className: "jGIS-Mainview-Container" },
|
|
1384
1421
|
this.state.displayTemporalController && (React.createElement(TemporalSlider, { model: this._model, filterStates: this.state.filterStates })),
|
|
1385
|
-
React.createElement("div", { className: "jGIS-Mainview", style: {
|
|
1422
|
+
React.createElement("div", { className: "jGIS-Mainview data-jgis-keybinding", tabIndex: -2, style: {
|
|
1386
1423
|
border: this.state.remoteUser
|
|
1387
1424
|
? `solid 3px ${this.state.remoteUser.color}`
|
|
1388
1425
|
: 'unset'
|
|
@@ -12,7 +12,7 @@ const LAYER_ITEM_CLASS = 'jp-gis-layerItem';
|
|
|
12
12
|
const LAYER_CLASS = 'jp-gis-layer';
|
|
13
13
|
const LAYER_TITLE_CLASS = 'jp-gis-layerTitle';
|
|
14
14
|
const LAYER_ICON_CLASS = 'jp-gis-layerIcon';
|
|
15
|
-
const LAYER_TEXT_CLASS = 'jp-gis-layerText';
|
|
15
|
+
const LAYER_TEXT_CLASS = 'jp-gis-layerText data-jgis-keybinding';
|
|
16
16
|
/**
|
|
17
17
|
* The layers panel widget.
|
|
18
18
|
*/
|
|
@@ -162,7 +162,7 @@ function LayerGroupComponent(props) {
|
|
|
162
162
|
return (React.createElement("div", { className: `${LAYER_ITEM_CLASS} ${LAYER_GROUP_CLASS}`, draggable: true, onDragStart: Private.onDragStart, onDragEnd: Private.onDragEnd, "data-id": name },
|
|
163
163
|
React.createElement("div", { onClick: handleExpand, onContextMenu: handleRightClick, className: `${LAYER_GROUP_HEADER_CLASS}${selected ? ' jp-mod-selected' : ''}`, onDragOver: Private.onDragOver, "data-id": name },
|
|
164
164
|
React.createElement(LabIcon.resolveReact, { icon: caretDownIcon, className: `${LAYER_GROUP_COLLAPSER_CLASS}${open ? ' jp-mod-expanded' : ''}`, tag: 'span' }),
|
|
165
|
-
React.createElement("span", { id: id, className: LAYER_TEXT_CLASS }, name)),
|
|
165
|
+
React.createElement("span", { id: id, className: LAYER_TEXT_CLASS, tabIndex: -2 }, name)),
|
|
166
166
|
open && (React.createElement("div", null, layers
|
|
167
167
|
.slice()
|
|
168
168
|
.reverse()
|
|
@@ -219,10 +219,10 @@ function LayerComponent(props) {
|
|
|
219
219
|
};
|
|
220
220
|
return (React.createElement("div", { className: `${LAYER_ITEM_CLASS} ${LAYER_CLASS}${selected ? ' jp-mod-selected' : ''}`, draggable: true, onDragStart: Private.onDragStart, onDragOver: Private.onDragOver, onDragEnd: Private.onDragEnd, "data-id": layerId },
|
|
221
221
|
React.createElement("div", { className: LAYER_TITLE_CLASS, onClick: setSelection, onContextMenu: setSelection },
|
|
222
|
+
React.createElement(Button, { title: layer.visible ? 'Hide layer' : 'Show layer', onClick: toggleVisibility, minimal: true },
|
|
223
|
+
React.createElement(LabIcon.resolveReact, { icon: layer.visible ? visibilityIcon : nonVisibilityIcon, className: `${LAYER_ICON_CLASS}${layer.visible ? '' : ' jp-gis-mod-hidden'}`, tag: "span" })),
|
|
222
224
|
icons.has(layer.type) && (React.createElement(LabIcon.resolveReact, Object.assign({}, icons.get(layer.type), { className: LAYER_ICON_CLASS }))),
|
|
223
|
-
React.createElement("span", { id: id, className: LAYER_TEXT_CLASS }, name))
|
|
224
|
-
React.createElement(Button, { title: layer.visible ? 'Hide layer' : 'Show layer', onClick: toggleVisibility, minimal: true },
|
|
225
|
-
React.createElement(LabIcon.resolveReact, { icon: layer.visible ? visibilityIcon : nonVisibilityIcon, className: `${LAYER_ICON_CLASS}${layer.visible ? '' : ' jp-gis-mod-hidden'}`, tag: "span" }))));
|
|
225
|
+
React.createElement("span", { id: id, className: LAYER_TEXT_CLASS, tabIndex: -2 }, name))));
|
|
226
226
|
}
|
|
227
227
|
var Private;
|
|
228
228
|
(function (Private) {
|
|
@@ -51,10 +51,12 @@ export class LeftPanelWidget extends SidePanel {
|
|
|
51
51
|
const updatedSelectedValue = Object.assign(Object.assign({}, selectedValue), { [item]: { type, selectedNodeId: nodeId } });
|
|
52
52
|
this._lastSelectedNodeId = nodeId;
|
|
53
53
|
jGISModel.syncSelected(updatedSelectedValue, this.id);
|
|
54
|
-
this.
|
|
54
|
+
this._notifyCommands();
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
57
|
this.addClass('jGIS-sidepanel-widget');
|
|
58
|
+
this.addClass('data-jgis-keybinding');
|
|
59
|
+
this.node.tabIndex = 0;
|
|
58
60
|
this._model = options.model;
|
|
59
61
|
this._state = options.state;
|
|
60
62
|
this._commands = options.commands;
|
|
@@ -132,6 +134,11 @@ export class LeftPanelWidget extends SidePanel {
|
|
|
132
134
|
this._lastSelectedNodeId = nodeId;
|
|
133
135
|
}
|
|
134
136
|
(_b = (_a = this._model) === null || _a === void 0 ? void 0 : _a.jGISModel) === null || _b === void 0 ? void 0 : _b.syncSelected(selection, this.id);
|
|
137
|
+
this._notifyCommands();
|
|
138
|
+
}
|
|
139
|
+
_notifyCommands() {
|
|
140
|
+
// Notify commands that need updating
|
|
141
|
+
this._commands.notifyCommandChanged(CommandIDs.identify);
|
|
135
142
|
this._commands.notifyCommandChanged(CommandIDs.temporalController);
|
|
136
143
|
}
|
|
137
144
|
}
|
|
@@ -7,6 +7,8 @@ export class RightPanelWidget extends SidePanel {
|
|
|
7
7
|
constructor(options) {
|
|
8
8
|
super();
|
|
9
9
|
this.addClass('jGIS-sidepanel-widget');
|
|
10
|
+
this.addClass('data-jgis-keybinding');
|
|
11
|
+
this.node.tabIndex = 0;
|
|
10
12
|
this._model = options.model;
|
|
11
13
|
this._annotationModel = options.annotationModel;
|
|
12
14
|
const header = new ControlPanelHeader();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { IDict, IJGISLayer, IJupyterGISModel, IJGISFormSchemaRegistry, LayerType } from '@jupytergis/schema';
|
|
2
|
+
import { JupyterGISTracker } from './types';
|
|
3
|
+
import { JupyterFrontEnd } from '@jupyterlab/application';
|
|
4
|
+
/**
|
|
5
|
+
* Get the currently selected layer from the shared model. Returns null if there is no selection or multiple layer is selected.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getSingleSelectedLayer(tracker: JupyterGISTracker): IJGISLayer | null;
|
|
8
|
+
/**
|
|
9
|
+
* Check if the selected layer is of one of the specified types
|
|
10
|
+
*/
|
|
11
|
+
export declare function selectedLayerIsOfType(allowedTypes: LayerType[], tracker: JupyterGISTracker): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Extract GeoJSON from selected layer's source
|
|
14
|
+
*/
|
|
15
|
+
export declare function getLayerGeoJSON(layer: IJGISLayer, sources: IDict, model: IJupyterGISModel): Promise<string | null>;
|
|
16
|
+
export type GdalFunctions = 'ogr2ogr' | 'gdal_rasterize' | 'gdalwarp' | 'gdal_translate';
|
|
17
|
+
/**
|
|
18
|
+
* Generalized processing function for Buffer & Dissolve
|
|
19
|
+
*/
|
|
20
|
+
export declare function processSelectedLayer(tracker: JupyterGISTracker, formSchemaRegistry: IJGISFormSchemaRegistry, processingType: 'Buffer' | 'Dissolve', processingOptions: {
|
|
21
|
+
sqlQueryFn: (layerName: string, param: any) => string;
|
|
22
|
+
gdalFunction: GdalFunctions;
|
|
23
|
+
options: (sqlQuery: string) => string[];
|
|
24
|
+
}, app: JupyterFrontEnd): Promise<void>;
|
|
25
|
+
export declare function executeSQLProcessing(model: IJupyterGISModel, geojsonString: string, gdalFunction: GdalFunctions, options: string[], layerNamePrefix: string, processingType: 'Buffer' | 'Dissolve', embedOutputLayer: boolean, tracker: JupyterGISTracker, app: JupyterFrontEnd): Promise<void>;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { getGdal } from './gdal';
|
|
2
|
+
import { UUID } from '@lumino/coreutils';
|
|
3
|
+
import { ProcessingFormDialog } from './dialogs/ProcessingFormDialog';
|
|
4
|
+
import { getGeoJSONDataFromLayerSource } from './tools';
|
|
5
|
+
/**
|
|
6
|
+
* Get the currently selected layer from the shared model. Returns null if there is no selection or multiple layer is selected.
|
|
7
|
+
*/
|
|
8
|
+
export function getSingleSelectedLayer(tracker) {
|
|
9
|
+
var _a, _b, _c;
|
|
10
|
+
const model = (_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model;
|
|
11
|
+
if (!model) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const localState = model.sharedModel.awareness.getLocalState();
|
|
15
|
+
if (!localState || !((_b = localState['selected']) === null || _b === void 0 ? void 0 : _b.value)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const selectedLayers = Object.keys(localState['selected'].value);
|
|
19
|
+
// Ensure only one layer is selected
|
|
20
|
+
if (selectedLayers.length !== 1) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const selectedLayerId = selectedLayers[0];
|
|
24
|
+
const layers = (_c = model.sharedModel.layers) !== null && _c !== void 0 ? _c : {};
|
|
25
|
+
const selectedLayer = layers[selectedLayerId];
|
|
26
|
+
return selectedLayer && selectedLayer.parameters ? selectedLayer : null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if the selected layer is of one of the specified types
|
|
30
|
+
*/
|
|
31
|
+
export function selectedLayerIsOfType(allowedTypes, tracker) {
|
|
32
|
+
const selectedLayer = getSingleSelectedLayer(tracker);
|
|
33
|
+
return selectedLayer ? allowedTypes.includes(selectedLayer.type) : false;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Extract GeoJSON from selected layer's source
|
|
37
|
+
*/
|
|
38
|
+
export async function getLayerGeoJSON(layer, sources, model) {
|
|
39
|
+
if (!layer.parameters || !layer.parameters.source) {
|
|
40
|
+
console.error('Selected layer does not have a valid source.');
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const source = sources[layer.parameters.source];
|
|
44
|
+
if (!source || !source.parameters) {
|
|
45
|
+
console.error(`Source with ID ${layer.parameters.source} not found or missing path.`);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return await getGeoJSONDataFromLayerSource(source, model);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Generalized processing function for Buffer & Dissolve
|
|
52
|
+
*/
|
|
53
|
+
export async function processSelectedLayer(tracker, formSchemaRegistry, processingType, processingOptions, app) {
|
|
54
|
+
var _a, _b, _c;
|
|
55
|
+
const selected = getSingleSelectedLayer(tracker);
|
|
56
|
+
if (!selected || !tracker.currentWidget) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const model = tracker.currentWidget.model;
|
|
60
|
+
const sources = (_a = model === null || model === void 0 ? void 0 : model.sharedModel.sources) !== null && _a !== void 0 ? _a : {};
|
|
61
|
+
const geojsonString = await getLayerGeoJSON(selected, sources, model);
|
|
62
|
+
if (!geojsonString) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const schema = Object.assign({}, formSchemaRegistry.getSchemas().get(processingType));
|
|
66
|
+
const selectedLayerId = Object.keys(((_c = (_b = model === null || model === void 0 ? void 0 : model.sharedModel.awareness.getLocalState()) === null || _b === void 0 ? void 0 : _b.selected) === null || _c === void 0 ? void 0 : _c.value) || {})[0];
|
|
67
|
+
// Open ProcessingFormDialog
|
|
68
|
+
const formValues = await new Promise(resolve => {
|
|
69
|
+
const dialog = new ProcessingFormDialog({
|
|
70
|
+
title: processingType.charAt(0).toUpperCase() + processingType.slice(1),
|
|
71
|
+
schema,
|
|
72
|
+
model,
|
|
73
|
+
sourceData: {
|
|
74
|
+
inputLayer: selectedLayerId,
|
|
75
|
+
outputLayerName: selected.name
|
|
76
|
+
},
|
|
77
|
+
formContext: 'create',
|
|
78
|
+
processingType,
|
|
79
|
+
syncData: (props) => {
|
|
80
|
+
resolve(props);
|
|
81
|
+
dialog.dispose();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
dialog.launch();
|
|
85
|
+
});
|
|
86
|
+
if (!formValues) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
let processParam;
|
|
90
|
+
switch (processingType) {
|
|
91
|
+
case 'Buffer':
|
|
92
|
+
processParam = formValues.bufferDistance;
|
|
93
|
+
break;
|
|
94
|
+
case 'Dissolve':
|
|
95
|
+
processParam = formValues.dissolveField;
|
|
96
|
+
break;
|
|
97
|
+
default:
|
|
98
|
+
console.error(`Unsupported processing type: ${processingType}`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const embedOutputLayer = formValues.embedOutputLayer;
|
|
102
|
+
const fileBlob = new Blob([geojsonString], {
|
|
103
|
+
type: 'application/geo+json'
|
|
104
|
+
});
|
|
105
|
+
const geoFile = new File([fileBlob], 'data.geojson', {
|
|
106
|
+
type: 'application/geo+json'
|
|
107
|
+
});
|
|
108
|
+
const Gdal = await getGdal();
|
|
109
|
+
const result = await Gdal.open(geoFile);
|
|
110
|
+
const dataset = result.datasets[0];
|
|
111
|
+
const layerName = dataset.info.layers[0].name;
|
|
112
|
+
const sqlQuery = processingOptions.sqlQueryFn(layerName, processParam);
|
|
113
|
+
const fullOptions = processingOptions.options(sqlQuery);
|
|
114
|
+
await executeSQLProcessing(model, geojsonString, processingOptions.gdalFunction, fullOptions, formValues.outputLayerName, processingType, embedOutputLayer, tracker, app);
|
|
115
|
+
}
|
|
116
|
+
export async function executeSQLProcessing(model, geojsonString, gdalFunction, options, layerNamePrefix, processingType, embedOutputLayer, tracker, app) {
|
|
117
|
+
var _a;
|
|
118
|
+
const geoFile = new File([new Blob([geojsonString], { type: 'application/geo+json' })], 'data.geojson', { type: 'application/geo+json' });
|
|
119
|
+
const Gdal = await getGdal();
|
|
120
|
+
const result = await Gdal.open(geoFile);
|
|
121
|
+
if (result.datasets.length === 0) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const dataset = result.datasets[0];
|
|
125
|
+
const outputFilePath = await Gdal[gdalFunction](dataset, options);
|
|
126
|
+
const processedBytes = await Gdal.getFileBytes(outputFilePath);
|
|
127
|
+
const processedGeoJSONString = new TextDecoder().decode(processedBytes);
|
|
128
|
+
Gdal.close(dataset);
|
|
129
|
+
if (!embedOutputLayer) {
|
|
130
|
+
// Save the output as a file
|
|
131
|
+
const jgisFilePath = (_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model.filePath;
|
|
132
|
+
const jgisDir = jgisFilePath
|
|
133
|
+
? jgisFilePath.substring(0, jgisFilePath.lastIndexOf('/'))
|
|
134
|
+
: '';
|
|
135
|
+
const outputFileName = `${layerNamePrefix}_${processingType}.json`;
|
|
136
|
+
const savePath = jgisDir ? `${jgisDir}/${outputFileName}` : outputFileName;
|
|
137
|
+
await app.serviceManager.contents.save(savePath, {
|
|
138
|
+
type: 'file',
|
|
139
|
+
format: 'text',
|
|
140
|
+
content: processedGeoJSONString
|
|
141
|
+
});
|
|
142
|
+
const newSourceId = UUID.uuid4();
|
|
143
|
+
const sourceModel = {
|
|
144
|
+
type: 'GeoJSONSource',
|
|
145
|
+
name: outputFileName,
|
|
146
|
+
parameters: {
|
|
147
|
+
path: outputFileName
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const layerModel = {
|
|
151
|
+
type: 'VectorLayer',
|
|
152
|
+
parameters: { source: newSourceId },
|
|
153
|
+
visible: true,
|
|
154
|
+
name: outputFileName
|
|
155
|
+
};
|
|
156
|
+
model.sharedModel.addSource(newSourceId, sourceModel);
|
|
157
|
+
model.addLayer(UUID.uuid4(), layerModel);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// Embed the output directly into the model
|
|
161
|
+
const processedGeoJSON = JSON.parse(processedGeoJSONString);
|
|
162
|
+
const newSourceId = UUID.uuid4();
|
|
163
|
+
const sourceModel = {
|
|
164
|
+
type: 'GeoJSONSource',
|
|
165
|
+
name: `${layerNamePrefix} ${processingType.charAt(0).toUpperCase() + processingType.slice(1)}`,
|
|
166
|
+
parameters: { data: processedGeoJSON }
|
|
167
|
+
};
|
|
168
|
+
const layerModel = {
|
|
169
|
+
type: 'VectorLayer',
|
|
170
|
+
parameters: { source: newSourceId },
|
|
171
|
+
visible: true,
|
|
172
|
+
name: `${layerNamePrefix} ${processingType.charAt(0).toUpperCase() + processingType.slice(1)}`
|
|
173
|
+
};
|
|
174
|
+
model.sharedModel.addSource(newSourceId, sourceModel);
|
|
175
|
+
model.addLayer(UUID.uuid4(), layerModel);
|
|
176
|
+
}
|
|
177
|
+
}
|