@jupytergis/base 0.9.2 → 0.10.1
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 +2 -1
- package/lib/dialogs/symbology/colorRampUtils.js +10 -4
- 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} +25 -7
- 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/hooks/useGetProperties.js +12 -6
- package/lib/dialogs/symbology/symbologyUtils.d.ts +2 -1
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +8 -4
- 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 +2 -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 -34
- package/lib/dialogs/symbology/components/color_ramp/ColorRampEntry.d.ts +0 -9
|
@@ -38,7 +38,7 @@ const Annotation = ({ itemId, annotationModel, jgisModel, children, }) => {
|
|
|
38
38
|
return (React.createElement(Message, { user: content.user, message: content.value, self: ((_a = annotationModel.user) === null || _a === void 0 ? void 0 : _a.username) === ((_b = content.user) === null || _b === void 0 ? void 0 : _b.username) }));
|
|
39
39
|
})),
|
|
40
40
|
React.createElement("div", { className: "jGIS-Annotation-Message" },
|
|
41
|
-
React.createElement("textarea", { rows: 3, placeholder: 'Ctrl+Enter to submit', value: messageContent, onChange: e => setMessageContent(e.currentTarget.value), onKeyDown: e => {
|
|
41
|
+
React.createElement("textarea", { "data-id": "annotation-textarea", rows: 3, placeholder: 'Ctrl+Enter to submit', value: messageContent, onChange: e => setMessageContent(e.currentTarget.value), onKeyDown: e => {
|
|
42
42
|
if (e.ctrlKey && e.key === 'Enter') {
|
|
43
43
|
handleSubmit();
|
|
44
44
|
}
|
|
@@ -4,6 +4,7 @@ export declare const undo = "jupytergis:undo";
|
|
|
4
4
|
export declare const symbology = "jupytergis:symbology";
|
|
5
5
|
export declare const identify = "jupytergis:identify";
|
|
6
6
|
export declare const temporalController = "jupytergis:temporalController";
|
|
7
|
+
export declare const addMarker = "jupytergis:addMarker";
|
|
7
8
|
export declare const getGeolocation = "jupytergis:getGeolocation";
|
|
8
9
|
export declare const openLayerBrowser = "jupytergis:openLayerBrowser";
|
|
9
10
|
export declare const newRasterEntry = "jupytergis:newRasterEntry";
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
*
|
|
3
3
|
* See the documentation for more details.
|
|
4
4
|
*/
|
|
5
|
+
// Toolbar
|
|
5
6
|
export const createNew = 'jupytergis:create-new-jGIS-file';
|
|
6
7
|
export const redo = 'jupytergis:redo';
|
|
7
8
|
export const undo = 'jupytergis:undo';
|
|
8
9
|
export const symbology = 'jupytergis:symbology';
|
|
9
10
|
export const identify = 'jupytergis:identify';
|
|
10
11
|
export const temporalController = 'jupytergis:temporalController';
|
|
12
|
+
export const addMarker = 'jupytergis:addMarker';
|
|
11
13
|
// geolocation
|
|
12
14
|
export const getGeolocation = 'jupytergis:getGeolocation';
|
|
13
15
|
// Layers and sources creation commands
|
package/lib/commands/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { getSingleSelectedLayer } from '../processing/index';
|
|
|
11
11
|
import { addProcessingCommands } from '../processing/processingCommands';
|
|
12
12
|
import { getGeoJSONDataFromLayerSource, downloadFile } from '../tools';
|
|
13
13
|
import { JupyterGISDocumentWidget } from '../widget';
|
|
14
|
+
const POINT_SELECTION_TOOL_CLASS = 'jGIS-point-selection-tool';
|
|
14
15
|
function loadKeybindings(commands, keybindings) {
|
|
15
16
|
keybindings.forEach(binding => {
|
|
16
17
|
commands.addKeyBinding({
|
|
@@ -89,7 +90,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
89
90
|
].includes(selectedLayer.type);
|
|
90
91
|
if (current.model.currentMode === 'identifying' && !canIdentify) {
|
|
91
92
|
current.model.currentMode = 'panning';
|
|
92
|
-
current.node.classList.remove(
|
|
93
|
+
current.node.classList.remove(POINT_SELECTION_TOOL_CLASS);
|
|
93
94
|
return false;
|
|
94
95
|
}
|
|
95
96
|
return current.model.currentMode === 'identifying';
|
|
@@ -118,13 +119,13 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
118
119
|
const keysPressed = luminoEvent.keys;
|
|
119
120
|
if (keysPressed === null || keysPressed === void 0 ? void 0 : keysPressed.includes('Escape')) {
|
|
120
121
|
current.model.currentMode = 'panning';
|
|
121
|
-
current.node.classList.remove(
|
|
122
|
+
current.node.classList.remove(POINT_SELECTION_TOOL_CLASS);
|
|
122
123
|
commands.notifyCommandChanged(CommandIDs.identify);
|
|
123
124
|
return;
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
|
-
current.node.classList.toggle(
|
|
127
|
-
current.model.
|
|
127
|
+
current.node.classList.toggle(POINT_SELECTION_TOOL_CLASS);
|
|
128
|
+
current.model.toggleMode('identifying');
|
|
128
129
|
commands.notifyCommandChanged(CommandIDs.identify);
|
|
129
130
|
} }, icons.get(CommandIDs.identify)));
|
|
130
131
|
commands.addCommand(CommandIDs.temporalController, Object.assign({ label: trans.__('Temporal Controller'), isToggled: () => {
|
|
@@ -344,13 +345,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
344
345
|
execute: async () => {
|
|
345
346
|
var _a;
|
|
346
347
|
const model = (_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model;
|
|
347
|
-
await Private.renameSelectedItem(model, 'layer'
|
|
348
|
-
const layer = model === null || model === void 0 ? void 0 : model.getLayer(layerId);
|
|
349
|
-
if (layer) {
|
|
350
|
-
layer.name = newName;
|
|
351
|
-
model === null || model === void 0 ? void 0 : model.sharedModel.updateLayer(layerId, layer);
|
|
352
|
-
}
|
|
353
|
-
});
|
|
348
|
+
await Private.renameSelectedItem(model, 'layer');
|
|
354
349
|
},
|
|
355
350
|
});
|
|
356
351
|
commands.addCommand(CommandIDs.removeLayer, {
|
|
@@ -368,9 +363,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
368
363
|
execute: async () => {
|
|
369
364
|
var _a;
|
|
370
365
|
const model = (_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model;
|
|
371
|
-
await Private.renameSelectedItem(model, 'group'
|
|
372
|
-
model === null || model === void 0 ? void 0 : model.renameLayerGroup(groupName, newName);
|
|
373
|
-
});
|
|
366
|
+
await Private.renameSelectedItem(model, 'group');
|
|
374
367
|
},
|
|
375
368
|
});
|
|
376
369
|
commands.addCommand(CommandIDs.removeGroup, {
|
|
@@ -458,13 +451,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
458
451
|
execute: async () => {
|
|
459
452
|
var _a;
|
|
460
453
|
const model = (_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model;
|
|
461
|
-
await Private.renameSelectedItem(model, 'source'
|
|
462
|
-
const source = model === null || model === void 0 ? void 0 : model.getSource(sourceId);
|
|
463
|
-
if (source) {
|
|
464
|
-
source.name = newName;
|
|
465
|
-
model === null || model === void 0 ? void 0 : model.sharedModel.updateSource(sourceId, source);
|
|
466
|
-
}
|
|
467
|
-
});
|
|
454
|
+
await Private.renameSelectedItem(model, 'source');
|
|
468
455
|
},
|
|
469
456
|
});
|
|
470
457
|
commands.addCommand(CommandIDs.removeSource, {
|
|
@@ -568,7 +555,6 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
568
555
|
if (!currentWidget || !completionProviderManager) {
|
|
569
556
|
return;
|
|
570
557
|
}
|
|
571
|
-
console.log('zooming');
|
|
572
558
|
const model = tracker.currentWidget.model;
|
|
573
559
|
const selectedItems = (_a = model.localState) === null || _a === void 0 ? void 0 : _a.selected.value;
|
|
574
560
|
if (!selectedItems) {
|
|
@@ -814,6 +800,24 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
814
800
|
commands.notifyCommandChanged(CommandIDs.showIdentifyPanelTab);
|
|
815
801
|
},
|
|
816
802
|
});
|
|
803
|
+
commands.addCommand(CommandIDs.addMarker, Object.assign({ label: trans.__('Add Marker'), isToggled: () => {
|
|
804
|
+
const current = tracker.currentWidget;
|
|
805
|
+
if (!current) {
|
|
806
|
+
return false;
|
|
807
|
+
}
|
|
808
|
+
return current.model.currentMode === 'marking';
|
|
809
|
+
}, isEnabled: () => {
|
|
810
|
+
// TODO should check if at least one layer exists?
|
|
811
|
+
return true;
|
|
812
|
+
}, execute: args => {
|
|
813
|
+
const current = tracker.currentWidget;
|
|
814
|
+
if (!current) {
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
current.node.classList.toggle(POINT_SELECTION_TOOL_CLASS);
|
|
818
|
+
current.model.toggleMode('marking');
|
|
819
|
+
commands.notifyCommandChanged(CommandIDs.addMarker);
|
|
820
|
+
} }, icons.get(CommandIDs.addMarker)));
|
|
817
821
|
loadKeybindings(commands, keybindings);
|
|
818
822
|
}
|
|
819
823
|
var Private;
|
|
@@ -868,39 +872,11 @@ var Private;
|
|
|
868
872
|
};
|
|
869
873
|
}
|
|
870
874
|
Private.createEntry = createEntry;
|
|
871
|
-
async function getUserInputForRename(text, input, original) {
|
|
872
|
-
const parent = text.parentElement;
|
|
873
|
-
parent.replaceChild(input, text);
|
|
874
|
-
input.value = original;
|
|
875
|
-
input.select();
|
|
876
|
-
input.focus();
|
|
877
|
-
return new Promise(resolve => {
|
|
878
|
-
input.addEventListener('blur', () => {
|
|
879
|
-
parent.replaceChild(text, input);
|
|
880
|
-
resolve(input.value);
|
|
881
|
-
});
|
|
882
|
-
input.addEventListener('keydown', (event) => {
|
|
883
|
-
if (event.key === 'Enter') {
|
|
884
|
-
event.stopPropagation();
|
|
885
|
-
event.preventDefault();
|
|
886
|
-
input.blur();
|
|
887
|
-
}
|
|
888
|
-
else if (event.key === 'Escape') {
|
|
889
|
-
event.stopPropagation();
|
|
890
|
-
event.preventDefault();
|
|
891
|
-
input.value = original;
|
|
892
|
-
input.blur();
|
|
893
|
-
text.focus();
|
|
894
|
-
}
|
|
895
|
-
});
|
|
896
|
-
});
|
|
897
|
-
}
|
|
898
|
-
Private.getUserInputForRename = getUserInputForRename;
|
|
899
875
|
function removeSelectedItems(model, itemTypeToRemove, removeFunction) {
|
|
900
876
|
var _a, _b;
|
|
901
877
|
const selected = (_b = (_a = model === null || model === void 0 ? void 0 : model.localState) === null || _a === void 0 ? void 0 : _a.selected) === null || _b === void 0 ? void 0 : _b.value;
|
|
902
878
|
if (!selected) {
|
|
903
|
-
console.
|
|
879
|
+
console.error('Failed to remove selected item -- nothing selected');
|
|
904
880
|
return;
|
|
905
881
|
}
|
|
906
882
|
for (const selection in selected) {
|
|
@@ -910,10 +886,10 @@ var Private;
|
|
|
910
886
|
}
|
|
911
887
|
}
|
|
912
888
|
Private.removeSelectedItems = removeSelectedItems;
|
|
913
|
-
async function renameSelectedItem(model, itemType
|
|
889
|
+
async function renameSelectedItem(model, itemType) {
|
|
914
890
|
var _a;
|
|
915
891
|
const selectedItems = (_a = model === null || model === void 0 ? void 0 : model.localState) === null || _a === void 0 ? void 0 : _a.selected.value;
|
|
916
|
-
if (!selectedItems) {
|
|
892
|
+
if (!selectedItems || !model) {
|
|
917
893
|
console.error(`No ${itemType} selected`);
|
|
918
894
|
return;
|
|
919
895
|
}
|
|
@@ -928,26 +904,8 @@ var Private;
|
|
|
928
904
|
if (!itemId) {
|
|
929
905
|
return;
|
|
930
906
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
return;
|
|
934
|
-
}
|
|
935
|
-
const node = document.getElementById(nodeId);
|
|
936
|
-
if (!node) {
|
|
937
|
-
console.warn(`Node with ID ${nodeId} not found`);
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
|
-
const edit = document.createElement('input');
|
|
941
|
-
edit.classList.add('jp-gis-left-panel-input');
|
|
942
|
-
const originalName = node.innerText;
|
|
943
|
-
const newName = await Private.getUserInputForRename(node, edit, originalName);
|
|
944
|
-
if (!newName) {
|
|
945
|
-
console.warn('New name cannot be empty');
|
|
946
|
-
return;
|
|
947
|
-
}
|
|
948
|
-
if (newName !== originalName) {
|
|
949
|
-
callback(itemId, newName);
|
|
950
|
-
}
|
|
907
|
+
// Set editing state - component will show inline input
|
|
908
|
+
model.setEditingItem(itemType, itemId);
|
|
951
909
|
}
|
|
952
910
|
Private.renameSelectedItem = renameSelectedItem;
|
|
953
911
|
function executeConsole(tracker) {
|
package/lib/constants.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ProcessingCommandIDs } from '@jupytergis/schema';
|
|
2
2
|
import { redoIcon, undoIcon } from '@jupyterlab/ui-components';
|
|
3
3
|
import * as BaseCommandIDs from './commands/BaseCommandIDs';
|
|
4
|
-
import { bookOpenIcon, clockIcon, geoJSONIcon, infoIcon, moundIcon, rasterIcon, vectorSquareIcon, } from './icons';
|
|
4
|
+
import { bookOpenIcon, clockIcon, geoJSONIcon, infoIcon, moundIcon, rasterIcon, vectorSquareIcon, markerIcon, } from './icons';
|
|
5
5
|
/**
|
|
6
6
|
* The command IDs.
|
|
7
7
|
*/
|
|
@@ -34,6 +34,7 @@ const iconObject = {
|
|
|
34
34
|
[CommandIDs.symbology]: { iconClass: 'fa fa-brush' },
|
|
35
35
|
[CommandIDs.identify]: { icon: infoIcon },
|
|
36
36
|
[CommandIDs.temporalController]: { icon: clockIcon },
|
|
37
|
+
[CommandIDs.addMarker]: { icon: markerIcon },
|
|
37
38
|
};
|
|
38
39
|
/**
|
|
39
40
|
* The registered icons
|
|
@@ -14,7 +14,7 @@ const ProcessingFormWrapper = props => {
|
|
|
14
14
|
(_a = props.formErrorSignalPromise) === null || _a === void 0 ? void 0 : _a.promise,
|
|
15
15
|
]).then(([ok, formChanged]) => {
|
|
16
16
|
okSignal.current = ok;
|
|
17
|
-
formErrorSignal.current = formChanged;
|
|
17
|
+
formErrorSignal.current = formChanged || undefined;
|
|
18
18
|
setReady(true);
|
|
19
19
|
});
|
|
20
20
|
let FormComponent;
|
|
@@ -25,7 +25,7 @@ const ProcessingFormWrapper = props => {
|
|
|
25
25
|
default:
|
|
26
26
|
FormComponent = BaseForm;
|
|
27
27
|
}
|
|
28
|
-
return (ready && (React.createElement(FormComponent, { formContext: props.formContext, filePath: props.model.filePath, model: props.model, ok: okSignal.current,
|
|
28
|
+
return (ready && (React.createElement(FormComponent, { formContext: props.formContext, filePath: props.model.filePath, model: props.model, ok: okSignal.current, sourceData: props.sourceData, schema: props.schema, syncData: props.syncData })));
|
|
29
29
|
};
|
|
30
30
|
/**
|
|
31
31
|
* Dialog for processing operations
|
|
@@ -2,7 +2,8 @@ export interface IColorMap {
|
|
|
2
2
|
name: ColorRampName;
|
|
3
3
|
colors: string[];
|
|
4
4
|
}
|
|
5
|
-
export declare const COLOR_RAMP_NAMES: readonly ["jet", "hot", "cool", "spring", "summer", "autumn", "winter", "bone", "copper", "greys", "YiGnBu", "greens", "YiOrRd", "bluered", "RdBu", "rainbow", "portland", "blackbody", "earth", "electric", "viridis", "inferno", "magma", "plasma", "warm", "bathymetry", "cdom", "chlorophyll", "density", "freesurface-blue", "freesurface-red", "oxygen", "par", "phase", "salinity", "temperature", "turbidity", "velocity-blue", "velocity-green", "ice", "oxy", "matter", "amp", "tempo", "rain", "topo", "balance", "delta", "curl", "diff", "tarn"];
|
|
5
|
+
export declare const COLOR_RAMP_NAMES: readonly ["jet", "hsv", "hot", "cool", "spring", "summer", "autumn", "winter", "bone", "copper", "greys", "YiGnBu", "greens", "YiOrRd", "bluered", "RdBu", "picnic", "rainbow", "portland", "blackbody", "earth", "electric", "viridis", "inferno", "magma", "plasma", "warm", "rainbow-soft", "bathymetry", "cdom", "chlorophyll", "density", "freesurface-blue", "freesurface-red", "oxygen", "par", "phase", "salinity", "temperature", "turbidity", "velocity-blue", "velocity-green", "cubehelix", "ice", "oxy", "matter", "amp", "tempo", "rain", "topo", "balance", "delta", "curl", "diff", "tarn"];
|
|
6
|
+
export declare const COLOR_RAMP_DEFAULTS: Partial<Record<ColorRampName, number>>;
|
|
6
7
|
export type ColorRampName = (typeof COLOR_RAMP_NAMES)[number];
|
|
7
8
|
export declare const getColorMapList: () => IColorMap[];
|
|
8
9
|
/**
|
|
@@ -17,7 +17,7 @@ const { __license__: _ } = rawCmocean, cmocean = __rest(rawCmocean, ["__license_
|
|
|
17
17
|
Object.assign(colorScale, cmocean);
|
|
18
18
|
export const COLOR_RAMP_NAMES = [
|
|
19
19
|
'jet',
|
|
20
|
-
|
|
20
|
+
'hsv',
|
|
21
21
|
'hot',
|
|
22
22
|
'cool',
|
|
23
23
|
'spring',
|
|
@@ -32,7 +32,7 @@ export const COLOR_RAMP_NAMES = [
|
|
|
32
32
|
'YiOrRd',
|
|
33
33
|
'bluered',
|
|
34
34
|
'RdBu',
|
|
35
|
-
|
|
35
|
+
'picnic',
|
|
36
36
|
'rainbow',
|
|
37
37
|
'portland',
|
|
38
38
|
'blackbody',
|
|
@@ -43,7 +43,7 @@ export const COLOR_RAMP_NAMES = [
|
|
|
43
43
|
'magma',
|
|
44
44
|
'plasma',
|
|
45
45
|
'warm',
|
|
46
|
-
|
|
46
|
+
'rainbow-soft',
|
|
47
47
|
'bathymetry',
|
|
48
48
|
'cdom',
|
|
49
49
|
'chlorophyll',
|
|
@@ -58,7 +58,7 @@ export const COLOR_RAMP_NAMES = [
|
|
|
58
58
|
'turbidity',
|
|
59
59
|
'velocity-blue',
|
|
60
60
|
'velocity-green',
|
|
61
|
-
|
|
61
|
+
'cubehelix',
|
|
62
62
|
'ice',
|
|
63
63
|
'oxy',
|
|
64
64
|
'matter',
|
|
@@ -72,6 +72,12 @@ export const COLOR_RAMP_NAMES = [
|
|
|
72
72
|
'diff',
|
|
73
73
|
'tarn',
|
|
74
74
|
];
|
|
75
|
+
export const COLOR_RAMP_DEFAULTS = {
|
|
76
|
+
hsv: 11,
|
|
77
|
+
picnic: 11,
|
|
78
|
+
'rainbow-soft': 11,
|
|
79
|
+
cubehelix: 16,
|
|
80
|
+
};
|
|
75
81
|
export const getColorMapList = () => {
|
|
76
82
|
const colorMapList = [];
|
|
77
83
|
COLOR_RAMP_NAMES.forEach(name => {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ColorRampControls
|
|
3
|
+
*
|
|
4
|
+
* This component provides the main UI controls for classifying raster layers
|
|
5
|
+
* using different color ramps and classification modes.
|
|
6
|
+
*
|
|
7
|
+
* Allows users to:
|
|
8
|
+
* - Select a color ramp (`ColorRampSelector`)
|
|
9
|
+
* - Choose classification mode and number of classes (`ModeSelectRow`)
|
|
10
|
+
* - Run classification via `classifyFunc`, with loading state (`LoadingIcon`)
|
|
11
|
+
*
|
|
12
|
+
* Props:
|
|
13
|
+
* - `modeOptions`: Available classification modes.
|
|
14
|
+
* - `layerParams`: Layer symbology state.
|
|
15
|
+
* - `classifyFunc`: Callback for classification.
|
|
16
|
+
* - `showModeRow`: Toggle for mode selector.
|
|
17
|
+
* - `showRampSelector`: Toggle for ramp selector.
|
|
18
|
+
*/
|
|
19
|
+
import { IDict } from '@jupytergis/schema';
|
|
20
|
+
import React from 'react';
|
|
21
|
+
import { ClassificationMode } from "../../../../types";
|
|
22
|
+
import { ColorRampName } from '../../colorRampUtils';
|
|
23
|
+
interface IColorRampControlsProps {
|
|
24
|
+
modeOptions: ClassificationMode[];
|
|
25
|
+
layerParams: IDict;
|
|
26
|
+
classifyFunc: (selectedMode: ClassificationMode, numberOfShades: number, selectedRamp: ColorRampName, setIsLoading: (isLoading: boolean) => void) => void;
|
|
27
|
+
showModeRow: boolean;
|
|
28
|
+
showRampSelector: boolean;
|
|
29
|
+
}
|
|
30
|
+
export type ColorRampControlsOptions = {
|
|
31
|
+
selectedRamp: ColorRampName;
|
|
32
|
+
numberOfShades: number;
|
|
33
|
+
selectedMode: ClassificationMode;
|
|
34
|
+
};
|
|
35
|
+
declare const ColorRampControls: React.FC<IColorRampControlsProps>;
|
|
36
|
+
export default ColorRampControls;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ColorRampControls
|
|
3
|
+
*
|
|
4
|
+
* This component provides the main UI controls for classifying raster layers
|
|
5
|
+
* using different color ramps and classification modes.
|
|
6
|
+
*
|
|
7
|
+
* Allows users to:
|
|
8
|
+
* - Select a color ramp (`ColorRampSelector`)
|
|
9
|
+
* - Choose classification mode and number of classes (`ModeSelectRow`)
|
|
10
|
+
* - Run classification via `classifyFunc`, with loading state (`LoadingIcon`)
|
|
11
|
+
*
|
|
12
|
+
* Props:
|
|
13
|
+
* - `modeOptions`: Available classification modes.
|
|
14
|
+
* - `layerParams`: Layer symbology state.
|
|
15
|
+
* - `classifyFunc`: Callback for classification.
|
|
16
|
+
* - `showModeRow`: Toggle for mode selector.
|
|
17
|
+
* - `showRampSelector`: Toggle for ramp selector.
|
|
18
|
+
*/
|
|
19
|
+
import { Button } from '@jupyterlab/ui-components';
|
|
20
|
+
import React, { useEffect, useState } from 'react';
|
|
21
|
+
import { LoadingIcon } from "../../../../shared/components/loading";
|
|
22
|
+
import ColorRampSelector from './ColorRampSelector';
|
|
23
|
+
import ModeSelectRow from './ModeSelectRow';
|
|
24
|
+
import { COLOR_RAMP_DEFAULTS } from '../../colorRampUtils';
|
|
25
|
+
const isValidNumberOfShades = (value) => !isNaN(value) && value > 0;
|
|
26
|
+
const ColorRampControls = ({ layerParams, modeOptions, classifyFunc, showModeRow, showRampSelector, }) => {
|
|
27
|
+
const [selectedRamp, setSelectedRamp] = useState('viridis');
|
|
28
|
+
const [selectedMode, setSelectedMode] = useState('equal interval');
|
|
29
|
+
const [numberOfShades, setNumberOfShades] = useState(9);
|
|
30
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
31
|
+
const [warning, setWarning] = useState(null);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (layerParams.symbologyState) {
|
|
34
|
+
populateOptions();
|
|
35
|
+
}
|
|
36
|
+
}, [
|
|
37
|
+
layerParams.symbologyState.nClasses,
|
|
38
|
+
layerParams.symbologyState.mode,
|
|
39
|
+
layerParams.symbologyState.colorRamp,
|
|
40
|
+
]);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
var _a;
|
|
43
|
+
if (!selectedRamp) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const defaultClasses = (_a = COLOR_RAMP_DEFAULTS[selectedRamp]) !== null && _a !== void 0 ? _a : 9;
|
|
47
|
+
setNumberOfShades(defaultClasses);
|
|
48
|
+
setWarning(null);
|
|
49
|
+
}, [selectedRamp]);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!selectedRamp || !numberOfShades) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const minRequired = COLOR_RAMP_DEFAULTS[selectedRamp];
|
|
55
|
+
const shades = numberOfShades;
|
|
56
|
+
const rampLabel = selectedRamp
|
|
57
|
+
.split('-')
|
|
58
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
59
|
+
.join('-');
|
|
60
|
+
if (minRequired && shades < minRequired) {
|
|
61
|
+
setWarning(`${rampLabel} requires at least ${minRequired} classes (got ${shades})`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
setWarning(null);
|
|
65
|
+
}
|
|
66
|
+
}, [selectedRamp, numberOfShades]);
|
|
67
|
+
const populateOptions = () => {
|
|
68
|
+
var _a, _b, _c;
|
|
69
|
+
const { nClasses, mode, colorRamp } = (_a = layerParams.symbologyState) !== null && _a !== void 0 ? _a : {};
|
|
70
|
+
setNumberOfShades(Number(nClasses !== null && nClasses !== void 0 ? nClasses : 9));
|
|
71
|
+
setSelectedMode((_b = mode) !== null && _b !== void 0 ? _b : 'equal interval');
|
|
72
|
+
setSelectedRamp((_c = colorRamp) !== null && _c !== void 0 ? _c : 'viridis');
|
|
73
|
+
};
|
|
74
|
+
return (React.createElement("div", { className: "jp-gis-color-ramp-container" },
|
|
75
|
+
showRampSelector && (React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
76
|
+
React.createElement("label", { htmlFor: "color-ramp-select" }, "Color Ramp:"),
|
|
77
|
+
React.createElement(ColorRampSelector, { selectedRamp: selectedRamp, setSelected: setSelectedRamp }))),
|
|
78
|
+
showModeRow && (React.createElement(ModeSelectRow, { modeOptions: modeOptions, numberOfShades: numberOfShades, setNumberOfShades: setNumberOfShades, selectedMode: selectedMode, setSelectedMode: setSelectedMode })),
|
|
79
|
+
warning && (React.createElement("div", { className: "jp-gis-warning", style: { color: 'orange', marginTop: 4 } }, warning)),
|
|
80
|
+
isLoading ? (React.createElement(LoadingIcon, null)) : (React.createElement(Button, { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", disabled: !isValidNumberOfShades(numberOfShades) || !selectedMode || !!warning, onClick: () => classifyFunc(selectedMode, numberOfShades, selectedRamp, setIsLoading) }, "Classify"))));
|
|
81
|
+
};
|
|
82
|
+
export default ColorRampControls;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ColorRampSelector
|
|
3
|
+
*
|
|
4
|
+
* Dropdown component for selecting a color ramp.
|
|
5
|
+
* - Displays the currently selected ramp as a preview on a canvas.
|
|
6
|
+
* - Expands to show a list of available ramps (`ColorRampSelectorEntry`).
|
|
7
|
+
* - Updates the preview and notifies parent via `setSelected` when a ramp is chosen.
|
|
8
|
+
*
|
|
9
|
+
* Props:
|
|
10
|
+
* - `selectedRamp`: Name of the currently selected color ramp.
|
|
11
|
+
* - `setSelected`: Callback fired with the new ramp when selected.
|
|
12
|
+
*/
|
|
13
|
+
import React from 'react';
|
|
14
|
+
import { ColorRampName } from "../../colorRampUtils";
|
|
15
|
+
interface IColorRampSelectorProps {
|
|
16
|
+
selectedRamp: ColorRampName;
|
|
17
|
+
setSelected: (item: any) => void;
|
|
18
|
+
}
|
|
19
|
+
declare const ColorRampSelector: React.FC<IColorRampSelectorProps>;
|
|
20
|
+
export default ColorRampSelector;
|
|
@@ -1,11 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ColorRampSelector
|
|
3
|
+
*
|
|
4
|
+
* Dropdown component for selecting a color ramp.
|
|
5
|
+
* - Displays the currently selected ramp as a preview on a canvas.
|
|
6
|
+
* - Expands to show a list of available ramps (`ColorRampSelectorEntry`).
|
|
7
|
+
* - Updates the preview and notifies parent via `setSelected` when a ramp is chosen.
|
|
8
|
+
*
|
|
9
|
+
* Props:
|
|
10
|
+
* - `selectedRamp`: Name of the currently selected color ramp.
|
|
11
|
+
* - `setSelected`: Callback fired with the new ramp when selected.
|
|
12
|
+
*/
|
|
1
13
|
import { Button } from '@jupyterlab/ui-components';
|
|
2
14
|
import React, { useEffect, useRef, useState } from 'react';
|
|
3
|
-
import { useColorMapList } from "../../colorRampUtils";
|
|
4
|
-
import
|
|
5
|
-
const
|
|
15
|
+
import { useColorMapList, } from "../../colorRampUtils";
|
|
16
|
+
import ColorRampSelectorEntry from './ColorRampSelectorEntry';
|
|
17
|
+
const ColorRampSelector = ({ selectedRamp, setSelected, }) => {
|
|
6
18
|
const containerRef = useRef(null);
|
|
7
19
|
const [isOpen, setIsOpen] = useState(false);
|
|
8
20
|
const [colorMaps, setColorMaps] = useState([]);
|
|
21
|
+
const canvasWidth = 512;
|
|
22
|
+
const canvasHeight = 30;
|
|
9
23
|
useColorMapList(setColorMaps);
|
|
10
24
|
useEffect(() => {
|
|
11
25
|
if (colorMaps.length > 0) {
|
|
@@ -40,11 +54,13 @@ const CanvasSelectComponent = ({ selectedRamp, setSelected, }) => {
|
|
|
40
54
|
return;
|
|
41
55
|
}
|
|
42
56
|
const ramp = colorMaps.filter(c => c.name === rampName);
|
|
57
|
+
canvas.width = canvasWidth;
|
|
58
|
+
canvas.height = canvasHeight;
|
|
43
59
|
for (let i = 0; i <= 255; i++) {
|
|
44
60
|
ctx.beginPath();
|
|
45
61
|
const color = ramp[0].colors[i];
|
|
46
62
|
ctx.fillStyle = color;
|
|
47
|
-
ctx.fillRect(i * 2, 0, 2,
|
|
63
|
+
ctx.fillRect(i * 2, 0, 2, canvasHeight);
|
|
48
64
|
}
|
|
49
65
|
canvas.style.visibility = 'initial';
|
|
50
66
|
};
|
|
@@ -56,7 +72,9 @@ const CanvasSelectComponent = ({ selectedRamp, setSelected, }) => {
|
|
|
56
72
|
}, []);
|
|
57
73
|
return (React.createElement("div", { ref: containerRef, className: "jp-gis-canvas-button-wrapper" },
|
|
58
74
|
React.createElement(Button, { id: "jp-gis-canvas-button", onClick: toggleDropdown, className: "jp-Dialog-button jp-gis-canvas-button" },
|
|
59
|
-
React.createElement("
|
|
60
|
-
|
|
75
|
+
React.createElement("div", { className: "jp-gis-color-ramp-entry jp-gis-selected-entry" },
|
|
76
|
+
React.createElement("span", { className: "jp-gis-color-label" }, selectedRamp),
|
|
77
|
+
React.createElement("canvas", { id: "cv", className: "jp-gis-color-canvas-display", width: canvasWidth, height: canvasHeight }))),
|
|
78
|
+
React.createElement("div", { className: `jp-gis-color-ramp-dropdown ${isOpen ? 'jp-gis-open' : ''}` }, colorMaps.map((item, index) => (React.createElement(ColorRampSelectorEntry, { index: index, colorMap: item, onClick: selectItem }))))));
|
|
61
79
|
};
|
|
62
|
-
export default
|
|
80
|
+
export default ColorRampSelector;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ColorRampSelectorEntry
|
|
3
|
+
*
|
|
4
|
+
* Represents a single selectable color ramp option in the `ColorRampSelector`.
|
|
5
|
+
* Renders a preview ColorRamp on a canvas and triggers `onClick` when selected.
|
|
6
|
+
*
|
|
7
|
+
* Props:
|
|
8
|
+
* - `index`: Unique index for canvas ID.
|
|
9
|
+
* - `colorMap`: Ramp definition including name and colors.
|
|
10
|
+
* - `onClick`: Callback fired with the ramp name when clicked.
|
|
11
|
+
*/
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { IColorMap } from "../../colorRampUtils";
|
|
14
|
+
interface IColorRampSelectorEntryProps {
|
|
15
|
+
index: number;
|
|
16
|
+
colorMap: IColorMap;
|
|
17
|
+
onClick: (item: any) => void;
|
|
18
|
+
}
|
|
19
|
+
declare const ColorRampSelectorEntry: React.FC<IColorRampSelectorEntryProps>;
|
|
20
|
+
export default ColorRampSelectorEntry;
|
package/lib/dialogs/symbology/components/color_ramp/{ColorRampEntry.js → ColorRampSelectorEntry.js}
RENAMED
|
@@ -1,11 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ColorRampSelectorEntry
|
|
3
|
+
*
|
|
4
|
+
* Represents a single selectable color ramp option in the `ColorRampSelector`.
|
|
5
|
+
* Renders a preview ColorRamp on a canvas and triggers `onClick` when selected.
|
|
6
|
+
*
|
|
7
|
+
* Props:
|
|
8
|
+
* - `index`: Unique index for canvas ID.
|
|
9
|
+
* - `colorMap`: Ramp definition including name and colors.
|
|
10
|
+
* - `onClick`: Callback fired with the ramp name when clicked.
|
|
11
|
+
*/
|
|
1
12
|
import React, { useEffect } from 'react';
|
|
2
|
-
const
|
|
13
|
+
const ColorRampSelectorEntry = ({ index, colorMap, onClick, }) => {
|
|
14
|
+
const canvasWidth = 512;
|
|
3
15
|
const canvasHeight = 30;
|
|
4
16
|
useEffect(() => {
|
|
5
17
|
const canvas = document.getElementById(`cv-${index}`);
|
|
6
18
|
if (!canvas) {
|
|
7
19
|
return;
|
|
8
20
|
}
|
|
21
|
+
canvas.width = canvasWidth;
|
|
22
|
+
canvas.height = canvasHeight;
|
|
9
23
|
const ctx = canvas.getContext('2d');
|
|
10
24
|
if (!ctx) {
|
|
11
25
|
return;
|
|
@@ -19,6 +33,6 @@ const ColorRampEntry = ({ index, colorMap, onClick, }) => {
|
|
|
19
33
|
}, []);
|
|
20
34
|
return (React.createElement("div", { key: colorMap.name, onClick: () => onClick(colorMap.name), className: "jp-gis-color-ramp-entry" },
|
|
21
35
|
React.createElement("span", { className: "jp-gis-color-label" }, colorMap.name),
|
|
22
|
-
React.createElement("canvas", { id: `cv-${index}`, height: canvasHeight, className: "jp-gis-color-canvas" })));
|
|
36
|
+
React.createElement("canvas", { id: `cv-${index}`, width: canvasWidth, height: canvasHeight, className: "jp-gis-color-canvas" })));
|
|
23
37
|
};
|
|
24
|
-
export default
|
|
38
|
+
export default ColorRampSelectorEntry;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { ClassificationMode } from "../../../../types";
|
|
2
3
|
interface IModeSelectRowProps {
|
|
3
|
-
numberOfShades:
|
|
4
|
-
setNumberOfShades:
|
|
5
|
-
selectedMode:
|
|
6
|
-
setSelectedMode:
|
|
7
|
-
modeOptions:
|
|
4
|
+
numberOfShades: number;
|
|
5
|
+
setNumberOfShades: React.Dispatch<React.SetStateAction<number>>;
|
|
6
|
+
selectedMode: ClassificationMode;
|
|
7
|
+
setSelectedMode: React.Dispatch<React.SetStateAction<ClassificationMode>>;
|
|
8
|
+
modeOptions: ClassificationMode[];
|
|
8
9
|
}
|
|
9
10
|
declare const ModeSelectRow: React.FC<IModeSelectRowProps>;
|
|
10
11
|
export default ModeSelectRow;
|
|
@@ -3,10 +3,15 @@ const ModeSelectRow = ({ numberOfShades, setNumberOfShades, selectedMode, setSel
|
|
|
3
3
|
return (React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
4
4
|
React.createElement("div", { className: "jp-gis-color-ramp-div" },
|
|
5
5
|
React.createElement("label", { htmlFor: "class-number-input" }, "Classes:"),
|
|
6
|
-
React.createElement("input", { className: "jp-mod-styled", name: "class-number-input", type: "number", value: selectedMode === 'continuous' ? 52 : numberOfShades, onChange: event =>
|
|
6
|
+
React.createElement("input", { className: "jp-mod-styled", name: "class-number-input", type: "number", value: selectedMode === 'continuous' ? 52 : numberOfShades, onChange: event => {
|
|
7
|
+
const value = Number(event.target.value);
|
|
8
|
+
if (!isNaN(value) && value > 0) {
|
|
9
|
+
setNumberOfShades(value);
|
|
10
|
+
}
|
|
11
|
+
}, disabled: selectedMode === 'continuous' })),
|
|
7
12
|
React.createElement("div", { className: "jp-gis-color-ramp-div" },
|
|
8
13
|
React.createElement("label", { htmlFor: "mode-select" }, "Mode:"),
|
|
9
14
|
React.createElement("div", { className: "jp-select-wrapper" },
|
|
10
|
-
React.createElement("select", { name: "mode-select", id: "mode-select", className: "jp-mod-styled", value: selectedMode, onChange: event => setSelectedMode(event.target.value) }, modeOptions.map(mode => (React.createElement("option", { key: mode, value: mode
|
|
15
|
+
React.createElement("select", { name: "mode-select", id: "mode-select", className: "jp-mod-styled", value: selectedMode, onChange: event => setSelectedMode(event.target.value) }, modeOptions.map(mode => (React.createElement("option", { key: mode, value: mode }, mode))))))));
|
|
11
16
|
};
|
|
12
17
|
export default ModeSelectRow;
|
|
@@ -2,13 +2,19 @@
|
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
import { loadFile } from "../../../tools";
|
|
4
4
|
async function getGeoJsonProperties({ source, model, }) {
|
|
5
|
-
var _a;
|
|
6
5
|
const result = {};
|
|
7
|
-
const data = await
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
const data = await (async () => {
|
|
7
|
+
if (source.parameters.path) {
|
|
8
|
+
return await loadFile({
|
|
9
|
+
filepath: source.parameters.path,
|
|
10
|
+
type: 'GeoJSONSource',
|
|
11
|
+
model,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
else if (source.parameters.data) {
|
|
15
|
+
return source.parameters.data;
|
|
16
|
+
}
|
|
17
|
+
})();
|
|
12
18
|
if (!data) {
|
|
13
19
|
throw new Error('Failed to read GeoJSON data');
|
|
14
20
|
}
|