@jupytergis/base 0.1.2 → 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.
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import { ISymbologyDialogProps } from '../../symbologyDialog';
3
+ declare const Graduated: ({ context, state, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element | undefined;
4
+ export default Graduated;
@@ -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,4 @@
1
+ import React from 'react';
2
+ import { ISymbologyDialogProps } from '../../symbologyDialog';
3
+ declare const SimpleSymbol: ({ context, state, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element | undefined;
4
+ export default SimpleSymbol;
@@ -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();
@@ -62,23 +62,19 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
62
62
  return;
63
63
  }
64
64
  let tifData;
65
- const tifDataState = (await state.fetch(layerId));
66
- if (tifDataState) {
67
- tifData = JSON.parse(tifDataState);
65
+ const layerState = await state.fetch(`jupytergis:${layerId}`);
66
+ if (layerState) {
67
+ tifData = JSON.parse(layerState.tifData);
68
68
  }
69
69
  else {
70
- //! This takes so long, maybe do when adding source instead
71
- const Gdal = await initGdalJs({
72
- path: 'lab/extensions/@jupytergis/jupytergis-core/static',
73
- useWorker: false
74
- });
70
+ const Gdal = await getGdal();
75
71
  const fileData = await fetch(sourceInfo.url);
76
72
  const file = new File([await fileData.blob()], 'loaded.tif');
77
73
  const result = await Gdal.open(file);
78
74
  const tifDataset = result.datasets[0];
79
75
  tifData = await Gdal.gdalinfo(tifDataset, ['-stats']);
80
76
  Gdal.close(tifDataset);
81
- state.save(layerId, JSON.stringify(tifData));
77
+ state.save(`jupytergis:${layerId}`, { tifData: JSON.stringify(tifData) });
82
78
  }
83
79
  tifData['bands'].forEach((bandData) => {
84
80
  var _a, _b;
@@ -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
- value: scaleValue(color[i]),
123
- color: color[i + 1]
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
- value: scaleValue(color[i][2]),
140
- color: color[i + 1]
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.value));
180
- colorExpr.push(stop.color);
175
+ colorExpr.push(unscaleValue(stop.stop));
176
+ colorExpr.push(stop.output);
181
177
  });
182
178
  break;
183
179
  }
@@ -190,9 +186,9 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
190
186
  colorExpr.push([
191
187
  '<=',
192
188
  ['band', selectedBand],
193
- unscaleValue(stop.value)
189
+ unscaleValue(stop.stop)
194
190
  ]);
195
- colorExpr.push(stop.color);
191
+ colorExpr.push(stop.output);
196
192
  });
197
193
  // fallback value
198
194
  colorExpr.push([0, 0, 0, 1.0]);
@@ -207,9 +203,9 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
207
203
  colorExpr.push([
208
204
  '==',
209
205
  ['band', selectedBand],
210
- unscaleValue(stop.value)
206
+ unscaleValue(stop.stop)
211
207
  ]);
212
- colorExpr.push(stop.color);
208
+ colorExpr.push(stop.output);
213
209
  });
214
210
  // fallback value
215
211
  colorExpr.push([0, 0, 0, 1.0]);
@@ -226,8 +222,8 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
226
222
  const addStopRow = () => {
227
223
  setStopRows([
228
224
  {
229
- value: 0,
230
- color: [0, 0, 0, 1]
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.color}`, index: index, value: stop.value, outputValue: stop.color, stopRows: stopRows, setStopRows: setStopRows, deleteRow: () => deleteStopRow(index) })))),
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 './SingleBandPseudoColor';
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 handleValueChange = (event) => {
32
+ const handleStopChange = (event) => {
30
33
  const newRows = [...stopRows];
31
- newRows[index].value = +event.target.value;
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.value < b.value) {
40
+ if (a.stop < b.stop) {
38
41
  return -1;
39
42
  }
40
- if (a.value > b.value) {
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 handleColorChange = (event) => {
50
+ const handleOutputChange = (event) => {
48
51
  const newRows = [...stopRows];
49
- newRows[index].color = hexToRgb(event.target.value);
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: handleValueChange, onBlur: handleBlur, className: "jp-mod-styled" }),
54
- React.createElement("input", { id: `jp-gis-color-color-${index}`, value: rgbArrToHex(outputValue), type: "color", onChange: handleColorChange, className: "jp-mod-styled" }),
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,4 @@
1
+ import React from 'react';
2
+ import { ISymbologyDialogProps } from '../../symbologyDialog';
3
+ declare const VectorRendering: ({ context, state, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element | undefined;
4
+ export default VectorRendering;
@@ -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);
@@ -3,6 +3,7 @@ import { PromiseDelegate } from '@lumino/coreutils';
3
3
  import { Signal } from '@lumino/signaling';
4
4
  import React, { useEffect, useState } from 'react';
5
5
  import BandRendering from './components/symbology/BandRendering';
6
+ import VectorRendering from './components/symbology/VectorRendering';
6
7
  const SymbologyDialog = ({ context, state, okSignalPromise, cancel }) => {
7
8
  const [selectedLayer, setSelectedLayer] = useState(null);
8
9
  const [componentToRender, setComponentToRender] = useState(null);
@@ -33,6 +34,10 @@ const SymbologyDialog = ({ context, state, okSignalPromise, cancel }) => {
33
34
  }
34
35
  // TODO WebGlLayers can also be used for other layers, need a better way to determine source + layer combo
35
36
  switch (layer.type) {
37
+ case 'VectorLayer':
38
+ case 'VectorTileLayer':
39
+ LayerSymbology = (React.createElement(VectorRendering, { context: context, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: selectedLayer }));
40
+ break;
36
41
  case 'WebGlLayer':
37
42
  LayerSymbology = (React.createElement(BandRendering, { context: context, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: selectedLayer }));
38
43
  break;
@@ -43,10 +43,8 @@ export class VectorLayerPropertiesForm extends LayerPropertiesForm {
43
43
  this.fetchSourceLayers(this.currentFormData, source.parameters);
44
44
  }
45
45
  processSchema(data, schema, uiSchema) {
46
+ this.removeFormEntry('color', data, schema, uiSchema);
46
47
  super.processSchema(data, schema, uiSchema);
47
- uiSchema['color'] = {
48
- 'ui:widget': 'color'
49
- };
50
48
  if (!data) {
51
49
  return;
52
50
  }
package/lib/gdal.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function getGdal(): Promise<Gdal>;
package/lib/gdal.js ADDED
@@ -0,0 +1,15 @@
1
+ import initGdalJs from 'gdal3.js';
2
+ export async function getGdal() {
3
+ const dataurl = new URL('./gdal3WebAssembly.data', import.meta.url);
4
+ const wasmurl = new URL('./gdal3WebAssembly.wasm', import.meta.url);
5
+ // TODO Pass gdal JS too and run gdal in a worker?
6
+ return await initGdalJs({
7
+ paths: {
8
+ wasm: wasmurl.href,
9
+ data: dataurl.href
10
+ },
11
+ useWorker: false
12
+ });
13
+ }
14
+ // Early load gdal
15
+ getGdal();