@jupytergis/base 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/lib/annotations/components/Annotation.d.ts +11 -0
  2. package/lib/annotations/components/Annotation.js +61 -0
  3. package/lib/annotations/components/AnnotationFloater.d.ts +7 -0
  4. package/lib/annotations/components/AnnotationFloater.js +30 -0
  5. package/lib/annotations/components/Message.d.ts +8 -0
  6. package/lib/annotations/components/Message.js +17 -0
  7. package/lib/annotations/index.d.ts +3 -0
  8. package/lib/annotations/index.js +3 -0
  9. package/lib/annotations/model.d.ts +28 -0
  10. package/lib/annotations/model.js +67 -0
  11. package/lib/classificationModes.d.ts +13 -0
  12. package/lib/classificationModes.js +326 -0
  13. package/lib/commands.js +52 -7
  14. package/lib/constants.d.ts +2 -0
  15. package/lib/constants.js +5 -1
  16. package/lib/dialogs/symbology/classificationModes.d.ts +13 -0
  17. package/lib/dialogs/symbology/classificationModes.js +326 -0
  18. package/lib/dialogs/symbology/components/color_ramp/CanvasSelectComponent.d.ts +11 -0
  19. package/lib/dialogs/symbology/components/color_ramp/CanvasSelectComponent.js +119 -0
  20. package/lib/dialogs/symbology/components/color_ramp/ColorRamp.d.ts +15 -0
  21. package/lib/dialogs/symbology/components/color_ramp/ColorRamp.js +33 -0
  22. package/lib/dialogs/symbology/components/color_ramp/ColorRampEntry.d.ts +9 -0
  23. package/lib/dialogs/symbology/components/color_ramp/ColorRampEntry.js +24 -0
  24. package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.d.ts +10 -0
  25. package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.js +11 -0
  26. package/lib/dialogs/symbology/components/color_stops/StopContainer.d.ts +9 -0
  27. package/lib/dialogs/symbology/components/color_stops/StopContainer.js +28 -0
  28. package/lib/dialogs/{components/symbology → symbology/components/color_stops}/StopRow.js +9 -2
  29. package/lib/dialogs/symbology/hooks/useGetProperties.d.ts +12 -0
  30. package/lib/dialogs/symbology/hooks/useGetProperties.js +47 -0
  31. package/lib/dialogs/{symbologyDialog.js → symbology/symbologyDialog.js} +3 -3
  32. package/lib/dialogs/symbology/symbologyUtils.d.ts +9 -0
  33. package/lib/dialogs/symbology/symbologyUtils.js +94 -0
  34. package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +4 -0
  35. package/lib/dialogs/{components/symbology/BandRendering.js → symbology/tiff_layer/TiffRendering.js} +3 -3
  36. package/lib/dialogs/{components/symbology → symbology/tiff_layer/components}/BandRow.d.ts +1 -1
  37. package/lib/dialogs/{components/symbology → symbology/tiff_layer/types}/SingleBandPseudoColor.d.ts +9 -1
  38. package/lib/dialogs/{components/symbology → symbology/tiff_layer/types}/SingleBandPseudoColor.js +131 -83
  39. package/lib/dialogs/{components/symbology → symbology/vector_layer}/VectorRendering.d.ts +1 -1
  40. package/lib/dialogs/{components/symbology → symbology/vector_layer}/VectorRendering.js +10 -13
  41. package/lib/dialogs/symbology/vector_layer/components/ValueSelect.d.ts +8 -0
  42. package/lib/dialogs/symbology/vector_layer/components/ValueSelect.js +7 -0
  43. package/lib/dialogs/symbology/vector_layer/types/Categorized.d.ts +4 -0
  44. package/lib/dialogs/symbology/vector_layer/types/Categorized.js +94 -0
  45. package/lib/dialogs/symbology/vector_layer/types/Graduated.js +169 -0
  46. package/lib/dialogs/{components/symbology → symbology/vector_layer/types}/SimpleSymbol.js +8 -13
  47. package/lib/formbuilder/formselectors.js +4 -0
  48. package/lib/formbuilder/objectform/baseform.d.ts +1 -1
  49. package/lib/formbuilder/objectform/baseform.js +31 -42
  50. package/lib/formbuilder/objectform/geojsonsource.js +33 -30
  51. package/lib/formbuilder/objectform/geotiffsource.d.ts +16 -0
  52. package/lib/formbuilder/objectform/geotiffsource.js +71 -0
  53. package/lib/formbuilder/objectform/vectorlayerform.js +1 -0
  54. package/lib/formbuilder/objectform/webGlLayerForm.js +1 -0
  55. package/lib/index.d.ts +7 -4
  56. package/lib/index.js +7 -4
  57. package/lib/mainview/CollaboratorPointers.d.ts +17 -0
  58. package/lib/mainview/CollaboratorPointers.js +37 -0
  59. package/lib/mainview/FollowIndicator.d.ts +7 -0
  60. package/lib/mainview/FollowIndicator.js +9 -0
  61. package/lib/mainview/mainView.d.ts +39 -3
  62. package/lib/mainview/mainView.js +451 -41
  63. package/lib/mainview/mainviewmodel.d.ts +2 -1
  64. package/lib/mainview/mainviewmodel.js +5 -0
  65. package/lib/panelview/annotationPanel.d.ts +27 -0
  66. package/lib/panelview/annotationPanel.js +45 -0
  67. package/lib/panelview/components/filter-panel/Filter.d.ts +7 -2
  68. package/lib/panelview/components/filter-panel/Filter.js +1 -1
  69. package/lib/panelview/components/filter-panel/FilterRow.js +3 -3
  70. package/lib/panelview/components/identify-panel/IdentifyPanel.d.ts +15 -0
  71. package/lib/panelview/components/identify-panel/IdentifyPanel.js +108 -0
  72. package/lib/panelview/components/layers.js +4 -4
  73. package/lib/panelview/leftpanel.js +8 -0
  74. package/lib/panelview/rightpanel.d.ts +4 -1
  75. package/lib/panelview/rightpanel.js +28 -7
  76. package/lib/store.d.ts +9 -0
  77. package/lib/store.js +25 -0
  78. package/lib/toolbar/widget.js +12 -2
  79. package/lib/tools.d.ts +35 -0
  80. package/lib/tools.js +86 -0
  81. package/lib/types.d.ts +14 -0
  82. package/package.json +18 -20
  83. package/style/base.css +4 -8
  84. package/style/dialog.css +1 -1
  85. package/style/icons/logo_mini.svg +70 -148
  86. package/style/icons/nonvisibility.svg +2 -7
  87. package/style/icons/visibility.svg +2 -6
  88. package/style/leftPanel.css +5 -0
  89. package/style/symbologyDialog.css +104 -3
  90. package/lib/dialogs/components/symbology/BandRendering.d.ts +0 -4
  91. package/lib/dialogs/components/symbology/Graduated.js +0 -188
  92. /package/lib/dialogs/{components/symbology → symbology/components/color_stops}/StopRow.d.ts +0 -0
  93. /package/lib/dialogs/{symbologyDialog.d.ts → symbology/symbologyDialog.d.ts} +0 -0
  94. /package/lib/dialogs/{components/symbology → symbology/tiff_layer/components}/BandRow.js +0 -0
  95. /package/lib/dialogs/{components/symbology → symbology/vector_layer/types}/Graduated.d.ts +0 -0
  96. /package/lib/dialogs/{components/symbology → symbology/vector_layer/types}/SimpleSymbol.d.ts +0 -0
@@ -1,4 +1,4 @@
1
- import { IJupyterGISModel } from '@jupytergis/schema';
1
+ import { IAnnotation, IJupyterGISModel } from '@jupytergis/schema';
2
2
  import { ObservableMap } from '@jupyterlab/observables';
3
3
  import { JSONValue } from '@lumino/coreutils';
4
4
  import { IDisposable } from '@lumino/disposable';
@@ -10,6 +10,7 @@ export declare class MainViewModel implements IDisposable {
10
10
  get viewSettingChanged(): import("@lumino/signaling").ISignal<ObservableMap<JSONValue>, import("@jupyterlab/observables").IObservableMap.IChangedArgs<JSONValue>>;
11
11
  dispose(): void;
12
12
  initSignal(): void;
13
+ addAnnotation(value: IAnnotation): void;
13
14
  private _onsharedLayersChanged;
14
15
  private _jGISModel;
15
16
  private _viewSetting;
@@ -1,3 +1,4 @@
1
+ import { v4 as uuid } from 'uuid';
1
2
  export class MainViewModel {
2
3
  constructor(options) {
3
4
  this._isDisposed = false;
@@ -26,6 +27,10 @@ export class MainViewModel {
26
27
  initSignal() {
27
28
  this._jGISModel.sharedLayersChanged.connect(this._onsharedLayersChanged, this);
28
29
  }
30
+ addAnnotation(value) {
31
+ var _a;
32
+ (_a = this._jGISModel.annotationModel) === null || _a === void 0 ? void 0 : _a.addAnnotation(uuid(), value);
33
+ }
29
34
  async _onsharedLayersChanged(_, change) {
30
35
  if (change.layerChange) {
31
36
  // TODO STUFF with the new updated shared model
@@ -0,0 +1,27 @@
1
+ import { PanelWithToolbar } from '@jupyterlab/ui-components';
2
+ import { Component } from 'react';
3
+ import { IAnnotationModel } from '@jupytergis/schema';
4
+ import { IControlPanelModel } from '../types';
5
+ interface IAnnotationPanelProps {
6
+ annotationModel: IAnnotationModel;
7
+ rightPanelModel: IControlPanelModel;
8
+ }
9
+ export declare class AnnotationsPanel extends Component<IAnnotationPanelProps> {
10
+ constructor(props: IAnnotationPanelProps);
11
+ render(): JSX.Element;
12
+ private _annotationModel;
13
+ private _rightPanelModel;
14
+ }
15
+ export declare class Annotations extends PanelWithToolbar {
16
+ constructor(options: Annotations.IOptions);
17
+ private _widget;
18
+ private _annotationModel;
19
+ private _rightPanelModel;
20
+ }
21
+ export declare namespace Annotations {
22
+ interface IOptions {
23
+ annotationModel: IAnnotationModel;
24
+ rightPanelModel: IControlPanelModel;
25
+ }
26
+ }
27
+ export {};
@@ -0,0 +1,45 @@
1
+ import { PanelWithToolbar, ReactWidget } from '@jupyterlab/ui-components';
2
+ import React, { Component } from 'react';
3
+ import Annotation from '../annotations/components/Annotation';
4
+ export class AnnotationsPanel extends Component {
5
+ constructor(props) {
6
+ super(props);
7
+ const updateCallback = () => {
8
+ this.forceUpdate();
9
+ };
10
+ this._annotationModel = props.annotationModel;
11
+ this._rightPanelModel = props.rightPanelModel;
12
+ this._annotationModel.contextChanged.connect(async () => {
13
+ var _a, _b, _c, _d, _e, _f, _g, _h;
14
+ await ((_b = (_a = this._annotationModel) === null || _a === void 0 ? void 0 : _a.context) === null || _b === void 0 ? void 0 : _b.ready);
15
+ (_e = (_d = (_c = this._annotationModel) === null || _c === void 0 ? void 0 : _c.context) === null || _d === void 0 ? void 0 : _d.model) === null || _e === void 0 ? void 0 : _e.sharedMetadataChanged.disconnect(updateCallback);
16
+ this._annotationModel = props.annotationModel;
17
+ (_h = (_g = (_f = this._annotationModel) === null || _f === void 0 ? void 0 : _f.context) === null || _g === void 0 ? void 0 : _g.model) === null || _h === void 0 ? void 0 : _h.sharedMetadataChanged.connect(updateCallback);
18
+ this.forceUpdate();
19
+ });
20
+ }
21
+ render() {
22
+ var _a;
23
+ const annotationIds = (_a = this._annotationModel) === null || _a === void 0 ? void 0 : _a.getAnnotationIds();
24
+ if (!annotationIds || !this._annotationModel) {
25
+ return React.createElement("div", null);
26
+ }
27
+ const annotations = annotationIds.map((id) => {
28
+ return (React.createElement("div", null,
29
+ React.createElement(Annotation, { rightPanelModel: this._rightPanelModel, annotationModel: this._annotationModel, itemId: id }),
30
+ React.createElement("hr", { className: "jGIS-Annotations-Separator" })));
31
+ });
32
+ return React.createElement("div", null, annotations);
33
+ }
34
+ }
35
+ export class Annotations extends PanelWithToolbar {
36
+ constructor(options) {
37
+ super({});
38
+ this.title.label = 'Annotations';
39
+ this.addClass('jGIS-Annotations');
40
+ this._annotationModel = options.annotationModel;
41
+ this._rightPanelModel = options.rightPanelModel;
42
+ this._widget = ReactWidget.create(React.createElement(AnnotationsPanel, { rightPanelModel: this._rightPanelModel, annotationModel: this._annotationModel }));
43
+ this.addWidget(this._widget);
44
+ }
45
+ }
@@ -2,15 +2,20 @@ import { IJupyterGISTracker } from '@jupytergis/schema';
2
2
  import { Panel } from '@lumino/widgets';
3
3
  import React from 'react';
4
4
  import { IControlPanelModel } from '../../../types';
5
- import { RightPanelWidget } from '../../rightpanel';
6
5
  /**
7
6
  * The filters panel widget.
8
7
  */
9
8
  export declare class FilterPanel extends Panel {
10
- constructor(options: RightPanelWidget.IOptions);
9
+ constructor(options: FilterPanel.IOptions);
11
10
  private _model;
12
11
  private _tracker;
13
12
  }
13
+ export declare namespace FilterPanel {
14
+ interface IOptions {
15
+ model: IControlPanelModel;
16
+ tracker: IJupyterGISTracker;
17
+ }
18
+ }
14
19
  interface IFilterComponentProps {
15
20
  model: IControlPanelModel;
16
21
  tracker: IJupyterGISTracker;
@@ -210,7 +210,7 @@ const FilterComponent = (props) => {
210
210
  };
211
211
  return (React.createElement(React.Fragment, null, shouldDisplay && (React.createElement("div", { className: "jp-gis-filter-main" },
212
212
  React.createElement("div", { id: "filter-container", className: "jp-gis-filter-select-container" },
213
- React.createElement("select", { className: "jp-mod-styled jp-SchemaForm jp-gis-logical-select", onChange: handleLogicalOpChange },
213
+ React.createElement("select", { className: "jp-mod-styled rjsf jp-gis-logical-select", onChange: handleLogicalOpChange },
214
214
  React.createElement("option", { key: "all", value: "all", selected: logicalOp === 'all' }, "All"),
215
215
  React.createElement("option", { key: "any", value: "any", selected: logicalOp === 'any' }, "Any")),
216
216
  filterRows.map((row, index) => (React.createElement(FilterRow, { key: index, index: index, features: featuresInLayer, filterRows: filterRows, setFilterRows: setFilterRows, deleteRow: () => deleteRow(index) })))),
@@ -51,9 +51,9 @@ const FilterRow = ({ index, features, filterRows, setFilterRows, deleteRow }) =>
51
51
  onValueChange(event.target.value);
52
52
  };
53
53
  return (React.createElement("div", { className: "jp-gis-filter-row" },
54
- React.createElement("select", { id: `jp-gis-feature-select-${index}`, className: "jp-mod-styled jp-SchemaForm", onChange: handleKeyChange }, Object.keys(sortedFeatures).map((feature, featureIndex) => (React.createElement("option", { key: featureIndex, value: feature, selected: feature === filterRows[index].feature }, feature)))),
55
- React.createElement("select", { id: `jp-gis-operator-select-${index}`, className: "jp-mod-styled jp-SchemaForm", onChange: handleOperatorChange }, operators.map((operator, operatorIndex) => (React.createElement("option", { key: operatorIndex, value: operator, selected: operator === filterRows[index].operator }, operator)))),
56
- React.createElement("select", { id: `jp-gis-value-select-${index}`, className: "jp-mod-styled jp-SchemaForm", onChange: handleValueChange }, sortedFeatures[selectedFeature] &&
54
+ React.createElement("select", { id: `jp-gis-feature-select-${index}`, className: "jp-mod-styled rjsf", onChange: handleKeyChange }, Object.keys(sortedFeatures).map((feature, featureIndex) => (React.createElement("option", { key: featureIndex, value: feature, selected: feature === filterRows[index].feature }, feature)))),
55
+ React.createElement("select", { id: `jp-gis-operator-select-${index}`, className: "jp-mod-styled rjsf", onChange: handleOperatorChange }, operators.map((operator, operatorIndex) => (React.createElement("option", { key: operatorIndex, value: operator, selected: operator === filterRows[index].operator }, operator)))),
56
+ React.createElement("select", { id: `jp-gis-value-select-${index}`, className: "jp-mod-styled rjsf", onChange: handleValueChange }, sortedFeatures[selectedFeature] &&
57
57
  [...sortedFeatures[selectedFeature]].map((value, valueIndex) => (React.createElement("option", { key: valueIndex, value: value, selected: value === filterRows[index].value }, value)))),
58
58
  React.createElement(Button, { id: `jp-gis-remove-filter-${index}`, className: "jp-Button jp-gis-filter-icon" },
59
59
  React.createElement(FontAwesomeIcon, { icon: faTrash, onClick: deleteRow }))));
@@ -0,0 +1,15 @@
1
+ import { IJupyterGISTracker } from '@jupytergis/schema';
2
+ import { Panel } from '@lumino/widgets';
3
+ import { IControlPanelModel } from '../../../types';
4
+ export declare class IdentifyPanel extends Panel {
5
+ constructor(options: IdentifyPanel.IOptions);
6
+ private _model;
7
+ private _tracker;
8
+ }
9
+ export declare namespace IdentifyPanel {
10
+ interface IOptions {
11
+ model: IControlPanelModel;
12
+ tracker: IJupyterGISTracker;
13
+ }
14
+ }
15
+ export default IdentifyPanel;
@@ -0,0 +1,108 @@
1
+ import { LabIcon, ReactWidget, caretDownIcon } from '@jupyterlab/ui-components';
2
+ import { Panel } from '@lumino/widgets';
3
+ import React, { useEffect, useRef, useState } from 'react';
4
+ export class IdentifyPanel extends Panel {
5
+ constructor(options) {
6
+ super();
7
+ this._model = options.model;
8
+ this._tracker = options.tracker;
9
+ this.id = 'jupytergis::identifyPanel';
10
+ this.title.caption = 'Identify';
11
+ this.title.label = 'Identify';
12
+ this.addWidget(ReactWidget.create(React.createElement(IdentifyPanelComponent, { controlPanelModel: this._model, tracker: this._tracker })));
13
+ }
14
+ }
15
+ const IdentifyPanelComponent = ({ controlPanelModel, tracker }) => {
16
+ var _a;
17
+ const [widgetId, setWidgetId] = useState('');
18
+ const [features, setFeatures] = useState();
19
+ const [visibleFeatures, setVisibleFeatures] = useState({
20
+ 0: true
21
+ });
22
+ const [remoteUser, setRemoteUser] = useState(null);
23
+ const [jgisModel, setJgisModel] = useState(controlPanelModel === null || controlPanelModel === void 0 ? void 0 : controlPanelModel.jGISModel);
24
+ const featuresRef = useRef(features);
25
+ /**
26
+ * Update the model when it changes.
27
+ */
28
+ controlPanelModel === null || controlPanelModel === void 0 ? void 0 : controlPanelModel.documentChanged.connect((_, widget) => {
29
+ setJgisModel(widget === null || widget === void 0 ? void 0 : widget.context.model);
30
+ });
31
+ // Reset state values when current widget changes
32
+ useEffect(() => {
33
+ const handleCurrentChanged = () => {
34
+ var _a;
35
+ if (((_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.id) === widgetId) {
36
+ return;
37
+ }
38
+ if (tracker.currentWidget) {
39
+ setWidgetId(tracker.currentWidget.id);
40
+ }
41
+ setFeatures({});
42
+ setVisibleFeatures({ 0: true });
43
+ };
44
+ tracker.currentChanged.connect(handleCurrentChanged);
45
+ return () => {
46
+ tracker.currentChanged.disconnect(handleCurrentChanged);
47
+ };
48
+ }, []);
49
+ useEffect(() => {
50
+ featuresRef.current = features;
51
+ }, [features]);
52
+ useEffect(() => {
53
+ const handleClientStateChanged = (sender, clients) => {
54
+ var _a, _b, _c, _d, _e, _f;
55
+ const remoteUserId = (_a = jgisModel === null || jgisModel === void 0 ? void 0 : jgisModel.localState) === null || _a === void 0 ? void 0 : _a.remoteUser;
56
+ // If following a collaborator
57
+ if (remoteUserId) {
58
+ const remoteState = clients.get(remoteUserId);
59
+ if (remoteState) {
60
+ if (((_b = remoteState.user) === null || _b === void 0 ? void 0 : _b.username) !== (remoteUser === null || remoteUser === void 0 ? void 0 : remoteUser.username)) {
61
+ setRemoteUser(remoteState.user);
62
+ }
63
+ setFeatures((_d = (_c = remoteState.identifiedFeatures) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : {});
64
+ }
65
+ return;
66
+ }
67
+ // If not following a collaborator
68
+ const identifiedFeatures = (_f = (_e = jgisModel === null || jgisModel === void 0 ? void 0 : jgisModel.localState) === null || _e === void 0 ? void 0 : _e.identifiedFeatures) === null || _f === void 0 ? void 0 : _f.value;
69
+ if (!identifiedFeatures) {
70
+ setFeatures({});
71
+ return;
72
+ }
73
+ if (jgisModel.isIdentifying &&
74
+ featuresRef.current !== identifiedFeatures) {
75
+ setFeatures(identifiedFeatures);
76
+ }
77
+ };
78
+ jgisModel === null || jgisModel === void 0 ? void 0 : jgisModel.clientStateChanged.connect(handleClientStateChanged);
79
+ return () => {
80
+ jgisModel === null || jgisModel === void 0 ? void 0 : jgisModel.clientStateChanged.disconnect(handleClientStateChanged);
81
+ };
82
+ }, [jgisModel]);
83
+ const toggleFeatureVisibility = (index) => {
84
+ console.log('visibleFeatures', visibleFeatures);
85
+ setVisibleFeatures(prev => (Object.assign(Object.assign({}, prev), { [index]: !prev[index] })));
86
+ };
87
+ return (React.createElement("div", { className: "jgis-identify-wrapper", style: {
88
+ border: ((_a = jgisModel === null || jgisModel === void 0 ? void 0 : jgisModel.localState) === null || _a === void 0 ? void 0 : _a.remoteUser)
89
+ ? `solid 3px ${remoteUser === null || remoteUser === void 0 ? void 0 : remoteUser.color}`
90
+ : 'unset'
91
+ } }, features &&
92
+ Object.values(features).map((feature, featureIndex) => (React.createElement("div", { key: featureIndex, className: "jgis-identify-grid-item" },
93
+ React.createElement("div", { className: "jgis-identify-grid-item-header", onClick: () => toggleFeatureVisibility(featureIndex) },
94
+ React.createElement(LabIcon.resolveReact, { icon: caretDownIcon, className: `jp-gis-layerGroupCollapser${visibleFeatures[featureIndex] ? ' jp-mod-expanded' : ''}`, tag: 'span' }),
95
+ React.createElement("span", null,
96
+ "Feature ",
97
+ featureIndex + 1,
98
+ ":")),
99
+ visibleFeatures[featureIndex] && (React.createElement(React.Fragment, null, Object.entries(feature)
100
+ .filter(([key, value]) => typeof value !== 'object' || value === null)
101
+ .map(([key, value]) => (React.createElement("div", { key: key, className: "jgis-identify-grid-body" },
102
+ React.createElement("strong", null,
103
+ key,
104
+ ":"),
105
+ typeof value === 'string' &&
106
+ /<\/?[a-z][\s\S]*>/i.test(value) ? (React.createElement("span", { dangerouslySetInnerHTML: { __html: `${value}` } })) : (React.createElement("span", null, String(value)))))))))))));
107
+ };
108
+ export default IdentifyPanel;
@@ -133,7 +133,7 @@ function LayerGroupComponent(props) {
133
133
  const getExpandedState = async () => {
134
134
  var _a;
135
135
  const groupState = await state.fetch(`jupytergis:${group.name}`);
136
- setOpen((_a = groupState.expanded) !== null && _a !== void 0 ? _a : false);
136
+ setOpen((_a = groupState === null || groupState === void 0 ? void 0 : groupState.expanded) !== null && _a !== void 0 ? _a : false);
137
137
  };
138
138
  getExpandedState();
139
139
  }, []);
@@ -160,8 +160,8 @@ function LayerGroupComponent(props) {
160
160
  setOpen(!open);
161
161
  };
162
162
  return (React.createElement("div", { className: `${LAYER_ITEM_CLASS} ${LAYER_GROUP_CLASS}`, draggable: true, onDragStart: Private.onDragStart, onDragEnd: Private.onDragEnd, "data-id": name },
163
- React.createElement("div", { onClick: handleExpand, onContextMenu: handleRightClick, className: `${LAYER_GROUP_HEADER_CLASS} ${selected ? ' jp-mod-selected' : ''}`, onDragOver: Private.onDragOver, "data-id": name },
164
- React.createElement(LabIcon.resolveReact, { icon: caretDownIcon, className: LAYER_GROUP_COLLAPSER_CLASS + (open ? ' jp-mod-expanded' : ''), tag: 'span' }),
163
+ React.createElement("div", { onClick: handleExpand, onContextMenu: handleRightClick, className: `${LAYER_GROUP_HEADER_CLASS}${selected ? ' jp-mod-selected' : ''}`, onDragOver: Private.onDragOver, "data-id": name },
164
+ React.createElement(LabIcon.resolveReact, { icon: caretDownIcon, className: `${LAYER_GROUP_COLLAPSER_CLASS}${open ? ' jp-mod-expanded' : ''}`, tag: 'span' }),
165
165
  React.createElement("span", { id: id, className: LAYER_TEXT_CLASS }, name)),
166
166
  open && (React.createElement("div", null, layers
167
167
  .slice()
@@ -222,7 +222,7 @@ function LayerComponent(props) {
222
222
  icons.has(layer.type) && (React.createElement(LabIcon.resolveReact, Object.assign({}, icons.get(layer.type), { className: LAYER_ICON_CLASS }))),
223
223
  React.createElement("span", { id: id, className: LAYER_TEXT_CLASS }, name)),
224
224
  React.createElement(Button, { title: layer.visible ? 'Hide layer' : 'Show layer', onClick: toggleVisibility, minimal: true },
225
- React.createElement(LabIcon.resolveReact, { icon: layer.visible ? visibilityIcon : nonVisibilityIcon, className: LAYER_ICON_CLASS, tag: "span" }))));
225
+ React.createElement(LabIcon.resolveReact, { icon: layer.visible ? visibilityIcon : nonVisibilityIcon, className: `${LAYER_ICON_CLASS}${layer.visible ? '' : ' jp-gis-mod-hidden'}`, tag: "span" }))));
226
226
  }
227
227
  var Private;
228
228
  (function (Private) {
@@ -2,6 +2,7 @@ import { SidePanel } from '@jupyterlab/ui-components';
2
2
  import { LayersPanel } from './components/layers';
3
3
  import { SourcesPanel } from './components/sources';
4
4
  import { ControlPanelHeader } from './header';
5
+ import { FilterPanel } from './components/filter-panel/Filter';
5
6
  export class LeftPanelWidget extends SidePanel {
6
7
  constructor(options) {
7
8
  super();
@@ -71,6 +72,13 @@ export class LeftPanelWidget extends SidePanel {
71
72
  layerTree.title.caption = 'Layer tree';
72
73
  layerTree.title.label = 'Layers';
73
74
  this.addWidget(layerTree);
75
+ const filterPanel = new FilterPanel({
76
+ model: this._model,
77
+ tracker: options.tracker
78
+ });
79
+ filterPanel.title.caption = 'Filters';
80
+ filterPanel.title.label = 'Filters';
81
+ this.addWidget(filterPanel);
74
82
  options.tracker.currentChanged.connect((_, changed) => {
75
83
  if (changed) {
76
84
  header.title.label = changed.context.localPath;
@@ -1,16 +1,19 @@
1
- import { IJGISFormSchemaRegistry, IJupyterGISTracker, JupyterGISDoc } from '@jupytergis/schema';
1
+ import { IAnnotationModel, IJGISFormSchemaRegistry, IJupyterGISTracker, JupyterGISDoc } from '@jupytergis/schema';
2
2
  import { SidePanel } from '@jupyterlab/ui-components';
3
3
  import { IControlPanelModel } from '../types';
4
4
  export declare class RightPanelWidget extends SidePanel {
5
5
  constructor(options: RightPanelWidget.IOptions);
6
6
  dispose(): void;
7
+ private _currentContext;
7
8
  private _model;
9
+ private _annotationModel;
8
10
  }
9
11
  export declare namespace RightPanelWidget {
10
12
  interface IOptions {
11
13
  model: IControlPanelModel;
12
14
  tracker: IJupyterGISTracker;
13
15
  formSchemaRegistry: IJGISFormSchemaRegistry;
16
+ annotationModel: IAnnotationModel;
14
17
  }
15
18
  interface IProps {
16
19
  filePath?: string;
@@ -1,12 +1,14 @@
1
1
  import { SidePanel } from '@jupyterlab/ui-components';
2
- import { FilterPanel } from './components/filter-panel/Filter';
3
2
  import { ControlPanelHeader } from './header';
4
3
  import { ObjectProperties } from './objectproperties';
4
+ import { Annotations } from './annotationPanel';
5
+ import IdentifyPanel from './components/identify-panel/IdentifyPanel';
5
6
  export class RightPanelWidget extends SidePanel {
6
7
  constructor(options) {
7
8
  super();
8
9
  this.addClass('jGIS-sidepanel-widget');
9
10
  this._model = options.model;
11
+ this._annotationModel = options.annotationModel;
10
12
  const header = new ControlPanelHeader();
11
13
  this.header.addWidget(header);
12
14
  const properties = new ObjectProperties({
@@ -15,14 +17,18 @@ export class RightPanelWidget extends SidePanel {
15
17
  tracker: options.tracker
16
18
  });
17
19
  this.addWidget(properties);
18
- const filterPanel = new FilterPanel({
20
+ const annotations = new Annotations({
21
+ rightPanelModel: this._model,
22
+ annotationModel: this._annotationModel
23
+ });
24
+ this.addWidget(annotations);
25
+ const identifyPanel = new IdentifyPanel({
19
26
  model: this._model,
20
- tracker: options.tracker,
21
- formSchemaRegistry: options.formSchemaRegistry
27
+ tracker: options.tracker
22
28
  });
23
- filterPanel.title.caption = 'Filters';
24
- filterPanel.title.label = 'Filters';
25
- this.addWidget(filterPanel);
29
+ identifyPanel.title.caption = 'Identify';
30
+ identifyPanel.title.label = 'Identify';
31
+ this.addWidget(identifyPanel);
26
32
  this._model.documentChanged.connect((_, changed) => {
27
33
  if (changed) {
28
34
  if (changed.context.model.sharedModel.editable) {
@@ -38,6 +44,21 @@ export class RightPanelWidget extends SidePanel {
38
44
  header.title.label = '-';
39
45
  }
40
46
  });
47
+ options.tracker.currentChanged.connect(async (_, changed) => {
48
+ var _a;
49
+ if (changed) {
50
+ this._currentContext = changed.context;
51
+ header.title.label = this._currentContext.localPath;
52
+ this._annotationModel.context =
53
+ ((_a = options.tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.context) || undefined;
54
+ await changed.context.ready;
55
+ }
56
+ else {
57
+ header.title.label = '-';
58
+ this._currentContext = null;
59
+ this._annotationModel.context = undefined;
60
+ }
61
+ });
41
62
  }
42
63
  dispose() {
43
64
  super.dispose();
package/lib/store.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { IStateDB } from '@jupyterlab/statedb';
2
+ export declare class GlobalStateDbManager {
3
+ private static instance;
4
+ private stateDb;
5
+ private constructor();
6
+ static getInstance(): GlobalStateDbManager;
7
+ initialize(stateDb: IStateDB): void;
8
+ getStateDb(): IStateDB | null;
9
+ }
package/lib/store.js ADDED
@@ -0,0 +1,25 @@
1
+ // Singleton class to manage Jupyter stateDb globally
2
+ export class GlobalStateDbManager {
3
+ // Private constructor to prevent direct instantiation
4
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
5
+ constructor() {
6
+ this.stateDb = null;
7
+ }
8
+ static getInstance() {
9
+ if (!GlobalStateDbManager.instance) {
10
+ GlobalStateDbManager.instance = new GlobalStateDbManager();
11
+ }
12
+ return GlobalStateDbManager.instance;
13
+ }
14
+ initialize(stateDb) {
15
+ if (this.stateDb === null) {
16
+ this.stateDb = stateDb;
17
+ }
18
+ else {
19
+ console.warn('stateDb is already initialized.');
20
+ }
21
+ }
22
+ getStateDb() {
23
+ return this.stateDb;
24
+ }
25
+ }
@@ -55,10 +55,9 @@ export class ToolbarWidget extends ReactiveToolbar {
55
55
  label: '',
56
56
  commands: options.commands
57
57
  }));
58
- this.addItem('separator2', new Separator());
59
58
  const NewButton = new ToolbarButton({
60
59
  icon: addIcon,
61
- actualOnClick: true,
60
+ noFocusOnClick: false,
62
61
  onClick: () => {
63
62
  if (!options.commands) {
64
63
  return;
@@ -96,6 +95,17 @@ export class ToolbarWidget extends ReactiveToolbar {
96
95
  }
97
96
  });
98
97
  this.addItem('New', NewButton);
98
+ this.addItem('separator2', new Separator());
99
+ this.addItem('identify', new CommandToolbarButton({
100
+ id: CommandIDs.identify,
101
+ label: '',
102
+ commands: options.commands
103
+ }));
104
+ options.commands.addKeyBinding({
105
+ command: CommandIDs.identify,
106
+ keys: ['Escape'],
107
+ selector: '#main'
108
+ });
99
109
  this.addItem('spacer', ReactiveToolbar.createSpacerItem());
100
110
  // Users
101
111
  this.addItem('users', ReactWidget.create(React.createElement(UsersItem, { model: options.model })));
package/lib/tools.d.ts CHANGED
@@ -32,3 +32,38 @@ export interface IParsedStyle {
32
32
  radius?: number;
33
33
  }
34
34
  export declare function parseColor(type: string, style: any): IParsedStyle | undefined;
35
+ /**
36
+ * Open or create an IndexedDB database for caching GeoTIFF files.
37
+ *
38
+ * @returns A promise that resolves to the opened IndexedDB database instance.
39
+ */
40
+ export declare const openDatabase: () => Promise<IDBDatabase>;
41
+ /**
42
+ * Save a file and its metadata to the IndexedDB database.
43
+ *
44
+ * @param key file ID (sourceUrl).
45
+ * @param file Blob object representing the file content.
46
+ * @param metadata metadata of file.
47
+ * @returns A promise that resolves once the data is successfully saved.
48
+ */
49
+ export declare const saveToIndexedDB: (key: string, file: Blob, metadata: any) => Promise<void>;
50
+ /**
51
+ * Retrieve a file and its metadata from the IndexedDB database.
52
+ *
53
+ * @param key fileID (sourceUrl).
54
+ * @returns A promise that resolves to the stored data object or undefined.
55
+ */
56
+ export declare const getFromIndexedDB: (key: string) => Promise<any>;
57
+ /**
58
+ * Load a GeoTIFF file from IndexedDB database cache or fetch it .
59
+ *
60
+ * @param sourceInfo object containing the URL of the GeoTIFF file.
61
+ * @returns A promise that resolves to the file as a Blob, or undefined .
62
+ */
63
+ export declare const loadGeoTIFFWithCache: (sourceInfo: {
64
+ url?: string | undefined;
65
+ }) => Promise<{
66
+ file: Blob;
67
+ metadata: any;
68
+ sourceUrl: string;
69
+ } | null>;
package/lib/tools.js CHANGED
@@ -4,6 +4,7 @@ import { URLExt } from '@jupyterlab/coreutils';
4
4
  import { ServerConnection } from '@jupyterlab/services';
5
5
  import * as d3Color from 'd3-color';
6
6
  import RASTER_LAYER_GALLERY from '../rasterlayer_gallery/raster_layer_gallery.json';
7
+ import { getGdal } from './gdal';
7
8
  export const debounce = (func, timeout = 100) => {
8
9
  let timeoutId;
9
10
  return (...args) => {
@@ -251,3 +252,88 @@ export function parseColor(type, style) {
251
252
  });
252
253
  return parsedStyle;
253
254
  }
255
+ /**
256
+ * Open or create an IndexedDB database for caching GeoTIFF files.
257
+ *
258
+ * @returns A promise that resolves to the opened IndexedDB database instance.
259
+ */
260
+ export const openDatabase = () => {
261
+ return new Promise((resolve, reject) => {
262
+ const request = indexedDB.open('GeoTIFFCache', 1);
263
+ request.onupgradeneeded = event => {
264
+ const db = request.result;
265
+ if (!db.objectStoreNames.contains('files')) {
266
+ db.createObjectStore('files', { keyPath: 'url' });
267
+ }
268
+ };
269
+ request.onsuccess = () => resolve(request.result);
270
+ request.onerror = () => reject(request.error);
271
+ });
272
+ };
273
+ /**
274
+ * Save a file and its metadata to the IndexedDB database.
275
+ *
276
+ * @param key file ID (sourceUrl).
277
+ * @param file Blob object representing the file content.
278
+ * @param metadata metadata of file.
279
+ * @returns A promise that resolves once the data is successfully saved.
280
+ */
281
+ export const saveToIndexedDB = async (key, file, metadata) => {
282
+ const db = await openDatabase();
283
+ return new Promise((resolve, reject) => {
284
+ const transaction = db.transaction('files', 'readwrite');
285
+ const store = transaction.objectStore('files');
286
+ store.put({ url: key, file, metadata });
287
+ transaction.oncomplete = () => resolve();
288
+ transaction.onerror = () => reject(transaction.error);
289
+ });
290
+ };
291
+ /**
292
+ * Retrieve a file and its metadata from the IndexedDB database.
293
+ *
294
+ * @param key fileID (sourceUrl).
295
+ * @returns A promise that resolves to the stored data object or undefined.
296
+ */
297
+ export const getFromIndexedDB = async (key) => {
298
+ const db = await openDatabase();
299
+ return new Promise((resolve, reject) => {
300
+ const transaction = db.transaction('files', 'readonly');
301
+ const store = transaction.objectStore('files');
302
+ const request = store.get(key);
303
+ request.onsuccess = () => resolve(request.result);
304
+ request.onerror = () => reject(request.error);
305
+ });
306
+ };
307
+ /**
308
+ * Load a GeoTIFF file from IndexedDB database cache or fetch it .
309
+ *
310
+ * @param sourceInfo object containing the URL of the GeoTIFF file.
311
+ * @returns A promise that resolves to the file as a Blob, or undefined .
312
+ */
313
+ export const loadGeoTIFFWithCache = async (sourceInfo) => {
314
+ if (!(sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.url)) {
315
+ return null;
316
+ }
317
+ const cachedData = await getFromIndexedDB(sourceInfo.url);
318
+ if (cachedData) {
319
+ return {
320
+ file: new Blob([cachedData.file]),
321
+ metadata: cachedData.metadata,
322
+ sourceUrl: sourceInfo.url
323
+ };
324
+ }
325
+ const response = await fetch(sourceInfo.url);
326
+ const fileBlob = await response.blob();
327
+ const file = new File([fileBlob], 'loaded.tif');
328
+ const Gdal = await getGdal();
329
+ const result = await Gdal.open(file);
330
+ const tifDataset = result.datasets[0];
331
+ const metadata = await Gdal.gdalinfo(tifDataset, ['-stats']);
332
+ Gdal.close(tifDataset);
333
+ await saveToIndexedDB(sourceInfo.url, fileBlob, metadata);
334
+ return {
335
+ file: fileBlob,
336
+ metadata,
337
+ sourceUrl: sourceInfo.url
338
+ };
339
+ };
package/lib/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { IDict, IJupyterGISDoc, IJupyterGISModel, IJupyterGISTracker, IJupyterGISWidget } from '@jupytergis/schema';
2
2
  import { ISignal } from '@lumino/signaling';
3
+ import { Map } from 'ol';
3
4
  export { IDict };
4
5
  export type ValueOf<T> = T[keyof T];
5
6
  export interface IControlPanelModel {
@@ -9,3 +10,16 @@ export interface IControlPanelModel {
9
10
  jGISModel: IJupyterGISModel | undefined;
10
11
  sharedModel: IJupyterGISDoc | undefined;
11
12
  }
13
+ /**
14
+ * Add jupytergisMaps object to the global variables.
15
+ */
16
+ declare global {
17
+ interface Window {
18
+ /**
19
+ * Access JupyterGIS map
20
+ */
21
+ jupytergisMaps: {
22
+ [name: string]: Map;
23
+ };
24
+ }
25
+ }