@jupytergis/base 0.8.0 → 0.9.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/commands/BaseCommandIDs.d.ts +8 -0
- package/lib/commands/BaseCommandIDs.js +11 -0
- package/lib/commands/index.js +174 -6
- package/lib/dialogs/symbology/hooks/useGetSymbology.d.ts +17 -0
- package/lib/dialogs/symbology/hooks/useGetSymbology.js +65 -0
- package/lib/dialogs/symbology/symbologyUtils.d.ts +1 -1
- package/lib/dialogs/symbology/symbologyUtils.js +4 -1
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +7 -1
- package/lib/dialogs/symbology/vector_layer/types/Graduated.js +7 -1
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +13 -2
- package/lib/gdal.js +0 -2
- package/lib/mainview/mainView.js +48 -12
- package/lib/panelview/components/identify-panel/IdentifyPanel.js +12 -4
- package/lib/panelview/components/layers.js +19 -4
- package/lib/panelview/components/legendItem.d.ts +6 -0
- package/lib/panelview/components/legendItem.js +165 -0
- package/lib/panelview/leftpanel.js +36 -23
- package/lib/panelview/rightpanel.js +55 -23
- package/lib/tools.d.ts +5 -1
- package/lib/tools.js +4 -0
- package/package.json +3 -3
- package/style/base.css +20 -0
- package/style/leftPanel.css +1 -1
|
@@ -31,3 +31,11 @@ export declare const selectCompleter = "jupytergis:selectConsoleCompleter";
|
|
|
31
31
|
export declare const addAnnotation = "jupytergis:addAnnotation";
|
|
32
32
|
export declare const zoomToLayer = "jupytergis:zoomToLayer";
|
|
33
33
|
export declare const downloadGeoJSON = "jupytergis:downloadGeoJSON";
|
|
34
|
+
export declare const toggleLeftPanel = "jupytergis:toggleLeftPanel";
|
|
35
|
+
export declare const toggleRightPanel = "jupytergis:toggleRightPanel";
|
|
36
|
+
export declare const showLayersTab = "jupytergis:showLayersTab";
|
|
37
|
+
export declare const showStacBrowserTab = "jupytergis:showStacBrowserTab";
|
|
38
|
+
export declare const showFiltersTab = "jupytergis:showFiltersTab";
|
|
39
|
+
export declare const showObjectPropertiesTab = "jupytergis:showObjectPropertiesTab";
|
|
40
|
+
export declare const showAnnotationsTab = "jupytergis:showAnnotationsTab";
|
|
41
|
+
export declare const showIdentifyPanelTab = "jupytergis:showIdentifyPanelTab";
|
|
@@ -42,3 +42,14 @@ export const selectCompleter = 'jupytergis:selectConsoleCompleter';
|
|
|
42
42
|
export const addAnnotation = 'jupytergis:addAnnotation';
|
|
43
43
|
export const zoomToLayer = 'jupytergis:zoomToLayer';
|
|
44
44
|
export const downloadGeoJSON = 'jupytergis:downloadGeoJSON';
|
|
45
|
+
// Panel toggles
|
|
46
|
+
export const toggleLeftPanel = 'jupytergis:toggleLeftPanel';
|
|
47
|
+
export const toggleRightPanel = 'jupytergis:toggleRightPanel';
|
|
48
|
+
// Left panel tabs
|
|
49
|
+
export const showLayersTab = 'jupytergis:showLayersTab';
|
|
50
|
+
export const showStacBrowserTab = 'jupytergis:showStacBrowserTab';
|
|
51
|
+
export const showFiltersTab = 'jupytergis:showFiltersTab';
|
|
52
|
+
// Right panel tabs
|
|
53
|
+
export const showObjectPropertiesTab = 'jupytergis:showObjectPropertiesTab';
|
|
54
|
+
export const showAnnotationsTab = 'jupytergis:showAnnotationsTab';
|
|
55
|
+
export const showIdentifyPanelTab = 'jupytergis:showIdentifyPanelTab';
|
package/lib/commands/index.js
CHANGED
|
@@ -85,20 +85,29 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
85
85
|
'VectorLayer',
|
|
86
86
|
'ShapefileLayer',
|
|
87
87
|
'WebGlLayer',
|
|
88
|
+
'VectorTileLayer',
|
|
88
89
|
].includes(selectedLayer.type);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
current.model.isIdentifying = false;
|
|
90
|
+
if (current.model.currentMode === 'identifying' && !canIdentify) {
|
|
91
|
+
current.model.currentMode = 'panning';
|
|
92
92
|
current.node.classList.remove('jGIS-identify-tool');
|
|
93
93
|
return false;
|
|
94
94
|
}
|
|
95
|
-
return
|
|
95
|
+
return current.model.currentMode === 'identifying';
|
|
96
96
|
}, isEnabled: () => {
|
|
97
|
+
var _a;
|
|
98
|
+
if ((_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model.jgisSettings.identifyDisabled) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
97
101
|
const selectedLayer = getSingleSelectedLayer(tracker);
|
|
98
102
|
if (!selectedLayer) {
|
|
99
103
|
return false;
|
|
100
104
|
}
|
|
101
|
-
return [
|
|
105
|
+
return [
|
|
106
|
+
'VectorLayer',
|
|
107
|
+
'ShapefileLayer',
|
|
108
|
+
'WebGlLayer',
|
|
109
|
+
'VectorTileLayer',
|
|
110
|
+
].includes(selectedLayer.type);
|
|
102
111
|
}, execute: args => {
|
|
103
112
|
const current = tracker.currentWidget;
|
|
104
113
|
if (!current) {
|
|
@@ -108,7 +117,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
108
117
|
if (luminoEvent) {
|
|
109
118
|
const keysPressed = luminoEvent.keys;
|
|
110
119
|
if (keysPressed === null || keysPressed === void 0 ? void 0 : keysPressed.includes('Escape')) {
|
|
111
|
-
current.model.
|
|
120
|
+
current.model.currentMode = 'panning';
|
|
112
121
|
current.node.classList.remove('jGIS-identify-tool');
|
|
113
122
|
commands.notifyCommandChanged(CommandIDs.identify);
|
|
114
123
|
return;
|
|
@@ -646,6 +655,165 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
646
655
|
},
|
|
647
656
|
icon: targetWithCenterIcon,
|
|
648
657
|
});
|
|
658
|
+
// Panel visibility commands
|
|
659
|
+
commands.addCommand(CommandIDs.toggleLeftPanel, {
|
|
660
|
+
label: trans.__('Toggle Left Panel'),
|
|
661
|
+
isEnabled: () => Boolean(tracker.currentWidget),
|
|
662
|
+
isToggled: () => {
|
|
663
|
+
const current = tracker.currentWidget;
|
|
664
|
+
return current ? !current.model.jgisSettings.leftPanelDisabled : false;
|
|
665
|
+
},
|
|
666
|
+
execute: async () => {
|
|
667
|
+
var _a, _b, _c;
|
|
668
|
+
const current = tracker.currentWidget;
|
|
669
|
+
if (!current) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
try {
|
|
673
|
+
const settings = await current.model.getSettings();
|
|
674
|
+
const currentValue = (_c = (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.composite) === null || _a === void 0 ? void 0 : _a.leftPanelDisabled) !== null && _b !== void 0 ? _b : current.model.jgisSettings.leftPanelDisabled) !== null && _c !== void 0 ? _c : false;
|
|
675
|
+
await (settings === null || settings === void 0 ? void 0 : settings.set('leftPanelDisabled', !currentValue));
|
|
676
|
+
commands.notifyCommandChanged(CommandIDs.toggleLeftPanel);
|
|
677
|
+
}
|
|
678
|
+
catch (err) {
|
|
679
|
+
console.error('Failed to toggle Left Panel:', err);
|
|
680
|
+
}
|
|
681
|
+
},
|
|
682
|
+
});
|
|
683
|
+
commands.addCommand(CommandIDs.toggleRightPanel, {
|
|
684
|
+
label: trans.__('Toggle Right Panel'),
|
|
685
|
+
isEnabled: () => Boolean(tracker.currentWidget),
|
|
686
|
+
isToggled: () => {
|
|
687
|
+
const current = tracker.currentWidget;
|
|
688
|
+
return current ? !current.model.jgisSettings.rightPanelDisabled : false;
|
|
689
|
+
},
|
|
690
|
+
execute: async () => {
|
|
691
|
+
var _a, _b, _c;
|
|
692
|
+
const current = tracker.currentWidget;
|
|
693
|
+
if (!current) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
try {
|
|
697
|
+
const settings = await current.model.getSettings();
|
|
698
|
+
const currentValue = (_c = (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.composite) === null || _a === void 0 ? void 0 : _a.rightPanelDisabled) !== null && _b !== void 0 ? _b : current.model.jgisSettings.rightPanelDisabled) !== null && _c !== void 0 ? _c : false;
|
|
699
|
+
await (settings === null || settings === void 0 ? void 0 : settings.set('rightPanelDisabled', !currentValue));
|
|
700
|
+
commands.notifyCommandChanged(CommandIDs.toggleRightPanel);
|
|
701
|
+
}
|
|
702
|
+
catch (err) {
|
|
703
|
+
console.error('Failed to toggle Right Panel:', err);
|
|
704
|
+
}
|
|
705
|
+
},
|
|
706
|
+
});
|
|
707
|
+
// Left panel tabs
|
|
708
|
+
commands.addCommand(CommandIDs.showLayersTab, {
|
|
709
|
+
label: trans.__('Show Layers Tab'),
|
|
710
|
+
isEnabled: () => Boolean(tracker.currentWidget),
|
|
711
|
+
isToggled: () => tracker.currentWidget
|
|
712
|
+
? !tracker.currentWidget.model.jgisSettings.layersDisabled
|
|
713
|
+
: false,
|
|
714
|
+
execute: async () => {
|
|
715
|
+
var _a, _b, _c;
|
|
716
|
+
const current = tracker.currentWidget;
|
|
717
|
+
if (!current) {
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
const settings = await current.model.getSettings();
|
|
721
|
+
const currentValue = (_c = (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.composite) === null || _a === void 0 ? void 0 : _a.layersDisabled) !== null && _b !== void 0 ? _b : current.model.jgisSettings.layersDisabled) !== null && _c !== void 0 ? _c : false;
|
|
722
|
+
await (settings === null || settings === void 0 ? void 0 : settings.set('layersDisabled', !currentValue));
|
|
723
|
+
commands.notifyCommandChanged(CommandIDs.showLayersTab);
|
|
724
|
+
},
|
|
725
|
+
});
|
|
726
|
+
commands.addCommand(CommandIDs.showStacBrowserTab, {
|
|
727
|
+
label: trans.__('Show STAC Browser Tab'),
|
|
728
|
+
isEnabled: () => Boolean(tracker.currentWidget),
|
|
729
|
+
isToggled: () => tracker.currentWidget
|
|
730
|
+
? !tracker.currentWidget.model.jgisSettings.stacBrowserDisabled
|
|
731
|
+
: false,
|
|
732
|
+
execute: async () => {
|
|
733
|
+
var _a, _b, _c;
|
|
734
|
+
const current = tracker.currentWidget;
|
|
735
|
+
if (!current) {
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
const settings = await current.model.getSettings();
|
|
739
|
+
const currentValue = (_c = (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.composite) === null || _a === void 0 ? void 0 : _a.stacBrowserDisabled) !== null && _b !== void 0 ? _b : current.model.jgisSettings.stacBrowserDisabled) !== null && _c !== void 0 ? _c : false;
|
|
740
|
+
await (settings === null || settings === void 0 ? void 0 : settings.set('stacBrowserDisabled', !currentValue));
|
|
741
|
+
commands.notifyCommandChanged(CommandIDs.showStacBrowserTab);
|
|
742
|
+
},
|
|
743
|
+
});
|
|
744
|
+
commands.addCommand(CommandIDs.showFiltersTab, {
|
|
745
|
+
label: trans.__('Show Filters Tab'),
|
|
746
|
+
isEnabled: () => Boolean(tracker.currentWidget),
|
|
747
|
+
isToggled: () => tracker.currentWidget
|
|
748
|
+
? !tracker.currentWidget.model.jgisSettings.filtersDisabled
|
|
749
|
+
: false,
|
|
750
|
+
execute: async () => {
|
|
751
|
+
var _a, _b, _c;
|
|
752
|
+
const current = tracker.currentWidget;
|
|
753
|
+
if (!current) {
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
const settings = await current.model.getSettings();
|
|
757
|
+
const currentValue = (_c = (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.composite) === null || _a === void 0 ? void 0 : _a.filtersDisabled) !== null && _b !== void 0 ? _b : current.model.jgisSettings.filtersDisabled) !== null && _c !== void 0 ? _c : false;
|
|
758
|
+
await (settings === null || settings === void 0 ? void 0 : settings.set('filtersDisabled', !currentValue));
|
|
759
|
+
commands.notifyCommandChanged(CommandIDs.showFiltersTab);
|
|
760
|
+
},
|
|
761
|
+
});
|
|
762
|
+
// Right panel tabs
|
|
763
|
+
commands.addCommand(CommandIDs.showObjectPropertiesTab, {
|
|
764
|
+
label: trans.__('Show Object Properties Tab'),
|
|
765
|
+
isEnabled: () => Boolean(tracker.currentWidget),
|
|
766
|
+
isToggled: () => tracker.currentWidget
|
|
767
|
+
? !tracker.currentWidget.model.jgisSettings.objectPropertiesDisabled
|
|
768
|
+
: false,
|
|
769
|
+
execute: async () => {
|
|
770
|
+
var _a, _b, _c;
|
|
771
|
+
const current = tracker.currentWidget;
|
|
772
|
+
if (!current) {
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
const settings = await current.model.getSettings();
|
|
776
|
+
const currentValue = (_c = (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.composite) === null || _a === void 0 ? void 0 : _a.objectPropertiesDisabled) !== null && _b !== void 0 ? _b : current.model.jgisSettings.objectPropertiesDisabled) !== null && _c !== void 0 ? _c : false;
|
|
777
|
+
await (settings === null || settings === void 0 ? void 0 : settings.set('objectPropertiesDisabled', !currentValue));
|
|
778
|
+
commands.notifyCommandChanged(CommandIDs.showObjectPropertiesTab);
|
|
779
|
+
},
|
|
780
|
+
});
|
|
781
|
+
commands.addCommand(CommandIDs.showAnnotationsTab, {
|
|
782
|
+
label: trans.__('Show Annotations Tab'),
|
|
783
|
+
isEnabled: () => Boolean(tracker.currentWidget),
|
|
784
|
+
isToggled: () => tracker.currentWidget
|
|
785
|
+
? !tracker.currentWidget.model.jgisSettings.annotationsDisabled
|
|
786
|
+
: false,
|
|
787
|
+
execute: async () => {
|
|
788
|
+
var _a, _b, _c;
|
|
789
|
+
const current = tracker.currentWidget;
|
|
790
|
+
if (!current) {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
const settings = await current.model.getSettings();
|
|
794
|
+
const currentValue = (_c = (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.composite) === null || _a === void 0 ? void 0 : _a.annotationsDisabled) !== null && _b !== void 0 ? _b : current.model.jgisSettings.annotationsDisabled) !== null && _c !== void 0 ? _c : false;
|
|
795
|
+
await (settings === null || settings === void 0 ? void 0 : settings.set('annotationsDisabled', !currentValue));
|
|
796
|
+
commands.notifyCommandChanged(CommandIDs.showAnnotationsTab);
|
|
797
|
+
},
|
|
798
|
+
});
|
|
799
|
+
commands.addCommand(CommandIDs.showIdentifyPanelTab, {
|
|
800
|
+
label: trans.__('Show Identify Panel Tab'),
|
|
801
|
+
isEnabled: () => Boolean(tracker.currentWidget),
|
|
802
|
+
isToggled: () => tracker.currentWidget
|
|
803
|
+
? !tracker.currentWidget.model.jgisSettings.identifyDisabled
|
|
804
|
+
: false,
|
|
805
|
+
execute: async () => {
|
|
806
|
+
var _a, _b, _c;
|
|
807
|
+
const current = tracker.currentWidget;
|
|
808
|
+
if (!current) {
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
const settings = await current.model.getSettings();
|
|
812
|
+
const currentValue = (_c = (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.composite) === null || _a === void 0 ? void 0 : _a.identifyDisabled) !== null && _b !== void 0 ? _b : current.model.jgisSettings.identifyDisabled) !== null && _c !== void 0 ? _c : false;
|
|
813
|
+
await (settings === null || settings === void 0 ? void 0 : settings.set('identifyDisabled', !currentValue));
|
|
814
|
+
commands.notifyCommandChanged(CommandIDs.showIdentifyPanelTab);
|
|
815
|
+
},
|
|
816
|
+
});
|
|
649
817
|
loadKeybindings(commands, keybindings);
|
|
650
818
|
}
|
|
651
819
|
var Private;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { IJupyterGISModel } from '@jupytergis/schema';
|
|
2
|
+
interface IUseGetSymbologyProps {
|
|
3
|
+
layerId?: string;
|
|
4
|
+
model: IJupyterGISModel;
|
|
5
|
+
}
|
|
6
|
+
interface IUseGetSymbologyResult {
|
|
7
|
+
symbology: Record<string, any> | null;
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
error?: Error;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Extracts symbology information (paint/layout + symbologyState)
|
|
13
|
+
* for a given layer from the JupyterGIS model.
|
|
14
|
+
* Keeps symbology updated when the layer changes.
|
|
15
|
+
*/
|
|
16
|
+
export declare const useGetSymbology: ({ layerId, model, }: IUseGetSymbologyProps) => IUseGetSymbologyResult;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { useEffect, useState } from 'react';
|
|
13
|
+
/**
|
|
14
|
+
* Extracts symbology information (paint/layout + symbologyState)
|
|
15
|
+
* for a given layer from the JupyterGIS model.
|
|
16
|
+
* Keeps symbology updated when the layer changes.
|
|
17
|
+
*/
|
|
18
|
+
export const useGetSymbology = ({ layerId, model, }) => {
|
|
19
|
+
const [symbology, setSymbology] = useState(null);
|
|
20
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
21
|
+
const [error, setError] = useState();
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!layerId) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
let disposed = false;
|
|
27
|
+
const fetchSymbology = () => {
|
|
28
|
+
var _a;
|
|
29
|
+
try {
|
|
30
|
+
setIsLoading(true);
|
|
31
|
+
setError(undefined);
|
|
32
|
+
const layer = model.getLayer(layerId);
|
|
33
|
+
if (!layer) {
|
|
34
|
+
throw new Error(`Layer not found: ${layerId}`);
|
|
35
|
+
}
|
|
36
|
+
const params = (_a = layer.parameters) !== null && _a !== void 0 ? _a : {};
|
|
37
|
+
const { symbologyState, color } = params, rest = __rest(params, ["symbologyState", "color"]);
|
|
38
|
+
const result = Object.assign(Object.assign(Object.assign({}, rest), (color ? { color } : {})), (symbologyState ? { symbologyState } : {}));
|
|
39
|
+
if (!disposed) {
|
|
40
|
+
setSymbology(result);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (!disposed) {
|
|
45
|
+
setError(err);
|
|
46
|
+
setSymbology(null);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
if (!disposed) {
|
|
51
|
+
setIsLoading(false);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
// initial load
|
|
56
|
+
fetchSymbology();
|
|
57
|
+
model.sharedModel.awareness.on('change', () => {
|
|
58
|
+
fetchSymbology();
|
|
59
|
+
});
|
|
60
|
+
return () => {
|
|
61
|
+
disposed = true;
|
|
62
|
+
};
|
|
63
|
+
}, [layerId, model]);
|
|
64
|
+
return { symbology, isLoading, error };
|
|
65
|
+
};
|
|
@@ -5,5 +5,5 @@ export declare namespace VectorUtils {
|
|
|
5
5
|
const buildRadiusInfo: (layer: IJGISLayer) => IStopRow[];
|
|
6
6
|
}
|
|
7
7
|
export declare namespace Utils {
|
|
8
|
-
const getValueColorPairs: (stops: number[], selectedRamp: string, nClasses: number) => IStopRow[];
|
|
8
|
+
const getValueColorPairs: (stops: number[], selectedRamp: string, nClasses: number, reverse?: boolean) => IStopRow[];
|
|
9
9
|
}
|
|
@@ -75,12 +75,15 @@ export var VectorUtils;
|
|
|
75
75
|
})(VectorUtils || (VectorUtils = {}));
|
|
76
76
|
export var Utils;
|
|
77
77
|
(function (Utils) {
|
|
78
|
-
Utils.getValueColorPairs = (stops, selectedRamp, nClasses) => {
|
|
78
|
+
Utils.getValueColorPairs = (stops, selectedRamp, nClasses, reverse = false) => {
|
|
79
79
|
let colorMap = colormap({
|
|
80
80
|
colormap: selectedRamp,
|
|
81
81
|
nshades: nClasses > 9 ? nClasses : 9,
|
|
82
82
|
format: 'rgba',
|
|
83
83
|
});
|
|
84
|
+
if (reverse) {
|
|
85
|
+
colorMap = [...colorMap].reverse();
|
|
86
|
+
}
|
|
84
87
|
const valueColorPairs = [];
|
|
85
88
|
// colormap requires 9 classes to generate the ramp
|
|
86
89
|
// so we do some tomfoolery to make it work with less than 9 stops
|
|
@@ -17,6 +17,7 @@ const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbology
|
|
|
17
17
|
radius: 5,
|
|
18
18
|
});
|
|
19
19
|
const manualStyleRef = useRef(manualStyle);
|
|
20
|
+
const [reverseRamp, setReverseRamp] = useState(false);
|
|
20
21
|
if (!layerId) {
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
@@ -85,7 +86,7 @@ const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbology
|
|
|
85
86
|
selectedMode: '',
|
|
86
87
|
});
|
|
87
88
|
const stops = Array.from(selectableAttributesAndValues[selectedAttribute]).sort((a, b) => a - b);
|
|
88
|
-
const valueColorPairs = Utils.getValueColorPairs(stops, selectedRamp, stops.length);
|
|
89
|
+
const valueColorPairs = Utils.getValueColorPairs(stops, selectedRamp, stops.length, reverseRamp);
|
|
89
90
|
setStopRows(valueColorPairs);
|
|
90
91
|
};
|
|
91
92
|
const handleOk = () => {
|
|
@@ -124,6 +125,7 @@ const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbology
|
|
|
124
125
|
nClasses: (_b = colorRampOptionsRef.current) === null || _b === void 0 ? void 0 : _b.numberOfShades,
|
|
125
126
|
mode: (_c = colorRampOptionsRef.current) === null || _c === void 0 ? void 0 : _c.selectedMode,
|
|
126
127
|
symbologyTab,
|
|
128
|
+
reverse: reverseRamp,
|
|
127
129
|
};
|
|
128
130
|
layer.parameters.symbologyState = symbologyState;
|
|
129
131
|
layer.parameters.color = newStyle;
|
|
@@ -188,6 +190,10 @@ const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbology
|
|
|
188
190
|
React.createElement("input", { type: "number", className: "jp-mod-styled", value: manualStyle.radius, onChange: e => {
|
|
189
191
|
setManualStyle(prev => (Object.assign(Object.assign({}, prev), { radius: +e.target.value })));
|
|
190
192
|
} })))),
|
|
193
|
+
symbologyTab === 'color' && (React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
194
|
+
React.createElement("label", null,
|
|
195
|
+
React.createElement("input", { type: "checkbox", checked: reverseRamp, onChange: e => setReverseRamp(e.target.checked) }),
|
|
196
|
+
"Reverse Color Ramp"))),
|
|
191
197
|
React.createElement("div", { className: "jp-gis-layer-symbology-container" },
|
|
192
198
|
React.createElement(ColorRamp, { layerParams: layer.parameters, modeOptions: [], classifyFunc: buildColorInfoFromClassification, showModeRow: false, showRampSelector: symbologyTab === 'color' }),
|
|
193
199
|
React.createElement(StopContainer, { selectedMethod: '', stopRows: stopRows, setStopRows: setStopRows }))));
|
|
@@ -29,6 +29,7 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
|
|
|
29
29
|
const [radiusManualStyle, setRadiusManualStyle] = useState({
|
|
30
30
|
radius: 5,
|
|
31
31
|
});
|
|
32
|
+
const [reverseRamp, setReverseRamp] = useState(false);
|
|
32
33
|
const colorManualStyleRef = useRef(colorManualStyle);
|
|
33
34
|
const radiusManualStyleRef = useRef(radiusManualStyle);
|
|
34
35
|
if (!layerId) {
|
|
@@ -161,6 +162,7 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
|
|
|
161
162
|
colorRamp: (_a = colorRampOptionsRef.current) === null || _a === void 0 ? void 0 : _a.selectedRamp,
|
|
162
163
|
nClasses: (_b = colorRampOptionsRef.current) === null || _b === void 0 ? void 0 : _b.numberOfShades,
|
|
163
164
|
mode: (_c = colorRampOptionsRef.current) === null || _c === void 0 ? void 0 : _c.selectedMode,
|
|
165
|
+
reverse: reverseRamp,
|
|
164
166
|
};
|
|
165
167
|
if (layer.type === 'HeatmapLayer') {
|
|
166
168
|
layer.type = 'VectorLayer';
|
|
@@ -198,7 +200,7 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
|
|
|
198
200
|
}
|
|
199
201
|
const stopOutputPairs = symbologyTab === 'radius'
|
|
200
202
|
? stops.map(v => ({ stop: v, output: v }))
|
|
201
|
-
: Utils.getValueColorPairs(stops, selectedRamp, +numberOfShades);
|
|
203
|
+
: Utils.getValueColorPairs(stops, selectedRamp, +numberOfShades, reverseRamp);
|
|
202
204
|
if (symbologyTab === 'radius') {
|
|
203
205
|
setRadiusStopRows(stopOutputPairs);
|
|
204
206
|
}
|
|
@@ -257,6 +259,10 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
|
|
|
257
259
|
handleReset('radius');
|
|
258
260
|
setRadiusManualStyle(Object.assign(Object.assign({}, radiusManualStyle), { radius: +e.target.value }));
|
|
259
261
|
} })))),
|
|
262
|
+
symbologyTab === 'color' && (React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
263
|
+
React.createElement("label", null,
|
|
264
|
+
React.createElement("input", { type: "checkbox", checked: reverseRamp, onChange: e => setReverseRamp(e.target.checked) }),
|
|
265
|
+
"Reverse Color Ramp"))),
|
|
260
266
|
React.createElement(ColorRamp, { layerParams: layer.parameters, modeOptions: modeOptions, classifyFunc: buildColorInfoFromClassification, showModeRow: true, showRampSelector: symbologyTab === 'color' }),
|
|
261
267
|
React.createElement(StopContainer, { selectedMethod: symbologyTab || 'color', stopRows: symbologyTab === 'color' ? colorStopRows : radiusStopRows, setStopRows: symbologyTab === 'color' ? setColorStopRows : setRadiusStopRows })));
|
|
262
268
|
}
|
|
@@ -14,11 +14,13 @@ const Heatmap = ({ model, state, okSignalPromise, cancel, layerId, }) => {
|
|
|
14
14
|
radius: 8,
|
|
15
15
|
blur: 15,
|
|
16
16
|
});
|
|
17
|
+
const [reverseRamp, setReverseRamp] = useState(false);
|
|
17
18
|
const selectedRampRef = useRef('viridis');
|
|
18
19
|
const heatmapOptionsRef = useRef({
|
|
19
20
|
radius: 8,
|
|
20
21
|
blur: 15,
|
|
21
22
|
});
|
|
23
|
+
const reverseRampRef = useRef(false);
|
|
22
24
|
useEffect(() => {
|
|
23
25
|
populateOptions();
|
|
24
26
|
okSignalPromise.promise.then(okSignal => {
|
|
@@ -33,7 +35,8 @@ const Heatmap = ({ model, state, okSignalPromise, cancel, layerId, }) => {
|
|
|
33
35
|
useEffect(() => {
|
|
34
36
|
selectedRampRef.current = selectedRamp;
|
|
35
37
|
heatmapOptionsRef.current = heatmapOptions;
|
|
36
|
-
|
|
38
|
+
reverseRampRef.current = reverseRamp;
|
|
39
|
+
}, [selectedRamp, heatmapOptions, reverseRamp]);
|
|
37
40
|
const populateOptions = async () => {
|
|
38
41
|
var _a;
|
|
39
42
|
let colorRamp;
|
|
@@ -46,14 +49,18 @@ const Heatmap = ({ model, state, okSignalPromise, cancel, layerId, }) => {
|
|
|
46
49
|
if (!layer.parameters) {
|
|
47
50
|
return;
|
|
48
51
|
}
|
|
49
|
-
|
|
52
|
+
let colorMap = colormap({
|
|
50
53
|
colormap: selectedRampRef.current,
|
|
51
54
|
nshades: 9,
|
|
52
55
|
format: 'hex',
|
|
53
56
|
});
|
|
57
|
+
if (reverseRampRef.current) {
|
|
58
|
+
colorMap = [...colorMap].reverse();
|
|
59
|
+
}
|
|
54
60
|
const symbologyState = {
|
|
55
61
|
renderType: 'Heatmap',
|
|
56
62
|
colorRamp: selectedRampRef.current,
|
|
63
|
+
reverse: reverseRampRef.current,
|
|
57
64
|
};
|
|
58
65
|
layer.parameters.symbologyState = symbologyState;
|
|
59
66
|
layer.parameters.color = colorMap;
|
|
@@ -68,6 +75,10 @@ const Heatmap = ({ model, state, okSignalPromise, cancel, layerId, }) => {
|
|
|
68
75
|
React.createElement("div", { className: "jp-gis-symbology-row jp-gis-heatmap" },
|
|
69
76
|
React.createElement("label", { htmlFor: "color-ramp-select" }, "Color Ramp:"),
|
|
70
77
|
React.createElement(CanvasSelectComponent, { selectedRamp: selectedRamp, setSelected: setSelectedRamp })),
|
|
78
|
+
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
79
|
+
React.createElement("label", null,
|
|
80
|
+
React.createElement("input", { type: "checkbox", checked: reverseRamp, onChange: e => setReverseRamp(e.target.checked) }),
|
|
81
|
+
"Reverse Color Ramp")),
|
|
71
82
|
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
72
83
|
React.createElement("label", { htmlFor: 'vector-value-select' }, "Radius:"),
|
|
73
84
|
React.createElement("input", { type: "number", value: heatmapOptions.radius, className: "jp-mod-styled", onChange: event => setHetamapOptions(prevState => (Object.assign(Object.assign({}, prevState), { radius: +event.target.value }))) })),
|
package/lib/gdal.js
CHANGED
package/lib/mainview/mainView.js
CHANGED
|
@@ -14,7 +14,7 @@ import { Heatmap as HeatmapLayer, Image as ImageLayer, Layer, Vector as VectorLa
|
|
|
14
14
|
import TileLayer from 'ol/layer/Tile';
|
|
15
15
|
import { fromLonLat, get as getProjection, toLonLat, transformExtent, } from 'ol/proj';
|
|
16
16
|
import { register } from 'ol/proj/proj4.js';
|
|
17
|
-
import RenderFeature from 'ol/render/Feature';
|
|
17
|
+
import RenderFeature, { toGeometry } from 'ol/render/Feature';
|
|
18
18
|
import { GeoTIFF as GeoTIFFSource, ImageTile as ImageTileSource, Vector as VectorSource, VectorTile as VectorTileSource, XYZ as XYZSource, Tile as TileSource, } from 'ol/source';
|
|
19
19
|
import Static from 'ol/source/ImageStatic';
|
|
20
20
|
import { Circle, Fill, Stroke, Style } from 'ol/style';
|
|
@@ -103,7 +103,7 @@ export class MainView extends React.Component {
|
|
|
103
103
|
return layer === this.getLayer(selectedLayerId);
|
|
104
104
|
},
|
|
105
105
|
condition: (event) => {
|
|
106
|
-
return singleClick(event) && this._model.
|
|
106
|
+
return singleClick(event) && this._model.currentMode === 'identifying';
|
|
107
107
|
},
|
|
108
108
|
style: styleFunction,
|
|
109
109
|
});
|
|
@@ -483,8 +483,7 @@ export class MainView extends React.Component {
|
|
|
483
483
|
// Watch isIdentifying and clear the highlight when Identify Tool is turned off
|
|
484
484
|
this._model.sharedModel.awareness.on('change', () => {
|
|
485
485
|
var _a;
|
|
486
|
-
|
|
487
|
-
if (!isIdentifying && this._highlightLayer) {
|
|
486
|
+
if (this._model.currentMode !== 'identifying' && this._highlightLayer) {
|
|
488
487
|
(_a = this._highlightLayer.getSource()) === null || _a === void 0 ? void 0 : _a.clear();
|
|
489
488
|
}
|
|
490
489
|
});
|
|
@@ -664,7 +663,8 @@ export class MainView extends React.Component {
|
|
|
664
663
|
switch (source.type) {
|
|
665
664
|
case 'RasterSource': {
|
|
666
665
|
const sourceParameters = source.parameters;
|
|
667
|
-
const pmTiles = sourceParameters.url.endsWith('.pmtiles')
|
|
666
|
+
const pmTiles = sourceParameters.url.endsWith('.pmtiles') ||
|
|
667
|
+
sourceParameters.url.endsWith('pmtiles.gz');
|
|
668
668
|
const url = this.computeSourceUrl(source);
|
|
669
669
|
if (!pmTiles) {
|
|
670
670
|
newSource = new XYZSource({
|
|
@@ -697,7 +697,8 @@ export class MainView extends React.Component {
|
|
|
697
697
|
}
|
|
698
698
|
case 'VectorTileSource': {
|
|
699
699
|
const sourceParameters = source.parameters;
|
|
700
|
-
const pmTiles = sourceParameters.url.endsWith('.pmtiles')
|
|
700
|
+
const pmTiles = sourceParameters.url.endsWith('.pmtiles') ||
|
|
701
|
+
sourceParameters.url.endsWith('pmtiles.gz');
|
|
701
702
|
const url = this.computeSourceUrl(source);
|
|
702
703
|
if (!pmTiles) {
|
|
703
704
|
newSource = new VectorTileSource({
|
|
@@ -720,7 +721,10 @@ export class MainView extends React.Component {
|
|
|
720
721
|
const tile = event.tile;
|
|
721
722
|
const features = tile.getFeatures();
|
|
722
723
|
if (features && features.length > 0) {
|
|
723
|
-
this._model.syncTileFeatures({
|
|
724
|
+
this._model.syncTileFeatures({
|
|
725
|
+
sourceId: id,
|
|
726
|
+
features,
|
|
727
|
+
});
|
|
724
728
|
}
|
|
725
729
|
});
|
|
726
730
|
break;
|
|
@@ -735,9 +739,7 @@ export class MainView extends React.Component {
|
|
|
735
739
|
const format = new GeoJSON({
|
|
736
740
|
featureProjection: this._Map.getView().getProjection(),
|
|
737
741
|
});
|
|
738
|
-
// TODO: Don't hardcode projection
|
|
739
742
|
const featureArray = format.readFeatures(data, {
|
|
740
|
-
dataProjection: 'EPSG:4326',
|
|
741
743
|
featureProjection: this._Map.getView().getProjection(),
|
|
742
744
|
});
|
|
743
745
|
const featureCollection = new Collection(featureArray);
|
|
@@ -1575,7 +1577,7 @@ export class MainView extends React.Component {
|
|
|
1575
1577
|
}
|
|
1576
1578
|
_identifyFeature(e) {
|
|
1577
1579
|
var _a, _b;
|
|
1578
|
-
if (
|
|
1580
|
+
if (this._model.currentMode !== 'identifying') {
|
|
1579
1581
|
return;
|
|
1580
1582
|
}
|
|
1581
1583
|
const localState = (_a = this._model) === null || _a === void 0 ? void 0 : _a.sharedModel.awareness.getLocalState();
|
|
@@ -1587,6 +1589,39 @@ export class MainView extends React.Component {
|
|
|
1587
1589
|
const layerId = Object.keys(selectedLayer)[0];
|
|
1588
1590
|
const jgisLayer = this._model.getLayer(layerId);
|
|
1589
1591
|
switch (jgisLayer === null || jgisLayer === void 0 ? void 0 : jgisLayer.type) {
|
|
1592
|
+
case 'VectorTileLayer': {
|
|
1593
|
+
const geometries = [];
|
|
1594
|
+
const features = [];
|
|
1595
|
+
this._Map.forEachFeatureAtPixel(e.pixel, (feature) => {
|
|
1596
|
+
let geom;
|
|
1597
|
+
if (feature instanceof RenderFeature) {
|
|
1598
|
+
geom = toGeometry(feature);
|
|
1599
|
+
}
|
|
1600
|
+
else if ('getGeometry' in feature) {
|
|
1601
|
+
geom = feature.getGeometry();
|
|
1602
|
+
}
|
|
1603
|
+
const props = feature.getProperties();
|
|
1604
|
+
if (geom) {
|
|
1605
|
+
geometries.push(geom);
|
|
1606
|
+
}
|
|
1607
|
+
features.push(Object.assign({}, props));
|
|
1608
|
+
return true;
|
|
1609
|
+
});
|
|
1610
|
+
if (features.length > 0) {
|
|
1611
|
+
this._model.syncIdentifiedFeatures(features, this._mainViewModel.id);
|
|
1612
|
+
}
|
|
1613
|
+
if (geometries.length > 0) {
|
|
1614
|
+
for (const geom of geometries) {
|
|
1615
|
+
this._model.highlightFeatureSignal.emit(geom);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
else {
|
|
1619
|
+
const coordinate = this._Map.getCoordinateFromPixel(e.pixel);
|
|
1620
|
+
const point = new Point(coordinate);
|
|
1621
|
+
this._model.highlightFeatureSignal.emit(point);
|
|
1622
|
+
}
|
|
1623
|
+
break;
|
|
1624
|
+
}
|
|
1590
1625
|
case 'WebGlLayer': {
|
|
1591
1626
|
const layer = this.getLayer(layerId);
|
|
1592
1627
|
const data = layer.getData(e.pixel);
|
|
@@ -1670,7 +1705,8 @@ export class MainView extends React.Component {
|
|
|
1670
1705
|
height: '100%',
|
|
1671
1706
|
} })),
|
|
1672
1707
|
React.createElement(StatusBar, { jgisModel: this._model, loading: this.state.loadingLayer, projection: this.state.viewProjection, scale: this.state.scale })),
|
|
1673
|
-
|
|
1674
|
-
|
|
1708
|
+
React.createElement("div", { className: "jgis-panels-wrapper" },
|
|
1709
|
+
this._state && (React.createElement(LeftPanel, { model: this._model, commands: this._mainViewModel.commands, state: this._state })),
|
|
1710
|
+
this._formSchemaRegistry && this._annotationModel && (React.createElement(RightPanel, { model: this._model, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel })))));
|
|
1675
1711
|
}
|
|
1676
1712
|
}
|
|
@@ -35,7 +35,8 @@ export const IdentifyPanelComponent = ({ model, }) => {
|
|
|
35
35
|
setFeatures({});
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
|
-
if (model.
|
|
38
|
+
if (model.currentMode === 'identifying' &&
|
|
39
|
+
featuresRef.current !== identifiedFeatures) {
|
|
39
40
|
setFeatures(identifiedFeatures);
|
|
40
41
|
}
|
|
41
42
|
};
|
|
@@ -53,6 +54,15 @@ export const IdentifyPanelComponent = ({ model, }) => {
|
|
|
53
54
|
const toggleFeatureVisibility = (index) => {
|
|
54
55
|
setVisibleFeatures(prev => (Object.assign(Object.assign({}, prev), { [index]: !prev[index] })));
|
|
55
56
|
};
|
|
57
|
+
const getFeatureNameOrId = (feature, featureIndex) => {
|
|
58
|
+
for (const key of Object.keys(feature)) {
|
|
59
|
+
const lowerCase = key.toLowerCase();
|
|
60
|
+
if ((lowerCase.includes('name') || lowerCase === 'id') && feature[key]) {
|
|
61
|
+
return feature[key];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return `Feature ${featureIndex + 1}`;
|
|
65
|
+
};
|
|
56
66
|
return (React.createElement("div", { className: "jgis-identify-wrapper", style: {
|
|
57
67
|
border: ((_a = model === null || model === void 0 ? void 0 : model.localState) === null || _a === void 0 ? void 0 : _a.remoteUser)
|
|
58
68
|
? `solid 3px ${remoteUser === null || remoteUser === void 0 ? void 0 : remoteUser.color}`
|
|
@@ -62,9 +72,7 @@ export const IdentifyPanelComponent = ({ model, }) => {
|
|
|
62
72
|
React.createElement("div", { className: "jgis-identify-grid-item-header" },
|
|
63
73
|
React.createElement("span", { onClick: () => toggleFeatureVisibility(featureIndex) },
|
|
64
74
|
React.createElement(LabIcon.resolveReact, { icon: caretDownIcon, className: `jp-gis-layerGroupCollapser${visibleFeatures[featureIndex] ? ' jp-mod-expanded' : ''}`, tag: 'span' }),
|
|
65
|
-
React.createElement("span", null,
|
|
66
|
-
"Feature ",
|
|
67
|
-
featureIndex + 1)),
|
|
75
|
+
React.createElement("span", null, getFeatureNameOrId(feature, featureIndex))),
|
|
68
76
|
(() => {
|
|
69
77
|
const isRasterFeature = !feature.geometry &&
|
|
70
78
|
!feature._geometry &&
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { DOMUtils } from '@jupyterlab/apputils';
|
|
2
|
-
import { Button, LabIcon, caretDownIcon } from '@jupyterlab/ui-components';
|
|
2
|
+
import { Button, LabIcon, caretDownIcon, caretRightIcon, } from '@jupyterlab/ui-components';
|
|
3
3
|
import { UUID } from '@lumino/coreutils';
|
|
4
4
|
import React, { useEffect, useState, } from 'react';
|
|
5
5
|
import { CommandIDs, icons } from "../../constants";
|
|
6
|
+
import { useGetSymbology } from "../../dialogs/symbology/hooks/useGetSymbology";
|
|
6
7
|
import { nonVisibilityIcon, visibilityIcon } from "../../icons";
|
|
8
|
+
import { LegendItem } from './legendItem';
|
|
7
9
|
const LAYER_GROUP_CLASS = 'jp-gis-layerGroup';
|
|
8
10
|
const LAYER_GROUP_HEADER_CLASS = 'jp-gis-layerGroupHeader';
|
|
9
11
|
const LAYER_GROUP_COLLAPSER_CLASS = 'jp-gis-layerGroupCollapser';
|
|
@@ -213,6 +215,12 @@ const LayerComponent = props => {
|
|
|
213
215
|
const [selected, setSelected] = useState(
|
|
214
216
|
// TODO Support multi-selection as `model?.jGISModel?.localState?.selected.value` does
|
|
215
217
|
isSelected(layerId, gisModel));
|
|
218
|
+
const [expanded, setExpanded] = useState(false);
|
|
219
|
+
const { symbology } = useGetSymbology({
|
|
220
|
+
layerId,
|
|
221
|
+
model: gisModel,
|
|
222
|
+
});
|
|
223
|
+
const hasSupportedSymbology = (symbology === null || symbology === void 0 ? void 0 : symbology.symbologyState) !== undefined;
|
|
216
224
|
const name = layer.name;
|
|
217
225
|
useEffect(() => {
|
|
218
226
|
setId(DOMUtils.createDomID());
|
|
@@ -248,12 +256,19 @@ const LayerComponent = props => {
|
|
|
248
256
|
event,
|
|
249
257
|
});
|
|
250
258
|
};
|
|
251
|
-
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 },
|
|
252
|
-
React.createElement("div", { className: LAYER_TITLE_CLASS, onClick: setSelection, onContextMenu: setSelection },
|
|
259
|
+
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
|
+
React.createElement("div", { className: LAYER_TITLE_CLASS, onClick: setSelection, onContextMenu: setSelection, style: { display: 'flex' } },
|
|
261
|
+
hasSupportedSymbology && (React.createElement(Button, { minimal: true, onClick: e => {
|
|
262
|
+
e.stopPropagation();
|
|
263
|
+
setExpanded(v => !v);
|
|
264
|
+
}, title: expanded ? 'Hide legend' : 'Show legend' },
|
|
265
|
+
React.createElement(LabIcon.resolveReact, { icon: expanded ? caretDownIcon : caretRightIcon, tag: "span" }))),
|
|
253
266
|
React.createElement(Button, { title: layer.visible ? 'Hide layer' : 'Show layer', onClick: toggleVisibility, minimal: true },
|
|
254
267
|
React.createElement(LabIcon.resolveReact, { icon: layer.visible ? visibilityIcon : nonVisibilityIcon, className: `${LAYER_ICON_CLASS}${layer.visible ? '' : ' jp-gis-mod-hidden'}`, tag: "span" })),
|
|
255
268
|
icons.has(layer.type) && (React.createElement(LabIcon.resolveReact, Object.assign({}, icons.get(layer.type), { className: LAYER_ICON_CLASS }))),
|
|
256
|
-
React.createElement("span", { id: id, className: LAYER_TEXT_CLASS, tabIndex: -2 }, name))
|
|
269
|
+
React.createElement("span", { id: id, className: LAYER_TEXT_CLASS, tabIndex: -2 }, name)),
|
|
270
|
+
expanded && gisModel && hasSupportedSymbology && (React.createElement("div", { style: { marginTop: 6, width: '100%' } },
|
|
271
|
+
React.createElement(LegendItem, { layerId: layerId, model: gisModel })))));
|
|
257
272
|
};
|
|
258
273
|
var Private;
|
|
259
274
|
(function (Private) {
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { useGetSymbology } from "../../dialogs/symbology/hooks/useGetSymbology";
|
|
3
|
+
export const LegendItem = ({ layerId, model }) => {
|
|
4
|
+
const { symbology, isLoading, error } = useGetSymbology({ layerId, model });
|
|
5
|
+
const [content, setContent] = useState(null);
|
|
6
|
+
const parseColorStops = (expr) => {
|
|
7
|
+
if (!Array.isArray(expr) || expr[0] !== 'interpolate') {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
const stops = [];
|
|
11
|
+
for (let i = 3; i < expr.length; i += 2) {
|
|
12
|
+
const value = expr[i];
|
|
13
|
+
const rgba = expr[i + 1];
|
|
14
|
+
const color = Array.isArray(rgba)
|
|
15
|
+
? `rgba(${rgba[0]},${rgba[1]},${rgba[2]},${rgba[3]})`
|
|
16
|
+
: String(rgba);
|
|
17
|
+
stops.push({ value, color });
|
|
18
|
+
}
|
|
19
|
+
return stops;
|
|
20
|
+
};
|
|
21
|
+
const parseCaseCategories = (expr) => {
|
|
22
|
+
if (!Array.isArray(expr) || expr[0] !== 'case') {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
const categories = [];
|
|
26
|
+
for (let i = 1; i < expr.length - 1; i += 2) {
|
|
27
|
+
const condition = expr[i];
|
|
28
|
+
const colorExpr = expr[i + 1];
|
|
29
|
+
let category = '';
|
|
30
|
+
if (Array.isArray(condition) && condition[0] === '==') {
|
|
31
|
+
category = condition[2];
|
|
32
|
+
}
|
|
33
|
+
let color = '';
|
|
34
|
+
if (Array.isArray(colorExpr)) {
|
|
35
|
+
color = `rgba(${colorExpr[0]},${colorExpr[1]},${colorExpr[2]},${colorExpr[3]})`;
|
|
36
|
+
}
|
|
37
|
+
else if (typeof colorExpr === 'string') {
|
|
38
|
+
color = colorExpr;
|
|
39
|
+
}
|
|
40
|
+
categories.push({ category, color });
|
|
41
|
+
}
|
|
42
|
+
return categories;
|
|
43
|
+
};
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
46
|
+
if (isLoading) {
|
|
47
|
+
setContent(React.createElement("p", { style: { fontSize: '0.8em' } }, "Loading\u2026"));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (error) {
|
|
51
|
+
setContent(React.createElement("p", { style: { color: 'red', fontSize: '0.8em' } }, error.message));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!symbology) {
|
|
55
|
+
setContent(React.createElement("p", { style: { fontSize: '0.8em' } }, "No symbology"));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const renderType = (_a = symbology.symbologyState) === null || _a === void 0 ? void 0 : _a.renderType;
|
|
59
|
+
const property = (_b = symbology.symbologyState) === null || _b === void 0 ? void 0 : _b.value;
|
|
60
|
+
const fill = (_d = (_c = symbology.color) === null || _c === void 0 ? void 0 : _c['fill-color']) !== null && _d !== void 0 ? _d : (_e = symbology.color) === null || _e === void 0 ? void 0 : _e['circle-fill-color'];
|
|
61
|
+
const stroke = (_g = (_f = symbology.color) === null || _f === void 0 ? void 0 : _f['stroke-color']) !== null && _g !== void 0 ? _g : (_h = symbology.color) === null || _h === void 0 ? void 0 : _h['circle-stroke-color'];
|
|
62
|
+
// Single Symbol
|
|
63
|
+
if (renderType === 'Single Symbol') {
|
|
64
|
+
setContent(React.createElement("div", { style: { display: 'flex', gap: 12, flexWrap: 'wrap', padding: 6 } },
|
|
65
|
+
fill && (React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: 6 } },
|
|
66
|
+
React.createElement("span", { style: {
|
|
67
|
+
width: 16,
|
|
68
|
+
height: 16,
|
|
69
|
+
border: '1px solid #000',
|
|
70
|
+
background: fill,
|
|
71
|
+
} }),
|
|
72
|
+
React.createElement("span", { style: { fontSize: '0.8em' } }, "Fill"))),
|
|
73
|
+
stroke && (React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: 6 } },
|
|
74
|
+
React.createElement("span", { style: {
|
|
75
|
+
width: 24,
|
|
76
|
+
height: 2,
|
|
77
|
+
background: stroke,
|
|
78
|
+
border: '1px solid #000',
|
|
79
|
+
} }),
|
|
80
|
+
React.createElement("span", { style: { fontSize: '0.8em' } }, "Stroke"))),
|
|
81
|
+
!fill && !stroke && (React.createElement("span", { style: { fontSize: '0.8em' } }, "No symbol colors"))));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Graduated
|
|
85
|
+
if (renderType === 'Graduated') {
|
|
86
|
+
const stops = parseColorStops(fill || stroke);
|
|
87
|
+
if (!stops.length) {
|
|
88
|
+
setContent(React.createElement("p", { style: { fontSize: '0.8em' } }, "No graduated symbology"));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const segments = stops
|
|
92
|
+
.map((s, i) => {
|
|
93
|
+
const pct = (i / (stops.length - 1)) * 100;
|
|
94
|
+
return `${s.color} ${pct}%`;
|
|
95
|
+
})
|
|
96
|
+
.join(', ');
|
|
97
|
+
const gradient = `linear-gradient(to right, ${segments})`;
|
|
98
|
+
setContent(React.createElement("div", { style: { padding: 6, width: '90%' } },
|
|
99
|
+
property && (React.createElement("div", { style: { fontSize: '1em', marginBottom: 20 } },
|
|
100
|
+
React.createElement("strong", null, property))),
|
|
101
|
+
React.createElement("div", { style: {
|
|
102
|
+
position: 'relative',
|
|
103
|
+
height: 12,
|
|
104
|
+
background: gradient,
|
|
105
|
+
border: '1px solid #ccc',
|
|
106
|
+
borderRadius: 3,
|
|
107
|
+
marginBottom: 20,
|
|
108
|
+
marginTop: 10,
|
|
109
|
+
} }, stops.map((s, i) => {
|
|
110
|
+
const left = (i / (stops.length - 1)) * 100;
|
|
111
|
+
const up = i % 2 === 0;
|
|
112
|
+
return (React.createElement("div", { key: i, style: {
|
|
113
|
+
position: 'absolute',
|
|
114
|
+
left: `${left}%`,
|
|
115
|
+
transform: 'translateX(-50%)',
|
|
116
|
+
} },
|
|
117
|
+
React.createElement("div", { style: {
|
|
118
|
+
width: 1,
|
|
119
|
+
height: 8,
|
|
120
|
+
background: '#333',
|
|
121
|
+
margin: '0 auto',
|
|
122
|
+
} }),
|
|
123
|
+
React.createElement("div", { style: {
|
|
124
|
+
position: 'absolute',
|
|
125
|
+
top: up ? -18 : 12,
|
|
126
|
+
fontSize: '0.7em',
|
|
127
|
+
whiteSpace: 'nowrap',
|
|
128
|
+
marginTop: up ? 0 : 4,
|
|
129
|
+
} }, s.value.toFixed(2))));
|
|
130
|
+
}))));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// Categorized
|
|
134
|
+
if (renderType === 'Categorized') {
|
|
135
|
+
const cats = parseCaseCategories(fill || stroke);
|
|
136
|
+
if (!cats.length) {
|
|
137
|
+
setContent(React.createElement("p", { style: { fontSize: '0.8em' } }, "No categorized symbology"));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
setContent(React.createElement("div", { style: { padding: 6 } },
|
|
141
|
+
property && (React.createElement("div", { style: { fontSize: '1em', marginBottom: 6 } },
|
|
142
|
+
React.createElement("strong", null, property))),
|
|
143
|
+
React.createElement("div", { style: {
|
|
144
|
+
display: 'grid',
|
|
145
|
+
gap: 6,
|
|
146
|
+
maxHeight: 200,
|
|
147
|
+
overflowY: 'auto',
|
|
148
|
+
paddingRight: 4,
|
|
149
|
+
} }, cats.map((c, i) => (React.createElement("div", { key: i, style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
150
|
+
React.createElement("span", { style: {
|
|
151
|
+
width: 16,
|
|
152
|
+
height: 16,
|
|
153
|
+
background: c.color || '#ccc',
|
|
154
|
+
border: '1px solid #000',
|
|
155
|
+
borderRadius: 2,
|
|
156
|
+
} }),
|
|
157
|
+
React.createElement("span", { style: { fontSize: '0.75em' } }, String(c.category))))))));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
setContent(React.createElement("p", null,
|
|
161
|
+
"Unsupported symbology: ",
|
|
162
|
+
String(renderType)));
|
|
163
|
+
}, [symbology, isLoading, error]);
|
|
164
|
+
return React.createElement("div", null, content);
|
|
165
|
+
};
|
|
@@ -4,29 +4,42 @@ import { PanelTabs, TabsContent, TabsList, TabsTrigger, } from '../shared/compon
|
|
|
4
4
|
import StacPanel from '../stacBrowser/components/StacPanel';
|
|
5
5
|
import FilterComponent from './components/filter-panel/Filter';
|
|
6
6
|
export const LeftPanel = (props) => {
|
|
7
|
+
const [settings, setSettings] = React.useState(props.model.jgisSettings);
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const onSettingsChanged = () => {
|
|
10
|
+
setSettings(Object.assign({}, props.model.jgisSettings));
|
|
11
|
+
};
|
|
12
|
+
props.model.settingsChanged.connect(onSettingsChanged);
|
|
13
|
+
return () => {
|
|
14
|
+
props.model.settingsChanged.disconnect(onSettingsChanged);
|
|
15
|
+
};
|
|
16
|
+
}, [props.model]);
|
|
17
|
+
const allLeftTabsDisabled = settings.layersDisabled &&
|
|
18
|
+
settings.stacBrowserDisabled &&
|
|
19
|
+
settings.filtersDisabled;
|
|
20
|
+
const leftPanelVisible = !settings.leftPanelDisabled && !allLeftTabsDisabled;
|
|
7
21
|
const tabInfo = [
|
|
8
|
-
{ name: 'layers', title: 'Layers' },
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
!settings.layersDisabled ? { name: 'layers', title: 'Layers' } : false,
|
|
23
|
+
!settings.stacBrowserDisabled
|
|
24
|
+
? { name: 'stac', title: 'Stac Browser' }
|
|
25
|
+
: false,
|
|
26
|
+
!settings.filtersDisabled ? { name: 'filters', title: 'Filters' } : false,
|
|
27
|
+
].filter(Boolean);
|
|
28
|
+
const [curTab, setCurTab] = React.useState(tabInfo.length > 0 ? tabInfo[0].name : undefined);
|
|
29
|
+
return (React.createElement("div", { className: "jgis-left-panel-container", style: { display: leftPanelVisible ? 'block' : 'none' } },
|
|
14
30
|
React.createElement(PanelTabs, { curTab: curTab, className: "jgis-panel-tabs" },
|
|
15
|
-
React.createElement(TabsList, null, tabInfo.map(e => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
React.createElement(TabsContent, { value: "
|
|
26
|
-
React.createElement(
|
|
27
|
-
React.createElement(TabsContent, { value: "
|
|
28
|
-
React.createElement(
|
|
29
|
-
React.createElement(TabsContent, { value: "filters", className: "jgis-panel-tab-content" },
|
|
30
|
-
React.createElement(FilterComponent, { model: props.model }),
|
|
31
|
-
","))));
|
|
31
|
+
React.createElement(TabsList, null, tabInfo.map(e => (React.createElement(TabsTrigger, { className: "jGIS-layer-browser-category", value: e.name, onClick: () => {
|
|
32
|
+
if (curTab !== e.name) {
|
|
33
|
+
setCurTab(e.name);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
setCurTab('');
|
|
37
|
+
}
|
|
38
|
+
} }, e.title)))),
|
|
39
|
+
!settings.layersDisabled && (React.createElement(TabsContent, { value: "layers", className: "jgis-panel-tab-content jp-gis-layerPanel" },
|
|
40
|
+
React.createElement(LayersBodyComponent, { model: props.model, commands: props.commands, state: props.state }))),
|
|
41
|
+
!settings.stacBrowserDisabled && (React.createElement(TabsContent, { value: "stac", className: "jgis-panel-tab-content" },
|
|
42
|
+
React.createElement(StacPanel, { model: props.model }))),
|
|
43
|
+
!settings.filtersDisabled && (React.createElement(TabsContent, { value: "filters", className: "jgis-panel-tab-content" },
|
|
44
|
+
React.createElement(FilterComponent, { model: props.model }))))));
|
|
32
45
|
};
|
|
@@ -4,29 +4,61 @@ import { IdentifyPanelComponent } from './components/identify-panel/IdentifyPane
|
|
|
4
4
|
import { ObjectPropertiesReact } from './objectproperties';
|
|
5
5
|
import { PanelTabs, TabsContent, TabsList, TabsTrigger, } from '../shared/components/Tabs';
|
|
6
6
|
export const RightPanel = props => {
|
|
7
|
-
const [
|
|
7
|
+
const [settings, setSettings] = React.useState(props.model.jgisSettings);
|
|
8
8
|
const tabInfo = [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
!settings.objectPropertiesDisabled
|
|
10
|
+
? { name: 'objectProperties', title: 'Object Properties' }
|
|
11
|
+
: false,
|
|
12
|
+
!settings.annotationsDisabled
|
|
13
|
+
? { name: 'annotations', title: 'Annotations' }
|
|
14
|
+
: false,
|
|
15
|
+
!settings.identifyDisabled
|
|
16
|
+
? { name: 'identifyPanel', title: 'Identified Features' }
|
|
17
|
+
: false,
|
|
18
|
+
].filter(Boolean);
|
|
19
|
+
const [curTab, setCurTab] = React.useState(tabInfo.length > 0 ? tabInfo[0].name : undefined);
|
|
20
|
+
React.useEffect(() => {
|
|
21
|
+
const onSettingsChanged = () => {
|
|
22
|
+
setSettings(Object.assign({}, props.model.jgisSettings));
|
|
23
|
+
};
|
|
24
|
+
let currentlyIdentifiedFeatures = undefined;
|
|
25
|
+
const onAwerenessChanged = (_, clients) => {
|
|
26
|
+
var _a;
|
|
27
|
+
const clientId = props.model.getClientId();
|
|
28
|
+
const localState = clientId ? clients.get(clientId) : null;
|
|
29
|
+
if (localState &&
|
|
30
|
+
((_a = localState.identifiedFeatures) === null || _a === void 0 ? void 0 : _a.value) &&
|
|
31
|
+
localState.identifiedFeatures.value !== currentlyIdentifiedFeatures) {
|
|
32
|
+
currentlyIdentifiedFeatures = localState.identifiedFeatures.value;
|
|
33
|
+
setCurTab('identifyPanel');
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
props.model.settingsChanged.connect(onSettingsChanged);
|
|
37
|
+
props.model.clientStateChanged.connect(onAwerenessChanged);
|
|
38
|
+
return () => {
|
|
39
|
+
props.model.settingsChanged.disconnect(onSettingsChanged);
|
|
40
|
+
props.model.clientStateChanged.disconnect(onAwerenessChanged);
|
|
41
|
+
};
|
|
42
|
+
}, [props.model]);
|
|
43
|
+
const allRightTabsDisabled = settings.objectPropertiesDisabled &&
|
|
44
|
+
settings.annotationsDisabled &&
|
|
45
|
+
settings.identifyDisabled;
|
|
46
|
+
const rightPanelVisible = !settings.rightPanelDisabled && !allRightTabsDisabled;
|
|
47
|
+
const [selectedObjectProperties, setSelectedObjectProperties] = React.useState(undefined);
|
|
48
|
+
return (React.createElement("div", { className: "jgis-right-panel-container", style: { display: rightPanelVisible ? 'block' : 'none' } },
|
|
15
49
|
React.createElement(PanelTabs, { className: "jgis-panel-tabs", curTab: curTab },
|
|
16
|
-
React.createElement(TabsList, null, tabInfo.map(e => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
React.createElement(TabsContent, { value: "
|
|
27
|
-
React.createElement(
|
|
28
|
-
React.createElement(TabsContent, { value: "
|
|
29
|
-
React.createElement(
|
|
30
|
-
React.createElement(TabsContent, { value: "identifyPanel", className: "jgis-panel-tab-content" },
|
|
31
|
-
React.createElement(IdentifyPanelComponent, { model: props.model })))));
|
|
50
|
+
React.createElement(TabsList, null, tabInfo.map(e => (React.createElement(TabsTrigger, { className: "jGIS-layer-browser-category", value: e.name, onClick: () => {
|
|
51
|
+
if (curTab !== e.name) {
|
|
52
|
+
setCurTab(e.name);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
setCurTab('');
|
|
56
|
+
}
|
|
57
|
+
} }, e.title)))),
|
|
58
|
+
!settings.objectPropertiesDisabled && (React.createElement(TabsContent, { value: "objectProperties", className: "jgis-panel-tab-content" },
|
|
59
|
+
React.createElement(ObjectPropertiesReact, { setSelectedObject: setSelectedObjectProperties, selectedObject: selectedObjectProperties, formSchemaRegistry: props.formSchemaRegistry, model: props.model }))),
|
|
60
|
+
!settings.annotationsDisabled && (React.createElement(TabsContent, { value: "annotations", className: "jgis-panel-tab-content" },
|
|
61
|
+
React.createElement(AnnotationsPanel, { annotationModel: props.annotationModel, jgisModel: props.model }))),
|
|
62
|
+
!settings.identifyDisabled && (React.createElement(TabsContent, { value: "identifyPanel", className: "jgis-panel-tab-content" },
|
|
63
|
+
React.createElement(IdentifyPanelComponent, { model: props.model }))))));
|
|
32
64
|
};
|
package/lib/tools.d.ts
CHANGED
|
@@ -72,7 +72,11 @@ export declare const loadGeoTiff: (sourceInfo: {
|
|
|
72
72
|
file: any;
|
|
73
73
|
metadata: any;
|
|
74
74
|
sourceUrl: string;
|
|
75
|
-
} |
|
|
75
|
+
} | {
|
|
76
|
+
file: Blob;
|
|
77
|
+
sourceUrl: string;
|
|
78
|
+
metadata?: undefined;
|
|
79
|
+
} | null>;
|
|
76
80
|
/**
|
|
77
81
|
* Generalized file reader for different source types.
|
|
78
82
|
*
|
package/lib/tools.js
CHANGED
|
@@ -353,6 +353,10 @@ export const loadGeoTiff = async (sourceInfo, model, file) => {
|
|
|
353
353
|
else {
|
|
354
354
|
fileBlob = await base64ToBlob(file.content, mimeType);
|
|
355
355
|
}
|
|
356
|
+
return {
|
|
357
|
+
file: fileBlob,
|
|
358
|
+
sourceUrl: url,
|
|
359
|
+
};
|
|
356
360
|
};
|
|
357
361
|
/**
|
|
358
362
|
* Generalized file reader for different source types.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupytergis/base",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "A JupyterLab extension for 3D modelling.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -40,11 +40,11 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
|
42
42
|
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
|
43
|
-
"@fortawesome/react-fontawesome": "
|
|
43
|
+
"@fortawesome/react-fontawesome": ">=0.2.6 <3.0.0",
|
|
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.9.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
|
@@ -101,3 +101,23 @@ button.jp-mod-styled.jp-mod-reject {
|
|
|
101
101
|
left: 0px;
|
|
102
102
|
margin: 5px;
|
|
103
103
|
}
|
|
104
|
+
|
|
105
|
+
@media (max-width: 768px) {
|
|
106
|
+
.jgis-panels-wrapper {
|
|
107
|
+
position: fixed;
|
|
108
|
+
top: 30px;
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
align-items: flex-start;
|
|
112
|
+
gap: 10px;
|
|
113
|
+
margin: 0 5px;
|
|
114
|
+
z-index: 1000;
|
|
115
|
+
width: 60%;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.jgis-left-panel-container,
|
|
119
|
+
.jgis-right-panel-container {
|
|
120
|
+
position: relative;
|
|
121
|
+
margin: 0;
|
|
122
|
+
}
|
|
123
|
+
}
|