@jupytergis/base 0.3.0 → 0.4.1
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/annotations/components/Annotation.js +1 -1
- package/lib/annotations/model.d.ts +6 -7
- package/lib/annotations/model.js +15 -15
- package/lib/commands.d.ts +2 -3
- package/lib/commands.js +117 -62
- package/lib/constants.d.ts +2 -0
- package/lib/constants.js +4 -1
- package/lib/dialogs/formdialog.js +2 -2
- package/lib/dialogs/layerBrowserDialog.d.ts +4 -5
- package/lib/dialogs/layerBrowserDialog.js +9 -9
- package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +3 -8
- package/lib/dialogs/symbology/hooks/useGetBandInfo.js +16 -28
- package/lib/dialogs/symbology/hooks/useGetProperties.d.ts +1 -1
- package/lib/dialogs/symbology/hooks/useGetProperties.js +6 -8
- package/lib/dialogs/symbology/symbologyDialog.d.ts +2 -3
- package/lib/dialogs/symbology/symbologyDialog.js +10 -9
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +1 -1
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +6 -6
- package/lib/dialogs/symbology/tiff_layer/components/BandRow.js +3 -1
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.d.ts +1 -1
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +5 -4
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.d.ts +1 -1
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +8 -7
- package/lib/dialogs/symbology/vector_layer/VectorRendering.d.ts +1 -1
- package/lib/dialogs/symbology/vector_layer/VectorRendering.js +18 -13
- package/lib/dialogs/symbology/vector_layer/types/Categorized.d.ts +1 -1
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +30 -19
- package/lib/dialogs/symbology/vector_layer/types/Graduated.d.ts +1 -1
- package/lib/dialogs/symbology/vector_layer/types/Graduated.js +16 -13
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.d.ts +4 -0
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +77 -0
- package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.d.ts +1 -1
- package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.js +4 -3
- package/lib/formbuilder/creationform.d.ts +1 -2
- package/lib/formbuilder/creationform.js +4 -4
- package/lib/formbuilder/editform.d.ts +1 -2
- package/lib/formbuilder/editform.js +7 -7
- package/lib/formbuilder/formselectors.js +5 -2
- package/lib/formbuilder/objectform/baseform.d.ts +3 -4
- package/lib/formbuilder/objectform/baseform.js +2 -2
- package/lib/formbuilder/objectform/fileselectorwidget.js +13 -6
- package/lib/formbuilder/objectform/geotiffsource.d.ts +5 -1
- package/lib/formbuilder/objectform/geotiffsource.js +51 -18
- package/lib/formbuilder/objectform/heatmapLayerForm.d.ts +11 -0
- package/lib/formbuilder/objectform/heatmapLayerForm.js +60 -0
- package/lib/formbuilder/objectform/vectorlayerform.d.ts +0 -2
- package/lib/formbuilder/objectform/vectorlayerform.js +0 -59
- package/lib/mainview/TemporalSlider.d.ts +8 -0
- package/lib/mainview/TemporalSlider.js +303 -0
- package/lib/mainview/mainView.d.ts +26 -5
- package/lib/mainview/mainView.js +221 -108
- package/lib/mainview/mainviewmodel.d.ts +4 -0
- package/lib/mainview/mainviewmodel.js +4 -0
- package/lib/mainview/mainviewwidget.d.ts +0 -2
- package/lib/mainview/mainviewwidget.js +0 -2
- package/lib/panelview/annotationPanel.js +5 -5
- package/lib/panelview/components/filter-panel/Filter.js +4 -25
- package/lib/panelview/components/identify-panel/IdentifyPanel.js +1 -1
- package/lib/panelview/components/layers.js +2 -2
- package/lib/panelview/components/sources.js +1 -1
- package/lib/panelview/leftpanel.d.ts +3 -0
- package/lib/panelview/leftpanel.js +5 -1
- package/lib/panelview/model.js +8 -8
- package/lib/panelview/objectproperties.js +10 -10
- package/lib/panelview/rightpanel.d.ts +1 -1
- package/lib/panelview/rightpanel.js +10 -10
- package/lib/toolbar/widget.d.ts +1 -1
- package/lib/toolbar/widget.js +44 -32
- package/lib/tools.d.ts +6 -16
- package/lib/tools.js +54 -56
- package/lib/types.d.ts +2 -0
- package/lib/widget.d.ts +30 -6
- package/lib/widget.js +43 -9
- package/package.json +4 -3
- package/style/base.css +10 -0
- package/style/symbologyDialog.css +7 -1
- package/style/temporalSlider.css +47 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ISymbologyDialogProps } from '../../symbologyDialog';
|
|
3
|
-
declare const Graduated: ({
|
|
3
|
+
declare const Graduated: ({ model, state, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element | undefined;
|
|
4
4
|
export default Graduated;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { getNumericFeatureAttributes } from '../../../../tools';
|
|
2
3
|
import { VectorClassifications } from '../../classificationModes';
|
|
3
4
|
import ColorRamp from '../../components/color_ramp/ColorRamp';
|
|
4
|
-
import ValueSelect from '../components/ValueSelect';
|
|
5
5
|
import StopContainer from '../../components/color_stops/StopContainer';
|
|
6
6
|
import { useGetProperties } from '../../hooks/useGetProperties';
|
|
7
7
|
import { Utils, VectorUtils } from '../../symbologyUtils';
|
|
8
|
-
|
|
8
|
+
import ValueSelect from '../components/ValueSelect';
|
|
9
|
+
const Graduated = ({ model, state, okSignalPromise, cancel, layerId }) => {
|
|
9
10
|
const modeOptions = [
|
|
10
11
|
'quantile',
|
|
11
12
|
'equal interval',
|
|
@@ -21,17 +22,18 @@ const Graduated = ({ context, state, okSignalPromise, cancel, layerId }) => {
|
|
|
21
22
|
const [selectedMethod, setSelectedMethod] = useState('color');
|
|
22
23
|
const [stopRows, setStopRows] = useState([]);
|
|
23
24
|
const [methodOptions, setMethodOptions] = useState(['color']);
|
|
25
|
+
const [features, setFeatures] = useState({});
|
|
24
26
|
const [colorRampOptions, setColorRampOptions] = useState();
|
|
25
27
|
if (!layerId) {
|
|
26
28
|
return;
|
|
27
29
|
}
|
|
28
|
-
const layer =
|
|
30
|
+
const layer = model.getLayer(layerId);
|
|
29
31
|
if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
|
|
30
32
|
return;
|
|
31
33
|
}
|
|
32
|
-
const {
|
|
34
|
+
const { featureProperties } = useGetProperties({
|
|
33
35
|
layerId,
|
|
34
|
-
model:
|
|
36
|
+
model: model
|
|
35
37
|
});
|
|
36
38
|
useEffect(() => {
|
|
37
39
|
var _a, _b;
|
|
@@ -61,21 +63,21 @@ const Graduated = ({ context, state, okSignalPromise, cancel, layerId }) => {
|
|
|
61
63
|
colorRampOptionsRef.current = colorRampOptions;
|
|
62
64
|
}, [selectedValue, selectedMethod, stopRows, colorRampOptions]);
|
|
63
65
|
useEffect(() => {
|
|
64
|
-
populateOptions();
|
|
65
|
-
}, [featureProps]);
|
|
66
|
-
const populateOptions = async () => {
|
|
67
66
|
var _a, _b, _c, _d, _e;
|
|
68
67
|
// Set up method options
|
|
69
68
|
if (((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.type) === 'circle') {
|
|
70
69
|
const options = ['color', 'radius'];
|
|
71
70
|
setMethodOptions(options);
|
|
72
71
|
}
|
|
72
|
+
// We only want number values here
|
|
73
|
+
const numericFeatures = getNumericFeatureAttributes(featureProperties);
|
|
74
|
+
setFeatures(numericFeatures);
|
|
73
75
|
const layerParams = layer.parameters;
|
|
74
|
-
const value = (_c = (_b = layerParams.symbologyState) === null || _b === void 0 ? void 0 : _b.value) !== null && _c !== void 0 ? _c : Object.keys(
|
|
76
|
+
const value = (_c = (_b = layerParams.symbologyState) === null || _b === void 0 ? void 0 : _b.value) !== null && _c !== void 0 ? _c : Object.keys(numericFeatures)[0];
|
|
75
77
|
const method = (_e = (_d = layerParams.symbologyState) === null || _d === void 0 ? void 0 : _d.method) !== null && _e !== void 0 ? _e : 'color';
|
|
76
78
|
setSelectedValue(value);
|
|
77
79
|
setSelectedMethod(method);
|
|
78
|
-
};
|
|
80
|
+
}, [featureProperties]);
|
|
79
81
|
const handleOk = () => {
|
|
80
82
|
var _a, _b, _c, _d;
|
|
81
83
|
if (!layer.parameters) {
|
|
@@ -116,7 +118,8 @@ const Graduated = ({ context, state, okSignalPromise, cancel, layerId }) => {
|
|
|
116
118
|
};
|
|
117
119
|
layer.parameters.symbologyState = symbologyState;
|
|
118
120
|
layer.parameters.color = newStyle;
|
|
119
|
-
|
|
121
|
+
layer.type = 'VectorLayer';
|
|
122
|
+
model.sharedModel.updateLayer(layerId, layer);
|
|
120
123
|
cancel();
|
|
121
124
|
};
|
|
122
125
|
const buildColorInfoFromClassification = (selectedMode, numberOfShades, selectedRamp) => {
|
|
@@ -126,7 +129,7 @@ const Graduated = ({ context, state, okSignalPromise, cancel, layerId }) => {
|
|
|
126
129
|
selectedMode
|
|
127
130
|
});
|
|
128
131
|
let stops;
|
|
129
|
-
const values = Array.from(
|
|
132
|
+
const values = Array.from(features[selectedValue]);
|
|
130
133
|
switch (selectedMode) {
|
|
131
134
|
case 'quantile':
|
|
132
135
|
stops = VectorClassifications.calculateQuantileBreaks(values, +numberOfShades);
|
|
@@ -159,7 +162,7 @@ const Graduated = ({ context, state, okSignalPromise, cancel, layerId }) => {
|
|
|
159
162
|
setStopRows(stopOutputPairs);
|
|
160
163
|
};
|
|
161
164
|
return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
|
|
162
|
-
React.createElement(ValueSelect, { featureProperties:
|
|
165
|
+
React.createElement(ValueSelect, { featureProperties: features, selectedValue: selectedValue, setSelectedValue: setSelectedValue }),
|
|
163
166
|
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
164
167
|
React.createElement("label", { htmlFor: 'vector-method-select' }, "Method:"),
|
|
165
168
|
React.createElement("select", { name: 'vector-method-select', onChange: event => setSelectedMethod(event.target.value), className: "jp-mod-styled" }, methodOptions.map((method, index) => (React.createElement("option", { key: index, value: method, selected: method === selectedMethod, className: "jp-mod-styled" }, method))))),
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import colormap from 'colormap';
|
|
2
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import CanvasSelectComponent from '../../components/color_ramp/CanvasSelectComponent';
|
|
4
|
+
const Heatmap = ({ model, state, okSignalPromise, cancel, layerId }) => {
|
|
5
|
+
if (!layerId) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const layer = model.getLayer(layerId);
|
|
9
|
+
if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const [selectedRamp, setSelectedRamp] = useState('');
|
|
13
|
+
const [heatmapOptions, setHetamapOptions] = useState({
|
|
14
|
+
radius: 8,
|
|
15
|
+
blur: 15
|
|
16
|
+
});
|
|
17
|
+
const selectedRampRef = useRef('cool');
|
|
18
|
+
const heatmapOptionsRef = useRef({
|
|
19
|
+
radius: 8,
|
|
20
|
+
blur: 15
|
|
21
|
+
});
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
populateOptions();
|
|
24
|
+
okSignalPromise.promise.then(okSignal => {
|
|
25
|
+
okSignal.connect(handleOk, this);
|
|
26
|
+
});
|
|
27
|
+
return () => {
|
|
28
|
+
okSignalPromise.promise.then(okSignal => {
|
|
29
|
+
okSignal.disconnect(handleOk, this);
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
}, []);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
selectedRampRef.current = selectedRamp;
|
|
35
|
+
heatmapOptionsRef.current = heatmapOptions;
|
|
36
|
+
}, [selectedRamp, heatmapOptions]);
|
|
37
|
+
const populateOptions = async () => {
|
|
38
|
+
var _a;
|
|
39
|
+
let colorRamp;
|
|
40
|
+
if ((_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.symbologyState) {
|
|
41
|
+
colorRamp = layer.parameters.symbologyState.colorRamp;
|
|
42
|
+
}
|
|
43
|
+
setSelectedRamp(colorRamp ? colorRamp : 'cool');
|
|
44
|
+
};
|
|
45
|
+
const handleOk = () => {
|
|
46
|
+
if (!layer.parameters) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const colorMap = colormap({
|
|
50
|
+
colormap: selectedRampRef.current,
|
|
51
|
+
nshades: 9,
|
|
52
|
+
format: 'hex'
|
|
53
|
+
});
|
|
54
|
+
const symbologyState = {
|
|
55
|
+
renderType: 'Heatmap',
|
|
56
|
+
colorRamp: selectedRampRef.current
|
|
57
|
+
};
|
|
58
|
+
layer.parameters.symbologyState = symbologyState;
|
|
59
|
+
layer.parameters.color = colorMap;
|
|
60
|
+
layer.parameters.blur = heatmapOptionsRef.current.blur;
|
|
61
|
+
layer.parameters.radius = heatmapOptionsRef.current.radius;
|
|
62
|
+
layer.type = 'HeatmapLayer';
|
|
63
|
+
model.sharedModel.updateLayer(layerId, layer);
|
|
64
|
+
cancel();
|
|
65
|
+
};
|
|
66
|
+
return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
|
|
67
|
+
React.createElement("div", { className: "jp-gis-symbology-row jp-gis-heatmap" },
|
|
68
|
+
React.createElement("label", { htmlFor: "color-ramp-select" }, "Color Ramp:"),
|
|
69
|
+
React.createElement(CanvasSelectComponent, { selectedRamp: selectedRamp, setSelected: setSelectedRamp })),
|
|
70
|
+
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
71
|
+
React.createElement("label", { htmlFor: 'vector-value-select' }, "Radius:"),
|
|
72
|
+
React.createElement("input", { type: "number", value: heatmapOptions.radius, className: "jp-mod-styled", onChange: event => setHetamapOptions(prevState => (Object.assign(Object.assign({}, prevState), { radius: +event.target.value }))) })),
|
|
73
|
+
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
74
|
+
React.createElement("label", { htmlFor: 'vector-value-select' }, "Blur:"),
|
|
75
|
+
React.createElement("input", { type: "number", value: heatmapOptions.blur, className: "jp-mod-styled", onChange: event => setHetamapOptions(prevState => (Object.assign(Object.assign({}, prevState), { blur: +event.target.value }))) }))));
|
|
76
|
+
};
|
|
77
|
+
export default Heatmap;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ISymbologyDialogProps } from '../../symbologyDialog';
|
|
3
|
-
declare const SimpleSymbol: ({
|
|
3
|
+
declare const SimpleSymbol: ({ model, state, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element | undefined;
|
|
4
4
|
export default SimpleSymbol;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { parseColor } from '../../../../tools';
|
|
3
|
-
const SimpleSymbol = ({
|
|
3
|
+
const SimpleSymbol = ({ model, state, okSignalPromise, cancel, layerId }) => {
|
|
4
4
|
const styleRef = useRef();
|
|
5
5
|
const [useCircleStuff, setUseCircleStuff] = useState(false);
|
|
6
6
|
const [style, setStyle] = useState({
|
|
@@ -16,7 +16,7 @@ const SimpleSymbol = ({ context, state, okSignalPromise, cancel, layerId }) => {
|
|
|
16
16
|
if (!layerId) {
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
|
-
const layer =
|
|
19
|
+
const layer = model.getLayer(layerId);
|
|
20
20
|
if (!layer) {
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
@@ -75,7 +75,8 @@ const SimpleSymbol = ({ context, state, okSignalPromise, cancel, layerId }) => {
|
|
|
75
75
|
};
|
|
76
76
|
layer.parameters.symbologyState = symbologyState;
|
|
77
77
|
layer.parameters.color = styleExpr;
|
|
78
|
-
|
|
78
|
+
layer.type = 'VectorLayer';
|
|
79
|
+
model.sharedModel.updateLayer(layerId, layer);
|
|
79
80
|
cancel();
|
|
80
81
|
};
|
|
81
82
|
return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
2
1
|
import { IDict, IJGISFormSchemaRegistry, IJupyterGISModel, LayerType, SourceType } from '@jupytergis/schema';
|
|
3
2
|
import { Dialog } from '@jupyterlab/apputils';
|
|
4
3
|
import { Signal } from '@lumino/signaling';
|
|
@@ -37,7 +36,7 @@ export interface ICreationFormProps {
|
|
|
37
36
|
*/
|
|
38
37
|
cancel?: () => void;
|
|
39
38
|
formSchemaRegistry: IJGISFormSchemaRegistry;
|
|
40
|
-
|
|
39
|
+
model: IJupyterGISModel;
|
|
41
40
|
/**
|
|
42
41
|
* A signal emitting when the form changed, with a boolean whether there are some
|
|
43
42
|
* extra errors or not.
|
|
@@ -21,8 +21,8 @@ export class CreationForm extends React.Component {
|
|
|
21
21
|
constructor(props) {
|
|
22
22
|
super(props);
|
|
23
23
|
this.sourceFormChangedSignal = new Signal(this);
|
|
24
|
-
this.filePath = props.
|
|
25
|
-
this.jGISModel = props.
|
|
24
|
+
this.filePath = props.model.filePath;
|
|
25
|
+
this.jGISModel = props.model;
|
|
26
26
|
}
|
|
27
27
|
render() {
|
|
28
28
|
var _a;
|
|
@@ -105,12 +105,12 @@ export class CreationForm extends React.Component {
|
|
|
105
105
|
return (React.createElement("div", null,
|
|
106
106
|
this.props.createSource && (React.createElement("div", null,
|
|
107
107
|
React.createElement("h3", null, "Source Properties"),
|
|
108
|
-
React.createElement(SourceForm, { formContext: "create", model: this.jGISModel, filePath:
|
|
108
|
+
React.createElement(SourceForm, { formContext: "create", model: this.jGISModel, filePath: this.filePath, schema: sourceSchema, sourceData: this.props.sourceData, syncData: (properties) => {
|
|
109
109
|
sourceCreationPromise === null || sourceCreationPromise === void 0 ? void 0 : sourceCreationPromise.resolve(properties);
|
|
110
110
|
}, ok: this.props.ok, cancel: this.props.cancel, formChangedSignal: this.sourceFormChangedSignal, formErrorSignal: this.props.formErrorSignal, dialogOptions: this.props.dialogOptions, sourceType: this.props.sourceType }))),
|
|
111
111
|
this.props.createLayer && (React.createElement("div", null,
|
|
112
112
|
React.createElement("h3", null, "Layer Properties"),
|
|
113
|
-
React.createElement(LayerForm, { formContext: "create", sourceType: this.props.sourceType, model: this.jGISModel, filePath:
|
|
113
|
+
React.createElement(LayerForm, { formContext: "create", sourceType: this.props.sourceType, model: this.jGISModel, filePath: this.filePath, schema: layerSchema, sourceData: layerData, syncData: (properties) => {
|
|
114
114
|
layerCreationPromise === null || layerCreationPromise === void 0 ? void 0 : layerCreationPromise.resolve(properties);
|
|
115
115
|
}, ok: this.props.ok, cancel: this.props.cancel, sourceFormChangedSignal: this.sourceFormChangedSignal, formErrorSignal: this.props.formErrorSignal, dialogOptions: this.props.dialogOptions })))));
|
|
116
116
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
2
1
|
import { IJGISFormSchemaRegistry, IJupyterGISModel } from '@jupytergis/schema';
|
|
3
2
|
import * as React from 'react';
|
|
4
3
|
export interface IEditFormProps {
|
|
@@ -11,7 +10,7 @@ export interface IEditFormProps {
|
|
|
11
10
|
*/
|
|
12
11
|
source: string | undefined;
|
|
13
12
|
formSchemaRegistry: IJGISFormSchemaRegistry;
|
|
14
|
-
|
|
13
|
+
model: IJupyterGISModel;
|
|
15
14
|
}
|
|
16
15
|
/**
|
|
17
16
|
* Form for editing a source, a layer or both at the same time
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Signal } from '@lumino/signaling';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
+
import { deepCopy } from '../tools';
|
|
3
4
|
import { getLayerTypeForm, getSourceTypeForm } from './formselectors';
|
|
4
|
-
import { Signal } from '@lumino/signaling';
|
|
5
5
|
/**
|
|
6
6
|
* Form for editing a source, a layer or both at the same time
|
|
7
7
|
*/
|
|
@@ -14,14 +14,14 @@ export class EditForm extends React.Component {
|
|
|
14
14
|
if (!id) {
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
|
-
this.props.
|
|
17
|
+
this.props.model.sharedModel.updateObjectParameters(id, properties);
|
|
18
18
|
}
|
|
19
19
|
render() {
|
|
20
20
|
let layerSchema = undefined;
|
|
21
21
|
let LayerForm = undefined;
|
|
22
22
|
let layerData = undefined;
|
|
23
23
|
if (this.props.layer) {
|
|
24
|
-
const layer = this.props.
|
|
24
|
+
const layer = this.props.model.getLayer(this.props.layer);
|
|
25
25
|
if (!layer) {
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
@@ -38,7 +38,7 @@ export class EditForm extends React.Component {
|
|
|
38
38
|
let sourceData = undefined;
|
|
39
39
|
let source = undefined;
|
|
40
40
|
if (this.props.source) {
|
|
41
|
-
source = this.props.
|
|
41
|
+
source = this.props.model.getSource(this.props.source);
|
|
42
42
|
if (!source) {
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
@@ -53,12 +53,12 @@ export class EditForm extends React.Component {
|
|
|
53
53
|
return (React.createElement("div", null,
|
|
54
54
|
this.props.layer && LayerForm && (React.createElement("div", null,
|
|
55
55
|
React.createElement("h3", { style: { paddingLeft: '5px' } }, "Layer Properties"),
|
|
56
|
-
React.createElement(LayerForm, { formContext: "create", sourceType: (source === null || source === void 0 ? void 0 : source.type) || 'RasterSource', model: this.props.
|
|
56
|
+
React.createElement(LayerForm, { formContext: "create", sourceType: (source === null || source === void 0 ? void 0 : source.type) || 'RasterSource', model: this.props.model, filePath: this.props.model.filePath, schema: layerSchema, sourceData: layerData, syncData: (properties) => {
|
|
57
57
|
this.syncObjectProperties(this.props.layer, properties);
|
|
58
58
|
} }))),
|
|
59
59
|
this.props.source && SourceForm && (React.createElement("div", null,
|
|
60
60
|
React.createElement("h3", { style: { paddingLeft: '5px' } }, "Source Properties"),
|
|
61
|
-
React.createElement(SourceForm, { formContext: "create", model: this.props.
|
|
61
|
+
React.createElement(SourceForm, { formContext: "create", model: this.props.model, filePath: this.props.model.filePath, schema: sourceSchema, sourceData: sourceData, syncData: (properties) => {
|
|
62
62
|
this.syncObjectProperties(this.props.source, properties);
|
|
63
63
|
}, formChangedSignal: this.sourceFormChangedSignal, sourceType: (source === null || source === void 0 ? void 0 : source.type) || 'RasterSource' })))));
|
|
64
64
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { BaseForm } from './objectform/baseform';
|
|
2
2
|
import { GeoJSONSourcePropertiesForm } from './objectform/geojsonsource';
|
|
3
|
+
import { GeoTiffSourcePropertiesForm } from './objectform/geotiffsource';
|
|
4
|
+
import { HeatmapLayerPropertiesForm } from './objectform/heatmapLayerForm';
|
|
3
5
|
import { HillshadeLayerPropertiesForm } from './objectform/hillshadeLayerForm';
|
|
4
6
|
import { LayerPropertiesForm } from './objectform/layerform';
|
|
7
|
+
import { PathBasedSourcePropertiesForm } from './objectform/pathbasedsource';
|
|
5
8
|
import { TileSourcePropertiesForm } from './objectform/tilesourceform';
|
|
6
9
|
import { VectorLayerPropertiesForm } from './objectform/vectorlayerform';
|
|
7
10
|
import { WebGlLayerPropertiesForm } from './objectform/webGlLayerForm';
|
|
8
|
-
import { GeoTiffSourcePropertiesForm } from './objectform/geotiffsource';
|
|
9
|
-
import { PathBasedSourcePropertiesForm } from './objectform/pathbasedsource';
|
|
10
11
|
export function getLayerTypeForm(layerType) {
|
|
11
12
|
let LayerForm = LayerPropertiesForm;
|
|
12
13
|
switch (layerType) {
|
|
@@ -20,6 +21,8 @@ export function getLayerTypeForm(layerType) {
|
|
|
20
21
|
case 'WebGlLayer':
|
|
21
22
|
LayerForm = WebGlLayerPropertiesForm;
|
|
22
23
|
break;
|
|
24
|
+
case 'HeatmapLayer':
|
|
25
|
+
LayerForm = HeatmapLayerPropertiesForm;
|
|
23
26
|
// ADD MORE FORM TYPES HERE
|
|
24
27
|
}
|
|
25
28
|
return LayerForm;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { IJupyterGISModel } from '@jupytergis/schema';
|
|
1
|
+
import { IJupyterGISModel, SourceType } from '@jupytergis/schema';
|
|
4
2
|
import { Dialog } from '@jupyterlab/apputils';
|
|
5
3
|
import { Signal } from '@lumino/signaling';
|
|
4
|
+
import { IChangeEvent, ISubmitEvent } from '@rjsf/core';
|
|
5
|
+
import * as React from 'react';
|
|
6
6
|
import { IDict } from '../../types';
|
|
7
|
-
import { SourceType } from '@jupytergis/schema';
|
|
8
7
|
export interface IBaseFormStates {
|
|
9
8
|
schema?: IDict;
|
|
10
9
|
extraErrors?: any;
|
|
@@ -9,11 +9,11 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
+
import { Slider } from '@jupyter/react-components';
|
|
12
13
|
import { FormComponent } from '@jupyterlab/ui-components';
|
|
13
14
|
import validatorAjv8 from '@rjsf/validator-ajv8';
|
|
14
15
|
import * as React from 'react';
|
|
15
16
|
import { deepCopy } from '../../tools';
|
|
16
|
-
import { Slider } from '@jupyter/react-components';
|
|
17
17
|
const WrappedFormComponent = (props) => {
|
|
18
18
|
const { fields } = props, rest = __rest(props, ["fields"]);
|
|
19
19
|
return (React.createElement(FormComponent, Object.assign({}, rest, { validator: validatorAjv8, fields: Object.assign({}, fields) })));
|
|
@@ -96,7 +96,7 @@ export class BaseForm extends React.Component {
|
|
|
96
96
|
}
|
|
97
97
|
};
|
|
98
98
|
return (React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' } },
|
|
99
|
-
React.createElement(Slider, { min: 1, max: 10, step: 1,
|
|
99
|
+
React.createElement(Slider, { min: 1, max: 10, step: 1, valueAsNumber: props.formData * 10, onChange: handleSliderChange }),
|
|
100
100
|
React.createElement("input", { type: "number", value: inputValue, step: 0.1, min: 0.1, max: 1, onChange: handleInputChange, style: {
|
|
101
101
|
width: '50px',
|
|
102
102
|
textAlign: 'center',
|
|
@@ -35,17 +35,24 @@ export const FileSelectorWidget = (props) => {
|
|
|
35
35
|
console.warn('No open dialog found.');
|
|
36
36
|
}
|
|
37
37
|
const output = await FileDialog.getOpenFiles({
|
|
38
|
-
title:
|
|
38
|
+
title: `Select ${formOptions.sourceType.split('Source')[0]} File`,
|
|
39
39
|
manager: docManager
|
|
40
40
|
});
|
|
41
41
|
if (output.value && output.value.length > 0) {
|
|
42
42
|
const selectedFilePath = output.value[0].path;
|
|
43
|
-
const relativePath = PathExt.relative(formOptions.filePath, selectedFilePath);
|
|
43
|
+
const relativePath = PathExt.relative(PathExt.dirname(formOptions.filePath), selectedFilePath);
|
|
44
44
|
setServerFilePath(relativePath);
|
|
45
45
|
setUrlPath('');
|
|
46
46
|
props.onChange(relativePath);
|
|
47
47
|
if (dialogElement) {
|
|
48
|
-
formOptions.
|
|
48
|
+
if (formOptions.sourceType === 'GeoTiffSource') {
|
|
49
|
+
formOptions.dialogOptions.sourceData = Object.assign(Object.assign({}, formOptions.sourceData), { urls: formOptions.dialogOptions.sourceData.urls.map((urlObject) => {
|
|
50
|
+
return Object.assign(Object.assign({}, urlObject), { url: relativePath });
|
|
51
|
+
}) });
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
formOptions.dialogOptions.sourceData = Object.assign(Object.assign({}, formOptions.sourceData), { path: relativePath });
|
|
55
|
+
}
|
|
49
56
|
const formDialog = new CreationFormDialog(Object.assign({}, formOptions.dialogOptions));
|
|
50
57
|
await formDialog.launch();
|
|
51
58
|
}
|
|
@@ -72,9 +79,9 @@ export const FileSelectorWidget = (props) => {
|
|
|
72
79
|
isTypingURL.current = false;
|
|
73
80
|
};
|
|
74
81
|
return (React.createElement("div", null,
|
|
75
|
-
React.createElement("div",
|
|
76
|
-
React.createElement("
|
|
77
|
-
React.createElement("
|
|
82
|
+
React.createElement("div", { className: "file-container" },
|
|
83
|
+
React.createElement("button", { className: "jp-mod-styled", onClick: handleBrowseServerFiles }, "Browse Server Files"),
|
|
84
|
+
React.createElement("p", null, serverFilePath || '')),
|
|
78
85
|
React.createElement("div", null,
|
|
79
86
|
React.createElement("h3", { className: "jp-FormGroup-fieldLabel jp-FormGroup-contentItem" }, "Or enter external URL"),
|
|
80
87
|
React.createElement("input", { type: "text", id: "root_path", className: "jp-mod-styled", onChange: handleURLChange, onBlur: handleURLBlur, value: urlPath || '', style: { width: '100%' } }))));
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import { IDict } from '@jupytergis/schema';
|
|
1
2
|
import { IChangeEvent, ISubmitEvent } from '@rjsf/core';
|
|
2
3
|
import { BaseForm, IBaseFormProps } from './baseform';
|
|
3
4
|
/**
|
|
4
5
|
* The form to modify a GeoTiff source.
|
|
5
6
|
*/
|
|
6
7
|
export declare class GeoTiffSourcePropertiesForm extends BaseForm {
|
|
8
|
+
private _isSubmitted;
|
|
7
9
|
constructor(props: IBaseFormProps);
|
|
10
|
+
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
8
11
|
protected onFormChange(e: IChangeEvent): void;
|
|
9
|
-
protected
|
|
12
|
+
protected onFormBlur(id: string, value: any): void;
|
|
13
|
+
protected onFormSubmit(e: ISubmitEvent<any>): Promise<void>;
|
|
10
14
|
/**
|
|
11
15
|
* Validate the URLs, ensuring that there is at least one object with required fields.
|
|
12
16
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { showErrorMessage } from '@jupyterlab/apputils';
|
|
2
2
|
import { BaseForm } from './baseform';
|
|
3
|
+
import { getMimeType } from '../../tools';
|
|
3
4
|
/**
|
|
4
5
|
* The form to modify a GeoTiff source.
|
|
5
6
|
*/
|
|
@@ -7,8 +8,17 @@ export class GeoTiffSourcePropertiesForm extends BaseForm {
|
|
|
7
8
|
constructor(props) {
|
|
8
9
|
var _a, _b;
|
|
9
10
|
super(props);
|
|
11
|
+
this._isSubmitted = false;
|
|
10
12
|
this._validateUrls((_b = (_a = props.sourceData) === null || _a === void 0 ? void 0 : _a.urls) !== null && _b !== void 0 ? _b : []);
|
|
11
13
|
}
|
|
14
|
+
processSchema(data, schema, uiSchema) {
|
|
15
|
+
super.processSchema(data, schema, uiSchema);
|
|
16
|
+
if (!schema.properties || !data) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// This is not user-editable
|
|
20
|
+
delete schema.properties.valid;
|
|
21
|
+
}
|
|
12
22
|
onFormChange(e) {
|
|
13
23
|
var _a;
|
|
14
24
|
super.onFormChange(e);
|
|
@@ -16,10 +26,21 @@ export class GeoTiffSourcePropertiesForm extends BaseForm {
|
|
|
16
26
|
this._validateUrls(e.formData.urls);
|
|
17
27
|
}
|
|
18
28
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if ((
|
|
22
|
-
|
|
29
|
+
onFormBlur(id, value) {
|
|
30
|
+
// Is there a better way to spot the url text entry?
|
|
31
|
+
if (!id.endsWith('_urls')) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this._validateUrls(value);
|
|
35
|
+
}
|
|
36
|
+
async onFormSubmit(e) {
|
|
37
|
+
this._isSubmitted = true;
|
|
38
|
+
// validate urls.url only when submitting for better performance
|
|
39
|
+
const { valid, errors } = await this._validateUrls(e.formData.urls);
|
|
40
|
+
if (!valid) {
|
|
41
|
+
if (errors.length > 0) {
|
|
42
|
+
showErrorMessage('Invalid URLs', errors[0]);
|
|
43
|
+
}
|
|
23
44
|
return;
|
|
24
45
|
}
|
|
25
46
|
super.onFormSubmit(e);
|
|
@@ -36,21 +57,32 @@ export class GeoTiffSourcePropertiesForm extends BaseForm {
|
|
|
36
57
|
if (urls && urls.length > 0) {
|
|
37
58
|
for (let i = 0; i < urls.length; i++) {
|
|
38
59
|
const { url, min, max } = urls[i];
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
valid = false;
|
|
46
|
-
}
|
|
47
|
-
if (max === undefined || typeof max !== 'number') {
|
|
48
|
-
errors.push(`Max value at index ${i} is required and must be a number.`);
|
|
49
|
-
valid = false;
|
|
60
|
+
if (this._isSubmitted) {
|
|
61
|
+
const mimeType = getMimeType(url);
|
|
62
|
+
if (!mimeType || !mimeType.startsWith('image/tiff')) {
|
|
63
|
+
valid = false;
|
|
64
|
+
errors.push(`"${url}" is not a valid ${this.props.sourceType} file.`);
|
|
65
|
+
}
|
|
50
66
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
67
|
+
else {
|
|
68
|
+
if (!url || typeof url !== 'string' || url.trim() === '') {
|
|
69
|
+
valid = false;
|
|
70
|
+
errors.push(`URL at index ${i} is required and must be a valid string.`);
|
|
71
|
+
}
|
|
72
|
+
if (min === undefined || typeof min !== 'number') {
|
|
73
|
+
errors.push(`Min value at index ${i} is required and must be a number.`);
|
|
74
|
+
valid = false;
|
|
75
|
+
}
|
|
76
|
+
if (max === undefined || typeof max !== 'number') {
|
|
77
|
+
errors.push(`Max value at index ${i} is required and must be a number.`);
|
|
78
|
+
valid = false;
|
|
79
|
+
}
|
|
80
|
+
if (typeof min === 'number' &&
|
|
81
|
+
typeof max === 'number' &&
|
|
82
|
+
max <= min) {
|
|
83
|
+
errors.push(`Max value at index ${i} must be greater than Min.`);
|
|
84
|
+
valid = false;
|
|
85
|
+
}
|
|
54
86
|
}
|
|
55
87
|
}
|
|
56
88
|
}
|
|
@@ -67,5 +99,6 @@ export class GeoTiffSourcePropertiesForm extends BaseForm {
|
|
|
67
99
|
if (this.props.formErrorSignal) {
|
|
68
100
|
this.props.formErrorSignal.emit(!valid);
|
|
69
101
|
}
|
|
102
|
+
return { valid, errors };
|
|
70
103
|
}
|
|
71
104
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IDict, IHeatmapLayer } from '@jupytergis/schema';
|
|
2
|
+
import { IChangeEvent } from '@rjsf/core';
|
|
3
|
+
import { ILayerProps, LayerPropertiesForm } from './layerform';
|
|
4
|
+
export declare class HeatmapLayerPropertiesForm extends LayerPropertiesForm {
|
|
5
|
+
protected currentFormData: IHeatmapLayer;
|
|
6
|
+
private features;
|
|
7
|
+
constructor(props: ILayerProps);
|
|
8
|
+
protected onFormChange(e: IChangeEvent): void;
|
|
9
|
+
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
10
|
+
private fetchFeatureNames;
|
|
11
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { loadFile } from '../../tools';
|
|
2
|
+
import { LayerPropertiesForm } from './layerform';
|
|
3
|
+
export class HeatmapLayerPropertiesForm extends LayerPropertiesForm {
|
|
4
|
+
constructor(props) {
|
|
5
|
+
super(props);
|
|
6
|
+
this.features = [];
|
|
7
|
+
this.fetchFeatureNames(this.props.sourceData);
|
|
8
|
+
if (this.sourceFormChangedSignal) {
|
|
9
|
+
this.sourceFormChangedSignal.connect((sender, sourceData) => {
|
|
10
|
+
if (this.props.sourceType === 'GeoJSONSource') {
|
|
11
|
+
this.fetchFeatureNames(this.currentFormData, sourceData);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
onFormChange(e) {
|
|
17
|
+
super.onFormChange(e);
|
|
18
|
+
const source = this.props.model.getSource(e.formData.source);
|
|
19
|
+
if (!source || source.type !== 'GeoJSONSource') {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
this.fetchFeatureNames(this.currentFormData, source.parameters);
|
|
23
|
+
}
|
|
24
|
+
processSchema(data, schema, uiSchema) {
|
|
25
|
+
this.removeFormEntry('color', data, schema, uiSchema);
|
|
26
|
+
this.removeFormEntry('symbologyState', data, schema, uiSchema);
|
|
27
|
+
this.removeFormEntry('blur', data, schema, uiSchema);
|
|
28
|
+
this.removeFormEntry('radius', data, schema, uiSchema);
|
|
29
|
+
super.processSchema(data, schema, uiSchema);
|
|
30
|
+
uiSchema['feature'] = { enum: this.features };
|
|
31
|
+
if (!data) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async fetchFeatureNames(data, sourceData) {
|
|
36
|
+
var _a;
|
|
37
|
+
if (data && data.source) {
|
|
38
|
+
if (!sourceData) {
|
|
39
|
+
const currentSource = this.props.model.getSource(data.source);
|
|
40
|
+
if (!currentSource || currentSource.type !== 'GeoJSONSource') {
|
|
41
|
+
this.features = [];
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
sourceData = currentSource.parameters;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const source = this.props.model.getSource(data.source);
|
|
48
|
+
if (!((_a = source === null || source === void 0 ? void 0 : source.parameters) === null || _a === void 0 ? void 0 : _a.path)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const jsonData = await loadFile({
|
|
52
|
+
filepath: source.parameters.path,
|
|
53
|
+
type: 'GeoJSONSource',
|
|
54
|
+
model: this.props.model
|
|
55
|
+
});
|
|
56
|
+
const featureProps = jsonData.features[0].properties;
|
|
57
|
+
this.features = Object.keys(featureProps);
|
|
58
|
+
this.forceUpdate();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -6,10 +6,8 @@ import { ILayerProps, LayerPropertiesForm } from './layerform';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare class VectorLayerPropertiesForm extends LayerPropertiesForm {
|
|
8
8
|
protected currentFormData: IVectorLayer;
|
|
9
|
-
private sourceLayers;
|
|
10
9
|
private currentSourceId;
|
|
11
10
|
constructor(props: ILayerProps);
|
|
12
11
|
protected onFormChange(e: IChangeEvent): void;
|
|
13
12
|
protected processSchema(data: IVectorLayer | undefined, schema: IDict, uiSchema: IDict): void;
|
|
14
|
-
private fetchSourceLayers;
|
|
15
13
|
}
|