@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.
- package/lib/annotations/components/Annotation.d.ts +11 -0
- package/lib/annotations/components/Annotation.js +61 -0
- package/lib/annotations/components/AnnotationFloater.d.ts +7 -0
- package/lib/annotations/components/AnnotationFloater.js +30 -0
- package/lib/annotations/components/Message.d.ts +8 -0
- package/lib/annotations/components/Message.js +17 -0
- package/lib/annotations/index.d.ts +3 -0
- package/lib/annotations/index.js +3 -0
- package/lib/annotations/model.d.ts +28 -0
- package/lib/annotations/model.js +67 -0
- package/lib/classificationModes.d.ts +13 -0
- package/lib/classificationModes.js +326 -0
- package/lib/commands.js +52 -7
- package/lib/constants.d.ts +2 -0
- package/lib/constants.js +5 -1
- package/lib/dialogs/symbology/classificationModes.d.ts +13 -0
- package/lib/dialogs/symbology/classificationModes.js +326 -0
- package/lib/dialogs/symbology/components/color_ramp/CanvasSelectComponent.d.ts +11 -0
- package/lib/dialogs/symbology/components/color_ramp/CanvasSelectComponent.js +119 -0
- package/lib/dialogs/symbology/components/color_ramp/ColorRamp.d.ts +15 -0
- package/lib/dialogs/symbology/components/color_ramp/ColorRamp.js +33 -0
- package/lib/dialogs/symbology/components/color_ramp/ColorRampEntry.d.ts +9 -0
- package/lib/dialogs/symbology/components/color_ramp/ColorRampEntry.js +24 -0
- package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.d.ts +10 -0
- package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.js +11 -0
- package/lib/dialogs/symbology/components/color_stops/StopContainer.d.ts +9 -0
- package/lib/dialogs/symbology/components/color_stops/StopContainer.js +28 -0
- package/lib/dialogs/{components/symbology → symbology/components/color_stops}/StopRow.js +9 -2
- package/lib/dialogs/symbology/hooks/useGetProperties.d.ts +12 -0
- package/lib/dialogs/symbology/hooks/useGetProperties.js +47 -0
- package/lib/dialogs/{symbologyDialog.js → symbology/symbologyDialog.js} +3 -3
- package/lib/dialogs/symbology/symbologyUtils.d.ts +9 -0
- package/lib/dialogs/symbology/symbologyUtils.js +94 -0
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +4 -0
- package/lib/dialogs/{components/symbology/BandRendering.js → symbology/tiff_layer/TiffRendering.js} +3 -3
- package/lib/dialogs/{components/symbology → symbology/tiff_layer/components}/BandRow.d.ts +1 -1
- package/lib/dialogs/{components/symbology → symbology/tiff_layer/types}/SingleBandPseudoColor.d.ts +9 -1
- package/lib/dialogs/{components/symbology → symbology/tiff_layer/types}/SingleBandPseudoColor.js +131 -83
- package/lib/dialogs/{components/symbology → symbology/vector_layer}/VectorRendering.d.ts +1 -1
- package/lib/dialogs/{components/symbology → symbology/vector_layer}/VectorRendering.js +10 -13
- package/lib/dialogs/symbology/vector_layer/components/ValueSelect.d.ts +8 -0
- package/lib/dialogs/symbology/vector_layer/components/ValueSelect.js +7 -0
- package/lib/dialogs/symbology/vector_layer/types/Categorized.d.ts +4 -0
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +94 -0
- package/lib/dialogs/symbology/vector_layer/types/Graduated.js +169 -0
- package/lib/dialogs/{components/symbology → symbology/vector_layer/types}/SimpleSymbol.js +8 -13
- package/lib/formbuilder/formselectors.js +4 -0
- package/lib/formbuilder/objectform/baseform.d.ts +1 -1
- package/lib/formbuilder/objectform/baseform.js +31 -42
- package/lib/formbuilder/objectform/geojsonsource.js +33 -30
- package/lib/formbuilder/objectform/geotiffsource.d.ts +16 -0
- package/lib/formbuilder/objectform/geotiffsource.js +71 -0
- package/lib/formbuilder/objectform/vectorlayerform.js +1 -0
- package/lib/formbuilder/objectform/webGlLayerForm.js +1 -0
- package/lib/index.d.ts +7 -4
- package/lib/index.js +7 -4
- package/lib/mainview/CollaboratorPointers.d.ts +17 -0
- package/lib/mainview/CollaboratorPointers.js +37 -0
- package/lib/mainview/FollowIndicator.d.ts +7 -0
- package/lib/mainview/FollowIndicator.js +9 -0
- package/lib/mainview/mainView.d.ts +39 -3
- package/lib/mainview/mainView.js +451 -41
- package/lib/mainview/mainviewmodel.d.ts +2 -1
- package/lib/mainview/mainviewmodel.js +5 -0
- package/lib/panelview/annotationPanel.d.ts +27 -0
- package/lib/panelview/annotationPanel.js +45 -0
- package/lib/panelview/components/filter-panel/Filter.d.ts +7 -2
- package/lib/panelview/components/filter-panel/Filter.js +1 -1
- package/lib/panelview/components/filter-panel/FilterRow.js +3 -3
- package/lib/panelview/components/identify-panel/IdentifyPanel.d.ts +15 -0
- package/lib/panelview/components/identify-panel/IdentifyPanel.js +108 -0
- package/lib/panelview/components/layers.js +4 -4
- package/lib/panelview/leftpanel.js +8 -0
- package/lib/panelview/rightpanel.d.ts +4 -1
- package/lib/panelview/rightpanel.js +28 -7
- package/lib/store.d.ts +9 -0
- package/lib/store.js +25 -0
- package/lib/toolbar/widget.js +12 -2
- package/lib/tools.d.ts +35 -0
- package/lib/tools.js +86 -0
- package/lib/types.d.ts +14 -0
- package/package.json +18 -20
- package/style/base.css +4 -8
- package/style/dialog.css +1 -1
- package/style/icons/logo_mini.svg +70 -148
- package/style/icons/nonvisibility.svg +2 -7
- package/style/icons/visibility.svg +2 -6
- package/style/leftPanel.css +5 -0
- package/style/symbologyDialog.css +104 -3
- package/lib/dialogs/components/symbology/BandRendering.d.ts +0 -4
- package/lib/dialogs/components/symbology/Graduated.js +0 -188
- /package/lib/dialogs/{components/symbology → symbology/components/color_stops}/StopRow.d.ts +0 -0
- /package/lib/dialogs/{symbologyDialog.d.ts → symbology/symbologyDialog.d.ts} +0 -0
- /package/lib/dialogs/{components/symbology → symbology/tiff_layer/components}/BandRow.js +0 -0
- /package/lib/dialogs/{components/symbology → symbology/vector_layer/types}/Graduated.d.ts +0 -0
- /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:
|
|
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
|
|
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
|
|
55
|
-
React.createElement("select", { id: `jp-gis-operator-select-${index}`, className: "jp-mod-styled
|
|
56
|
-
React.createElement("select", { id: `jp-gis-value-select-${index}`, className: "jp-mod-styled
|
|
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}
|
|
164
|
-
React.createElement(LabIcon.resolveReact, { icon: caretDownIcon, className: LAYER_GROUP_COLLAPSER_CLASS
|
|
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
|
|
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
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
this.addWidget(
|
|
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
|
+
}
|
package/lib/toolbar/widget.js
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|