@jupytergis/base 0.1.6 → 0.1.7

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 (53) hide show
  1. package/lib/classificationModes.d.ts +13 -0
  2. package/lib/classificationModes.js +326 -0
  3. package/lib/commands.js +1 -1
  4. package/lib/dialogs/symbology/classificationModes.d.ts +13 -0
  5. package/lib/dialogs/symbology/classificationModes.js +326 -0
  6. package/lib/dialogs/symbology/components/color_ramp/CanvasSelectComponent.d.ts +11 -0
  7. package/lib/dialogs/symbology/components/color_ramp/CanvasSelectComponent.js +119 -0
  8. package/lib/dialogs/symbology/components/color_ramp/ColorRamp.d.ts +15 -0
  9. package/lib/dialogs/symbology/components/color_ramp/ColorRamp.js +33 -0
  10. package/lib/dialogs/symbology/components/color_ramp/ColorRampEntry.d.ts +9 -0
  11. package/lib/dialogs/symbology/components/color_ramp/ColorRampEntry.js +24 -0
  12. package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.d.ts +10 -0
  13. package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.js +11 -0
  14. package/lib/dialogs/symbology/components/color_stops/StopContainer.d.ts +9 -0
  15. package/lib/dialogs/symbology/components/color_stops/StopContainer.js +28 -0
  16. package/lib/dialogs/{components/symbology → symbology/components/color_stops}/StopRow.js +9 -2
  17. package/lib/dialogs/symbology/hooks/useGetProperties.d.ts +12 -0
  18. package/lib/dialogs/symbology/hooks/useGetProperties.js +47 -0
  19. package/lib/dialogs/{symbologyDialog.js → symbology/symbologyDialog.js} +3 -3
  20. package/lib/dialogs/symbology/symbologyUtils.d.ts +9 -0
  21. package/lib/dialogs/symbology/symbologyUtils.js +94 -0
  22. package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +4 -0
  23. package/lib/dialogs/{components/symbology/BandRendering.js → symbology/tiff_layer/TiffRendering.js} +3 -3
  24. package/lib/dialogs/{components/symbology → symbology/tiff_layer/components}/BandRow.d.ts +1 -1
  25. package/lib/dialogs/{components/symbology → symbology/tiff_layer/types}/SingleBandPseudoColor.d.ts +9 -1
  26. package/lib/dialogs/{components/symbology → symbology/tiff_layer/types}/SingleBandPseudoColor.js +113 -57
  27. package/lib/dialogs/{components/symbology → symbology/vector_layer}/VectorRendering.d.ts +1 -1
  28. package/lib/dialogs/{components/symbology → symbology/vector_layer}/VectorRendering.js +10 -13
  29. package/lib/dialogs/symbology/vector_layer/components/ValueSelect.d.ts +8 -0
  30. package/lib/dialogs/symbology/vector_layer/components/ValueSelect.js +7 -0
  31. package/lib/dialogs/symbology/vector_layer/types/Categorized.d.ts +4 -0
  32. package/lib/dialogs/symbology/vector_layer/types/Categorized.js +94 -0
  33. package/lib/dialogs/symbology/vector_layer/types/Graduated.js +169 -0
  34. package/lib/dialogs/{components/symbology → symbology/vector_layer/types}/SimpleSymbol.js +8 -13
  35. package/lib/formbuilder/objectform/vectorlayerform.js +1 -0
  36. package/lib/formbuilder/objectform/webGlLayerForm.js +1 -0
  37. package/lib/index.d.ts +6 -4
  38. package/lib/index.js +6 -4
  39. package/lib/mainview/mainView.d.ts +3 -1
  40. package/lib/mainview/mainView.js +66 -18
  41. package/lib/store.d.ts +9 -0
  42. package/lib/store.js +25 -0
  43. package/lib/toolbar/widget.js +1 -1
  44. package/lib/types.d.ts +14 -0
  45. package/package.json +16 -17
  46. package/style/symbologyDialog.css +104 -3
  47. package/lib/dialogs/components/symbology/BandRendering.d.ts +0 -4
  48. package/lib/dialogs/components/symbology/Graduated.js +0 -188
  49. /package/lib/dialogs/{components/symbology → symbology/components/color_stops}/StopRow.d.ts +0 -0
  50. /package/lib/dialogs/{symbologyDialog.d.ts → symbology/symbologyDialog.d.ts} +0 -0
  51. /package/lib/dialogs/{components/symbology → symbology/tiff_layer/components}/BandRow.js +0 -0
  52. /package/lib/dialogs/{components/symbology → symbology/vector_layer/types}/Graduated.d.ts +0 -0
  53. /package/lib/dialogs/{components/symbology → symbology/vector_layer/types}/SimpleSymbol.d.ts +0 -0
@@ -1,57 +1,71 @@
1
- import { faSpinner } from '@fortawesome/free-solid-svg-icons';
2
- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3
1
  import { Button } from '@jupyterlab/ui-components';
4
2
  import React, { useEffect, useRef, useState } from 'react';
5
- import { getGdal } from '../../../gdal';
6
- import BandRow from './BandRow';
7
- import StopRow from './StopRow';
8
- const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerId }) => {
3
+ import { GeoTiffClassifications } from '../../classificationModes';
4
+ import { GlobalStateDbManager } from '../../../../store';
5
+ import BandRow from '../components/BandRow';
6
+ import ColorRamp from '../../components/color_ramp/ColorRamp';
7
+ import StopRow from '../../components/color_stops/StopRow';
8
+ import { Utils } from '../../symbologyUtils';
9
+ import { getGdal } from '../../../../gdal';
10
+ import { Spinner } from '../../../../mainview/spinner';
11
+ const SingleBandPseudoColor = ({ context, okSignalPromise, cancel, layerId }) => {
9
12
  const functions = ['discrete', 'linear', 'exact'];
13
+ const modeOptions = ['continuous', 'equal interval', 'quantile'];
10
14
  const stopRowsRef = useRef();
11
15
  const bandRowsRef = useRef([]);
12
16
  const selectedFunctionRef = useRef();
13
- const [selectedFunction, setSelectedFunction] = useState();
17
+ const colorRampOptionsRef = useRef();
18
+ const layerStateRef = useRef();
19
+ const selectedBandRef = useRef();
20
+ const [layerState, setLayerState] = useState();
14
21
  const [selectedBand, setSelectedBand] = useState(1);
15
22
  const [stopRows, setStopRows] = useState([]);
16
23
  const [bandRows, setBandRows] = useState([]);
24
+ const [selectedFunction, setSelectedFunction] = useState('linear');
25
+ const [colorRampOptions, setColorRampOptions] = useState();
17
26
  if (!layerId) {
18
27
  return;
19
28
  }
20
29
  const layer = context.model.getLayer(layerId);
21
- if (!layer) {
30
+ if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
22
31
  return;
23
32
  }
33
+ const stateDb = GlobalStateDbManager.getInstance().getStateDb();
24
34
  useEffect(() => {
25
- getBandInfo();
26
- setInitialFunction();
35
+ populateOptions();
36
+ okSignalPromise.promise.then(okSignal => {
37
+ okSignal.connect(handleOk);
38
+ });
39
+ return () => {
40
+ okSignalPromise.promise.then(okSignal => {
41
+ okSignal.disconnect(handleOk, this);
42
+ });
43
+ };
27
44
  }, []);
45
+ useEffect(() => {
46
+ layerStateRef.current = layerState;
47
+ getBandInfo();
48
+ }, [layerState]);
28
49
  useEffect(() => {
29
50
  bandRowsRef.current = bandRows;
30
51
  buildColorInfo();
31
52
  }, [bandRows]);
32
53
  useEffect(() => {
33
54
  stopRowsRef.current = stopRows;
34
- }, [stopRows]);
35
- useEffect(() => {
36
55
  selectedFunctionRef.current = selectedFunction;
37
- }, [selectedFunction]);
38
- const setInitialFunction = () => {
39
- var _a;
40
- if (!((_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.color)) {
41
- setSelectedFunction('linear');
42
- return;
43
- }
44
- const color = layer.parameters.color;
45
- if (color[0] === 'interpolate') {
46
- setSelectedFunction('linear');
47
- return;
48
- }
49
- // If expression is using 'case' we look at the comparison operator to set selected function
50
- // Looking at fourth element because second is for nodata
51
- const operator = color[3][0];
52
- operator === '<='
53
- ? setSelectedFunction('discrete')
54
- : setSelectedFunction('exact');
56
+ colorRampOptionsRef.current = colorRampOptions;
57
+ selectedBandRef.current = selectedBand;
58
+ layerStateRef.current = layerState;
59
+ }, [stopRows, selectedFunction, colorRampOptions, selectedBand, layerState]);
60
+ const populateOptions = async () => {
61
+ var _a, _b, _c, _d;
62
+ const layerState = (await (stateDb === null || stateDb === void 0 ? void 0 : stateDb.fetch(`jupytergis:${layerId}`)));
63
+ setLayerState(layerState);
64
+ const layerParams = layer.parameters;
65
+ const band = (_b = (_a = layerParams.symbologyState) === null || _a === void 0 ? void 0 : _a.band) !== null && _b !== void 0 ? _b : 1;
66
+ const interpolation = (_d = (_c = layerParams.symbologyState) === null || _c === void 0 ? void 0 : _c.interpolation) !== null && _d !== void 0 ? _d : 'linear';
67
+ setSelectedBand(band);
68
+ setSelectedFunction(interpolation);
55
69
  };
56
70
  const getBandInfo = async () => {
57
71
  var _a, _b;
@@ -62,8 +76,7 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
62
76
  return;
63
77
  }
64
78
  let tifData;
65
- const layerState = await state.fetch(`jupytergis:${layerId}`);
66
- if (layerState) {
79
+ if (layerState && layerState.tifData) {
67
80
  tifData = JSON.parse(layerState.tifData);
68
81
  }
69
82
  else {
@@ -74,7 +87,9 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
74
87
  const tifDataset = result.datasets[0];
75
88
  tifData = await Gdal.gdalinfo(tifDataset, ['-stats']);
76
89
  Gdal.close(tifDataset);
77
- state.save(`jupytergis:${layerId}`, { tifData: JSON.stringify(tifData) });
90
+ await (stateDb === null || stateDb === void 0 ? void 0 : stateDb.save(`jupytergis:${layerId}`, {
91
+ tifData: JSON.stringify(tifData)
92
+ }));
78
93
  }
79
94
  tifData['bands'].forEach((bandData) => {
80
95
  var _a, _b;
@@ -87,7 +102,8 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
87
102
  mean: bandData.mean,
88
103
  stdDev: bandData.stdDev
89
104
  },
90
- metadata: bandData.metadata
105
+ metadata: bandData.metadata,
106
+ histogram: bandData.histogram
91
107
  });
92
108
  });
93
109
  setBandRows(bandsArr);
@@ -95,7 +111,7 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
95
111
  const buildColorInfo = () => {
96
112
  var _a;
97
113
  // This it to parse a color object on the layer
98
- if (!((_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.color)) {
114
+ if (!((_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.color) || !layerState) {
99
115
  return;
100
116
  }
101
117
  const color = layer.parameters.color;
@@ -103,6 +119,7 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
103
119
  if (typeof color === 'string') {
104
120
  return;
105
121
  }
122
+ const isQuantile = layerState.selectedMode === 'quantile';
106
123
  const valueColorPairs = [];
107
124
  // So if it's not a string then it's an array and we parse
108
125
  // Color[0] is the operator used for the color expression
@@ -115,7 +132,7 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
115
132
  // Sixth and on is value:color pairs
116
133
  for (let i = 5; i < color.length; i += 2) {
117
134
  const obj = {
118
- stop: scaleValue(color[i]),
135
+ stop: scaleValue(color[i], isQuantile),
119
136
  output: color[i + 1]
120
137
  };
121
138
  valueColorPairs.push(obj);
@@ -132,7 +149,7 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
132
149
  // Last element is fallback value
133
150
  for (let i = 3; i < color.length - 1; i += 2) {
134
151
  const obj = {
135
- stop: scaleValue(color[i][2]),
152
+ stop: scaleValue(color[i][2], isQuantile),
136
153
  output: color[i + 1]
137
154
  };
138
155
  valueColorPairs.push(obj);
@@ -143,7 +160,7 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
143
160
  setStopRows(valueColorPairs);
144
161
  };
145
162
  const handleOk = () => {
146
- var _a, _b, _c, _d;
163
+ var _a, _b, _c, _d, _e, _f, _g, _h;
147
164
  // Update source
148
165
  const bandRow = bandRowsRef.current[selectedBand - 1];
149
166
  if (!bandRow) {
@@ -154,6 +171,7 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
154
171
  if (!source || !source.parameters) {
155
172
  return;
156
173
  }
174
+ const isQuantile = ((_b = colorRampOptionsRef.current) === null || _b === void 0 ? void 0 : _b.selectedMode) === 'quantile';
157
175
  const sourceInfo = source.parameters.urls[0];
158
176
  sourceInfo.min = bandRow.stats.minimum;
159
177
  sourceInfo.max = bandRow.stats.maximum;
@@ -171,8 +189,8 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
171
189
  colorExpr.push(['band', selectedBand]);
172
190
  // Set NoData values to transparent
173
191
  colorExpr.push(0.0, [0.0, 0.0, 0.0, 0.0]);
174
- (_b = stopRowsRef.current) === null || _b === void 0 ? void 0 : _b.map(stop => {
175
- colorExpr.push(unscaleValue(stop.stop));
192
+ (_c = stopRowsRef.current) === null || _c === void 0 ? void 0 : _c.map(stop => {
193
+ colorExpr.push(unscaleValue(stop.stop, isQuantile));
176
194
  colorExpr.push(stop.output);
177
195
  });
178
196
  break;
@@ -182,11 +200,11 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
182
200
  // Set NoData values to transparent
183
201
  colorExpr.push(['==', ['band', selectedBand], 0]);
184
202
  colorExpr.push([0.0, 0.0, 0.0, 0.0]);
185
- (_c = stopRowsRef.current) === null || _c === void 0 ? void 0 : _c.map(stop => {
203
+ (_d = stopRowsRef.current) === null || _d === void 0 ? void 0 : _d.map(stop => {
186
204
  colorExpr.push([
187
205
  '<=',
188
206
  ['band', selectedBand],
189
- unscaleValue(stop.stop)
207
+ unscaleValue(stop.stop, isQuantile)
190
208
  ]);
191
209
  colorExpr.push(stop.output);
192
210
  });
@@ -199,11 +217,11 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
199
217
  // Set NoData values to transparent
200
218
  colorExpr.push(['==', ['band', selectedBand], 0]);
201
219
  colorExpr.push([0.0, 0.0, 0.0, 0.0]);
202
- (_d = stopRowsRef.current) === null || _d === void 0 ? void 0 : _d.map(stop => {
220
+ (_e = stopRowsRef.current) === null || _e === void 0 ? void 0 : _e.map(stop => {
203
221
  colorExpr.push([
204
222
  '==',
205
223
  ['band', selectedBand],
206
- unscaleValue(stop.stop)
224
+ unscaleValue(stop.stop, isQuantile)
207
225
  ]);
208
226
  colorExpr.push(stop.output);
209
227
  });
@@ -212,13 +230,19 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
212
230
  break;
213
231
  }
214
232
  }
233
+ const symbologyState = {
234
+ renderType: 'Singleband Pseudocolor',
235
+ band: selectedBandRef.current,
236
+ interpolation: selectedFunctionRef.current,
237
+ colorRamp: (_f = colorRampOptionsRef.current) === null || _f === void 0 ? void 0 : _f.selectedRamp,
238
+ nClasses: (_g = colorRampOptionsRef.current) === null || _g === void 0 ? void 0 : _g.numberOfShades,
239
+ mode: (_h = colorRampOptionsRef.current) === null || _h === void 0 ? void 0 : _h.selectedMode
240
+ };
241
+ layer.parameters.symbologyState = symbologyState;
215
242
  layer.parameters.color = colorExpr;
216
243
  context.model.sharedModel.updateLayer(layerId, layer);
217
244
  cancel();
218
245
  };
219
- okSignalPromise.promise.then(okSignal => {
220
- okSignal.connect(handleOk);
221
- });
222
246
  const addStopRow = () => {
223
247
  setStopRows([
224
248
  {
@@ -233,24 +257,55 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
233
257
  newFilters.splice(index, 1);
234
258
  setStopRows(newFilters);
235
259
  };
236
- const scaleValue = (bandValue) => {
260
+ const buildColorInfoFromClassification = async (selectedMode, numberOfShades, selectedRamp, setIsLoading) => {
261
+ var _a, _b;
262
+ // Update layer state with selected options
263
+ setColorRampOptions({
264
+ selectedRamp,
265
+ numberOfShades,
266
+ selectedMode
267
+ });
268
+ let stops = [];
269
+ const currentBand = bandRows[selectedBand - 1];
270
+ const source = context.model.getSource((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.source);
271
+ const sourceInfo = (_b = source === null || source === void 0 ? void 0 : source.parameters) === null || _b === void 0 ? void 0 : _b.urls[0];
272
+ const nClasses = selectedMode === 'continuous' ? 52 : +numberOfShades;
273
+ setIsLoading(true);
274
+ switch (selectedMode) {
275
+ case 'quantile':
276
+ stops = await GeoTiffClassifications.classifyQuantileBreaks(nClasses, selectedBand, sourceInfo.url, selectedFunction);
277
+ break;
278
+ case 'continuous':
279
+ stops = GeoTiffClassifications.classifyContinuousBreaks(nClasses, currentBand.stats.minimum, currentBand.stats.maximum, selectedFunction);
280
+ break;
281
+ case 'equal interval':
282
+ stops = GeoTiffClassifications.classifyEqualIntervalBreaks(nClasses, currentBand.stats.minimum, currentBand.stats.maximum, selectedFunction);
283
+ break;
284
+ default:
285
+ console.warn('No mode selected');
286
+ return;
287
+ }
288
+ setIsLoading(false);
289
+ const valueColorPairs = Utils.getValueColorPairs(stops, selectedRamp, nClasses);
290
+ setStopRows(valueColorPairs);
291
+ };
292
+ const scaleValue = (bandValue, isQuantile) => {
237
293
  const currentBand = bandRows[selectedBand - 1];
238
294
  if (!currentBand) {
239
295
  return bandValue;
240
296
  }
241
- return ((bandValue * (currentBand.stats.maximum - currentBand.stats.minimum)) /
242
- (1 - 0) +
243
- currentBand.stats.minimum);
297
+ const min = isQuantile ? 1 : currentBand.stats.minimum;
298
+ const max = isQuantile ? 65535 : currentBand.stats.maximum;
299
+ return (bandValue * (max - min)) / (1 - 0) + min;
244
300
  };
245
- const unscaleValue = (value) => {
301
+ const unscaleValue = (value, isQuantile) => {
246
302
  const currentBand = bandRowsRef.current[selectedBand - 1];
247
- return ((value * (1 - 0) - currentBand.stats.minimum * (1 - 0)) /
248
- (currentBand.stats.maximum - currentBand.stats.minimum));
303
+ const min = isQuantile ? 1 : currentBand.stats.minimum;
304
+ const max = isQuantile ? 65535 : currentBand.stats.maximum;
305
+ return (value * (1 - 0) - min * (1 - 0)) / (max - min);
249
306
  };
250
307
  return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
251
- React.createElement("div", { className: "jp-gis-band-container" }, bandRows.length === 0 ? (React.createElement("div", { className: "jp-gis-band-info-loading-container" },
252
- React.createElement("span", null, "Fetching band info..."),
253
- React.createElement(FontAwesomeIcon, { icon: faSpinner, className: "jp-gis-loading-spinner" }))) : (React.createElement(BandRow
308
+ React.createElement("div", { className: "jp-gis-band-container" }, bandRows.length === 0 ? (React.createElement(Spinner, { loading: bandRows.length === 0 })) : (React.createElement(BandRow
254
309
  // Band numbers are 1 indexed
255
310
  , {
256
311
  // Band numbers are 1 indexed
@@ -261,6 +316,7 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
261
316
  React.createElement("select", { name: "function-select", id: "function-select", className: "jp-mod-styled", value: selectedFunction, style: { textTransform: 'capitalize' }, onChange: event => {
262
317
  setSelectedFunction(event.target.value);
263
318
  } }, functions.map((func, funcIndex) => (React.createElement("option", { key: func, value: func, style: { textTransform: 'capitalize' } }, func)))))),
319
+ bandRows.length > 0 && (React.createElement(ColorRamp, { layerParams: layer.parameters, modeOptions: modeOptions, classifyFunc: buildColorInfoFromClassification, showModeRow: true })),
264
320
  React.createElement("div", { className: "jp-gis-stop-container" },
265
321
  React.createElement("div", { className: "jp-gis-stop-labels", style: { display: 'flex', gap: 6 } },
266
322
  React.createElement("span", { style: { flex: '0 0 18%' } },
@@ -1,4 +1,4 @@
1
1
  import React from 'react';
2
- import { ISymbologyDialogProps } from '../../symbologyDialog';
2
+ import { ISymbologyDialogProps } from '../symbologyDialog';
3
3
  declare const VectorRendering: ({ context, state, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element | undefined;
4
4
  export default VectorRendering;
@@ -1,6 +1,7 @@
1
1
  import React, { useEffect, useState } from 'react';
2
- import Graduated from './Graduated';
3
- import SimpleSymbol from './SimpleSymbol';
2
+ import Graduated from './types/Graduated';
3
+ import SimpleSymbol from './types/SimpleSymbol';
4
+ import Categorized from './types/Categorized';
4
5
  const VectorRendering = ({ context, state, okSignalPromise, cancel, layerId }) => {
5
6
  const [selectedRenderType, setSelectedRenderType] = useState('Single Symbol');
6
7
  const [componentToRender, setComponentToRender] = useState(null);
@@ -16,20 +17,13 @@ const VectorRendering = ({ context, state, okSignalPromise, cancel, layerId }) =
16
17
  return;
17
18
  }
18
19
  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
- };
20
+ var _a, _b;
21
+ const renderType = (_b = (_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.symbologyState) === null || _b === void 0 ? void 0 : _b.renderType;
22
+ setSelectedRenderType(renderType !== null && renderType !== void 0 ? renderType : 'Single Symbol');
28
23
  if (layer.type === 'VectorLayer') {
29
- const options = ['Single Symbol', 'Graduated'];
24
+ const options = ['Single Symbol', 'Graduated', 'Categorized'];
30
25
  setRenderTypeOptions(options);
31
26
  }
32
- getSelectedRenderType();
33
27
  }, []);
34
28
  useEffect(() => {
35
29
  switch (selectedRenderType) {
@@ -39,6 +33,9 @@ const VectorRendering = ({ context, state, okSignalPromise, cancel, layerId }) =
39
33
  case 'Graduated':
40
34
  RenderComponent = (React.createElement(Graduated, { context: context, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: layerId }));
41
35
  break;
36
+ case 'Categorized':
37
+ RenderComponent = (React.createElement(Categorized, { context: context, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: layerId }));
38
+ break;
42
39
  default:
43
40
  RenderComponent = React.createElement("div", null, "Render Type Not Implemented (yet)");
44
41
  }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ interface IValueSelectProps {
3
+ featureProperties: any;
4
+ selectedValue: string;
5
+ setSelectedValue: (value: string) => void;
6
+ }
7
+ declare const ValueSelect: ({ featureProperties, selectedValue, setSelectedValue }: IValueSelectProps) => React.JSX.Element;
8
+ export default ValueSelect;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ const ValueSelect = ({ featureProperties, selectedValue, setSelectedValue }) => {
3
+ return (React.createElement("div", { className: "jp-gis-symbology-row" },
4
+ React.createElement("label", { htmlFor: 'vector-value-select' }, "Value:"),
5
+ 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))))));
6
+ };
7
+ export default ValueSelect;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import { ISymbologyDialogProps } from '../../symbologyDialog';
3
+ declare const Categorized: ({ context, state, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element | undefined;
4
+ export default Categorized;
@@ -0,0 +1,94 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import ValueSelect from '../components/ValueSelect';
3
+ import { useGetProperties } from '../../hooks/useGetProperties';
4
+ import StopContainer from '../../components/color_stops/StopContainer';
5
+ import { Utils, VectorUtils } from '../../symbologyUtils';
6
+ import ColorRamp from '../../components/color_ramp/ColorRamp';
7
+ const Categorized = ({ context, state, okSignalPromise, cancel, layerId }) => {
8
+ const selectedValueRef = useRef();
9
+ const stopRowsRef = useRef();
10
+ const colorRampOptionsRef = useRef();
11
+ const [selectedValue, setSelectedValue] = useState('');
12
+ const [stopRows, setStopRows] = useState([]);
13
+ const [colorRampOptions, setColorRampOptions] = useState();
14
+ if (!layerId) {
15
+ return;
16
+ }
17
+ const layer = context.model.getLayer(layerId);
18
+ if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
19
+ return;
20
+ }
21
+ const { featureProps } = useGetProperties({
22
+ layerId,
23
+ model: context.model
24
+ });
25
+ useEffect(() => {
26
+ const valueColorPairs = VectorUtils.buildColorInfo(layer);
27
+ setStopRows(valueColorPairs);
28
+ okSignalPromise.promise.then(okSignal => {
29
+ okSignal.connect(handleOk, this);
30
+ });
31
+ return () => {
32
+ okSignalPromise.promise.then(okSignal => {
33
+ okSignal.disconnect(handleOk, this);
34
+ });
35
+ };
36
+ }, []);
37
+ useEffect(() => {
38
+ populateOptions();
39
+ }, [featureProps]);
40
+ useEffect(() => {
41
+ selectedValueRef.current = selectedValue;
42
+ stopRowsRef.current = stopRows;
43
+ colorRampOptionsRef.current = colorRampOptions;
44
+ }, [selectedValue, stopRows, colorRampOptions]);
45
+ const populateOptions = async () => {
46
+ var _a, _b;
47
+ const layerParams = layer.parameters;
48
+ const value = (_b = (_a = layerParams.symbologyState) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : Object.keys(featureProps)[0];
49
+ setSelectedValue(value);
50
+ };
51
+ const buildColorInfoFromClassification = (selectedMode, numberOfShades, selectedRamp, setIsLoading) => {
52
+ setColorRampOptions({
53
+ selectedFunction: '',
54
+ selectedRamp,
55
+ numberOfShades: '',
56
+ selectedMode: ''
57
+ });
58
+ const stops = Array.from(featureProps[selectedValue]).sort((a, b) => a - b);
59
+ const valueColorPairs = Utils.getValueColorPairs(stops, selectedRamp, stops.length);
60
+ setStopRows(valueColorPairs);
61
+ };
62
+ const handleOk = () => {
63
+ var _a, _b, _c, _d;
64
+ if (!layer.parameters) {
65
+ return;
66
+ }
67
+ const colorExpr = [];
68
+ colorExpr.push('case');
69
+ (_a = stopRowsRef.current) === null || _a === void 0 ? void 0 : _a.map(stop => {
70
+ colorExpr.push(['==', ['get', selectedValueRef.current], stop.stop]);
71
+ colorExpr.push(stop.output);
72
+ });
73
+ // fallback value
74
+ colorExpr.push([0, 0, 0, 0.0]);
75
+ const newStyle = Object.assign({}, layer.parameters.color);
76
+ newStyle['circle-fill-color'] = colorExpr;
77
+ const symbologyState = {
78
+ renderType: 'Categorized',
79
+ value: selectedValueRef.current,
80
+ colorRamp: (_b = colorRampOptionsRef.current) === null || _b === void 0 ? void 0 : _b.selectedRamp,
81
+ nClasses: (_c = colorRampOptionsRef.current) === null || _c === void 0 ? void 0 : _c.numberOfShades,
82
+ mode: (_d = colorRampOptionsRef.current) === null || _d === void 0 ? void 0 : _d.selectedMode
83
+ };
84
+ layer.parameters.symbologyState = symbologyState;
85
+ layer.parameters.color = newStyle;
86
+ context.model.sharedModel.updateLayer(layerId, layer);
87
+ cancel();
88
+ };
89
+ return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
90
+ React.createElement(ValueSelect, { featureProperties: featureProps, selectedValue: selectedValue, setSelectedValue: setSelectedValue }),
91
+ React.createElement(ColorRamp, { layerParams: layer.parameters, modeOptions: [], classifyFunc: buildColorInfoFromClassification, showModeRow: false }),
92
+ React.createElement(StopContainer, { selectedMethod: '', stopRows: stopRows, setStopRows: setStopRows })));
93
+ };
94
+ export default Categorized;
@@ -0,0 +1,169 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { VectorClassifications } from '../../classificationModes';
3
+ import ColorRamp from '../../components/color_ramp/ColorRamp';
4
+ import ValueSelect from '../components/ValueSelect';
5
+ import StopContainer from '../../components/color_stops/StopContainer';
6
+ import { useGetProperties } from '../../hooks/useGetProperties';
7
+ import { Utils, VectorUtils } from '../../symbologyUtils';
8
+ const Graduated = ({ context, state, okSignalPromise, cancel, layerId }) => {
9
+ const modeOptions = [
10
+ 'quantile',
11
+ 'equal interval',
12
+ 'jenks',
13
+ 'pretty',
14
+ 'logarithmic'
15
+ ];
16
+ const selectedValueRef = useRef();
17
+ const selectedMethodRef = useRef();
18
+ const stopRowsRef = useRef();
19
+ const colorRampOptionsRef = useRef();
20
+ const [selectedValue, setSelectedValue] = useState('');
21
+ const [selectedMethod, setSelectedMethod] = useState('color');
22
+ const [stopRows, setStopRows] = useState([]);
23
+ const [methodOptions, setMethodOptions] = useState(['color']);
24
+ const [colorRampOptions, setColorRampOptions] = useState();
25
+ if (!layerId) {
26
+ return;
27
+ }
28
+ const layer = context.model.getLayer(layerId);
29
+ if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
30
+ return;
31
+ }
32
+ const { featureProps } = useGetProperties({
33
+ layerId,
34
+ model: context.model
35
+ });
36
+ useEffect(() => {
37
+ var _a, _b;
38
+ let stopOutputPairs = [];
39
+ const layerParams = layer.parameters;
40
+ const method = (_b = (_a = layerParams.symbologyState) === null || _a === void 0 ? void 0 : _a.method) !== null && _b !== void 0 ? _b : 'color';
41
+ if (method === 'color') {
42
+ stopOutputPairs = VectorUtils.buildColorInfo(layer);
43
+ }
44
+ if (method === 'radius') {
45
+ stopOutputPairs = VectorUtils.buildRadiusInfo(layer);
46
+ }
47
+ setStopRows(stopOutputPairs);
48
+ okSignalPromise.promise.then(okSignal => {
49
+ okSignal.connect(handleOk, this);
50
+ });
51
+ return () => {
52
+ okSignalPromise.promise.then(okSignal => {
53
+ okSignal.disconnect(handleOk, this);
54
+ });
55
+ };
56
+ }, []);
57
+ useEffect(() => {
58
+ selectedValueRef.current = selectedValue;
59
+ selectedMethodRef.current = selectedMethod;
60
+ stopRowsRef.current = stopRows;
61
+ colorRampOptionsRef.current = colorRampOptions;
62
+ }, [selectedValue, selectedMethod, stopRows, colorRampOptions]);
63
+ useEffect(() => {
64
+ populateOptions();
65
+ }, [featureProps]);
66
+ const populateOptions = async () => {
67
+ var _a, _b, _c, _d, _e;
68
+ // Set up method options
69
+ if (((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.type) === 'circle') {
70
+ const options = ['color', 'radius'];
71
+ setMethodOptions(options);
72
+ }
73
+ const layerParams = layer.parameters;
74
+ const value = (_c = (_b = layerParams.symbologyState) === null || _b === void 0 ? void 0 : _b.value) !== null && _c !== void 0 ? _c : Object.keys(featureProps)[0];
75
+ const method = (_e = (_d = layerParams.symbologyState) === null || _d === void 0 ? void 0 : _d.method) !== null && _e !== void 0 ? _e : 'color';
76
+ setSelectedValue(value);
77
+ setSelectedMethod(method);
78
+ };
79
+ const handleOk = () => {
80
+ var _a, _b, _c, _d;
81
+ if (!layer.parameters) {
82
+ return;
83
+ }
84
+ const colorExpr = [];
85
+ colorExpr.push('interpolate');
86
+ colorExpr.push(['linear']);
87
+ colorExpr.push(['get', selectedValueRef.current]);
88
+ (_a = stopRowsRef.current) === null || _a === void 0 ? void 0 : _a.map(stop => {
89
+ colorExpr.push(stop.stop);
90
+ colorExpr.push(stop.output);
91
+ });
92
+ const newStyle = Object.assign({}, layer.parameters.color);
93
+ if (selectedMethodRef.current === 'color') {
94
+ if (layer.parameters.type === 'fill') {
95
+ newStyle['fill-color'] = colorExpr;
96
+ }
97
+ if (layer.parameters.type === 'line') {
98
+ newStyle['stroke-color'] = colorExpr;
99
+ }
100
+ if (layer.parameters.type === 'circle') {
101
+ newStyle['circle-fill-color'] = colorExpr;
102
+ }
103
+ }
104
+ if (selectedMethodRef.current === 'radius') {
105
+ if (layer.parameters.type === 'circle') {
106
+ newStyle['circle-radius'] = colorExpr;
107
+ }
108
+ }
109
+ const symbologyState = {
110
+ renderType: 'Graduated',
111
+ value: selectedValueRef.current,
112
+ method: selectedMethodRef.current,
113
+ colorRamp: (_b = colorRampOptionsRef.current) === null || _b === void 0 ? void 0 : _b.selectedRamp,
114
+ nClasses: (_c = colorRampOptionsRef.current) === null || _c === void 0 ? void 0 : _c.numberOfShades,
115
+ mode: (_d = colorRampOptionsRef.current) === null || _d === void 0 ? void 0 : _d.selectedMode
116
+ };
117
+ layer.parameters.symbologyState = symbologyState;
118
+ layer.parameters.color = newStyle;
119
+ context.model.sharedModel.updateLayer(layerId, layer);
120
+ cancel();
121
+ };
122
+ const buildColorInfoFromClassification = (selectedMode, numberOfShades, selectedRamp) => {
123
+ setColorRampOptions({
124
+ selectedRamp,
125
+ numberOfShades,
126
+ selectedMode
127
+ });
128
+ let stops;
129
+ const values = Array.from(featureProps[selectedValue]);
130
+ switch (selectedMode) {
131
+ case 'quantile':
132
+ stops = VectorClassifications.calculateQuantileBreaks(values, +numberOfShades);
133
+ break;
134
+ case 'equal interval':
135
+ stops = VectorClassifications.calculateEqualIntervalBreaks(values, +numberOfShades);
136
+ break;
137
+ case 'jenks':
138
+ stops = VectorClassifications.calculateJenksBreaks(values, +numberOfShades);
139
+ break;
140
+ case 'pretty':
141
+ stops = VectorClassifications.calculatePrettyBreaks(values, +numberOfShades);
142
+ break;
143
+ case 'logarithmic':
144
+ stops = VectorClassifications.calculateLogarithmicBreaks(values, +numberOfShades);
145
+ break;
146
+ default:
147
+ console.warn('No mode selected');
148
+ return;
149
+ }
150
+ let stopOutputPairs = [];
151
+ if (selectedMethod === 'radius') {
152
+ for (let i = 0; i < +numberOfShades; i++) {
153
+ stopOutputPairs.push({ stop: stops[i], output: stops[i] });
154
+ }
155
+ }
156
+ else {
157
+ stopOutputPairs = Utils.getValueColorPairs(stops, selectedRamp, +numberOfShades);
158
+ }
159
+ setStopRows(stopOutputPairs);
160
+ };
161
+ return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
162
+ React.createElement(ValueSelect, { featureProperties: featureProps, selectedValue: selectedValue, setSelectedValue: setSelectedValue }),
163
+ React.createElement("div", { className: "jp-gis-symbology-row" },
164
+ React.createElement("label", { htmlFor: 'vector-method-select' }, "Method:"),
165
+ 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))))),
166
+ React.createElement(ColorRamp, { layerParams: layer.parameters, modeOptions: modeOptions, classifyFunc: buildColorInfoFromClassification, showModeRow: true }),
167
+ React.createElement(StopContainer, { selectedMethod: selectedMethod, stopRows: stopRows, setStopRows: setStopRows })));
168
+ };
169
+ export default Graduated;