@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.
Files changed (34) hide show
  1. package/lib/commands.d.ts +1 -1
  2. package/lib/commands.js +1 -10
  3. package/lib/dialogs/components/symbology/Graduated.d.ts +4 -0
  4. package/lib/dialogs/components/symbology/Graduated.js +188 -0
  5. package/lib/dialogs/components/symbology/SimpleSymbol.d.ts +4 -0
  6. package/lib/dialogs/components/symbology/SimpleSymbol.js +108 -0
  7. package/lib/dialogs/components/symbology/SingleBandPseudoColor.d.ts +0 -4
  8. package/lib/dialogs/components/symbology/SingleBandPseudoColor.js +22 -26
  9. package/lib/dialogs/components/symbology/StopRow.d.ts +4 -3
  10. package/lib/dialogs/components/symbology/StopRow.js +14 -9
  11. package/lib/dialogs/components/symbology/VectorRendering.d.ts +4 -0
  12. package/lib/dialogs/components/symbology/VectorRendering.js +55 -0
  13. package/lib/dialogs/symbologyDialog.d.ts +4 -0
  14. package/lib/dialogs/symbologyDialog.js +5 -0
  15. package/lib/formbuilder/objectform/vectorlayerform.js +1 -3
  16. package/lib/gdal.d.ts +1 -0
  17. package/lib/gdal.js +15 -0
  18. package/lib/gdal3WebAssembly.data +97604 -44
  19. package/lib/gdal3WebAssembly.wasm +0 -0
  20. package/lib/icons.d.ts +3 -0
  21. package/lib/icons.js +15 -0
  22. package/lib/index.d.ts +1 -0
  23. package/lib/index.js +1 -0
  24. package/lib/mainview/mainView.d.ts +6 -3
  25. package/lib/mainview/mainView.js +91 -77
  26. package/lib/panelview/components/layers.js +3 -3
  27. package/lib/toolbar/usertoolbaritem.js +3 -1
  28. package/lib/tools.d.ts +9 -0
  29. package/lib/tools.js +38 -0
  30. package/package.json +7 -5
  31. package/style/icons/logo.svg +3 -0
  32. package/style/icons/logo_mini.svg +248 -0
  33. package/style/icons/logo_mini_alternative.svg +49 -0
  34. 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,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();
@@ -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 tifDataState = (await state.fetch(layerId));
65
- if (tifDataState) {
66
- tifData = JSON.parse(tifDataState);
65
+ const layerState = await state.fetch(`jupytergis:${layerId}`);
66
+ if (layerState) {
67
+ tifData = JSON.parse(layerState.tifData);
67
68
  }
68
69
  else {
69
- //! This takes so long, maybe do when adding source instead
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
- 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,12 +186,12 @@ 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
- 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.value)
206
+ unscaleValue(stop.stop)
211
207
  ]);
212
- colorExpr.push(stop.color);
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
- 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);