@jupytergis/base 0.12.2 → 0.13.0
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/index.js +2 -6
- package/lib/dialogs/symbology/hooks/useEffectiveSymbologyParams.d.ts +16 -0
- package/lib/dialogs/symbology/hooks/useEffectiveSymbologyParams.js +24 -0
- package/lib/dialogs/symbology/hooks/useOkSignal.d.ts +6 -0
- package/lib/dialogs/symbology/hooks/useOkSignal.js +25 -0
- package/lib/dialogs/symbology/symbologyDialog.d.ts +4 -2
- package/lib/dialogs/symbology/symbologyDialog.js +6 -10
- package/lib/dialogs/symbology/symbologyUtils.d.ts +25 -2
- package/lib/dialogs/symbology/symbologyUtils.js +74 -4
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +3 -3
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +31 -34
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +68 -62
- package/lib/dialogs/symbology/vector_layer/VectorRendering.js +33 -21
- package/lib/dialogs/symbology/vector_layer/types/Canonical.js +23 -24
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +48 -49
- package/lib/dialogs/symbology/vector_layer/types/Graduated.js +53 -62
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +35 -34
- package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.js +45 -47
- package/lib/formbuilder/objectform/StoryEditorForm.js +0 -17
- package/lib/formbuilder/objectform/baseform.d.ts +6 -0
- package/lib/formbuilder/objectform/baseform.js +21 -38
- package/lib/formbuilder/objectform/components/LayerSelect.d.ts +7 -0
- package/lib/formbuilder/objectform/components/LayerSelect.js +43 -0
- package/lib/formbuilder/objectform/components/OpacitySlider.d.ts +4 -0
- package/lib/formbuilder/objectform/components/OpacitySlider.js +40 -0
- package/lib/formbuilder/objectform/components/SegmentFormSymbology.d.ts +3 -0
- package/lib/formbuilder/objectform/components/SegmentFormSymbology.js +59 -0
- package/lib/formbuilder/objectform/layer/storySegmentLayerForm.d.ts +2 -2
- package/lib/formbuilder/objectform/layer/storySegmentLayerForm.js +19 -0
- package/lib/mainview/mainView.js +6 -1
- package/lib/panelview/rightpanel.d.ts +3 -1
- package/lib/panelview/rightpanel.js +2 -2
- package/lib/panelview/story-maps/StoryViewerPanel.d.ts +3 -1
- package/lib/panelview/story-maps/StoryViewerPanel.js +127 -19
- package/lib/shared/hooks/useLatest.d.ts +1 -0
- package/lib/shared/hooks/useLatest.js +8 -0
- package/lib/types.d.ts +1 -0
- package/lib/types.js +6 -1
- package/package.json +2 -2
- package/style/base.css +8 -0
- package/style/storyPanel.css +4 -4
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import React, { useEffect,
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import { VectorClassifications } from "../../classificationModes";
|
|
3
3
|
import ColorRampControls from "../../components/color_ramp/ColorRampControls";
|
|
4
4
|
import StopContainer from "../../components/color_stops/StopContainer";
|
|
5
|
-
import {
|
|
5
|
+
import { useOkSignal } from "../../hooks/useOkSignal";
|
|
6
|
+
import { saveSymbology, Utils, VectorUtils, } from "../../symbologyUtils";
|
|
6
7
|
import ValueSelect from "../components/ValueSelect";
|
|
7
|
-
|
|
8
|
+
import { useLatest } from "../../../../shared/hooks/useLatest";
|
|
9
|
+
import { useEffectiveSymbologyParams } from '../../hooks/useEffectiveSymbologyParams';
|
|
10
|
+
const Graduated = ({ model, okSignalPromise, layerId, symbologyTab, selectableAttributesAndValues, isStorySegmentOverride, segmentId, }) => {
|
|
8
11
|
const modeOptions = [
|
|
9
12
|
'quantile',
|
|
10
13
|
'equal interval',
|
|
@@ -12,11 +15,6 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
|
|
|
12
15
|
'pretty',
|
|
13
16
|
'logarithmic',
|
|
14
17
|
];
|
|
15
|
-
const selectableAttributeRef = useRef();
|
|
16
|
-
const symbologyTabRef = useRef();
|
|
17
|
-
const colorStopRowsRef = useRef([]);
|
|
18
|
-
const radiusStopRowsRef = useRef([]);
|
|
19
|
-
const colorRampOptionsRef = useRef();
|
|
20
18
|
const [selectedAttribute, setSelectedAttribute] = useState('');
|
|
21
19
|
const [colorStopRows, setColorStopRows] = useState([]);
|
|
22
20
|
const [radiusStopRows, setRadiusStopRows] = useState([]);
|
|
@@ -29,31 +27,34 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
|
|
|
29
27
|
radius: 5,
|
|
30
28
|
});
|
|
31
29
|
const [reverseRamp, setReverseRamp] = useState(false);
|
|
32
|
-
const
|
|
33
|
-
const
|
|
30
|
+
const selectableAttributeRef = useLatest(selectedAttribute);
|
|
31
|
+
const symbologyTabRef = useLatest(symbologyTab);
|
|
32
|
+
const colorStopRowsRef = useLatest(colorStopRows);
|
|
33
|
+
const radiusStopRowsRef = useLatest(radiusStopRows);
|
|
34
|
+
const colorRampOptionsRef = useLatest(colorRampOptions);
|
|
35
|
+
const colorManualStyleRef = useLatest(colorManualStyle);
|
|
36
|
+
const radiusManualStyleRef = useLatest(radiusManualStyle);
|
|
34
37
|
if (!layerId) {
|
|
35
38
|
return;
|
|
36
39
|
}
|
|
37
40
|
const layer = model.getLayer(layerId);
|
|
38
|
-
|
|
41
|
+
const params = useEffectiveSymbologyParams({
|
|
42
|
+
model,
|
|
43
|
+
layerId: layerId,
|
|
44
|
+
layer,
|
|
45
|
+
isStorySegmentOverride,
|
|
46
|
+
segmentId,
|
|
47
|
+
});
|
|
48
|
+
if (!params) {
|
|
39
49
|
return;
|
|
40
50
|
}
|
|
41
51
|
useEffect(() => {
|
|
42
52
|
updateStopRowsBasedOnLayer();
|
|
43
|
-
okSignalPromise.promise.then(okSignal => {
|
|
44
|
-
okSignal.connect(handleOk, this);
|
|
45
|
-
});
|
|
46
|
-
return () => {
|
|
47
|
-
okSignalPromise.promise.then(okSignal => {
|
|
48
|
-
okSignal.disconnect(handleOk, this);
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
53
|
}, []);
|
|
52
54
|
useEffect(() => {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
const circleStrokeColor = layer.parameters.color['circle-stroke-color'];
|
|
55
|
+
if (params.color) {
|
|
56
|
+
const strokeColor = params.color['stroke-color'];
|
|
57
|
+
const circleStrokeColor = params.color['circle-stroke-color'];
|
|
57
58
|
const isSimpleColor = (val) => typeof val === 'string' && /^#?[0-9A-Fa-f]{3,8}$/.test(val);
|
|
58
59
|
setColorManualStyle({
|
|
59
60
|
strokeColor: isSimpleColor(strokeColor)
|
|
@@ -61,51 +62,30 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
|
|
|
61
62
|
: isSimpleColor(circleStrokeColor)
|
|
62
63
|
? circleStrokeColor
|
|
63
64
|
: '#3399CC',
|
|
64
|
-
strokeWidth:
|
|
65
|
-
|
|
65
|
+
strokeWidth: params.color['stroke-width'] ||
|
|
66
|
+
params.color['circle-stroke-width'] ||
|
|
66
67
|
1.25,
|
|
67
68
|
});
|
|
68
69
|
setRadiusManualStyle({
|
|
69
|
-
radius:
|
|
70
|
+
radius: params.color['circle-radius'] || 5,
|
|
70
71
|
});
|
|
71
72
|
}
|
|
72
73
|
}, [layerId]);
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
colorStopRowsRef.current = colorStopRows;
|
|
75
|
-
radiusStopRowsRef.current = radiusStopRows;
|
|
76
|
-
selectableAttributeRef.current = selectedAttribute;
|
|
77
|
-
symbologyTabRef.current = symbologyTab;
|
|
78
|
-
colorRampOptionsRef.current = colorRampOptions;
|
|
79
|
-
}, [
|
|
80
|
-
colorStopRows,
|
|
81
|
-
radiusStopRows,
|
|
82
|
-
selectedAttribute,
|
|
83
|
-
symbologyTab,
|
|
84
|
-
colorRampOptions,
|
|
85
|
-
]);
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
colorManualStyleRef.current = colorManualStyle;
|
|
88
|
-
radiusManualStyleRef.current = radiusManualStyle;
|
|
89
|
-
}, [colorManualStyle, radiusManualStyle]);
|
|
90
74
|
useEffect(() => {
|
|
91
75
|
var _a, _b;
|
|
92
|
-
const
|
|
93
|
-
const attribute = (_b = (_a = layerParams.symbologyState) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : Object.keys(selectableAttributesAndValues)[0];
|
|
76
|
+
const attribute = (_b = (_a = params.symbologyState) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : Object.keys(selectableAttributesAndValues)[0];
|
|
94
77
|
setSelectedAttribute(attribute);
|
|
95
78
|
}, [selectableAttributesAndValues]);
|
|
96
79
|
const updateStopRowsBasedOnLayer = () => {
|
|
97
80
|
if (!layer) {
|
|
98
81
|
return;
|
|
99
82
|
}
|
|
100
|
-
setColorStopRows(VectorUtils.buildColorInfo(
|
|
83
|
+
setColorStopRows(VectorUtils.buildColorInfo(params));
|
|
101
84
|
setRadiusStopRows(VectorUtils.buildRadiusInfo(layer));
|
|
102
85
|
};
|
|
103
86
|
const handleOk = () => {
|
|
104
87
|
var _a, _b, _c;
|
|
105
|
-
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
const newStyle = Object.assign({}, layer.parameters.color);
|
|
88
|
+
const newStyle = Object.assign({}, params.color);
|
|
109
89
|
// Apply color symbology
|
|
110
90
|
if (colorStopRowsRef.current.length > 0) {
|
|
111
91
|
const colorExpr = [
|
|
@@ -145,8 +125,7 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
|
|
|
145
125
|
else {
|
|
146
126
|
newStyle['circle-radius'] = radiusManualStyleRef.current.radius;
|
|
147
127
|
}
|
|
148
|
-
|
|
149
|
-
layer.parameters.symbologyState = {
|
|
128
|
+
const symbologyState = {
|
|
150
129
|
renderType: 'Graduated',
|
|
151
130
|
value: selectableAttributeRef.current,
|
|
152
131
|
method: symbologyTabRef.current,
|
|
@@ -155,12 +134,23 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
|
|
|
155
134
|
mode: (_c = colorRampOptionsRef.current) === null || _c === void 0 ? void 0 : _c.selectedMode,
|
|
156
135
|
reverse: reverseRamp,
|
|
157
136
|
};
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
137
|
+
saveSymbology({
|
|
138
|
+
model,
|
|
139
|
+
layerId,
|
|
140
|
+
isStorySegmentOverride,
|
|
141
|
+
segmentId,
|
|
142
|
+
payload: {
|
|
143
|
+
symbologyState,
|
|
144
|
+
color: newStyle,
|
|
145
|
+
},
|
|
146
|
+
mutateLayerBeforeSave: targetLayer => {
|
|
147
|
+
if (targetLayer.type === 'HeatmapLayer') {
|
|
148
|
+
targetLayer.type = 'VectorLayer';
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
});
|
|
163
152
|
};
|
|
153
|
+
useOkSignal(okSignalPromise, handleOk);
|
|
164
154
|
const buildColorInfoFromClassification = (selectedMode, numberOfShades, selectedRamp) => {
|
|
165
155
|
setColorRampOptions({
|
|
166
156
|
selectedRamp,
|
|
@@ -200,10 +190,7 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
|
|
|
200
190
|
}
|
|
201
191
|
};
|
|
202
192
|
const handleReset = (method) => {
|
|
203
|
-
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
const newStyle = Object.assign({}, layer.parameters.color);
|
|
193
|
+
const newStyle = Object.assign({}, params.color);
|
|
207
194
|
if (method === 'color') {
|
|
208
195
|
delete newStyle['stroke-color'];
|
|
209
196
|
setColorStopRows([]);
|
|
@@ -213,6 +200,10 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
|
|
|
213
200
|
delete newStyle['circle-radius'];
|
|
214
201
|
setRadiusStopRows([]);
|
|
215
202
|
}
|
|
203
|
+
const layer = model.getLayer(layerId);
|
|
204
|
+
if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
216
207
|
layer.parameters.color = newStyle;
|
|
217
208
|
model.sharedModel.updateLayer(layerId, layer);
|
|
218
209
|
};
|
|
@@ -251,7 +242,7 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
|
|
|
251
242
|
React.createElement("label", null,
|
|
252
243
|
React.createElement("input", { type: "checkbox", checked: reverseRamp, onChange: e => setReverseRamp(e.target.checked) }),
|
|
253
244
|
"Reverse Color Ramp"))),
|
|
254
|
-
React.createElement(ColorRampControls, { layerParams:
|
|
245
|
+
React.createElement(ColorRampControls, { layerParams: params, modeOptions: modeOptions, classifyFunc: buildColorInfoFromClassification, showModeRow: true, showRampSelector: symbologyTab === 'color' }),
|
|
255
246
|
React.createElement(StopContainer, { selectedMethod: symbologyTab || 'color', stopRows: symbologyTab === 'color' ? colorStopRows : radiusStopRows, setStopRows: symbologyTab === 'color' ? setColorStopRows : setRadiusStopRows })));
|
|
256
247
|
}
|
|
257
248
|
})();
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import colormap from 'colormap';
|
|
2
|
-
import React, { useEffect,
|
|
2
|
+
import React, { useEffect, useState } from 'react';
|
|
3
3
|
import ColorRampSelector from "../../components/color_ramp/ColorRampSelector";
|
|
4
|
-
|
|
4
|
+
import { useOkSignal } from "../../hooks/useOkSignal";
|
|
5
|
+
import { saveSymbology, } from "../../symbologyUtils";
|
|
6
|
+
import { useLatest } from "../../../../shared/hooks/useLatest";
|
|
7
|
+
import { useEffectiveSymbologyParams } from '../../hooks/useEffectiveSymbologyParams';
|
|
8
|
+
const Heatmap = ({ model, okSignalPromise, layerId, isStorySegmentOverride, segmentId, }) => {
|
|
5
9
|
if (!layerId) {
|
|
6
10
|
return;
|
|
7
11
|
}
|
|
8
12
|
const layer = model.getLayer(layerId);
|
|
9
|
-
|
|
13
|
+
const params = useEffectiveSymbologyParams({
|
|
14
|
+
model,
|
|
15
|
+
layerId: layerId,
|
|
16
|
+
layer,
|
|
17
|
+
isStorySegmentOverride,
|
|
18
|
+
segmentId,
|
|
19
|
+
});
|
|
20
|
+
if (!params) {
|
|
10
21
|
return;
|
|
11
22
|
}
|
|
12
23
|
const [selectedRamp, setSelectedRamp] = useState('viridis');
|
|
@@ -15,40 +26,21 @@ const Heatmap = ({ model, state, okSignalPromise, cancel, layerId, }) => {
|
|
|
15
26
|
blur: 15,
|
|
16
27
|
});
|
|
17
28
|
const [reverseRamp, setReverseRamp] = useState(false);
|
|
18
|
-
const selectedRampRef =
|
|
19
|
-
const heatmapOptionsRef =
|
|
20
|
-
|
|
21
|
-
blur: 15,
|
|
22
|
-
});
|
|
23
|
-
const reverseRampRef = useRef(false);
|
|
29
|
+
const selectedRampRef = useLatest(selectedRamp);
|
|
30
|
+
const heatmapOptionsRef = useLatest(heatmapOptions);
|
|
31
|
+
const reverseRampRef = useLatest(reverseRamp);
|
|
24
32
|
useEffect(() => {
|
|
25
33
|
populateOptions();
|
|
26
|
-
okSignalPromise.promise.then(okSignal => {
|
|
27
|
-
okSignal.connect(handleOk, this);
|
|
28
|
-
});
|
|
29
|
-
return () => {
|
|
30
|
-
okSignalPromise.promise.then(okSignal => {
|
|
31
|
-
okSignal.disconnect(handleOk, this);
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
34
|
}, []);
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
selectedRampRef.current = selectedRamp;
|
|
37
|
-
heatmapOptionsRef.current = heatmapOptions;
|
|
38
|
-
reverseRampRef.current = reverseRamp;
|
|
39
|
-
}, [selectedRamp, heatmapOptions, reverseRamp]);
|
|
40
35
|
const populateOptions = async () => {
|
|
41
36
|
var _a;
|
|
42
37
|
let colorRamp;
|
|
43
|
-
if ((_a =
|
|
44
|
-
colorRamp =
|
|
38
|
+
if ((_a = params.symbologyState) === null || _a === void 0 ? void 0 : _a.colorRamp) {
|
|
39
|
+
colorRamp = params.symbologyState.colorRamp;
|
|
45
40
|
}
|
|
46
41
|
setSelectedRamp(colorRamp ? colorRamp : 'viridis');
|
|
47
42
|
};
|
|
48
43
|
const handleOk = () => {
|
|
49
|
-
if (!layer.parameters) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
44
|
let colorMap = colormap({
|
|
53
45
|
colormap: selectedRampRef.current,
|
|
54
46
|
nshades: 9,
|
|
@@ -62,14 +54,23 @@ const Heatmap = ({ model, state, okSignalPromise, cancel, layerId, }) => {
|
|
|
62
54
|
colorRamp: selectedRampRef.current,
|
|
63
55
|
reverse: reverseRampRef.current,
|
|
64
56
|
};
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
saveSymbology({
|
|
58
|
+
model,
|
|
59
|
+
layerId,
|
|
60
|
+
isStorySegmentOverride,
|
|
61
|
+
segmentId,
|
|
62
|
+
payload: {
|
|
63
|
+
symbologyState,
|
|
64
|
+
color: colorMap,
|
|
65
|
+
},
|
|
66
|
+
mutateLayerBeforeSave: targetLayer => {
|
|
67
|
+
targetLayer.parameters.blur = heatmapOptionsRef.current.blur;
|
|
68
|
+
targetLayer.parameters.radius = heatmapOptionsRef.current.radius;
|
|
69
|
+
targetLayer.type = 'HeatmapLayer';
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
72
|
};
|
|
73
|
+
useOkSignal(okSignalPromise, handleOk);
|
|
73
74
|
return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
|
|
74
75
|
React.createElement("p", null, "Represent features based on their density using a heatmap."),
|
|
75
76
|
React.createElement("div", { className: "jp-gis-symbology-row jp-gis-heatmap" },
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import React, { useEffect,
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { useEffectiveSymbologyParams } from "../../hooks/useEffectiveSymbologyParams";
|
|
3
|
+
import { useOkSignal } from "../../hooks/useOkSignal";
|
|
4
|
+
import { saveSymbology, } from "../../symbologyUtils";
|
|
5
|
+
import { useLatest } from "../../../../shared/hooks/useLatest";
|
|
2
6
|
import { parseColor } from "../../../../tools";
|
|
3
|
-
const SimpleSymbol = ({ model,
|
|
4
|
-
const styleRef = useRef();
|
|
7
|
+
const SimpleSymbol = ({ model, okSignalPromise, layerId, symbologyTab, isStorySegmentOverride, segmentId, }) => {
|
|
5
8
|
const [style, setStyle] = useState({
|
|
6
9
|
fillColor: '#3399CC',
|
|
7
10
|
joinStyle: 'round',
|
|
@@ -10,49 +13,30 @@ const SimpleSymbol = ({ model, state, okSignalPromise, cancel, layerId, symbolog
|
|
|
10
13
|
strokeWidth: 1.25,
|
|
11
14
|
radius: 5,
|
|
12
15
|
});
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
16
|
+
const styleRef = useLatest(style);
|
|
17
|
+
const layer = layerId !== undefined ? model.getLayer(layerId) : null;
|
|
18
|
+
const params = useEffectiveSymbologyParams({
|
|
19
|
+
model,
|
|
20
|
+
layerId: layerId,
|
|
21
|
+
layer,
|
|
22
|
+
isStorySegmentOverride,
|
|
23
|
+
segmentId,
|
|
24
|
+
});
|
|
22
25
|
useEffect(() => {
|
|
23
|
-
|
|
26
|
+
var _a;
|
|
27
|
+
if (!params) {
|
|
24
28
|
return;
|
|
25
29
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
+
if (((_a = params.symbologyState) === null || _a === void 0 ? void 0 : _a.renderType) === 'Single Symbol' && params.color) {
|
|
31
|
+
const parsed = parseColor(params.color);
|
|
32
|
+
if (parsed) {
|
|
33
|
+
setStyle(parsed);
|
|
30
34
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// Parse with fallback logic inside
|
|
34
|
-
const parsedStyle = parseColor(layer.parameters.color);
|
|
35
|
-
if (parsedStyle) {
|
|
36
|
-
setStyle(parsedStyle);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
initStyle();
|
|
41
|
-
okSignalPromise.promise.then(okSignal => {
|
|
42
|
-
okSignal.connect(handleOk, this);
|
|
43
|
-
});
|
|
44
|
-
return () => {
|
|
45
|
-
okSignalPromise.promise.then(okSignal => {
|
|
46
|
-
okSignal.disconnect(handleOk, this);
|
|
47
|
-
});
|
|
48
|
-
};
|
|
49
|
-
}, []);
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
styleRef.current = style;
|
|
52
|
-
}, [style]);
|
|
35
|
+
}
|
|
36
|
+
}, [params]);
|
|
53
37
|
const handleOk = () => {
|
|
54
38
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
55
|
-
if (!layer.parameters) {
|
|
39
|
+
if (!layerId || !(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
|
|
56
40
|
return;
|
|
57
41
|
}
|
|
58
42
|
const styleExpr = {
|
|
@@ -71,14 +55,28 @@ const SimpleSymbol = ({ model, state, okSignalPromise, cancel, layerId, symbolog
|
|
|
71
55
|
const symbologyState = {
|
|
72
56
|
renderType: 'Single Symbol',
|
|
73
57
|
};
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
58
|
+
saveSymbology({
|
|
59
|
+
model,
|
|
60
|
+
layerId,
|
|
61
|
+
isStorySegmentOverride,
|
|
62
|
+
segmentId,
|
|
63
|
+
payload: {
|
|
64
|
+
symbologyState,
|
|
65
|
+
color: styleExpr,
|
|
66
|
+
},
|
|
67
|
+
mutateLayerBeforeSave: targetLayer => {
|
|
68
|
+
if (targetLayer.type === 'HeatmapLayer') {
|
|
69
|
+
targetLayer.type = 'VectorLayer';
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
});
|
|
81
73
|
};
|
|
74
|
+
useOkSignal(okSignalPromise, handleOk);
|
|
75
|
+
const joinStyleOptions = ['bevel', 'round', 'miter'];
|
|
76
|
+
const capStyleOptions = ['butt', 'round', 'square'];
|
|
77
|
+
if (!layerId || !layer) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
82
80
|
const renderColorTab = () => (React.createElement(React.Fragment, null,
|
|
83
81
|
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
84
82
|
React.createElement("label", { htmlFor: 'vector-value-select' }, "Fill Color:"),
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { getCssVarAsColor } from "../../tools";
|
|
2
1
|
import { BaseForm } from './baseform';
|
|
3
2
|
/**
|
|
4
3
|
* The form to modify story map properties.
|
|
@@ -13,21 +12,5 @@ export class StoryEditorPropertiesForm extends BaseForm {
|
|
|
13
12
|
uiSchema.presentationTextColor = {
|
|
14
13
|
'ui:widget': 'color',
|
|
15
14
|
};
|
|
16
|
-
// Set default values from theme CSS variables when not already in data
|
|
17
|
-
const schemaProps = schema.properties;
|
|
18
|
-
if ((schemaProps === null || schemaProps === void 0 ? void 0 : schemaProps.presentationBgColor) &&
|
|
19
|
-
(data === null || data === void 0 ? void 0 : data.presentationBgColor) === undefined) {
|
|
20
|
-
const defaultBg = getCssVarAsColor('--jp-layout-color0');
|
|
21
|
-
if (defaultBg) {
|
|
22
|
-
schemaProps.presentationBgColor.default = defaultBg;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
if ((schemaProps === null || schemaProps === void 0 ? void 0 : schemaProps.presentationTextColor) &&
|
|
26
|
-
(data === null || data === void 0 ? void 0 : data.presentationTextColor) === undefined) {
|
|
27
|
-
const defaultText = getCssVarAsColor('--jp-ui-font-color0');
|
|
28
|
-
if (defaultText) {
|
|
29
|
-
schemaProps.presentationTextColor.default = defaultText;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
15
|
}
|
|
33
16
|
}
|
|
@@ -5,6 +5,10 @@ import { IChangeEvent, ISubmitEvent } from '@rjsf/core';
|
|
|
5
5
|
import { RJSFSchema, UiSchema } from '@rjsf/utils';
|
|
6
6
|
import * as React from 'react';
|
|
7
7
|
import { IDict } from "../../types";
|
|
8
|
+
export interface IJupyterGISFormContext<TFormData = IDict | undefined> {
|
|
9
|
+
model: IJupyterGISModel;
|
|
10
|
+
formData: TFormData;
|
|
11
|
+
}
|
|
8
12
|
export interface IBaseFormStates {
|
|
9
13
|
schema?: RJSFSchema;
|
|
10
14
|
extraErrors?: any;
|
|
@@ -59,6 +63,8 @@ export interface IBaseFormProps {
|
|
|
59
63
|
* It will be up to the user of this class to actually perform the creation/edit using syncdata.
|
|
60
64
|
*/
|
|
61
65
|
export declare class BaseForm extends React.Component<IBaseFormProps, IBaseFormStates> {
|
|
66
|
+
/** Skip syncData for the initial onChange (RJSF populating form), only sync on user edits. */
|
|
67
|
+
private isInitialLoadRef;
|
|
62
68
|
constructor(props: IBaseFormProps);
|
|
63
69
|
componentDidUpdate(prevProps: IBaseFormProps, prevState: IBaseFormStates): void;
|
|
64
70
|
componentDidMount(): void;
|
|
@@ -9,13 +9,18 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import { Slider } from '@jupyter/react-components';
|
|
13
12
|
import { FormComponent } from '@jupyterlab/ui-components';
|
|
14
13
|
import validatorAjv8 from '@rjsf/validator-ajv8';
|
|
15
14
|
import * as React from 'react';
|
|
16
15
|
import { deepCopy } from "../../tools";
|
|
16
|
+
import { LayerSelect } from './components/LayerSelect';
|
|
17
|
+
import OpacitySlider from './components/OpacitySlider';
|
|
17
18
|
const WrappedFormComponent = props => {
|
|
18
|
-
const
|
|
19
|
+
const rest = __rest(props, []);
|
|
20
|
+
const fields = {
|
|
21
|
+
opacity: OpacitySlider,
|
|
22
|
+
layerSelect: LayerSelect,
|
|
23
|
+
};
|
|
19
24
|
return (React.createElement(FormComponent, Object.assign({}, rest, { validator: validatorAjv8, fields: Object.assign({}, fields) })));
|
|
20
25
|
};
|
|
21
26
|
/**
|
|
@@ -26,6 +31,8 @@ const WrappedFormComponent = props => {
|
|
|
26
31
|
export class BaseForm extends React.Component {
|
|
27
32
|
constructor(props) {
|
|
28
33
|
super(props);
|
|
34
|
+
/** Skip syncData for the initial onChange (RJSF populating form), only sync on user edits. */
|
|
35
|
+
this.isInitialLoadRef = true;
|
|
29
36
|
this.currentFormData = deepCopy(this.props.sourceData);
|
|
30
37
|
this.state = {
|
|
31
38
|
schema: props.schema,
|
|
@@ -37,6 +44,7 @@ export class BaseForm extends React.Component {
|
|
|
37
44
|
this.currentFormData = deepCopy(this.props.sourceData);
|
|
38
45
|
const schema = deepCopy(this.props.schema);
|
|
39
46
|
this.setState(old => (Object.assign(Object.assign({}, old), { schema })));
|
|
47
|
+
this.isInitialLoadRef = true;
|
|
40
48
|
}
|
|
41
49
|
}
|
|
42
50
|
componentDidMount() {
|
|
@@ -72,40 +80,7 @@ export class BaseForm extends React.Component {
|
|
|
72
80
|
}
|
|
73
81
|
if (k === 'opacity') {
|
|
74
82
|
uiSchema[k] = {
|
|
75
|
-
'ui:field':
|
|
76
|
-
const [inputValue, setInputValue] = React.useState(props.formData.toFixed(1));
|
|
77
|
-
React.useEffect(() => {
|
|
78
|
-
setInputValue(props.formData.toFixed(1));
|
|
79
|
-
}, [props.formData]);
|
|
80
|
-
const handleSliderChange = (event) => {
|
|
81
|
-
const target = event.target;
|
|
82
|
-
if (target && '_value' in target) {
|
|
83
|
-
const sliderValue = parseFloat(target._value); // Slider value is in 0–10 range
|
|
84
|
-
const normalizedValue = sliderValue / 10; // Normalize to 0.1–1 range
|
|
85
|
-
props.onChange(normalizedValue);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
const handleInputChange = (event) => {
|
|
89
|
-
const value = event.target.value;
|
|
90
|
-
setInputValue(value);
|
|
91
|
-
const parsedValue = parseFloat(value);
|
|
92
|
-
if (!isNaN(parsedValue) &&
|
|
93
|
-
parsedValue >= 0.1 &&
|
|
94
|
-
parsedValue <= 1) {
|
|
95
|
-
props.onChange(parsedValue);
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
return (React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' } },
|
|
99
|
-
React.createElement(Slider, { min: 1, max: 10, step: 1, valueAsNumber: props.formData * 10, onChange: handleSliderChange }),
|
|
100
|
-
React.createElement("input", { type: "number", value: inputValue, step: 0.1, min: 0.1, max: 1, onChange: handleInputChange, style: {
|
|
101
|
-
width: '50px',
|
|
102
|
-
textAlign: 'center',
|
|
103
|
-
border: '1px solid #ccc',
|
|
104
|
-
borderRadius: '4px',
|
|
105
|
-
padding: '4px',
|
|
106
|
-
marginBottom: '5px',
|
|
107
|
-
} })));
|
|
108
|
-
},
|
|
83
|
+
'ui:field': 'opacity',
|
|
109
84
|
};
|
|
110
85
|
}
|
|
111
86
|
// Don't show readOnly properties when it's a form for updating an object
|
|
@@ -154,7 +129,12 @@ export class BaseForm extends React.Component {
|
|
|
154
129
|
this.props.formErrorSignal.emit(extraErrors);
|
|
155
130
|
}
|
|
156
131
|
if (this.props.formContext === 'update') {
|
|
157
|
-
|
|
132
|
+
if (!this.isInitialLoadRef) {
|
|
133
|
+
this.syncData(this.currentFormData);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
this.isInitialLoadRef = false;
|
|
137
|
+
}
|
|
158
138
|
}
|
|
159
139
|
}
|
|
160
140
|
onFormBlur(id, value) {
|
|
@@ -191,7 +171,10 @@ export class BaseForm extends React.Component {
|
|
|
191
171
|
(_a = submitRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
192
172
|
}
|
|
193
173
|
} },
|
|
194
|
-
React.createElement(WrappedFormComponent, { schema: schema, uiSchema: uiSchema, formData: formData,
|
|
174
|
+
React.createElement(WrappedFormComponent, { schema: schema, uiSchema: uiSchema, formData: formData, formContext: {
|
|
175
|
+
model: this.props.model,
|
|
176
|
+
formData,
|
|
177
|
+
}, 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 }))));
|
|
195
178
|
}
|
|
196
179
|
}
|
|
197
180
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { FieldProps } from '@rjsf/utils';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Simple select populated with layers (valid types only).
|
|
5
|
+
* Used as the targetLayer field inside layerOverride array items.
|
|
6
|
+
*/
|
|
7
|
+
export declare function LayerSelect(props: FieldProps): React.JSX.Element | null;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
function extractlayerOverrideIndex(idSchema) {
|
|
3
|
+
var _a;
|
|
4
|
+
const id = (_a = idSchema === null || idSchema === void 0 ? void 0 : idSchema.$id) !== null && _a !== void 0 ? _a : '';
|
|
5
|
+
const match = id.match(/layerOverride_(\d+)/);
|
|
6
|
+
return match ? parseInt(match[1], 10) : undefined;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Simple select populated with layers (valid types only).
|
|
10
|
+
* Used as the targetLayer field inside layerOverride array items.
|
|
11
|
+
*/
|
|
12
|
+
export function LayerSelect(props) {
|
|
13
|
+
var _a, _b, _c, _d, _e, _f;
|
|
14
|
+
const { idSchema, formContext, formData, onChange } = props;
|
|
15
|
+
const context = formContext;
|
|
16
|
+
const model = context === null || context === void 0 ? void 0 : context.model;
|
|
17
|
+
const fullFormData = (_a = context === null || context === void 0 ? void 0 : context.formData) !== null && _a !== void 0 ? _a : formData;
|
|
18
|
+
const arrayIndex = extractlayerOverrideIndex(idSchema !== null && idSchema !== void 0 ? idSchema : {});
|
|
19
|
+
const value = arrayIndex !== undefined && ((_b = fullFormData === null || fullFormData === void 0 ? void 0 : fullFormData.layerOverride) === null || _b === void 0 ? void 0 : _b[arrayIndex])
|
|
20
|
+
? ((_c = fullFormData.layerOverride[arrayIndex].targetLayer) !== null && _c !== void 0 ? _c : '')
|
|
21
|
+
: '';
|
|
22
|
+
if (!model) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const layerOverride = (_d = fullFormData === null || fullFormData === void 0 ? void 0 : fullFormData.layerOverride) !== null && _d !== void 0 ? _d : [];
|
|
26
|
+
const currentTargetLayer = arrayIndex !== undefined
|
|
27
|
+
? (_f = (_e = fullFormData === null || fullFormData === void 0 ? void 0 : fullFormData.layerOverride) === null || _e === void 0 ? void 0 : _e[arrayIndex]) === null || _f === void 0 ? void 0 : _f.targetLayer
|
|
28
|
+
: undefined;
|
|
29
|
+
const usedTargetLayerIds = new Set(layerOverride
|
|
30
|
+
.filter((_, i) => i !== arrayIndex)
|
|
31
|
+
.map(override => override.targetLayer)
|
|
32
|
+
.filter(id => id !== undefined && id !== '')
|
|
33
|
+
.filter(id => id !== currentTargetLayer));
|
|
34
|
+
const availableLayers = model.getLayers();
|
|
35
|
+
const optionsList = Object.entries(availableLayers).filter(([layerId]) => !usedTargetLayerIds.has(layerId));
|
|
36
|
+
const handleChange = (e) => {
|
|
37
|
+
const newValue = e.target.value;
|
|
38
|
+
onChange(newValue === '' ? undefined : newValue);
|
|
39
|
+
};
|
|
40
|
+
return (React.createElement("select", { value: value !== null && value !== void 0 ? value : '', onChange: handleChange, style: { width: '100%' } },
|
|
41
|
+
React.createElement("option", { value: "" }, "Select a layer"),
|
|
42
|
+
optionsList.map(([layerId, layer]) => (React.createElement("option", { key: layerId, value: layerId }, layer.name.charAt(0).toUpperCase() + layer.name.slice(1))))));
|
|
43
|
+
}
|