@jupytergis/base 0.1.1 → 0.1.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.d.ts +1 -1
- package/lib/commands.js +1 -10
- package/lib/dialogs/components/symbology/Graduated.d.ts +4 -0
- package/lib/dialogs/components/symbology/Graduated.js +188 -0
- package/lib/dialogs/components/symbology/SimpleSymbol.d.ts +4 -0
- package/lib/dialogs/components/symbology/SimpleSymbol.js +108 -0
- package/lib/dialogs/components/symbology/SingleBandPseudoColor.d.ts +0 -4
- package/lib/dialogs/components/symbology/SingleBandPseudoColor.js +22 -26
- package/lib/dialogs/components/symbology/StopRow.d.ts +4 -3
- package/lib/dialogs/components/symbology/StopRow.js +14 -9
- package/lib/dialogs/components/symbology/VectorRendering.d.ts +4 -0
- package/lib/dialogs/components/symbology/VectorRendering.js +55 -0
- package/lib/dialogs/symbologyDialog.d.ts +4 -0
- package/lib/dialogs/symbologyDialog.js +5 -0
- package/lib/formbuilder/objectform/vectorlayerform.js +1 -3
- package/lib/gdal.d.ts +1 -0
- package/lib/gdal.js +15 -0
- package/lib/gdal3WebAssembly.data +97604 -44
- package/lib/gdal3WebAssembly.wasm +0 -0
- package/lib/icons.d.ts +3 -0
- package/lib/icons.js +15 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/mainview/mainView.d.ts +6 -3
- package/lib/mainview/mainView.js +91 -77
- package/lib/panelview/components/layers.js +3 -3
- package/lib/toolbar/usertoolbaritem.js +3 -1
- package/lib/tools.d.ts +9 -0
- package/lib/tools.js +38 -0
- package/package.json +7 -5
- package/style/icons/logo.svg +3 -0
- package/style/icons/logo_mini.svg +248 -0
- package/style/icons/logo_mini_alternative.svg +49 -0
- package/style/symbologyDialog.css +11 -3
package/lib/commands.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { IJGISFormSchemaRegistry, IJGISLayerBrowserRegistry } from '@jupytergis/schema';
|
|
2
2
|
import { JupyterFrontEnd } from '@jupyterlab/application';
|
|
3
3
|
import { WidgetTracker } from '@jupyterlab/apputils';
|
|
4
|
+
import { ICompletionProviderManager } from '@jupyterlab/completer';
|
|
4
5
|
import { IStateDB } from '@jupyterlab/statedb';
|
|
5
6
|
import { ITranslator } from '@jupyterlab/translation';
|
|
6
7
|
import { JupyterGISWidget } from './widget';
|
|
7
|
-
import { ICompletionProviderManager } from '@jupyterlab/completer';
|
|
8
8
|
/**
|
|
9
9
|
* Add the commands to the application's command registry.
|
|
10
10
|
*/
|
package/lib/commands.js
CHANGED
|
@@ -186,16 +186,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
186
186
|
createSource: true,
|
|
187
187
|
sourceData: {
|
|
188
188
|
name: 'Custom GeoTiff Source',
|
|
189
|
-
urls: [
|
|
190
|
-
{
|
|
191
|
-
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/21/H/UB/2021/9/S2B_21HUB_20210915_0_L2A/B04.tif',
|
|
192
|
-
max: 10000
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/21/H/UB/2021/9/S2B_21HUB_20210915_0_L2A/B08.tif',
|
|
196
|
-
max: 10000
|
|
197
|
-
}
|
|
198
|
-
]
|
|
189
|
+
urls: ['']
|
|
199
190
|
},
|
|
200
191
|
layerData: { name: 'Custom GeoTiff Layer' },
|
|
201
192
|
sourceType: 'GeoTiffSource',
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Button } from '@jupyterlab/ui-components';
|
|
2
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import StopRow from './StopRow';
|
|
4
|
+
const Graduated = ({ context, state, okSignalPromise, cancel, layerId }) => {
|
|
5
|
+
const selectedValueRef = useRef();
|
|
6
|
+
const selectedMethodRef = useRef();
|
|
7
|
+
const stopRowsRef = useRef();
|
|
8
|
+
const layerStateRef = useRef();
|
|
9
|
+
const [selectedValue, setSelectedValue] = useState('');
|
|
10
|
+
const [featureProperties, setFeatureProperties] = useState({});
|
|
11
|
+
const [selectedMethod, setSelectedMethod] = useState('color');
|
|
12
|
+
const [stopRows, setStopRows] = useState([]);
|
|
13
|
+
const [methodOptions, setMethodOptions] = useState(['color']);
|
|
14
|
+
const [layerState, setLayerState] = useState();
|
|
15
|
+
if (!layerId) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const layer = context.model.getLayer(layerId);
|
|
19
|
+
if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const getProperties = async () => {
|
|
24
|
+
var _a, _b;
|
|
25
|
+
if (!layerId) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const model = context.model;
|
|
29
|
+
const layer = model.getLayer(layerId);
|
|
30
|
+
const source = model.getSource((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.source);
|
|
31
|
+
if (!source) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const data = await model.readGeoJSON((_b = source.parameters) === null || _b === void 0 ? void 0 : _b.path);
|
|
35
|
+
const featureProps = {};
|
|
36
|
+
data === null || data === void 0 ? void 0 : data.features.forEach((feature) => {
|
|
37
|
+
feature.properties &&
|
|
38
|
+
Object.entries(feature.properties).forEach(([key, value]) => {
|
|
39
|
+
if (!(key in featureProps)) {
|
|
40
|
+
featureProps[key] = new Set();
|
|
41
|
+
}
|
|
42
|
+
featureProps[key].add(value);
|
|
43
|
+
});
|
|
44
|
+
setFeatureProperties(featureProps);
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
getProperties();
|
|
48
|
+
buildColorInfo();
|
|
49
|
+
okSignalPromise.promise.then(okSignal => {
|
|
50
|
+
okSignal.connect(handleOk, this);
|
|
51
|
+
});
|
|
52
|
+
return () => {
|
|
53
|
+
okSignalPromise.promise.then(okSignal => {
|
|
54
|
+
okSignal.disconnect(handleOk, this);
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
}, []);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
selectedValueRef.current = selectedValue;
|
|
60
|
+
selectedMethodRef.current = selectedMethod;
|
|
61
|
+
stopRowsRef.current = stopRows;
|
|
62
|
+
layerStateRef.current = layerState;
|
|
63
|
+
}, [selectedValue, selectedMethod, stopRows, layerState]);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
populateOptions();
|
|
66
|
+
}, [featureProperties]);
|
|
67
|
+
const buildColorInfo = () => {
|
|
68
|
+
var _a;
|
|
69
|
+
// This it to parse a color object on the layer
|
|
70
|
+
if (!((_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.color)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const color = layer.parameters.color;
|
|
74
|
+
// If color is a string we don't need to parse
|
|
75
|
+
if (typeof color === 'string') {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const prefix = layer.parameters.type === 'circle' ? 'circle-' : '';
|
|
79
|
+
if (!color[`${prefix}fill-color`]) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const valueColorPairs = [];
|
|
83
|
+
// So if it's not a string then it's an array and we parse
|
|
84
|
+
// Color[0] is the operator used for the color expression
|
|
85
|
+
switch (color[`${prefix}fill-color`][0]) {
|
|
86
|
+
case 'interpolate': {
|
|
87
|
+
// First element is interpolate for linear selection
|
|
88
|
+
// Second element is type of interpolation (ie linear)
|
|
89
|
+
// Third is input value that stop values are compared with
|
|
90
|
+
// Fourth and on is value:color pairs
|
|
91
|
+
for (let i = 3; i < color[`${prefix}fill-color`].length; i += 2) {
|
|
92
|
+
const obj = {
|
|
93
|
+
stop: color[`${prefix}fill-color`][i],
|
|
94
|
+
output: color[`${prefix}fill-color`][i + 1]
|
|
95
|
+
};
|
|
96
|
+
valueColorPairs.push(obj);
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
setStopRows(valueColorPairs);
|
|
102
|
+
};
|
|
103
|
+
const populateOptions = async () => {
|
|
104
|
+
var _a;
|
|
105
|
+
// Set up method options
|
|
106
|
+
if (((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.type) === 'circle') {
|
|
107
|
+
const options = ['color', 'radius'];
|
|
108
|
+
setMethodOptions(options);
|
|
109
|
+
}
|
|
110
|
+
const layerState = await state.fetch(`jupytergis:${layerId}`);
|
|
111
|
+
let value, method;
|
|
112
|
+
if (layerState) {
|
|
113
|
+
value = layerState
|
|
114
|
+
.graduatedValue;
|
|
115
|
+
method = layerState
|
|
116
|
+
.graduatedMethod;
|
|
117
|
+
}
|
|
118
|
+
setLayerState(layerState);
|
|
119
|
+
setSelectedValue(value ? value : Object.keys(featureProperties)[0]);
|
|
120
|
+
setSelectedMethod(method ? method : 'color');
|
|
121
|
+
};
|
|
122
|
+
const handleOk = () => {
|
|
123
|
+
var _a;
|
|
124
|
+
if (!layer.parameters) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
state.save(`jupytergis:${layerId}`, Object.assign(Object.assign({}, layerStateRef.current), { renderType: 'Graduated', graduatedValue: selectedValueRef.current, graduatedMethod: selectedMethodRef.current }));
|
|
128
|
+
const colorExpr = [];
|
|
129
|
+
colorExpr.push('interpolate');
|
|
130
|
+
colorExpr.push(['linear']);
|
|
131
|
+
colorExpr.push(['get', selectedValueRef.current]);
|
|
132
|
+
(_a = stopRowsRef.current) === null || _a === void 0 ? void 0 : _a.map(stop => {
|
|
133
|
+
colorExpr.push(stop.stop);
|
|
134
|
+
colorExpr.push(stop.output);
|
|
135
|
+
});
|
|
136
|
+
const newStyle = Object.assign({}, layer.parameters.color);
|
|
137
|
+
if (selectedMethodRef.current === 'color') {
|
|
138
|
+
if (layer.parameters.type === 'fill') {
|
|
139
|
+
newStyle['fill-color'] = colorExpr;
|
|
140
|
+
}
|
|
141
|
+
if (layer.parameters.type === 'line') {
|
|
142
|
+
newStyle['stroke-color'] = colorExpr;
|
|
143
|
+
}
|
|
144
|
+
if (layer.parameters.type === 'circle') {
|
|
145
|
+
newStyle['circle-fill-color'] = colorExpr;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (selectedMethodRef.current === 'radius') {
|
|
149
|
+
if (layer.parameters.type === 'circle') {
|
|
150
|
+
newStyle['circle-radius'] = colorExpr;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
layer.parameters.color = newStyle;
|
|
154
|
+
context.model.sharedModel.updateLayer(layerId, layer);
|
|
155
|
+
cancel();
|
|
156
|
+
};
|
|
157
|
+
const addStopRow = () => {
|
|
158
|
+
setStopRows([
|
|
159
|
+
{
|
|
160
|
+
stop: 0,
|
|
161
|
+
output: [0, 0, 0, 1]
|
|
162
|
+
},
|
|
163
|
+
...stopRows
|
|
164
|
+
]);
|
|
165
|
+
};
|
|
166
|
+
const deleteStopRow = (index) => {
|
|
167
|
+
const newFilters = [...stopRows];
|
|
168
|
+
newFilters.splice(index, 1);
|
|
169
|
+
setStopRows(newFilters);
|
|
170
|
+
};
|
|
171
|
+
return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
|
|
172
|
+
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
173
|
+
React.createElement("label", { htmlFor: 'vector-value-select' }, "Value:"),
|
|
174
|
+
React.createElement("div", { className: "jp-select-wrapper" },
|
|
175
|
+
React.createElement("select", { name: 'vector-value-select', onChange: event => setSelectedValue(event.target.value), className: "jp-mod-styled" }, Object.keys(featureProperties).map((feature, index) => (React.createElement("option", { key: index, value: feature, selected: feature === selectedValue, className: "jp-mod-styled" }, feature)))))),
|
|
176
|
+
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
177
|
+
React.createElement("label", { htmlFor: 'vector-method-select' }, "Method:"),
|
|
178
|
+
React.createElement("div", { className: "jp-select-wrapper" },
|
|
179
|
+
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)))))),
|
|
180
|
+
React.createElement("div", { className: "jp-gis-stop-container" },
|
|
181
|
+
React.createElement("div", { className: "jp-gis-stop-labels", style: { display: 'flex', gap: 6 } },
|
|
182
|
+
React.createElement("span", { style: { flex: '0 0 18%' } }, "Value"),
|
|
183
|
+
React.createElement("span", null, "Output Value")),
|
|
184
|
+
stopRows.map((stop, index) => (React.createElement(StopRow, { key: `${index}-${stop.output}`, index: index, value: stop.stop, outputValue: stop.output, stopRows: stopRows, setStopRows: setStopRows, deleteRow: () => deleteStopRow(index), useNumber: selectedMethod === 'radius' ? true : false })))),
|
|
185
|
+
React.createElement("div", { className: "jp-gis-symbology-button-container" },
|
|
186
|
+
React.createElement(Button, { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: addStopRow }, "Add Stop"))));
|
|
187
|
+
};
|
|
188
|
+
export default Graduated;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { parseColor } from '../../../tools';
|
|
3
|
+
const SimpleSymbol = ({ context, state, okSignalPromise, cancel, layerId }) => {
|
|
4
|
+
const styleRef = useRef();
|
|
5
|
+
const layerStateRef = useRef();
|
|
6
|
+
const [layerState, setLayerState] = useState();
|
|
7
|
+
const [useCircleStuff, setUseCircleStuff] = useState(false);
|
|
8
|
+
const [style, setStyle] = useState({
|
|
9
|
+
fillColor: '#3399CC',
|
|
10
|
+
joinStyle: 'round',
|
|
11
|
+
strokeColor: '#3399CC',
|
|
12
|
+
capStyle: 'round',
|
|
13
|
+
strokeWidth: 1.25,
|
|
14
|
+
radius: 5
|
|
15
|
+
});
|
|
16
|
+
const joinStyleOptions = ['bevel', 'round', 'miter'];
|
|
17
|
+
const capStyleOptions = ['butt', 'round', 'square'];
|
|
18
|
+
if (!layerId) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const layer = context.model.getLayer(layerId);
|
|
22
|
+
if (!layer) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!layer.parameters) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
setUseCircleStuff(layer.parameters.type === 'circle');
|
|
30
|
+
// Mimicking QGIS here,
|
|
31
|
+
// Read values from file if we chose them using the single symbol thing
|
|
32
|
+
// but if we're switching to simple symbol, use defaults
|
|
33
|
+
const initStyle = async () => {
|
|
34
|
+
if (!layer.parameters) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const layerState = await state.fetch(`jupytergis:${layerId}`);
|
|
38
|
+
if (!layerState) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
setLayerState(layerState);
|
|
42
|
+
const renderType = layerState
|
|
43
|
+
.renderType;
|
|
44
|
+
if (renderType === 'Single Symbol') {
|
|
45
|
+
// Read from current color or use defaults
|
|
46
|
+
const parsedStyle = parseColor(layer.parameters.type, layer.parameters.color);
|
|
47
|
+
if (parsedStyle) {
|
|
48
|
+
setStyle(parsedStyle);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
initStyle();
|
|
53
|
+
okSignalPromise.promise.then(okSignal => {
|
|
54
|
+
okSignal.connect(handleOk, this);
|
|
55
|
+
});
|
|
56
|
+
return () => {
|
|
57
|
+
okSignalPromise.promise.then(okSignal => {
|
|
58
|
+
okSignal.disconnect(handleOk, this);
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
}, []);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
styleRef.current = style;
|
|
64
|
+
layerStateRef.current = layerState;
|
|
65
|
+
}, [style, layerState]);
|
|
66
|
+
const handleOk = () => {
|
|
67
|
+
var _a, _b, _c, _d, _e, _f;
|
|
68
|
+
if (!layer.parameters) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
state.save(`jupytergis:${layerId}`, Object.assign(Object.assign({}, layerStateRef.current), { renderType: 'Single Symbol' }));
|
|
72
|
+
const styleExpr = {};
|
|
73
|
+
const prefix = layer.parameters.type === 'circle' ? 'circle-' : '';
|
|
74
|
+
if (layer.parameters.type === 'circle') {
|
|
75
|
+
styleExpr['circle-radius'] = (_a = styleRef.current) === null || _a === void 0 ? void 0 : _a.radius;
|
|
76
|
+
}
|
|
77
|
+
styleExpr[`${prefix}fill-color`] = (_b = styleRef.current) === null || _b === void 0 ? void 0 : _b.fillColor;
|
|
78
|
+
styleExpr[`${prefix}stroke-color`] = (_c = styleRef.current) === null || _c === void 0 ? void 0 : _c.strokeColor;
|
|
79
|
+
styleExpr[`${prefix}stroke-width`] = (_d = styleRef.current) === null || _d === void 0 ? void 0 : _d.strokeWidth;
|
|
80
|
+
styleExpr[`${prefix}stroke-line-join`] = (_e = styleRef.current) === null || _e === void 0 ? void 0 : _e.joinStyle;
|
|
81
|
+
styleExpr[`${prefix}stroke-line-cap`] = (_f = styleRef.current) === null || _f === void 0 ? void 0 : _f.capStyle;
|
|
82
|
+
layer.parameters.color = styleExpr;
|
|
83
|
+
context.model.sharedModel.updateLayer(layerId, layer);
|
|
84
|
+
cancel();
|
|
85
|
+
};
|
|
86
|
+
return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
|
|
87
|
+
useCircleStuff ? (React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
88
|
+
React.createElement("label", { htmlFor: 'vector-value-select' }, "Radius:"),
|
|
89
|
+
React.createElement("input", { type: "number", value: style.radius, className: "jp-mod-styled", onChange: event => setStyle(prevState => (Object.assign(Object.assign({}, prevState), { radius: +event.target.value }))) }))) : null,
|
|
90
|
+
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
91
|
+
React.createElement("label", { htmlFor: 'vector-value-select' }, "Fill Color:"),
|
|
92
|
+
React.createElement("input", { type: "color", value: style.fillColor, className: "jp-mod-styled", onChange: event => setStyle(prevState => (Object.assign(Object.assign({}, prevState), { fillColor: event.target.value }))) })),
|
|
93
|
+
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
94
|
+
React.createElement("label", { htmlFor: 'vector-value-select' }, "Stroke Color:"),
|
|
95
|
+
React.createElement("input", { type: "color", value: style.strokeColor, className: "jp-mod-styled", onChange: event => setStyle(prevState => (Object.assign(Object.assign({}, prevState), { strokeColor: event.target.value }))) })),
|
|
96
|
+
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
97
|
+
React.createElement("label", { htmlFor: 'vector-value-select' }, "Stroke Width:"),
|
|
98
|
+
React.createElement("input", { type: "number", value: style.strokeWidth, className: "jp-mod-styled", onChange: event => setStyle(prevState => (Object.assign(Object.assign({}, prevState), { strokeWidth: +event.target.value }))) })),
|
|
99
|
+
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
100
|
+
React.createElement("label", { htmlFor: 'vector-join-select' }, "Join Style:"),
|
|
101
|
+
React.createElement("div", { className: "jp-select-wrapper" },
|
|
102
|
+
React.createElement("select", { name: 'vector-join-select', onChange: event => setStyle(prevState => (Object.assign(Object.assign({}, prevState), { joinStyle: event.target.value }))), className: "jp-mod-styled" }, joinStyleOptions.map((method, index) => (React.createElement("option", { key: index, value: method, selected: method === style.joinStyle, className: "jp-mod-styled" }, method)))))),
|
|
103
|
+
useCircleStuff ? (React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
104
|
+
React.createElement("label", { htmlFor: 'vector-cap-select' }, "Cap Style:"),
|
|
105
|
+
React.createElement("div", { className: "jp-select-wrapper" },
|
|
106
|
+
React.createElement("select", { name: 'vector-cap-select', onChange: event => setStyle(prevState => (Object.assign(Object.assign({}, prevState), { capStyle: event.target.value }))), className: "jp-mod-styled" }, capStyleOptions.map((cap, index) => (React.createElement("option", { key: index, value: cap, selected: cap === style.capStyle, className: "jp-mod-styled" }, cap))))))) : null));
|
|
107
|
+
};
|
|
108
|
+
export default SimpleSymbol;
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { IDict } from '@jupytergis/schema';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { ISymbologyDialogProps } from '../../symbologyDialog';
|
|
4
|
-
export interface IStopRow {
|
|
5
|
-
value: number;
|
|
6
|
-
color: number[];
|
|
7
|
-
}
|
|
8
4
|
export interface IBandRow {
|
|
9
5
|
band: number;
|
|
10
6
|
colorInterpretation: string;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
|
|
2
2
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
3
3
|
import { Button } from '@jupyterlab/ui-components';
|
|
4
|
-
import initGdalJs from 'gdal3.js';
|
|
5
4
|
import React, { useEffect, useRef, useState } from 'react';
|
|
6
5
|
import BandRow from './BandRow';
|
|
7
6
|
import StopRow from './StopRow';
|
|
7
|
+
import { getGdal } from '../../../gdal';
|
|
8
8
|
const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerId }) => {
|
|
9
9
|
const functions = ['discrete', 'linear', 'exact'];
|
|
10
10
|
const stopRowsRef = useRef();
|
|
@@ -38,6 +38,7 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
|
|
|
38
38
|
const setInitialFunction = () => {
|
|
39
39
|
var _a;
|
|
40
40
|
if (!((_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.color)) {
|
|
41
|
+
setSelectedFunction('linear');
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
43
44
|
const color = layer.parameters.color;
|
|
@@ -61,22 +62,19 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
|
|
|
61
62
|
return;
|
|
62
63
|
}
|
|
63
64
|
let tifData;
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
66
|
-
tifData = JSON.parse(
|
|
65
|
+
const layerState = await state.fetch(`jupytergis:${layerId}`);
|
|
66
|
+
if (layerState) {
|
|
67
|
+
tifData = JSON.parse(layerState.tifData);
|
|
67
68
|
}
|
|
68
69
|
else {
|
|
69
|
-
|
|
70
|
-
const Gdal = await initGdalJs({
|
|
71
|
-
path: 'lab/extensions/@jupytergis/jupytergis-core/static',
|
|
72
|
-
useWorker: false
|
|
73
|
-
});
|
|
70
|
+
const Gdal = await getGdal();
|
|
74
71
|
const fileData = await fetch(sourceInfo.url);
|
|
75
72
|
const file = new File([await fileData.blob()], 'loaded.tif');
|
|
76
73
|
const result = await Gdal.open(file);
|
|
77
74
|
const tifDataset = result.datasets[0];
|
|
78
75
|
tifData = await Gdal.gdalinfo(tifDataset, ['-stats']);
|
|
79
76
|
Gdal.close(tifDataset);
|
|
77
|
+
state.save(`jupytergis:${layerId}`, { tifData: JSON.stringify(tifData) });
|
|
80
78
|
}
|
|
81
79
|
tifData['bands'].forEach((bandData) => {
|
|
82
80
|
var _a, _b;
|
|
@@ -93,8 +91,6 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
|
|
|
93
91
|
});
|
|
94
92
|
});
|
|
95
93
|
setBandRows(bandsArr);
|
|
96
|
-
console.log('tifData', tifData);
|
|
97
|
-
console.log('bandsArr', bandsArr);
|
|
98
94
|
};
|
|
99
95
|
const buildColorInfo = () => {
|
|
100
96
|
var _a;
|
|
@@ -119,8 +115,8 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
|
|
|
119
115
|
// Sixth and on is value:color pairs
|
|
120
116
|
for (let i = 5; i < color.length; i += 2) {
|
|
121
117
|
const obj = {
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
stop: scaleValue(color[i]),
|
|
119
|
+
output: color[i + 1]
|
|
124
120
|
};
|
|
125
121
|
valueColorPairs.push(obj);
|
|
126
122
|
}
|
|
@@ -136,8 +132,8 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
|
|
|
136
132
|
// Last element is fallback value
|
|
137
133
|
for (let i = 3; i < color.length - 1; i += 2) {
|
|
138
134
|
const obj = {
|
|
139
|
-
|
|
140
|
-
|
|
135
|
+
stop: scaleValue(color[i][2]),
|
|
136
|
+
output: color[i + 1]
|
|
141
137
|
};
|
|
142
138
|
valueColorPairs.push(obj);
|
|
143
139
|
}
|
|
@@ -176,8 +172,8 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
|
|
|
176
172
|
// Set NoData values to transparent
|
|
177
173
|
colorExpr.push(0.0, [0.0, 0.0, 0.0, 0.0]);
|
|
178
174
|
(_b = stopRowsRef.current) === null || _b === void 0 ? void 0 : _b.map(stop => {
|
|
179
|
-
colorExpr.push(unscaleValue(stop.
|
|
180
|
-
colorExpr.push(stop.
|
|
175
|
+
colorExpr.push(unscaleValue(stop.stop));
|
|
176
|
+
colorExpr.push(stop.output);
|
|
181
177
|
});
|
|
182
178
|
break;
|
|
183
179
|
}
|
|
@@ -190,12 +186,12 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
|
|
|
190
186
|
colorExpr.push([
|
|
191
187
|
'<=',
|
|
192
188
|
['band', selectedBand],
|
|
193
|
-
unscaleValue(stop.
|
|
189
|
+
unscaleValue(stop.stop)
|
|
194
190
|
]);
|
|
195
|
-
colorExpr.push(stop.
|
|
191
|
+
colorExpr.push(stop.output);
|
|
196
192
|
});
|
|
197
193
|
// fallback value
|
|
198
|
-
colorExpr.push([0, 0, 0]);
|
|
194
|
+
colorExpr.push([0, 0, 0, 1.0]);
|
|
199
195
|
break;
|
|
200
196
|
}
|
|
201
197
|
case 'exact': {
|
|
@@ -207,12 +203,12 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
|
|
|
207
203
|
colorExpr.push([
|
|
208
204
|
'==',
|
|
209
205
|
['band', selectedBand],
|
|
210
|
-
unscaleValue(stop.
|
|
206
|
+
unscaleValue(stop.stop)
|
|
211
207
|
]);
|
|
212
|
-
colorExpr.push(stop.
|
|
208
|
+
colorExpr.push(stop.output);
|
|
213
209
|
});
|
|
214
210
|
// fallback value
|
|
215
|
-
colorExpr.push([0, 0, 0]);
|
|
211
|
+
colorExpr.push([0, 0, 0, 1.0]);
|
|
216
212
|
break;
|
|
217
213
|
}
|
|
218
214
|
}
|
|
@@ -226,8 +222,8 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
|
|
|
226
222
|
const addStopRow = () => {
|
|
227
223
|
setStopRows([
|
|
228
224
|
{
|
|
229
|
-
|
|
230
|
-
|
|
225
|
+
stop: 0,
|
|
226
|
+
output: [0, 0, 0, 1]
|
|
231
227
|
},
|
|
232
228
|
...stopRows
|
|
233
229
|
]);
|
|
@@ -274,7 +270,7 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
|
|
|
274
270
|
? '='
|
|
275
271
|
: ''),
|
|
276
272
|
React.createElement("span", null, "Output Value")),
|
|
277
|
-
stopRows.map((stop, index) => (React.createElement(StopRow, { key: `${index}-${stop.
|
|
273
|
+
stopRows.map((stop, index) => (React.createElement(StopRow, { key: `${index}-${stop.output}`, index: index, value: stop.stop, outputValue: stop.output, stopRows: stopRows, setStopRows: setStopRows, deleteRow: () => deleteStopRow(index) })))),
|
|
278
274
|
React.createElement("div", { className: "jp-gis-symbology-button-container" },
|
|
279
275
|
React.createElement(Button, { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: addStopRow }, "Add Stop"))));
|
|
280
276
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { IStopRow } from '
|
|
3
|
-
declare const StopRow: ({ index, value, outputValue, stopRows, setStopRows, deleteRow }: {
|
|
2
|
+
import { IStopRow } from '../../symbologyDialog';
|
|
3
|
+
declare const StopRow: ({ index, value, outputValue, stopRows, setStopRows, deleteRow, useNumber }: {
|
|
4
4
|
index: number;
|
|
5
5
|
value: number;
|
|
6
|
-
outputValue: number[];
|
|
6
|
+
outputValue: number | number[];
|
|
7
7
|
stopRows: IStopRow[];
|
|
8
8
|
setStopRows: (stopRows: IStopRow[]) => void;
|
|
9
9
|
deleteRow: () => void;
|
|
10
|
+
useNumber?: boolean;
|
|
10
11
|
}) => React.JSX.Element;
|
|
11
12
|
export default StopRow;
|
|
@@ -2,8 +2,11 @@ import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
|
|
2
2
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
3
3
|
import { Button } from '@jupyterlab/ui-components';
|
|
4
4
|
import React from 'react';
|
|
5
|
-
const StopRow = ({ index, value, outputValue, stopRows, setStopRows, deleteRow }) => {
|
|
5
|
+
const StopRow = ({ index, value, outputValue, stopRows, setStopRows, deleteRow, useNumber }) => {
|
|
6
6
|
const rgbArrToHex = (rgbArr) => {
|
|
7
|
+
if (!Array.isArray(rgbArr)) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
7
10
|
const hex = rgbArr
|
|
8
11
|
.slice(0, -1) // Color input doesn't support hex alpha values so cut that out
|
|
9
12
|
.map((val) => {
|
|
@@ -26,32 +29,34 @@ const StopRow = ({ index, value, outputValue, stopRows, setStopRows, deleteRow }
|
|
|
26
29
|
];
|
|
27
30
|
return rgbValues;
|
|
28
31
|
};
|
|
29
|
-
const
|
|
32
|
+
const handleStopChange = (event) => {
|
|
30
33
|
const newRows = [...stopRows];
|
|
31
|
-
newRows[index].
|
|
34
|
+
newRows[index].stop = +event.target.value;
|
|
32
35
|
setStopRows(newRows);
|
|
33
36
|
};
|
|
34
37
|
const handleBlur = () => {
|
|
35
38
|
const newRows = [...stopRows];
|
|
36
39
|
newRows.sort((a, b) => {
|
|
37
|
-
if (a.
|
|
40
|
+
if (a.stop < b.stop) {
|
|
38
41
|
return -1;
|
|
39
42
|
}
|
|
40
|
-
if (a.
|
|
43
|
+
if (a.stop > b.stop) {
|
|
41
44
|
return 1;
|
|
42
45
|
}
|
|
43
46
|
return 0;
|
|
44
47
|
});
|
|
45
48
|
setStopRows(newRows);
|
|
46
49
|
};
|
|
47
|
-
const
|
|
50
|
+
const handleOutputChange = (event) => {
|
|
48
51
|
const newRows = [...stopRows];
|
|
49
|
-
|
|
52
|
+
useNumber
|
|
53
|
+
? (newRows[index].output = +event.target.value)
|
|
54
|
+
: (newRows[index].output = hexToRgb(event.target.value));
|
|
50
55
|
setStopRows(newRows);
|
|
51
56
|
};
|
|
52
57
|
return (React.createElement("div", { className: "jp-gis-color-row" },
|
|
53
|
-
React.createElement("input", { id: `jp-gis-color-value-${index}`, type: "number", value: value, onChange:
|
|
54
|
-
React.createElement("input", { id: `jp-gis-color-color-${index}`, value: rgbArrToHex(outputValue), type: "color", onChange:
|
|
58
|
+
React.createElement("input", { id: `jp-gis-color-value-${index}`, type: "number", value: value, onChange: handleStopChange, onBlur: handleBlur, className: "jp-mod-styled jp-gis-color-row-value-input" }),
|
|
59
|
+
useNumber ? (React.createElement("input", { type: "number", value: outputValue, onChange: handleOutputChange, className: "jp-mod-styled jp-gis-color-row-output-input" })) : (React.createElement("input", { id: `jp-gis-color-color-${index}`, value: rgbArrToHex(outputValue), type: "color", onChange: handleOutputChange, className: "jp-mod-styled jp-gis-color-row-output-input" })),
|
|
55
60
|
React.createElement(Button, { id: `jp-gis-remove-color-${index}`, className: "jp-Button jp-gis-filter-icon" },
|
|
56
61
|
React.createElement(FontAwesomeIcon, { icon: faTrash, onClick: deleteRow }))));
|
|
57
62
|
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import Graduated from './Graduated';
|
|
3
|
+
import SimpleSymbol from './SimpleSymbol';
|
|
4
|
+
const VectorRendering = ({ context, state, okSignalPromise, cancel, layerId }) => {
|
|
5
|
+
const [selectedRenderType, setSelectedRenderType] = useState('Single Symbol');
|
|
6
|
+
const [componentToRender, setComponentToRender] = useState(null);
|
|
7
|
+
const [renderTypeOptions, setRenderTypeOptions] = useState([
|
|
8
|
+
'Single Symbol'
|
|
9
|
+
]);
|
|
10
|
+
let RenderComponent;
|
|
11
|
+
if (!layerId) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const layer = context.model.getLayer(layerId);
|
|
15
|
+
if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const getSelectedRenderType = async () => {
|
|
20
|
+
const layerState = await state.fetch(`jupytergis:${layerId}`);
|
|
21
|
+
if (!layerState) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const renderType = layerState
|
|
25
|
+
.renderType;
|
|
26
|
+
setSelectedRenderType(renderType !== null && renderType !== void 0 ? renderType : 'Single Symbol');
|
|
27
|
+
};
|
|
28
|
+
if (layer.type === 'VectorLayer') {
|
|
29
|
+
const options = ['Single Symbol', 'Graduated'];
|
|
30
|
+
setRenderTypeOptions(options);
|
|
31
|
+
}
|
|
32
|
+
getSelectedRenderType();
|
|
33
|
+
}, []);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
switch (selectedRenderType) {
|
|
36
|
+
case 'Single Symbol':
|
|
37
|
+
RenderComponent = (React.createElement(SimpleSymbol, { context: context, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: layerId }));
|
|
38
|
+
break;
|
|
39
|
+
case 'Graduated':
|
|
40
|
+
RenderComponent = (React.createElement(Graduated, { context: context, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: layerId }));
|
|
41
|
+
break;
|
|
42
|
+
default:
|
|
43
|
+
RenderComponent = React.createElement("div", null, "Render Type Not Implemented (yet)");
|
|
44
|
+
}
|
|
45
|
+
setComponentToRender(RenderComponent);
|
|
46
|
+
}, [selectedRenderType]);
|
|
47
|
+
return (React.createElement(React.Fragment, null,
|
|
48
|
+
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
49
|
+
React.createElement("label", { htmlFor: "render-type-select" }, "Render Type:"),
|
|
50
|
+
React.createElement("select", { name: "render-type-select", id: "render-type-select", value: selectedRenderType, onChange: event => {
|
|
51
|
+
setSelectedRenderType(event.target.value);
|
|
52
|
+
} }, renderTypeOptions.map((func, funcIndex) => (React.createElement("option", { key: func, value: func }, func))))),
|
|
53
|
+
componentToRender));
|
|
54
|
+
};
|
|
55
|
+
export default VectorRendering;
|
|
@@ -15,6 +15,10 @@ export interface ISymbologyWidgetOptions {
|
|
|
15
15
|
context: DocumentRegistry.IContext<IJupyterGISModel>;
|
|
16
16
|
state: IStateDB;
|
|
17
17
|
}
|
|
18
|
+
export interface IStopRow {
|
|
19
|
+
stop: number;
|
|
20
|
+
output: number | number[];
|
|
21
|
+
}
|
|
18
22
|
export declare class SymbologyWidget extends Dialog<boolean> {
|
|
19
23
|
private okSignal;
|
|
20
24
|
constructor(options: ISymbologyWidgetOptions);
|