@jupytergis/base 0.8.1 → 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.
@@ -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';
@@ -87,14 +87,17 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
87
87
  'WebGlLayer',
88
88
  'VectorTileLayer',
89
89
  ].includes(selectedLayer.type);
90
- const isIdentifying = current.model.isIdentifying;
91
- if (isIdentifying && !canIdentify) {
92
- current.model.isIdentifying = false;
90
+ if (current.model.currentMode === 'identifying' && !canIdentify) {
91
+ current.model.currentMode = 'panning';
93
92
  current.node.classList.remove('jGIS-identify-tool');
94
93
  return false;
95
94
  }
96
- return isIdentifying;
95
+ return current.model.currentMode === 'identifying';
97
96
  }, isEnabled: () => {
97
+ var _a;
98
+ if ((_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model.jgisSettings.identifyDisabled) {
99
+ return false;
100
+ }
98
101
  const selectedLayer = getSingleSelectedLayer(tracker);
99
102
  if (!selectedLayer) {
100
103
  return false;
@@ -114,7 +117,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
114
117
  if (luminoEvent) {
115
118
  const keysPressed = luminoEvent.keys;
116
119
  if (keysPressed === null || keysPressed === void 0 ? void 0 : keysPressed.includes('Escape')) {
117
- current.model.isIdentifying = false;
120
+ current.model.currentMode = 'panning';
118
121
  current.node.classList.remove('jGIS-identify-tool');
119
122
  commands.notifyCommandChanged(CommandIDs.identify);
120
123
  return;
@@ -652,6 +655,165 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
652
655
  },
653
656
  icon: targetWithCenterIcon,
654
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
+ });
655
817
  loadKeybindings(commands, keybindings);
656
818
  }
657
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
- }, [selectedRamp, heatmapOptions]);
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
- const colorMap = colormap({
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
@@ -11,5 +11,3 @@ export async function getGdal() {
11
11
  useWorker: false,
12
12
  });
13
13
  }
14
- // Early load gdal
15
- getGdal();
@@ -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.isIdentifying;
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
- const isIdentifying = this._model.isIdentifying;
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
  });
@@ -740,9 +739,7 @@ export class MainView extends React.Component {
740
739
  const format = new GeoJSON({
741
740
  featureProjection: this._Map.getView().getProjection(),
742
741
  });
743
- // TODO: Don't hardcode projection
744
742
  const featureArray = format.readFeatures(data, {
745
- dataProjection: 'EPSG:4326',
746
743
  featureProjection: this._Map.getView().getProjection(),
747
744
  });
748
745
  const featureCollection = new Collection(featureArray);
@@ -1580,7 +1577,7 @@ export class MainView extends React.Component {
1580
1577
  }
1581
1578
  _identifyFeature(e) {
1582
1579
  var _a, _b;
1583
- if (!this._model.isIdentifying) {
1580
+ if (this._model.currentMode !== 'identifying') {
1584
1581
  return;
1585
1582
  }
1586
1583
  const localState = (_a = this._model) === null || _a === void 0 ? void 0 : _a.sharedModel.awareness.getLocalState();
@@ -1708,7 +1705,8 @@ export class MainView extends React.Component {
1708
1705
  height: '100%',
1709
1706
  } })),
1710
1707
  React.createElement(StatusBar, { jgisModel: this._model, loading: this.state.loadingLayer, projection: this.state.viewProjection, scale: this.state.scale })),
1711
- this._state && (React.createElement(LeftPanel, { model: this._model, commands: this._mainViewModel.commands, state: this._state })),
1712
- this._formSchemaRegistry && this._annotationModel && (React.createElement(RightPanel, { model: this._model, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel }))));
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 })))));
1713
1711
  }
1714
1712
  }
@@ -35,7 +35,8 @@ export const IdentifyPanelComponent = ({ model, }) => {
35
35
  setFeatures({});
36
36
  return;
37
37
  }
38
- if (model.isIdentifying && featuresRef.current !== identifiedFeatures) {
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,6 @@
1
+ import { IJupyterGISModel } from '@jupytergis/schema';
2
+ import React from 'react';
3
+ export declare const LegendItem: React.FC<{
4
+ layerId: string;
5
+ model: IJupyterGISModel;
6
+ }>;
@@ -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
+ };
@@ -1,34 +1,45 @@
1
- import { PageConfig } from '@jupyterlab/coreutils';
2
1
  import * as React from 'react';
3
2
  import { LayersBodyComponent } from './components/layers';
4
3
  import { PanelTabs, TabsContent, TabsList, TabsTrigger, } from '../shared/components/Tabs';
5
4
  import StacPanel from '../stacBrowser/components/StacPanel';
6
5
  import FilterComponent from './components/filter-panel/Filter';
7
6
  export const LeftPanel = (props) => {
8
- const hideStacPanel = PageConfig.getOption('HIDE_STAC_PANEL') === 'true';
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;
9
21
  const tabInfo = [
10
- { name: 'layers', title: 'Layers' },
11
- ...(hideStacPanel ? [] : [{ name: 'stac', title: 'Stac Browser' }]),
12
- { name: 'filters', title: 'Filters' },
13
- ];
14
- const [curTab, setCurTab] = React.useState(tabInfo[0].name);
15
- return (React.createElement("div", { className: "jgis-left-panel-container" },
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' } },
16
30
  React.createElement(PanelTabs, { curTab: curTab, className: "jgis-panel-tabs" },
17
- React.createElement(TabsList, null, tabInfo.map(e => {
18
- return (React.createElement(TabsTrigger, { className: "jGIS-layer-browser-category", value: e.name, onClick: () => {
19
- if (curTab !== e.name) {
20
- setCurTab(e.name);
21
- }
22
- else {
23
- setCurTab('');
24
- }
25
- } }, e.title));
26
- })),
27
- React.createElement(TabsContent, { value: "layers", className: "jgis-panel-tab-content jp-gis-layerPanel" },
28
- React.createElement(LayersBodyComponent, { model: props.model, commands: props.commands, state: props.state })),
29
- !hideStacPanel && (React.createElement(TabsContent, { value: "stac" },
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" },
30
42
  React.createElement(StacPanel, { model: props.model }))),
31
- React.createElement(TabsContent, { value: "filters", className: "jgis-panel-tab-content" },
32
- React.createElement(FilterComponent, { model: props.model }),
33
- ","))));
43
+ !settings.filtersDisabled && (React.createElement(TabsContent, { value: "filters", className: "jgis-panel-tab-content" },
44
+ React.createElement(FilterComponent, { model: props.model }))))));
34
45
  };
@@ -1,36 +1,64 @@
1
- import { PageConfig } from '@jupyterlab/coreutils';
2
1
  import * as React from 'react';
3
2
  import { AnnotationsPanel } from './annotationPanel';
4
3
  import { IdentifyPanelComponent } from './components/identify-panel/IdentifyPanel';
5
4
  import { ObjectPropertiesReact } from './objectproperties';
6
5
  import { PanelTabs, TabsContent, TabsList, TabsTrigger, } from '../shared/components/Tabs';
7
6
  export const RightPanel = props => {
8
- const hideAnnotationPanel = PageConfig.getOption('HIDE_ANNOTATION_PANEL') === 'true';
9
- const [selectedObjectProperties, setSelectedObjectProperties] = React.useState(undefined);
7
+ const [settings, setSettings] = React.useState(props.model.jgisSettings);
10
8
  const tabInfo = [
11
- { name: 'objectProperties', title: 'Object Properties' },
12
- ...(hideAnnotationPanel
13
- ? []
14
- : [{ name: 'annotations', title: 'Annotations' }]),
15
- { name: 'identifyPanel', title: 'Identify Features' },
16
- ];
17
- const [curTab, setCurTab] = React.useState(tabInfo[0].name);
18
- return (React.createElement("div", { className: "jgis-right-panel-container" },
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' } },
19
49
  React.createElement(PanelTabs, { className: "jgis-panel-tabs", curTab: curTab },
20
- React.createElement(TabsList, null, tabInfo.map(e => {
21
- return (React.createElement(TabsTrigger, { className: "jGIS-layer-browser-category", value: e.name, onClick: () => {
22
- if (curTab !== e.name) {
23
- setCurTab(e.name);
24
- }
25
- else {
26
- setCurTab('');
27
- }
28
- } }, e.title));
29
- })),
30
- React.createElement(TabsContent, { value: "objectProperties", className: "jgis-panel-tab-content" },
31
- React.createElement(ObjectPropertiesReact, { setSelectedObject: setSelectedObjectProperties, selectedObject: selectedObjectProperties, formSchemaRegistry: props.formSchemaRegistry, model: props.model })),
32
- React.createElement(TabsContent, { value: "annotations" },
33
- React.createElement(AnnotationsPanel, { annotationModel: props.annotationModel, jgisModel: props.model })),
34
- React.createElement(TabsContent, { value: "identifyPanel", className: "jgis-panel-tab-content" },
35
- 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 }))))));
36
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
- } | null | undefined>;
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.8.1",
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": "latest",
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.8.1",
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
+ }
@@ -41,7 +41,7 @@
41
41
  .jp-gis-source {
42
42
  display: flex;
43
43
  flex-direction: row;
44
- align-items: center;
44
+ /* align-items: center; */
45
45
  color: var(--jp-ui-font-color1);
46
46
  }
47
47