@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.
- package/lib/annotations/components/Annotation.js +1 -1
- package/lib/commands/BaseCommandIDs.d.ts +1 -0
- package/lib/commands/BaseCommandIDs.js +2 -0
- package/lib/commands/index.js +31 -73
- package/lib/constants.js +2 -1
- package/lib/dialogs/ProcessingFormDialog.js +2 -2
- package/lib/dialogs/symbology/colorRampUtils.d.ts +20 -0
- package/lib/dialogs/symbology/colorRampUtils.js +132 -0
- package/lib/dialogs/symbology/components/color_ramp/ColorRampControls.d.ts +36 -0
- package/lib/dialogs/symbology/components/color_ramp/ColorRampControls.js +82 -0
- package/lib/dialogs/symbology/components/color_ramp/ColorRampSelector.d.ts +20 -0
- package/lib/dialogs/symbology/components/color_ramp/{CanvasSelectComponent.js → ColorRampSelector.js} +26 -65
- package/lib/dialogs/symbology/components/color_ramp/ColorRampSelectorEntry.d.ts +20 -0
- package/lib/dialogs/symbology/components/color_ramp/{ColorRampEntry.js → ColorRampSelectorEntry.js} +17 -3
- package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.d.ts +6 -5
- package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.js +7 -2
- package/lib/dialogs/symbology/components/color_ramp/cmocean.json +459 -0
- package/lib/dialogs/symbology/components/color_stops/StopContainer.js +1 -1
- package/lib/dialogs/symbology/components/color_stops/StopRow.d.ts +3 -2
- package/lib/dialogs/symbology/components/color_stops/StopRow.js +4 -29
- package/lib/dialogs/symbology/hooks/useGetProperties.js +12 -6
- package/lib/dialogs/symbology/symbologyDialog.d.ts +2 -2
- package/lib/dialogs/symbology/symbologyUtils.d.ts +2 -1
- package/lib/dialogs/symbology/symbologyUtils.js +10 -4
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +9 -5
- package/lib/dialogs/symbology/vector_layer/VectorRendering.js +2 -2
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +4 -5
- package/lib/dialogs/symbology/vector_layer/types/Graduated.js +16 -28
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +3 -3
- package/lib/formbuilder/editform.js +4 -3
- package/lib/formbuilder/objectform/layer/heatmapLayerForm.d.ts +2 -1
- package/lib/formbuilder/objectform/layer/heatmapLayerForm.js +4 -0
- package/lib/formbuilder/objectform/layer/vectorlayerform.d.ts +2 -1
- package/lib/formbuilder/objectform/layer/vectorlayerform.js +4 -0
- package/lib/formbuilder/objectform/layer/webGlLayerForm.d.ts +2 -0
- package/lib/formbuilder/objectform/layer/webGlLayerForm.js +4 -0
- package/lib/icons.d.ts +1 -0
- package/lib/icons.js +5 -0
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/mainview/mainView.d.ts +1 -0
- package/lib/mainview/mainView.js +62 -12
- package/lib/panelview/annotationPanel.js +1 -1
- package/lib/panelview/components/filter-panel/Filter.js +1 -1
- package/lib/panelview/components/layers.js +152 -51
- package/lib/panelview/components/legendItem.js +36 -1
- package/lib/panelview/leftpanel.d.ts +0 -1
- package/lib/panelview/leftpanel.js +4 -4
- package/lib/panelview/rightpanel.js +4 -4
- package/lib/processing/processingFormToParam.js +3 -0
- package/lib/shared/components/ToggleGroup.d.ts +2 -2
- package/lib/stacBrowser/hooks/useStacSearch.js +2 -2
- package/lib/toolbar/widget.js +19 -28
- package/lib/tools.d.ts +1 -0
- package/lib/tools.js +2 -2
- package/lib/types.d.ts +8 -0
- package/lib/types.js +8 -0
- package/package.json +2 -2
- package/style/base.css +2 -0
- package/style/icons/book_open.svg +1 -1
- package/style/icons/clock-solid.svg +1 -1
- package/style/icons/geolocation.svg +1 -1
- package/style/icons/info-solid.svg +1 -1
- package/style/icons/logo_mini.svg +1 -1
- package/style/icons/marker.svg +5 -0
- package/style/icons/target_without_center.svg +1 -1
- package/style/icons/vector_square.svg +1 -1
- package/style/shared/tabs.css +1 -0
- package/style/symbologyDialog.css +12 -4
- package/lib/classificationModes.d.ts +0 -13
- package/lib/classificationModes.js +0 -326
- package/lib/dialogs/symbology/components/color_ramp/CanvasSelectComponent.d.ts +0 -11
- package/lib/dialogs/symbology/components/color_ramp/ColorRamp.d.ts +0 -16
- package/lib/dialogs/symbology/components/color_ramp/ColorRamp.js +0 -32
- package/lib/dialogs/symbology/components/color_ramp/ColorRampEntry.d.ts +0 -9
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { DOMUtils } from '@jupyterlab/apputils';
|
|
2
2
|
import { Button, LabIcon, caretDownIcon, caretRightIcon, } from '@jupyterlab/ui-components';
|
|
3
|
-
import { UUID } from '@lumino/coreutils';
|
|
4
3
|
import React, { useEffect, useState, } from 'react';
|
|
5
4
|
import { CommandIDs, icons } from "../../constants";
|
|
6
5
|
import { useGetSymbology } from "../../dialogs/symbology/hooks/useGetSymbology";
|
|
@@ -16,7 +15,6 @@ const LAYER_ICON_CLASS = 'jp-gis-layerIcon';
|
|
|
16
15
|
const LAYER_TEXT_CLASS = 'jp-gis-layerText data-jgis-keybinding';
|
|
17
16
|
export const LayersBodyComponent = props => {
|
|
18
17
|
const model = props.model;
|
|
19
|
-
const id = UUID.uuid4();
|
|
20
18
|
const [layerTree, setLayerTree] = useState((model === null || model === void 0 ? void 0 : model.getLayerTree()) || []);
|
|
21
19
|
const notifyCommands = () => {
|
|
22
20
|
// Notify commands that need updating
|
|
@@ -63,62 +61,61 @@ export const LayersBodyComponent = props => {
|
|
|
63
61
|
}
|
|
64
62
|
model === null || model === void 0 ? void 0 : model.moveItemRelatedTo(draggedId, dragOverId, dragOverPosition === 'above');
|
|
65
63
|
};
|
|
66
|
-
const onSelect = ({ type, item,
|
|
67
|
-
|
|
68
|
-
if (!props.model || !nodeId) {
|
|
64
|
+
const onSelect = ({ type, item, event }) => {
|
|
65
|
+
if (!props.model) {
|
|
69
66
|
return;
|
|
70
67
|
}
|
|
71
|
-
const selectedValue =
|
|
72
|
-
|
|
73
|
-
if (
|
|
68
|
+
const selectedValue = props.model.selected;
|
|
69
|
+
// Don't want to reset selected if right clicking a selected item
|
|
70
|
+
if (selectedValue &&
|
|
71
|
+
!event.ctrlKey &&
|
|
72
|
+
event.button === 2 &&
|
|
73
|
+
item in selectedValue) {
|
|
74
74
|
return;
|
|
75
75
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// Early return if no selection exists
|
|
76
|
+
// Calculate the new selection value
|
|
77
|
+
let newSelection;
|
|
78
|
+
// Early return if no selection exists - single selection
|
|
79
79
|
if (!selectedValue) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return;
|
|
80
|
+
newSelection = {
|
|
81
|
+
[item]: {
|
|
82
|
+
type,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
86
85
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
else if (!event.ctrlKey) {
|
|
87
|
+
// Reset selection for normal left click - single selection
|
|
88
|
+
newSelection = {
|
|
89
|
+
[item]: {
|
|
90
|
+
type,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
91
93
|
}
|
|
92
|
-
|
|
94
|
+
else {
|
|
93
95
|
// Check if new selection is the same type as previous selections
|
|
94
96
|
const isSelectedSameType = Object.values(selectedValue).some(selection => selection.type === type);
|
|
95
97
|
if (!isSelectedSameType) {
|
|
96
|
-
// Selecting a new type, so reset selected
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
// Selecting a new type, so reset selected - single selection
|
|
99
|
+
newSelection = {
|
|
100
|
+
[item]: {
|
|
101
|
+
type,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
// If types are the same add the selection - multi-selection
|
|
107
|
+
newSelection = Object.assign(Object.assign({}, selectedValue), { [item]: { type } });
|
|
99
108
|
}
|
|
100
|
-
// If types are the same add the selection
|
|
101
|
-
const updatedSelectedValue = Object.assign(Object.assign({}, selectedValue), { [item]: { type, selectedNodeId: nodeId } });
|
|
102
|
-
props.model.syncSelected(updatedSelectedValue, id);
|
|
103
|
-
notifyCommands();
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
const resetSelected = (type, nodeId, item) => {
|
|
107
|
-
const selection = {};
|
|
108
|
-
if (item && nodeId) {
|
|
109
|
-
selection[item] = {
|
|
110
|
-
type,
|
|
111
|
-
selectedNodeId: nodeId,
|
|
112
|
-
};
|
|
113
109
|
}
|
|
114
|
-
|
|
110
|
+
// Set the selection
|
|
111
|
+
props.model.selected = newSelection;
|
|
115
112
|
notifyCommands();
|
|
116
113
|
};
|
|
117
114
|
/**
|
|
118
115
|
* Propagate the layer selection.
|
|
119
116
|
*/
|
|
120
|
-
const onItemClick = ({ type, item,
|
|
121
|
-
onSelect({ type, item,
|
|
117
|
+
const onItemClick = ({ type, item, event }) => {
|
|
118
|
+
onSelect({ type, item, event });
|
|
122
119
|
};
|
|
123
120
|
/**
|
|
124
121
|
* Listen to the layers and layer tree changes.
|
|
@@ -156,6 +153,8 @@ const LayerGroupComponent = props => {
|
|
|
156
153
|
const [selected, setSelected] = useState(
|
|
157
154
|
// TODO Support multi-selection as `model?.jGISModel?.localState?.selected.value` does
|
|
158
155
|
isSelected(group.name, gisModel));
|
|
156
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
157
|
+
const [editValue, setEditValue] = useState('');
|
|
159
158
|
useEffect(() => {
|
|
160
159
|
setId(DOMUtils.createDomID());
|
|
161
160
|
const getExpandedState = async () => {
|
|
@@ -177,20 +176,70 @@ const LayerGroupComponent = props => {
|
|
|
177
176
|
return () => {
|
|
178
177
|
gisModel === null || gisModel === void 0 ? void 0 : gisModel.clientStateChanged.disconnect(onClientSharedStateChanged);
|
|
179
178
|
};
|
|
180
|
-
}, [gisModel]);
|
|
179
|
+
}, [gisModel, group.name]);
|
|
180
|
+
/**
|
|
181
|
+
* Listen to editing state changes.
|
|
182
|
+
*/
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
const onEditingChanged = (sender, editing) => {
|
|
185
|
+
if ((editing === null || editing === void 0 ? void 0 : editing.type) === 'group' && editing.itemId === name) {
|
|
186
|
+
setIsEditing(true);
|
|
187
|
+
setEditValue(name);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
setIsEditing(false);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
// Check initial editing state
|
|
194
|
+
const editing = gisModel === null || gisModel === void 0 ? void 0 : gisModel.editing;
|
|
195
|
+
if ((editing === null || editing === void 0 ? void 0 : editing.type) === 'group' && editing.itemId === name) {
|
|
196
|
+
setIsEditing(true);
|
|
197
|
+
setEditValue(name);
|
|
198
|
+
}
|
|
199
|
+
gisModel === null || gisModel === void 0 ? void 0 : gisModel.editingChanged.connect(onEditingChanged);
|
|
200
|
+
return () => {
|
|
201
|
+
gisModel === null || gisModel === void 0 ? void 0 : gisModel.editingChanged.disconnect(onEditingChanged);
|
|
202
|
+
};
|
|
203
|
+
}, [gisModel, name]);
|
|
181
204
|
const handleRightClick = (event) => {
|
|
182
|
-
|
|
183
|
-
const childId = (_a = event.currentTarget.children.namedItem(id)) === null || _a === void 0 ? void 0 : _a.id;
|
|
184
|
-
onClick({ type: 'group', item: name, nodeId: childId, event });
|
|
205
|
+
onClick({ type: 'group', item: name, event });
|
|
185
206
|
};
|
|
186
207
|
const handleExpand = async () => {
|
|
187
208
|
state.save(`jupytergis:${group.name}`, { expanded: !open });
|
|
188
209
|
setOpen(!open);
|
|
189
210
|
};
|
|
211
|
+
const handleRenameSave = () => {
|
|
212
|
+
const newName = editValue.trim();
|
|
213
|
+
if (newName && newName !== name && gisModel) {
|
|
214
|
+
gisModel.renameLayerGroup(name, newName);
|
|
215
|
+
}
|
|
216
|
+
gisModel === null || gisModel === void 0 ? void 0 : gisModel.clearEditingItem();
|
|
217
|
+
};
|
|
218
|
+
const handleRenameCancel = () => {
|
|
219
|
+
setEditValue(name);
|
|
220
|
+
gisModel === null || gisModel === void 0 ? void 0 : gisModel.clearEditingItem();
|
|
221
|
+
};
|
|
222
|
+
const handleRenameKeyDown = (e) => {
|
|
223
|
+
if (e.key === 'Enter') {
|
|
224
|
+
e.preventDefault();
|
|
225
|
+
handleRenameSave();
|
|
226
|
+
}
|
|
227
|
+
else if (e.key === 'Escape') {
|
|
228
|
+
e.preventDefault();
|
|
229
|
+
handleRenameCancel();
|
|
230
|
+
}
|
|
231
|
+
};
|
|
190
232
|
return (React.createElement("div", { className: `${LAYER_ITEM_CLASS} ${LAYER_GROUP_CLASS}`, draggable: true, onDragStart: Private.onDragStart, onDragEnd: Private.onDragEnd, "data-id": name },
|
|
191
233
|
React.createElement("div", { onClick: handleExpand, onContextMenu: handleRightClick, className: `${LAYER_GROUP_HEADER_CLASS}${selected ? ' jp-mod-selected' : ''}`, onDragOver: Private.onDragOver, "data-id": name },
|
|
192
234
|
React.createElement(LabIcon.resolveReact, { icon: caretDownIcon, className: `${LAYER_GROUP_COLLAPSER_CLASS}${open ? ' jp-mod-expanded' : ''}`, tag: 'span' }),
|
|
193
|
-
React.createElement("
|
|
235
|
+
isEditing ? (React.createElement("input", { type: "text", value: editValue, onChange: e => setEditValue(e.target.value), onKeyDown: handleRenameKeyDown, onBlur: handleRenameSave, className: LAYER_TEXT_CLASS, style: {
|
|
236
|
+
flex: 1,
|
|
237
|
+
border: '1px solid var(--jp-border-color1)',
|
|
238
|
+
borderRadius: '2px',
|
|
239
|
+
padding: '2px 4px',
|
|
240
|
+
fontSize: 'inherit',
|
|
241
|
+
fontFamily: 'inherit',
|
|
242
|
+
}, autoFocus: true })) : (React.createElement("span", { id: id, className: LAYER_TEXT_CLASS, tabIndex: -2 }, name))),
|
|
194
243
|
open && (React.createElement("div", null, layers
|
|
195
244
|
.slice()
|
|
196
245
|
.reverse()
|
|
@@ -216,6 +265,8 @@ const LayerComponent = props => {
|
|
|
216
265
|
// TODO Support multi-selection as `model?.jGISModel?.localState?.selected.value` does
|
|
217
266
|
isSelected(layerId, gisModel));
|
|
218
267
|
const [expanded, setExpanded] = useState(false);
|
|
268
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
269
|
+
const [editValue, setEditValue] = useState('');
|
|
219
270
|
const { symbology } = useGetSymbology({
|
|
220
271
|
layerId,
|
|
221
272
|
model: gisModel,
|
|
@@ -237,7 +288,31 @@ const LayerComponent = props => {
|
|
|
237
288
|
return () => {
|
|
238
289
|
gisModel === null || gisModel === void 0 ? void 0 : gisModel.clientStateChanged.disconnect(onClientSharedStateChanged);
|
|
239
290
|
};
|
|
240
|
-
}, [gisModel]);
|
|
291
|
+
}, [gisModel, layerId]);
|
|
292
|
+
/**
|
|
293
|
+
* Listen to editing state changes.
|
|
294
|
+
*/
|
|
295
|
+
useEffect(() => {
|
|
296
|
+
const onEditingChanged = (sender, editing) => {
|
|
297
|
+
if ((editing === null || editing === void 0 ? void 0 : editing.type) === 'layer' && editing.itemId === layerId) {
|
|
298
|
+
setIsEditing(true);
|
|
299
|
+
setEditValue(name);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
setIsEditing(false);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
// Check initial editing state
|
|
306
|
+
const editing = gisModel === null || gisModel === void 0 ? void 0 : gisModel.editing;
|
|
307
|
+
if ((editing === null || editing === void 0 ? void 0 : editing.type) === 'layer' && editing.itemId === layerId) {
|
|
308
|
+
setIsEditing(true);
|
|
309
|
+
setEditValue(name);
|
|
310
|
+
}
|
|
311
|
+
gisModel === null || gisModel === void 0 ? void 0 : gisModel.editingChanged.connect(onEditingChanged);
|
|
312
|
+
return () => {
|
|
313
|
+
gisModel === null || gisModel === void 0 ? void 0 : gisModel.editingChanged.disconnect(onEditingChanged);
|
|
314
|
+
};
|
|
315
|
+
}, [gisModel, layerId, name]);
|
|
241
316
|
/**
|
|
242
317
|
* Toggle layer visibility.
|
|
243
318
|
*/
|
|
@@ -247,15 +322,34 @@ const LayerComponent = props => {
|
|
|
247
322
|
(_a = gisModel === null || gisModel === void 0 ? void 0 : gisModel.sharedModel) === null || _a === void 0 ? void 0 : _a.updateLayer(layerId, layer);
|
|
248
323
|
};
|
|
249
324
|
const setSelection = (event) => {
|
|
250
|
-
var _a;
|
|
251
|
-
const childId = (_a = event.currentTarget.children.namedItem(id)) === null || _a === void 0 ? void 0 : _a.id;
|
|
252
325
|
onClick({
|
|
253
326
|
type: 'layer',
|
|
254
327
|
item: layerId,
|
|
255
|
-
nodeId: childId,
|
|
256
328
|
event,
|
|
257
329
|
});
|
|
258
330
|
};
|
|
331
|
+
const handleRenameSave = () => {
|
|
332
|
+
const newName = editValue.trim();
|
|
333
|
+
if (newName && newName !== name && gisModel) {
|
|
334
|
+
const updatedLayer = Object.assign(Object.assign({}, layer), { name: newName });
|
|
335
|
+
gisModel.sharedModel.updateLayer(layerId, updatedLayer);
|
|
336
|
+
}
|
|
337
|
+
gisModel === null || gisModel === void 0 ? void 0 : gisModel.clearEditingItem();
|
|
338
|
+
};
|
|
339
|
+
const handleRenameCancel = () => {
|
|
340
|
+
setEditValue(name);
|
|
341
|
+
gisModel === null || gisModel === void 0 ? void 0 : gisModel.clearEditingItem();
|
|
342
|
+
};
|
|
343
|
+
const handleRenameKeyDown = (e) => {
|
|
344
|
+
if (e.key === 'Enter') {
|
|
345
|
+
e.preventDefault();
|
|
346
|
+
handleRenameSave();
|
|
347
|
+
}
|
|
348
|
+
else if (e.key === 'Escape') {
|
|
349
|
+
e.preventDefault();
|
|
350
|
+
handleRenameCancel();
|
|
351
|
+
}
|
|
352
|
+
};
|
|
259
353
|
return (React.createElement("div", { className: `${LAYER_ITEM_CLASS} ${LAYER_CLASS}${selected ? ' jp-mod-selected' : ''}`, draggable: true, onDragStart: Private.onDragStart, onDragOver: Private.onDragOver, onDragEnd: Private.onDragEnd, "data-id": layerId, style: { display: 'flex', flexDirection: 'column' } },
|
|
260
354
|
React.createElement("div", { className: LAYER_TITLE_CLASS, onClick: setSelection, onContextMenu: setSelection, style: { display: 'flex' } },
|
|
261
355
|
hasSupportedSymbology && (React.createElement(Button, { minimal: true, onClick: e => {
|
|
@@ -266,7 +360,14 @@ const LayerComponent = props => {
|
|
|
266
360
|
React.createElement(Button, { title: layer.visible ? 'Hide layer' : 'Show layer', onClick: toggleVisibility, minimal: true },
|
|
267
361
|
React.createElement(LabIcon.resolveReact, { icon: layer.visible ? visibilityIcon : nonVisibilityIcon, className: `${LAYER_ICON_CLASS}${layer.visible ? '' : ' jp-gis-mod-hidden'}`, tag: "span" })),
|
|
268
362
|
icons.has(layer.type) && (React.createElement(LabIcon.resolveReact, Object.assign({}, icons.get(layer.type), { className: LAYER_ICON_CLASS }))),
|
|
269
|
-
React.createElement("
|
|
363
|
+
isEditing ? (React.createElement("input", { type: "text", value: editValue, onChange: e => setEditValue(e.target.value), onKeyDown: handleRenameKeyDown, onBlur: handleRenameSave, className: LAYER_TEXT_CLASS, style: {
|
|
364
|
+
flex: 1,
|
|
365
|
+
border: '1px solid var(--jp-border-color1)',
|
|
366
|
+
borderRadius: '2px',
|
|
367
|
+
padding: '2px 4px',
|
|
368
|
+
fontSize: 'inherit',
|
|
369
|
+
fontFamily: 'inherit',
|
|
370
|
+
}, autoFocus: true })) : (React.createElement("span", { id: id, className: LAYER_TEXT_CLASS, tabIndex: -2 }, name))),
|
|
270
371
|
expanded && gisModel && hasSupportedSymbology && (React.createElement("div", { style: { marginTop: 6, width: '100%' } },
|
|
271
372
|
React.createElement(LegendItem, { layerId: layerId, model: gisModel })))));
|
|
272
373
|
};
|
|
@@ -42,7 +42,7 @@ export const LegendItem = ({ layerId, model }) => {
|
|
|
42
42
|
return categories;
|
|
43
43
|
};
|
|
44
44
|
useEffect(() => {
|
|
45
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
45
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
46
46
|
if (isLoading) {
|
|
47
47
|
setContent(React.createElement("p", { style: { fontSize: '0.8em' } }, "Loading\u2026"));
|
|
48
48
|
return;
|
|
@@ -157,6 +157,41 @@ export const LegendItem = ({ layerId, model }) => {
|
|
|
157
157
|
React.createElement("span", { style: { fontSize: '0.75em' } }, String(c.category))))))));
|
|
158
158
|
return;
|
|
159
159
|
}
|
|
160
|
+
// Heatmap
|
|
161
|
+
if (renderType === 'Heatmap') {
|
|
162
|
+
const colors = Array.isArray(symbology.color) ? symbology.color : [];
|
|
163
|
+
if (!colors.length) {
|
|
164
|
+
setContent(React.createElement("p", { style: { fontSize: '0.8em' } }, "No heatmap colors"));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const gradient = `linear-gradient(to right, ${colors.join(', ')})`;
|
|
168
|
+
const reversed = (_j = symbology.symbologyState) === null || _j === void 0 ? void 0 : _j.reverse;
|
|
169
|
+
setContent(React.createElement("div", { style: { padding: 6, width: '90%' } },
|
|
170
|
+
React.createElement("div", { style: { fontSize: '1em', marginBottom: 10 } },
|
|
171
|
+
React.createElement("strong", null, "Heatmap")),
|
|
172
|
+
React.createElement("div", { style: {
|
|
173
|
+
height: 12,
|
|
174
|
+
background: gradient,
|
|
175
|
+
border: '1px solid #ccc',
|
|
176
|
+
borderRadius: 3,
|
|
177
|
+
marginBottom: 4,
|
|
178
|
+
} }),
|
|
179
|
+
React.createElement("div", { style: {
|
|
180
|
+
display: 'flex',
|
|
181
|
+
justifyContent: 'space-between',
|
|
182
|
+
fontSize: '0.75em',
|
|
183
|
+
marginBottom: 8,
|
|
184
|
+
} },
|
|
185
|
+
React.createElement("span", { style: { fontWeight: 'bold' } }, "Low"),
|
|
186
|
+
React.createElement("span", { style: { fontWeight: 'bold' } }, "High")),
|
|
187
|
+
React.createElement("div", { style: {
|
|
188
|
+
fontSize: '0.75em',
|
|
189
|
+
display: 'flex',
|
|
190
|
+
flexDirection: 'column',
|
|
191
|
+
gap: 2,
|
|
192
|
+
} }, reversed && (React.createElement("span", { style: { fontWeight: 'bold' } }, "Reversed ramp")))));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
160
195
|
setContent(React.createElement("p", null,
|
|
161
196
|
"Unsupported symbology: ",
|
|
162
197
|
String(renderType)));
|
|
@@ -28,14 +28,14 @@ export const LeftPanel = (props) => {
|
|
|
28
28
|
const [curTab, setCurTab] = React.useState(tabInfo.length > 0 ? tabInfo[0].name : undefined);
|
|
29
29
|
return (React.createElement("div", { className: "jgis-left-panel-container", style: { display: leftPanelVisible ? 'block' : 'none' } },
|
|
30
30
|
React.createElement(PanelTabs, { curTab: curTab, className: "jgis-panel-tabs" },
|
|
31
|
-
React.createElement(TabsList, null, tabInfo.map(
|
|
32
|
-
if (curTab !==
|
|
33
|
-
setCurTab(
|
|
31
|
+
React.createElement(TabsList, null, tabInfo.map(tab => (React.createElement(TabsTrigger, { className: "jGIS-layer-browser-category", key: tab.name, value: tab.name, onClick: () => {
|
|
32
|
+
if (curTab !== tab.name) {
|
|
33
|
+
setCurTab(tab.name);
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
36
36
|
setCurTab('');
|
|
37
37
|
}
|
|
38
|
-
} },
|
|
38
|
+
} }, tab.title)))),
|
|
39
39
|
!settings.layersDisabled && (React.createElement(TabsContent, { value: "layers", className: "jgis-panel-tab-content jp-gis-layerPanel" },
|
|
40
40
|
React.createElement(LayersBodyComponent, { model: props.model, commands: props.commands, state: props.state }))),
|
|
41
41
|
!settings.stacBrowserDisabled && (React.createElement(TabsContent, { value: "stac", className: "jgis-panel-tab-content" },
|
|
@@ -47,14 +47,14 @@ export const RightPanel = props => {
|
|
|
47
47
|
const [selectedObjectProperties, setSelectedObjectProperties] = React.useState(undefined);
|
|
48
48
|
return (React.createElement("div", { className: "jgis-right-panel-container", style: { display: rightPanelVisible ? 'block' : 'none' } },
|
|
49
49
|
React.createElement(PanelTabs, { className: "jgis-panel-tabs", curTab: curTab },
|
|
50
|
-
React.createElement(TabsList, null, tabInfo.map(
|
|
51
|
-
if (curTab !==
|
|
52
|
-
setCurTab(
|
|
50
|
+
React.createElement(TabsList, null, tabInfo.map(tab => (React.createElement(TabsTrigger, { className: "jGIS-layer-browser-category", key: tab.name, value: tab.name, onClick: () => {
|
|
51
|
+
if (curTab !== tab.name) {
|
|
52
|
+
setCurTab(tab.name);
|
|
53
53
|
}
|
|
54
54
|
else {
|
|
55
55
|
setCurTab('');
|
|
56
56
|
}
|
|
57
|
-
} },
|
|
57
|
+
} }, tab.title)))),
|
|
58
58
|
!settings.objectPropertiesDisabled && (React.createElement(TabsContent, { value: "objectProperties", className: "jgis-panel-tab-content" },
|
|
59
59
|
React.createElement(ObjectPropertiesReact, { setSelectedObject: setSelectedObjectProperties, selectedObject: selectedObjectProperties, formSchemaRegistry: props.formSchemaRegistry, model: props.model }))),
|
|
60
60
|
!settings.annotationsDisabled && (React.createElement(TabsContent, { value: "annotations", className: "jgis-panel-tab-content" },
|
|
@@ -5,6 +5,9 @@ export function processingFormToParam(formValues, processingType) {
|
|
|
5
5
|
return;
|
|
6
6
|
}
|
|
7
7
|
const processingElement = ProcessingMerge.find(e => e.description === processingType);
|
|
8
|
+
if (!processingElement) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
8
11
|
const params = processingElement.operationParams;
|
|
9
12
|
const out = {};
|
|
10
13
|
for (let i = 0; i < params.length; i++) {
|
|
@@ -3,10 +3,10 @@ import { type VariantProps } from 'class-variance-authority';
|
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
declare const ToggleGroup: React.ForwardRefExoticComponent<((Omit<ToggleGroupPrimitive.ToggleGroupSingleProps & React.RefAttributes<HTMLDivElement>, "ref"> | Omit<ToggleGroupPrimitive.ToggleGroupMultipleProps & React.RefAttributes<HTMLDivElement>, "ref">) & VariantProps<(props?: ({
|
|
5
5
|
variant?: "default" | "outline" | null | undefined;
|
|
6
|
-
size?: "
|
|
6
|
+
size?: "default" | "sm" | "lg" | null | undefined;
|
|
7
7
|
} & import("class-variance-authority/dist/types").ClassProp) | undefined) => string>) & React.RefAttributes<HTMLDivElement>>;
|
|
8
8
|
declare const ToggleGroupItem: React.ForwardRefExoticComponent<Omit<ToggleGroupPrimitive.ToggleGroupItemProps & React.RefAttributes<HTMLButtonElement>, "ref"> & VariantProps<(props?: ({
|
|
9
9
|
variant?: "default" | "outline" | null | undefined;
|
|
10
|
-
size?: "
|
|
10
|
+
size?: "default" | "sm" | "lg" | null | undefined;
|
|
11
11
|
} & import("class-variance-authority/dist/types").ClassProp) | undefined) => string> & React.RefAttributes<HTMLButtonElement>>;
|
|
12
12
|
export { ToggleGroup, ToggleGroupItem };
|
|
@@ -138,7 +138,7 @@ function useStacSearch({ model }) {
|
|
|
138
138
|
//@ts-expect-error Jupyter requires X-XSRFToken header
|
|
139
139
|
options, 'internal'));
|
|
140
140
|
if (!data) {
|
|
141
|
-
console.
|
|
141
|
+
console.debug('STAC search failed -- no results found');
|
|
142
142
|
setResults([]);
|
|
143
143
|
setTotalPages(1);
|
|
144
144
|
setTotalResults(0);
|
|
@@ -150,7 +150,7 @@ function useStacSearch({ model }) {
|
|
|
150
150
|
setTotalResults(data.context.matched);
|
|
151
151
|
}
|
|
152
152
|
catch (error) {
|
|
153
|
-
console.error('
|
|
153
|
+
console.error('STAC search failed -- error fetching data:', error);
|
|
154
154
|
setResults([]);
|
|
155
155
|
setTotalPages(1);
|
|
156
156
|
setTotalResults(0);
|
package/lib/toolbar/widget.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UsersItem, DefaultIconRenderer } from '@jupyter/collaboration';
|
|
2
2
|
import { CommandToolbarButton } from '@jupyterlab/apputils';
|
|
3
|
-
import { MenuSvg, ReactWidget, ReactiveToolbar, ToolbarButton, addIcon,
|
|
3
|
+
import { MenuSvg, ReactWidget, ReactiveToolbar, ToolbarButton, addIcon, } from '@jupyterlab/ui-components';
|
|
4
4
|
import { Widget } from '@lumino/widgets';
|
|
5
5
|
import * as React from 'react';
|
|
6
6
|
import { CommandIDs } from "../constants";
|
|
@@ -42,31 +42,6 @@ export class ToolbarWidget extends ReactiveToolbar {
|
|
|
42
42
|
this._model = options.model;
|
|
43
43
|
this.addClass('jGIS-toolbar-widget');
|
|
44
44
|
if (options.commands) {
|
|
45
|
-
const undoButton = new CommandToolbarButton({
|
|
46
|
-
id: CommandIDs.undo,
|
|
47
|
-
label: '',
|
|
48
|
-
icon: undoIcon,
|
|
49
|
-
commands: options.commands,
|
|
50
|
-
});
|
|
51
|
-
this.addItem('undo', undoButton);
|
|
52
|
-
undoButton.node.dataset.testid = 'undo-button';
|
|
53
|
-
const redoButton = new CommandToolbarButton({
|
|
54
|
-
id: CommandIDs.redo,
|
|
55
|
-
label: '',
|
|
56
|
-
icon: redoIcon,
|
|
57
|
-
commands: options.commands,
|
|
58
|
-
});
|
|
59
|
-
this.addItem('redo', redoButton);
|
|
60
|
-
this.addItem('separator0', new Separator());
|
|
61
|
-
const toggleConsoleButton = new CommandToolbarButton({
|
|
62
|
-
id: CommandIDs.toggleConsole,
|
|
63
|
-
commands: options.commands,
|
|
64
|
-
label: '',
|
|
65
|
-
icon: terminalToolbarIcon,
|
|
66
|
-
});
|
|
67
|
-
this.addItem('Toggle console', toggleConsoleButton);
|
|
68
|
-
toggleConsoleButton.node.dataset.testid = 'toggle-console-button';
|
|
69
|
-
this.addItem('separator1', new Separator());
|
|
70
45
|
const openLayersBrowserButton = new CommandToolbarButton({
|
|
71
46
|
id: CommandIDs.openLayerBrowser,
|
|
72
47
|
label: '',
|
|
@@ -95,9 +70,9 @@ export class ToolbarWidget extends ReactiveToolbar {
|
|
|
95
70
|
NewSubMenu.open(bbox.x, bbox.bottom);
|
|
96
71
|
},
|
|
97
72
|
});
|
|
98
|
-
NewEntryButton.node.dataset.testid = 'new-entry-button';
|
|
99
73
|
this.addItem('New', NewEntryButton);
|
|
100
|
-
|
|
74
|
+
NewEntryButton.node.dataset.testid = 'new-entry-button';
|
|
75
|
+
this.addItem('separator1', new Separator());
|
|
101
76
|
const geolocationButton = new CommandToolbarButton({
|
|
102
77
|
id: CommandIDs.getGeolocation,
|
|
103
78
|
commands: options.commands,
|
|
@@ -120,6 +95,22 @@ export class ToolbarWidget extends ReactiveToolbar {
|
|
|
120
95
|
this.addItem('temporalController', temporalControllerButton);
|
|
121
96
|
temporalControllerButton.node.dataset.testid =
|
|
122
97
|
'temporal-controller-button';
|
|
98
|
+
const addMarkerButton = new CommandToolbarButton({
|
|
99
|
+
id: CommandIDs.addMarker,
|
|
100
|
+
label: '',
|
|
101
|
+
commands: options.commands,
|
|
102
|
+
});
|
|
103
|
+
this.addItem('addMarker', addMarkerButton);
|
|
104
|
+
addMarkerButton.node.dataset.testid = 'add-marker-controller-button';
|
|
105
|
+
this.addItem('separator2', new Separator());
|
|
106
|
+
const toggleConsoleButton = new CommandToolbarButton({
|
|
107
|
+
id: CommandIDs.toggleConsole,
|
|
108
|
+
commands: options.commands,
|
|
109
|
+
label: '',
|
|
110
|
+
icon: terminalToolbarIcon,
|
|
111
|
+
});
|
|
112
|
+
this.addItem('Toggle console', toggleConsoleButton);
|
|
113
|
+
toggleConsoleButton.node.dataset.testid = 'toggle-console-button';
|
|
123
114
|
this.addItem('spacer', ReactiveToolbar.createSpacerItem());
|
|
124
115
|
// Users
|
|
125
116
|
const iconRenderer = createUserIconRenderer(this._model);
|
package/lib/tools.d.ts
CHANGED
|
@@ -116,6 +116,7 @@ export declare const getMimeType: (filename: string) => string;
|
|
|
116
116
|
* @returns An ArrayBuffer.
|
|
117
117
|
*/
|
|
118
118
|
export declare const stringToArrayBuffer: (content: string) => Promise<ArrayBuffer>;
|
|
119
|
+
export declare const getFeatureAttributes: <T>(featureProperties: Record<string, Set<any>>, predicate?: (key: string, value: any) => boolean) => Record<string, Set<T>>;
|
|
119
120
|
/**
|
|
120
121
|
* Get attributes of the feature which are numeric.
|
|
121
122
|
*
|
package/lib/tools.js
CHANGED
|
@@ -81,7 +81,7 @@ export async function requestAPI(endPoint = '', init = {}) {
|
|
|
81
81
|
data = JSON.parse(data);
|
|
82
82
|
}
|
|
83
83
|
catch (error) {
|
|
84
|
-
console.
|
|
84
|
+
console.error('Jupyter API request failed -- not a JSON response body:', response);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
if (!response.ok) {
|
|
@@ -701,7 +701,7 @@ export const stringToArrayBuffer = async (content) => {
|
|
|
701
701
|
const base64Response = await fetch(`data:application/octet-stream;base64,${content}`);
|
|
702
702
|
return await base64Response.arrayBuffer();
|
|
703
703
|
};
|
|
704
|
-
const getFeatureAttributes = (featureProperties, predicate = (key, value) => true) => {
|
|
704
|
+
export const getFeatureAttributes = (featureProperties, predicate = (key, value) => true) => {
|
|
705
705
|
const filteredRecord = {};
|
|
706
706
|
for (const [key, set] of Object.entries(featureProperties)) {
|
|
707
707
|
const firstValue = set.values().next().value;
|
package/lib/types.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ export { IDict };
|
|
|
5
5
|
export type ValueOf<T> = T[keyof T];
|
|
6
6
|
export type JupyterGISTracker = WidgetTracker<IJupyterGISWidget>;
|
|
7
7
|
export type SymbologyTab = 'color' | 'radius';
|
|
8
|
+
type RgbColorValue = [number, number, number] | [number, number, number, number];
|
|
9
|
+
type HexColorValue = string;
|
|
10
|
+
type InternalRgbArray = number[];
|
|
11
|
+
export type ColorValue = RgbColorValue | HexColorValue;
|
|
12
|
+
export type SizeValue = number;
|
|
13
|
+
export type SymbologyValue = SizeValue | ColorValue | InternalRgbArray;
|
|
8
14
|
export type VectorRenderType = 'Single Symbol' | 'Canonical' | 'Graduated' | 'Categorized' | 'Heatmap';
|
|
9
15
|
/**
|
|
10
16
|
* Add jupytergisMaps object to the global variables.
|
|
@@ -19,3 +25,5 @@ declare global {
|
|
|
19
25
|
};
|
|
20
26
|
}
|
|
21
27
|
}
|
|
28
|
+
declare const classificationModes: readonly ["quantile", "equal interval", "jenks", "pretty", "logarithmic", "continuous"];
|
|
29
|
+
export type ClassificationMode = (typeof classificationModes)[number];
|
package/lib/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupytergis/base",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "A JupyterLab extension for 3D modelling.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@jupyter/collaboration": "^3.1.0",
|
|
45
45
|
"@jupyter/react-components": "^0.16.6",
|
|
46
46
|
"@jupyter/ydoc": "^2.0.0 || ^3.0.0",
|
|
47
|
-
"@jupytergis/schema": "^0.
|
|
47
|
+
"@jupytergis/schema": "^0.10.0",
|
|
48
48
|
"@jupyterlab/application": "^4.3.0",
|
|
49
49
|
"@jupyterlab/apputils": "^4.3.0",
|
|
50
50
|
"@jupyterlab/completer": "^4.3.0",
|
package/style/base.css
CHANGED
|
@@ -92,6 +92,7 @@ button.jp-mod-styled.jp-mod-reject {
|
|
|
92
92
|
right: 0px;
|
|
93
93
|
position: absolute;
|
|
94
94
|
margin: 5px;
|
|
95
|
+
z-index: 40;
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
.jgis-left-panel-container {
|
|
@@ -100,6 +101,7 @@ button.jp-mod-styled.jp-mod-reject {
|
|
|
100
101
|
top: 30px;
|
|
101
102
|
left: 0px;
|
|
102
103
|
margin: 5px;
|
|
104
|
+
z-index: 40;
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
@media (max-width: 768px) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
version="1.1"
|
|
5
5
|
id="svg1"
|
|
6
6
|
xmlns="http://www.w3.org/2000/svg"
|
|
7
|
-
|
|
7
|
+
>
|
|
8
8
|
<defs
|
|
9
9
|
id="defs1" />
|
|
10
10
|
<!--!FontAwesome Free 6.7.2 by @fontawesome - https://fontawesome.com License -https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.-->
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
version="1.1"
|
|
5
5
|
id="svg1"
|
|
6
6
|
xmlns="http://www.w3.org/2000/svg"
|
|
7
|
-
|
|
7
|
+
>
|
|
8
8
|
<defs
|
|
9
9
|
id="defs1" />
|
|
10
10
|
<!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.-->
|