@jupytergis/base 0.1.7 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/annotations/components/Annotation.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/commands.js +51 -6
- package/lib/constants.d.ts +2 -0
- package/lib/constants.js +5 -1
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +25 -33
- 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/index.d.ts +1 -0
- package/lib/index.js +1 -0
- 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 +36 -2
- package/lib/mainview/mainView.js +393 -28
- 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/toolbar/widget.js +11 -1
- package/lib/tools.d.ts +35 -0
- package/lib/tools.js +86 -0
- package/package.json +5 -6
- 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
|
@@ -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/toolbar/widget.js
CHANGED
|
@@ -55,7 +55,6 @@ 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,
|
|
@@ -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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupytergis/base",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "A JupyterLab extension for 3D modelling.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -38,9 +38,8 @@
|
|
|
38
38
|
"watch": "tsc -w"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@deathbeds/jupyterlab-rjsf": "^1.1.0",
|
|
42
41
|
"@jupyter/ydoc": "^2.0.0 || ^3.0.0",
|
|
43
|
-
"@jupytergis/schema": "^0.1
|
|
42
|
+
"@jupytergis/schema": "^0.2.1",
|
|
44
43
|
"@jupyterlab/application": "^4.3.0",
|
|
45
44
|
"@jupyterlab/apputils": "^4.3.0",
|
|
46
45
|
"@jupyterlab/completer": "^4.3.0",
|
|
@@ -52,7 +51,7 @@
|
|
|
52
51
|
"@jupyterlab/observables": "^5.3.0",
|
|
53
52
|
"@jupyterlab/services": "^7.3.0",
|
|
54
53
|
"@jupyterlab/translation": "^4.3.0",
|
|
55
|
-
"@jupyterlab/ui-components": "^4.3.
|
|
54
|
+
"@jupyterlab/ui-components": "^4.3.1",
|
|
56
55
|
"@lumino/commands": "^2.0.0",
|
|
57
56
|
"@lumino/coreutils": "^2.0.0",
|
|
58
57
|
"@lumino/messaging": "^2.0.0",
|
|
@@ -61,12 +60,12 @@
|
|
|
61
60
|
"@mapbox/vector-tile": "^2.0.3",
|
|
62
61
|
"@naisutech/react-tree": "^3.0.1",
|
|
63
62
|
"@rjsf/core": "^4.2.0",
|
|
63
|
+
"@rjsf/validator-ajv8": "^5.23.1",
|
|
64
64
|
"@types/d3-color": "^3.1.0",
|
|
65
65
|
"@types/three": "^0.134.0",
|
|
66
66
|
"ajv": "^8.14.0",
|
|
67
67
|
"d3-color": "^3.1.0",
|
|
68
68
|
"gdal3.js": "^2.8.1",
|
|
69
|
-
"geojson-schema": "^1.0.5",
|
|
70
69
|
"ol-pmtiles": "^0.5.0",
|
|
71
70
|
"pbf": "^4.0.1",
|
|
72
71
|
"react": "^18.0.1",
|
|
@@ -74,7 +73,7 @@
|
|
|
74
73
|
"styled-components": "^5.3.6",
|
|
75
74
|
"three": "^0.135.0",
|
|
76
75
|
"three-mesh-bvh": "^0.5.17",
|
|
77
|
-
"uuid": "^
|
|
76
|
+
"uuid": "^11.0.3"
|
|
78
77
|
},
|
|
79
78
|
"devDependencies": {
|
|
80
79
|
"@apidevtools/json-schema-ref-parser": "^9.0.9",
|
package/style/base.css
CHANGED
|
@@ -21,27 +21,23 @@
|
|
|
21
21
|
gap: 1rem;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
.jGIS-property-panel .
|
|
24
|
+
.jGIS-property-panel .rjsf .array-item:not(:last-child) {
|
|
25
25
|
border-bottom: none;
|
|
26
26
|
margin-bottom: unset;
|
|
27
27
|
padding-bottom: unset;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
.jGIS-property-panel .
|
|
30
|
+
.jGIS-property-panel .rjsf .array-item {
|
|
31
31
|
flex: 1 0 0%;
|
|
32
32
|
align-items: center;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
.jGIS-property-panel .
|
|
36
|
-
display: none;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.jGIS-property-panel .jp-SchemaForm fieldset fieldset {
|
|
35
|
+
.jGIS-property-panel .rjsf fieldset fieldset {
|
|
40
36
|
padding-left: unset;
|
|
41
37
|
border-left: none;
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
.jGIS-property-panel .
|
|
40
|
+
.jGIS-property-panel .rjsf .array-item-list:has(.field-array) {
|
|
45
41
|
flex-direction: column;
|
|
46
42
|
}
|
|
47
43
|
|
package/style/dialog.css
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
| Distributed under the terms of the Modified BSD License.
|
|
4
4
|
|---------------------------------------------------------------------------- */
|
|
5
5
|
|
|
6
|
-
.
|
|
6
|
+
.jGIS-property-panel .rjsf .jp-select-wrapper select {
|
|
7
7
|
background-image: unset;
|
|
8
8
|
}
|