@jupytergis/base 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/lib/annotations/components/Annotation.js +1 -1
  2. package/lib/commands/BaseCommandIDs.d.ts +1 -0
  3. package/lib/commands/BaseCommandIDs.js +2 -0
  4. package/lib/commands/index.js +31 -73
  5. package/lib/constants.js +2 -1
  6. package/lib/dialogs/ProcessingFormDialog.js +2 -2
  7. package/lib/dialogs/symbology/colorRampUtils.d.ts +20 -0
  8. package/lib/dialogs/symbology/colorRampUtils.js +132 -0
  9. package/lib/dialogs/symbology/components/color_ramp/ColorRampControls.d.ts +36 -0
  10. package/lib/dialogs/symbology/components/color_ramp/ColorRampControls.js +82 -0
  11. package/lib/dialogs/symbology/components/color_ramp/ColorRampSelector.d.ts +20 -0
  12. package/lib/dialogs/symbology/components/color_ramp/{CanvasSelectComponent.js → ColorRampSelector.js} +26 -65
  13. package/lib/dialogs/symbology/components/color_ramp/ColorRampSelectorEntry.d.ts +20 -0
  14. package/lib/dialogs/symbology/components/color_ramp/{ColorRampEntry.js → ColorRampSelectorEntry.js} +17 -3
  15. package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.d.ts +6 -5
  16. package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.js +7 -2
  17. package/lib/dialogs/symbology/components/color_ramp/cmocean.json +459 -0
  18. package/lib/dialogs/symbology/components/color_stops/StopContainer.js +1 -1
  19. package/lib/dialogs/symbology/components/color_stops/StopRow.d.ts +3 -2
  20. package/lib/dialogs/symbology/components/color_stops/StopRow.js +4 -29
  21. package/lib/dialogs/symbology/hooks/useGetProperties.js +12 -6
  22. package/lib/dialogs/symbology/symbologyDialog.d.ts +2 -2
  23. package/lib/dialogs/symbology/symbologyUtils.d.ts +2 -1
  24. package/lib/dialogs/symbology/symbologyUtils.js +10 -4
  25. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +9 -5
  26. package/lib/dialogs/symbology/vector_layer/VectorRendering.js +2 -2
  27. package/lib/dialogs/symbology/vector_layer/types/Categorized.js +4 -5
  28. package/lib/dialogs/symbology/vector_layer/types/Graduated.js +16 -28
  29. package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +3 -3
  30. package/lib/formbuilder/editform.js +4 -3
  31. package/lib/formbuilder/objectform/layer/heatmapLayerForm.d.ts +2 -1
  32. package/lib/formbuilder/objectform/layer/heatmapLayerForm.js +4 -0
  33. package/lib/formbuilder/objectform/layer/vectorlayerform.d.ts +2 -1
  34. package/lib/formbuilder/objectform/layer/vectorlayerform.js +4 -0
  35. package/lib/formbuilder/objectform/layer/webGlLayerForm.d.ts +2 -0
  36. package/lib/formbuilder/objectform/layer/webGlLayerForm.js +4 -0
  37. package/lib/icons.d.ts +1 -0
  38. package/lib/icons.js +5 -0
  39. package/lib/index.d.ts +0 -1
  40. package/lib/index.js +0 -1
  41. package/lib/mainview/mainView.d.ts +1 -0
  42. package/lib/mainview/mainView.js +62 -12
  43. package/lib/panelview/annotationPanel.js +1 -1
  44. package/lib/panelview/components/filter-panel/Filter.js +1 -1
  45. package/lib/panelview/components/layers.js +152 -51
  46. package/lib/panelview/components/legendItem.js +36 -1
  47. package/lib/panelview/leftpanel.d.ts +0 -1
  48. package/lib/panelview/leftpanel.js +4 -4
  49. package/lib/panelview/rightpanel.js +4 -4
  50. package/lib/processing/processingFormToParam.js +3 -0
  51. package/lib/shared/components/ToggleGroup.d.ts +2 -2
  52. package/lib/stacBrowser/hooks/useStacSearch.js +2 -2
  53. package/lib/toolbar/widget.js +19 -28
  54. package/lib/tools.d.ts +1 -0
  55. package/lib/tools.js +2 -2
  56. package/lib/types.d.ts +8 -0
  57. package/lib/types.js +8 -0
  58. package/package.json +2 -2
  59. package/style/base.css +2 -0
  60. package/style/icons/book_open.svg +1 -1
  61. package/style/icons/clock-solid.svg +1 -1
  62. package/style/icons/geolocation.svg +1 -1
  63. package/style/icons/info-solid.svg +1 -1
  64. package/style/icons/logo_mini.svg +1 -1
  65. package/style/icons/marker.svg +5 -0
  66. package/style/icons/target_without_center.svg +1 -1
  67. package/style/icons/vector_square.svg +1 -1
  68. package/style/shared/tabs.css +1 -0
  69. package/style/symbologyDialog.css +12 -4
  70. package/lib/classificationModes.d.ts +0 -13
  71. package/lib/classificationModes.js +0 -326
  72. package/lib/dialogs/symbology/components/color_ramp/CanvasSelectComponent.d.ts +0 -11
  73. package/lib/dialogs/symbology/components/color_ramp/ColorRamp.d.ts +0 -16
  74. package/lib/dialogs/symbology/components/color_ramp/ColorRamp.js +0 -32
  75. package/lib/dialogs/symbology/components/color_ramp/ColorRampEntry.d.ts +0 -9
@@ -1,4 +1,5 @@
1
1
  import colormap from 'colormap';
2
+ const COLOR_EXPR_STOPS_START = 3;
2
3
  export var VectorUtils;
3
4
  (function (VectorUtils) {
4
5
  VectorUtils.buildColorInfo = (layer) => {
@@ -25,7 +26,7 @@ export var VectorUtils;
25
26
  // Second element is type of interpolation (ie linear)
26
27
  // Third is input value that stop values are compared with
27
28
  // Fourth and on is value:color pairs
28
- for (let i = 3; i < color[key].length; i += 2) {
29
+ for (let i = COLOR_EXPR_STOPS_START; i < color[key].length; i += 2) {
29
30
  const pairKey = `${color[key][i]}-${color[key][i + 1]}`;
30
31
  if (!seenPairs.has(pairKey)) {
31
32
  valueColorPairs.push({
@@ -63,10 +64,15 @@ export var VectorUtils;
63
64
  return [];
64
65
  }
65
66
  const stopOutputPairs = [];
66
- for (let i = 3; i < color['circle-radius'].length; i += 2) {
67
+ const circleRadius = color['circle-radius'];
68
+ if (!Array.isArray(circleRadius) ||
69
+ circleRadius.length <= COLOR_EXPR_STOPS_START) {
70
+ return [];
71
+ }
72
+ for (let i = COLOR_EXPR_STOPS_START; i < circleRadius.length; i += 2) {
67
73
  const obj = {
68
- stop: color['circle-radius'][i],
69
- output: color['circle-radius'][i + 1],
74
+ stop: circleRadius[i],
75
+ output: circleRadius[i + 1],
70
76
  };
71
77
  stopOutputPairs.push(obj);
72
78
  }
@@ -1,7 +1,7 @@
1
1
  import { Button } from '@jupyterlab/ui-components';
2
2
  import React, { useEffect, useRef, useState } from 'react';
3
3
  import { GeoTiffClassifications } from "../../classificationModes";
4
- import ColorRamp from "../../components/color_ramp/ColorRamp";
4
+ import ColorRampControls from "../../components/color_ramp/ColorRampControls";
5
5
  import StopRow from "../../components/color_stops/StopRow";
6
6
  import useGetBandInfo from "../../hooks/useGetBandInfo";
7
7
  import { Utils } from "../../symbologyUtils";
@@ -17,7 +17,11 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
17
17
  return;
18
18
  }
19
19
  const functions = ['discrete', 'linear', 'exact'];
20
- const modeOptions = ['continuous', 'equal interval', 'quantile'];
20
+ const modeOptions = [
21
+ 'continuous',
22
+ 'equal interval',
23
+ 'quantile',
24
+ ];
21
25
  const stateDb = GlobalStateDbManager.getInstance().getStateDb();
22
26
  const { bandRows, setBandRows, loading } = useGetBandInfo(model, layer);
23
27
  const [layerState, setLayerState] = useState();
@@ -223,7 +227,7 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
223
227
  const currentBand = bandRows[selectedBand - 1];
224
228
  const source = model.getSource((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.source);
225
229
  const sourceInfo = (_b = source === null || source === void 0 ? void 0 : source.parameters) === null || _b === void 0 ? void 0 : _b.urls[0];
226
- const nClasses = selectedMode === 'continuous' ? 52 : +numberOfShades;
230
+ const nClasses = selectedMode === 'continuous' ? 52 : numberOfShades;
227
231
  setIsLoading(true);
228
232
  switch (selectedMode) {
229
233
  case 'quantile':
@@ -270,7 +274,7 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
270
274
  React.createElement("select", { name: "function-select", id: "function-select", className: "jp-mod-styled", value: selectedFunction, style: { textTransform: 'capitalize' }, onChange: event => {
271
275
  setSelectedFunction(event.target.value);
272
276
  } }, functions.map((func, funcIndex) => (React.createElement("option", { key: func, value: func, style: { textTransform: 'capitalize' } }, func)))))),
273
- bandRows.length > 0 && (React.createElement(ColorRamp, { layerParams: layer.parameters, modeOptions: modeOptions, classifyFunc: buildColorInfoFromClassification, showModeRow: true, showRampSelector: true })),
277
+ bandRows.length > 0 && (React.createElement(ColorRampControls, { layerParams: layer.parameters, modeOptions: modeOptions, classifyFunc: buildColorInfoFromClassification, showModeRow: true, showRampSelector: true })),
274
278
  React.createElement("div", { className: "jp-gis-stop-container" },
275
279
  React.createElement("div", { className: "jp-gis-stop-labels", style: { display: 'flex', gap: 6 } },
276
280
  React.createElement("span", { style: { flex: '0 0 18%' } },
@@ -282,7 +286,7 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
282
286
  ? '='
283
287
  : ''),
284
288
  React.createElement("span", null, "Output Value")),
285
- 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) })))),
289
+ stopRows.map((stop, index) => (React.createElement(StopRow, { key: `${index}-${stop.output}`, index: index, dataValue: stop.stop, symbologyValue: stop.output, stopRows: stopRows, setStopRows: setStopRows, deleteRow: () => deleteStopRow(index) })))),
286
290
  React.createElement("div", { className: "jp-gis-symbology-button-container" },
287
291
  React.createElement(Button, { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: addStopRow }, "Add Stop"))));
288
292
  };
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import { useGetProperties } from "../hooks/useGetProperties";
3
- import { getColorCodeFeatureAttributes, getNumericFeatureAttributes, objectEntries, } from "../../../tools";
3
+ import { getColorCodeFeatureAttributes, getFeatureAttributes, getNumericFeatureAttributes, objectEntries, } from "../../../tools";
4
4
  import Canonical from './types/Canonical';
5
5
  import Categorized from './types/Categorized';
6
6
  import Graduated from './types/Graduated';
@@ -26,7 +26,7 @@ const RENDER_TYPE_OPTIONS = {
26
26
  },
27
27
  Categorized: {
28
28
  component: Categorized,
29
- attributeChecker: getNumericFeatureAttributes,
29
+ attributeChecker: getFeatureAttributes,
30
30
  supportedLayerTypes: ['VectorLayer', 'VectorTileLayer', 'HeatmapLayer'],
31
31
  isTabbed: true,
32
32
  },
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
- import ColorRamp from "../../components/color_ramp/ColorRamp";
2
+ import ColorRampControls from "../../components/color_ramp/ColorRampControls";
3
3
  import StopContainer from "../../components/color_stops/StopContainer";
4
4
  import { Utils, VectorUtils } from "../../symbologyUtils";
5
5
  import ValueSelect from "../components/ValueSelect";
@@ -82,8 +82,8 @@ const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbology
82
82
  setColorRampOptions({
83
83
  selectedFunction: '',
84
84
  selectedRamp,
85
- numberOfShades: '',
86
- selectedMode: '',
85
+ numberOfShades,
86
+ selectedMode,
87
87
  });
88
88
  const stops = Array.from(selectableAttributesAndValues[selectedAttribute]).sort((a, b) => a - b);
89
89
  const valueColorPairs = Utils.getValueColorPairs(stops, selectedRamp, stops.length, reverseRamp);
@@ -141,7 +141,6 @@ const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbology
141
141
  }
142
142
  const newStyle = Object.assign({}, layer.parameters.color);
143
143
  if (method === 'color') {
144
- console.log('delecol');
145
144
  delete newStyle['fill-color'];
146
145
  delete newStyle['stroke-color'];
147
146
  delete newStyle['circle-fill-color'];
@@ -195,7 +194,7 @@ const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbology
195
194
  React.createElement("input", { type: "checkbox", checked: reverseRamp, onChange: e => setReverseRamp(e.target.checked) }),
196
195
  "Reverse Color Ramp"))),
197
196
  React.createElement("div", { className: "jp-gis-layer-symbology-container" },
198
- React.createElement(ColorRamp, { layerParams: layer.parameters, modeOptions: [], classifyFunc: buildColorInfoFromClassification, showModeRow: false, showRampSelector: symbologyTab === 'color' }),
197
+ React.createElement(ColorRampControls, { layerParams: layer.parameters, modeOptions: [], classifyFunc: buildColorInfoFromClassification, showModeRow: false, showRampSelector: symbologyTab === 'color' }),
199
198
  React.createElement(StopContainer, { selectedMethod: '', stopRows: stopRows, setStopRows: setStopRows }))));
200
199
  }
201
200
  })();
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
2
  import { VectorClassifications } from "../../classificationModes";
3
- import ColorRamp from "../../components/color_ramp/ColorRamp";
3
+ import ColorRampControls from "../../components/color_ramp/ColorRampControls";
4
4
  import StopContainer from "../../components/color_stops/StopContainer";
5
5
  import { Utils, VectorUtils } from "../../symbologyUtils";
6
6
  import ValueSelect from "../components/ValueSelect";
@@ -22,7 +22,6 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
22
22
  const [radiusStopRows, setRadiusStopRows] = useState([]);
23
23
  const [colorRampOptions, setColorRampOptions] = useState();
24
24
  const [colorManualStyle, setColorManualStyle] = useState({
25
- fillColor: '#3399CC',
26
25
  strokeColor: '#3399CC',
27
26
  strokeWidth: 1.25,
28
27
  });
@@ -53,17 +52,10 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
53
52
  useEffect(() => {
54
53
  var _a;
55
54
  if ((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.color) {
56
- const fillColor = layer.parameters.color['fill-color'];
57
- const circleFillColor = layer.parameters.color['circle-fill-color'];
58
55
  const strokeColor = layer.parameters.color['stroke-color'];
59
56
  const circleStrokeColor = layer.parameters.color['circle-stroke-color'];
60
57
  const isSimpleColor = (val) => typeof val === 'string' && /^#?[0-9A-Fa-f]{3,8}$/.test(val);
61
58
  setColorManualStyle({
62
- fillColor: isSimpleColor(fillColor)
63
- ? fillColor
64
- : isSimpleColor(circleFillColor)
65
- ? circleFillColor
66
- : '#3399CC',
67
59
  strokeColor: isSimpleColor(strokeColor)
68
60
  ? strokeColor
69
61
  : isSimpleColor(circleStrokeColor)
@@ -131,11 +123,10 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
131
123
  newStyle['circle-stroke-color'] = colorExpr;
132
124
  }
133
125
  else {
134
- newStyle['fill-color'] = undefined;
135
- newStyle['circle-fill-color'] = undefined;
136
- newStyle['stroke-color'] = colorManualStyleRef.current.strokeColor;
137
- newStyle['circle-stroke-color'] = colorManualStyleRef.current.strokeColor;
126
+ // use manual style
138
127
  }
128
+ newStyle['stroke-color'] = colorManualStyleRef.current.strokeColor;
129
+ newStyle['circle-stroke-color'] = colorManualStyleRef.current.strokeColor;
139
130
  newStyle['stroke-width'] = colorManualStyleRef.current.strokeWidth;
140
131
  newStyle['circle-stroke-width'] = colorManualStyleRef.current.strokeWidth;
141
132
  // Apply radius symbology
@@ -180,19 +171,19 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
180
171
  const values = Array.from(selectableAttributesAndValues[selectedAttribute]);
181
172
  switch (selectedMode) {
182
173
  case 'quantile':
183
- stops = VectorClassifications.calculateQuantileBreaks(values, +numberOfShades);
174
+ stops = VectorClassifications.calculateQuantileBreaks(values, numberOfShades);
184
175
  break;
185
176
  case 'equal interval':
186
- stops = VectorClassifications.calculateEqualIntervalBreaks(values, +numberOfShades);
177
+ stops = VectorClassifications.calculateEqualIntervalBreaks(values, numberOfShades);
187
178
  break;
188
179
  case 'jenks':
189
- stops = VectorClassifications.calculateJenksBreaks(values, +numberOfShades);
180
+ stops = VectorClassifications.calculateJenksBreaks(values, numberOfShades);
190
181
  break;
191
182
  case 'pretty':
192
- stops = VectorClassifications.calculatePrettyBreaks(values, +numberOfShades);
183
+ stops = VectorClassifications.calculatePrettyBreaks(values, numberOfShades);
193
184
  break;
194
185
  case 'logarithmic':
195
- stops = VectorClassifications.calculateLogarithmicBreaks(values, +numberOfShades);
186
+ stops = VectorClassifications.calculateLogarithmicBreaks(values, numberOfShades);
196
187
  break;
197
188
  default:
198
189
  console.warn('No mode selected');
@@ -200,7 +191,7 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
200
191
  }
201
192
  const stopOutputPairs = symbologyTab === 'radius'
202
193
  ? stops.map(v => ({ stop: v, output: v }))
203
- : Utils.getValueColorPairs(stops, selectedRamp, +numberOfShades, reverseRamp);
194
+ : Utils.getValueColorPairs(stops, selectedRamp, numberOfShades, reverseRamp);
204
195
  if (symbologyTab === 'radius') {
205
196
  setRadiusStopRows(stopOutputPairs);
206
197
  }
@@ -214,9 +205,7 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
214
205
  }
215
206
  const newStyle = Object.assign({}, layer.parameters.color);
216
207
  if (method === 'color') {
217
- delete newStyle['fill-color'];
218
208
  delete newStyle['stroke-color'];
219
- delete newStyle['circle-fill-color'];
220
209
  setColorStopRows([]);
221
210
  setColorRampOptions(undefined);
222
211
  }
@@ -237,12 +226,11 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
237
226
  React.createElement(ValueSelect, { featureProperties: selectableAttributesAndValues, selectedValue: selectedAttribute, setSelectedValue: setSelectedAttribute }),
238
227
  React.createElement("div", { className: "jp-gis-layer-symbology-container" },
239
228
  symbologyTab === 'color' && (React.createElement(React.Fragment, null,
240
- React.createElement("div", { className: "jp-gis-symbology-row" },
241
- React.createElement("label", null, "Fill Color:"),
242
- React.createElement("input", { type: "color", className: "jp-mod-styled", value: colorManualStyle.fillColor, onChange: e => {
243
- handleReset('color');
244
- setColorManualStyle(Object.assign(Object.assign({}, colorManualStyle), { fillColor: e.target.value }));
245
- } })),
229
+ React.createElement("p", { className: "jp-info-text" },
230
+ "Fill color is automatically controlled by the color ramp. To control fill manually, switch to ",
231
+ React.createElement("strong", null, "Simple"),
232
+ ' ',
233
+ "symbology."),
246
234
  React.createElement("div", { className: "jp-gis-symbology-row" },
247
235
  React.createElement("label", null, "Stroke Color:"),
248
236
  React.createElement("input", { type: "color", className: "jp-mod-styled", value: colorManualStyle.strokeColor, onChange: e => {
@@ -263,7 +251,7 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
263
251
  React.createElement("label", null,
264
252
  React.createElement("input", { type: "checkbox", checked: reverseRamp, onChange: e => setReverseRamp(e.target.checked) }),
265
253
  "Reverse Color Ramp"))),
266
- React.createElement(ColorRamp, { layerParams: layer.parameters, modeOptions: modeOptions, classifyFunc: buildColorInfoFromClassification, showModeRow: true, showRampSelector: symbologyTab === 'color' }),
254
+ React.createElement(ColorRampControls, { layerParams: layer.parameters, modeOptions: modeOptions, classifyFunc: buildColorInfoFromClassification, showModeRow: true, showRampSelector: symbologyTab === 'color' }),
267
255
  React.createElement(StopContainer, { selectedMethod: symbologyTab || 'color', stopRows: symbologyTab === 'color' ? colorStopRows : radiusStopRows, setStopRows: symbologyTab === 'color' ? setColorStopRows : setRadiusStopRows })));
268
256
  }
269
257
  })();
@@ -1,6 +1,6 @@
1
1
  import colormap from 'colormap';
2
2
  import React, { useEffect, useRef, useState } from 'react';
3
- import CanvasSelectComponent from "../../components/color_ramp/CanvasSelectComponent";
3
+ import ColorRampSelector from "../../components/color_ramp/ColorRampSelector";
4
4
  const Heatmap = ({ model, state, okSignalPromise, cancel, layerId, }) => {
5
5
  if (!layerId) {
6
6
  return;
@@ -9,7 +9,7 @@ const Heatmap = ({ model, state, okSignalPromise, cancel, layerId, }) => {
9
9
  if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
10
10
  return;
11
11
  }
12
- const [selectedRamp, setSelectedRamp] = useState('');
12
+ const [selectedRamp, setSelectedRamp] = useState('viridis');
13
13
  const [heatmapOptions, setHetamapOptions] = useState({
14
14
  radius: 8,
15
15
  blur: 15,
@@ -74,7 +74,7 @@ const Heatmap = ({ model, state, okSignalPromise, cancel, layerId, }) => {
74
74
  React.createElement("p", null, "Represent features based on their density using a heatmap."),
75
75
  React.createElement("div", { className: "jp-gis-symbology-row jp-gis-heatmap" },
76
76
  React.createElement("label", { htmlFor: "color-ramp-select" }, "Color Ramp:"),
77
- React.createElement(CanvasSelectComponent, { selectedRamp: selectedRamp, setSelected: setSelectedRamp })),
77
+ React.createElement(ColorRampSelector, { selectedRamp: selectedRamp, setSelected: setSelectedRamp })),
78
78
  React.createElement("div", { className: "jp-gis-symbology-row" },
79
79
  React.createElement("label", null,
80
80
  React.createElement("input", { type: "checkbox", checked: reverseRamp, onChange: e => setReverseRamp(e.target.checked) }),
@@ -20,8 +20,9 @@ export class EditForm extends React.Component {
20
20
  let layerSchema = undefined;
21
21
  let LayerForm = undefined;
22
22
  let layerData = undefined;
23
+ let layer = undefined;
23
24
  if (this.props.layer) {
24
- const layer = this.props.model.getLayer(this.props.layer);
25
+ layer = this.props.model.getLayer(this.props.layer);
25
26
  if (!layer) {
26
27
  return;
27
28
  }
@@ -53,12 +54,12 @@ export class EditForm extends React.Component {
53
54
  return (React.createElement("div", null,
54
55
  this.props.layer && LayerForm && (React.createElement("div", null,
55
56
  React.createElement("h3", { style: { paddingLeft: '5px' } }, "Layer Properties"),
56
- React.createElement(LayerForm, { formContext: "update", sourceType: (source === null || source === void 0 ? void 0 : source.type) || 'RasterSource', model: this.props.model, filePath: this.props.model.filePath, schema: layerSchema, sourceData: layerData, syncData: (properties) => {
57
+ React.createElement(LayerForm, { key: `${this.props.layer}-${source === null || source === void 0 ? void 0 : source.type}`, formContext: "update", sourceType: (source === null || source === void 0 ? void 0 : source.type) || 'RasterSource', model: this.props.model, filePath: this.props.model.filePath, schema: layerSchema, sourceData: layerData, syncData: (properties) => {
57
58
  this.syncObjectProperties(this.props.layer, properties);
58
59
  } }))),
59
60
  this.props.source && SourceForm && (React.createElement("div", null,
60
61
  React.createElement("h3", { style: { paddingLeft: '5px' } }, "Source Properties"),
61
- React.createElement(SourceForm, { formContext: "update", model: this.props.model, filePath: this.props.model.filePath, schema: sourceSchema, sourceData: sourceData, syncData: (properties) => {
62
+ React.createElement(SourceForm, { key: `${this.props.source}-${layer === null || layer === void 0 ? void 0 : layer.type}`, formContext: "update", model: this.props.model, filePath: this.props.model.filePath, schema: sourceSchema, sourceData: sourceData, syncData: (properties) => {
62
63
  this.syncObjectProperties(this.props.source, properties);
63
64
  }, formChangedSignal: this.sourceFormChangedSignal, sourceType: (source === null || source === void 0 ? void 0 : source.type) || 'RasterSource' })))));
64
65
  }
@@ -1,10 +1,11 @@
1
1
  import { IDict, IHeatmapLayer } from '@jupytergis/schema';
2
- import { IChangeEvent } from '@rjsf/core';
2
+ import { IChangeEvent, ISubmitEvent } from '@rjsf/core';
3
3
  import { ILayerProps, LayerPropertiesForm } from './layerform';
4
4
  export declare class HeatmapLayerPropertiesForm extends LayerPropertiesForm {
5
5
  protected currentFormData: IHeatmapLayer;
6
6
  private features;
7
7
  constructor(props: ILayerProps);
8
+ protected onFormSubmit(e: ISubmitEvent<any>): void;
8
9
  protected onFormChange(e: IChangeEvent): void;
9
10
  protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
10
11
  private fetchFeatureNames;
@@ -13,6 +13,10 @@ export class HeatmapLayerPropertiesForm extends LayerPropertiesForm {
13
13
  });
14
14
  }
15
15
  }
16
+ onFormSubmit(e) {
17
+ e.formData.symbologyState = {};
18
+ return super.onFormSubmit(e);
19
+ }
16
20
  onFormChange(e) {
17
21
  super.onFormChange(e);
18
22
  const source = this.props.model.getSource(e.formData.source);
@@ -1,5 +1,5 @@
1
1
  import { IDict, IVectorLayer } from '@jupytergis/schema';
2
- import { IChangeEvent } from '@rjsf/core';
2
+ import { IChangeEvent, ISubmitEvent } from '@rjsf/core';
3
3
  import { ILayerProps, LayerPropertiesForm } from './layerform';
4
4
  /**
5
5
  * The form to modify a vector layer.
@@ -8,6 +8,7 @@ export declare class VectorLayerPropertiesForm extends LayerPropertiesForm {
8
8
  protected currentFormData: IVectorLayer;
9
9
  private currentSourceId;
10
10
  constructor(props: ILayerProps);
11
+ protected onFormSubmit(e: ISubmitEvent<any>): void;
11
12
  protected onFormChange(e: IChangeEvent): void;
12
13
  protected processSchema(data: IVectorLayer | undefined, schema: IDict, uiSchema: IDict): void;
13
14
  }
@@ -6,6 +6,10 @@ export class VectorLayerPropertiesForm extends LayerPropertiesForm {
6
6
  constructor(props) {
7
7
  super(props);
8
8
  }
9
+ onFormSubmit(e) {
10
+ e.formData.symbologyState = {};
11
+ return super.onFormSubmit(e);
12
+ }
9
13
  onFormChange(e) {
10
14
  super.onFormChange(e);
11
15
  // We only force update if we just updated the source
@@ -1,8 +1,10 @@
1
1
  import { IDict } from '@jupytergis/schema';
2
2
  import { LayerPropertiesForm } from './layerform';
3
+ import { ISubmitEvent } from '@rjsf/core';
3
4
  /**
4
5
  * The form to modify a hillshade layer.
5
6
  */
6
7
  export declare class WebGlLayerPropertiesForm extends LayerPropertiesForm {
8
+ protected onFormSubmit(e: ISubmitEvent<any>): void;
7
9
  protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
8
10
  }
@@ -3,6 +3,10 @@ import { LayerPropertiesForm } from './layerform';
3
3
  * The form to modify a hillshade layer.
4
4
  */
5
5
  export class WebGlLayerPropertiesForm extends LayerPropertiesForm {
6
+ onFormSubmit(e) {
7
+ e.formData.symbologyState = {};
8
+ return super.onFormSubmit(e);
9
+ }
6
10
  processSchema(data, schema, uiSchema) {
7
11
  this.removeFormEntry('color', data, schema, uiSchema);
8
12
  this.removeFormEntry('symbologyState', data, schema, uiSchema);
package/lib/icons.d.ts CHANGED
@@ -16,3 +16,4 @@ export declare const terminalToolbarIcon: LabIcon;
16
16
  export declare const geolocationIcon: LabIcon;
17
17
  export declare const targetWithoutCenterIcon: LabIcon;
18
18
  export declare const targetWithCenterIcon: LabIcon;
19
+ export declare const markerIcon: LabIcon;
package/lib/icons.js CHANGED
@@ -13,6 +13,7 @@ import logoSvgStr from '../style/icons/logo.svg';
13
13
  import logoMiniSvgStr from '../style/icons/logo_mini.svg';
14
14
  import logoMiniAlternativeSvgStr from '../style/icons/logo_mini_alternative.svg';
15
15
  import logoMiniQGZ from '../style/icons/logo_mini_qgz.svg';
16
+ import markerSvgStr from '../style/icons/marker.svg';
16
17
  import moundSvgStr from '../style/icons/mound.svg';
17
18
  import nonVisibilitySvgStr from '../style/icons/nonvisibility.svg';
18
19
  import rasterSvgStr from '../style/icons/raster.svg';
@@ -89,3 +90,7 @@ export const targetWithCenterIcon = new LabIcon({
89
90
  name: 'jupytergis::targetWithoutCenter',
90
91
  svgstr: targetWithoutCenterSvgStr,
91
92
  });
93
+ export const markerIcon = new LabIcon({
94
+ name: 'jupytergis::marker',
95
+ svgstr: markerSvgStr,
96
+ });
package/lib/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export * from './annotations';
2
- export * from './classificationModes';
3
2
  export * from './commands/index';
4
3
  export * from './constants';
5
4
  export * from './dialogs/layerCreationFormDialog';
package/lib/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  export * from './annotations';
2
- export * from './classificationModes';
3
2
  export * from './commands/index';
4
3
  export * from './constants';
5
4
  export * from './dialogs/layerCreationFormDialog';
@@ -175,6 +175,7 @@ export declare class MainView extends React.Component<IProps, IStates> {
175
175
  private _flyToPosition;
176
176
  private _onPointerMove;
177
177
  private _syncPointer;
178
+ private _addMarker;
178
179
  private _identifyFeature;
179
180
  private _triggerLayerUpdate;
180
181
  private _convertFeatureToMs;
@@ -28,7 +28,7 @@ import { register } from 'ol/proj/proj4.js';
28
28
  import RenderFeature, { toGeometry } from 'ol/render/Feature';
29
29
  import { GeoTIFF as GeoTIFFSource, ImageTile as ImageTileSource, Vector as VectorSource, VectorTile as VectorTileSource, XYZ as XYZSource, Tile as TileSource, } from 'ol/source';
30
30
  import Static from 'ol/source/ImageStatic';
31
- import { Circle, Fill, Stroke, Style } from 'ol/style';
31
+ import { Circle, Fill, Icon, Stroke, Style } from 'ol/style';
32
32
  //@ts-expect-error no types for ol-pmtiles
33
33
  import { PMTilesRasterSource, PMTilesVectorSource } from 'ol-pmtiles';
34
34
  import StacLayer from 'ol-stac';
@@ -43,6 +43,7 @@ import { debounce, isLightTheme, loadFile, throttle } from "../tools";
43
43
  import CollaboratorPointers from './CollaboratorPointers';
44
44
  import { FollowIndicator } from './FollowIndicator';
45
45
  import TemporalSlider from './TemporalSlider';
46
+ import { markerIcon } from '../icons';
46
47
  import { LeftPanel, RightPanel } from '../panelview';
47
48
  export class MainView extends React.Component {
48
49
  constructor(props) {
@@ -642,6 +643,7 @@ export class MainView extends React.Component {
642
643
  }
643
644
  });
644
645
  this._Map.on('click', this._identifyFeature.bind(this));
646
+ this._Map.on('click', this._addMarker.bind(this));
645
647
  this._Map
646
648
  .getViewport()
647
649
  .addEventListener('pointermove', this._onPointerMove.bind(this));
@@ -860,6 +862,27 @@ export class MainView extends React.Component {
860
862
  });
861
863
  break;
862
864
  }
865
+ case 'MarkerSource': {
866
+ const parameters = source.parameters;
867
+ const point = new Point(parameters.feature.coords);
868
+ const marker = new Feature({
869
+ type: 'icon',
870
+ geometry: point,
871
+ });
872
+ // Replace color placeholder in SVG with the parameter color
873
+ const markerColor = parameters.color || '#3463a0';
874
+ const svgString = markerIcon.svgstr.replace('{{COLOR}}', markerColor);
875
+ const iconStyle = new Style({
876
+ image: new Icon({
877
+ src: `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`,
878
+ scale: 0.25,
879
+ }),
880
+ });
881
+ marker.setStyle(iconStyle);
882
+ newSource = new VectorSource({
883
+ features: [marker],
884
+ });
885
+ }
863
886
  }
864
887
  newSource.set('id', id);
865
888
  // _sources is a list of OpenLayers sources
@@ -1429,15 +1452,11 @@ export class MainView extends React.Component {
1429
1452
  return;
1430
1453
  }
1431
1454
  const layer = this.getLayer(id);
1432
- let nextIndex = index;
1433
1455
  // should not be undefined since the id exists above
1434
1456
  if (layer === undefined) {
1435
1457
  return;
1436
1458
  }
1437
1459
  this._Map.getLayers().removeAt(currentIndex);
1438
- if (currentIndex < index) {
1439
- nextIndex -= 1;
1440
- }
1441
1460
  // Adjust index to ensure it's within bounds
1442
1461
  const numLayers = this._Map.getLayers().getLength();
1443
1462
  const safeIndex = Math.min(index, numLayers);
@@ -1587,6 +1606,37 @@ export class MainView extends React.Component {
1587
1606
  const coordinates = this._Map.getCoordinateFromPixel(pixel);
1588
1607
  this._syncPointer(coordinates);
1589
1608
  }
1609
+ async _addMarker(e) {
1610
+ if (this._model.currentMode !== 'marking') {
1611
+ return;
1612
+ }
1613
+ const coordinate = this._Map.getCoordinateFromPixel(e.pixel);
1614
+ const sourceId = UUID.uuid4();
1615
+ const layerId = UUID.uuid4();
1616
+ const sourceParameters = {
1617
+ feature: { coords: [coordinate[0], coordinate[1]] },
1618
+ };
1619
+ const layerParams = {
1620
+ opacity: 1.0,
1621
+ source: sourceId,
1622
+ symbologyState: { renderType: 'Single Symbol' },
1623
+ };
1624
+ const sourceModel = {
1625
+ type: 'MarkerSource',
1626
+ name: 'Marker',
1627
+ parameters: sourceParameters,
1628
+ };
1629
+ const layerModel = {
1630
+ type: 'VectorLayer',
1631
+ visible: true,
1632
+ name: 'Marker',
1633
+ parameters: layerParams,
1634
+ };
1635
+ this._model.sharedModel.addSource(sourceId, sourceModel);
1636
+ await this.addSource(sourceId, sourceModel);
1637
+ this._model.addLayer(layerId, layerModel);
1638
+ await this.addLayer(layerId, layerModel, this.getLayerIDs().length);
1639
+ }
1590
1640
  _identifyFeature(e) {
1591
1641
  var _a, _b;
1592
1642
  if (this._model.currentMode !== 'identifying') {
@@ -1619,7 +1669,7 @@ export class MainView extends React.Component {
1619
1669
  const rawProps = feature.getProperties();
1620
1670
  const fid = (_b = (_a = feature.getId) === null || _a === void 0 ? void 0 : _a.call(feature)) !== null && _b !== void 0 ? _b : rawProps === null || rawProps === void 0 ? void 0 : rawProps.fid;
1621
1671
  if (rawProps && Object.keys(rawProps).length > 1) {
1622
- const { geometry } = rawProps, clean = __rest(rawProps, ["geometry"]);
1672
+ const clean = __rest(rawProps, []);
1623
1673
  props = clean;
1624
1674
  if (fid !== null) {
1625
1675
  // TODO Clean the cache under some condition?
@@ -1684,7 +1734,7 @@ export class MainView extends React.Component {
1684
1734
  const { layerId, layer: jgisLayer } = json;
1685
1735
  const olLayer = this.getLayer(layerId);
1686
1736
  if (!jgisLayer || !olLayer) {
1687
- console.log('Layer not found');
1737
+ console.error('Failed to update layer -- layer not found');
1688
1738
  return;
1689
1739
  }
1690
1740
  this.updateLayer(layerId, jgisLayer, olLayer);
@@ -1736,10 +1786,10 @@ export class MainView extends React.Component {
1736
1786
  React.createElement("div", { ref: this.divRef, style: {
1737
1787
  width: '100%',
1738
1788
  height: '100%',
1739
- } })),
1740
- React.createElement(StatusBar, { jgisModel: this._model, loading: this.state.loadingLayer, projection: this.state.viewProjection, scale: this.state.scale })),
1741
- React.createElement("div", { className: "jgis-panels-wrapper" },
1742
- this._state && (React.createElement(LeftPanel, { model: this._model, commands: this._mainViewModel.commands, state: this._state })),
1743
- this._formSchemaRegistry && this._annotationModel && (React.createElement(RightPanel, { model: this._model, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel })))));
1789
+ } },
1790
+ React.createElement("div", { className: "jgis-panels-wrapper" },
1791
+ this._state && (React.createElement(LeftPanel, { model: this._model, commands: this._mainViewModel.commands, state: this._state })),
1792
+ this._formSchemaRegistry && this._annotationModel && (React.createElement(RightPanel, { model: this._model, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel }))))),
1793
+ React.createElement(StatusBar, { jgisModel: this._model, loading: this.state.loadingLayer, projection: this.state.viewProjection, scale: this.state.scale }))));
1744
1794
  }
1745
1795
  }
@@ -19,7 +19,7 @@ export class AnnotationsPanel extends Component {
19
19
  return React.createElement("div", null);
20
20
  }
21
21
  const annotations = annotationIds.map((id) => {
22
- return (React.createElement("div", null,
22
+ return (React.createElement("div", { className: "jgis-annotation-panel" },
23
23
  React.createElement(Annotation, { jgisModel: this._jgisModel, annotationModel: this._annotationModel, itemId: id }),
24
24
  React.createElement("hr", { className: "jGIS-Annotations-Separator" })));
25
25
  });
@@ -166,7 +166,7 @@ const FilterComponent = ({ model }) => {
166
166
  React.createElement("div", { style: {
167
167
  justifyContent: 'flex-start',
168
168
  } },
169
- React.createElement(Button, { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: addFilterRow }, "Add"),
169
+ React.createElement(Button, { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: addFilterRow, "data-testid": "add-filter-button" }, "Add"),
170
170
  React.createElement(Button, { className: "jp-Dialog-button jp-mod-reject jp-mod-styled", onClick: clearFilters }, "Clear")),
171
171
  React.createElement(Button, { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: submitFilter }, "Submit")))),
172
172
  !shouldDisplay && (React.createElement("div", { style: { textAlign: 'center' } }, "No layer selected"))));