@jupytergis/base 0.3.0 → 0.4.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.js +1 -1
- package/lib/annotations/model.d.ts +6 -7
- package/lib/annotations/model.js +15 -15
- package/lib/commands.d.ts +2 -3
- package/lib/commands.js +117 -62
- package/lib/constants.d.ts +2 -0
- package/lib/constants.js +4 -1
- package/lib/dialogs/formdialog.js +2 -2
- package/lib/dialogs/layerBrowserDialog.d.ts +4 -5
- package/lib/dialogs/layerBrowserDialog.js +9 -9
- package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +1 -2
- package/lib/dialogs/symbology/hooks/useGetBandInfo.js +11 -6
- package/lib/dialogs/symbology/hooks/useGetProperties.d.ts +1 -1
- package/lib/dialogs/symbology/hooks/useGetProperties.js +6 -8
- package/lib/dialogs/symbology/symbologyDialog.d.ts +2 -3
- package/lib/dialogs/symbology/symbologyDialog.js +10 -9
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +1 -1
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +6 -6
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.d.ts +1 -1
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +5 -4
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.d.ts +1 -1
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +8 -7
- package/lib/dialogs/symbology/vector_layer/VectorRendering.d.ts +1 -1
- package/lib/dialogs/symbology/vector_layer/VectorRendering.js +18 -13
- package/lib/dialogs/symbology/vector_layer/types/Categorized.d.ts +1 -1
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +30 -19
- package/lib/dialogs/symbology/vector_layer/types/Graduated.d.ts +1 -1
- package/lib/dialogs/symbology/vector_layer/types/Graduated.js +16 -13
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.d.ts +4 -0
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +77 -0
- package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.d.ts +1 -1
- package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.js +4 -3
- package/lib/formbuilder/creationform.d.ts +1 -2
- package/lib/formbuilder/creationform.js +4 -4
- package/lib/formbuilder/editform.d.ts +1 -2
- package/lib/formbuilder/editform.js +7 -7
- package/lib/formbuilder/formselectors.js +5 -2
- package/lib/formbuilder/objectform/baseform.d.ts +3 -4
- package/lib/formbuilder/objectform/baseform.js +2 -2
- package/lib/formbuilder/objectform/fileselectorwidget.js +13 -6
- package/lib/formbuilder/objectform/geotiffsource.d.ts +5 -1
- package/lib/formbuilder/objectform/geotiffsource.js +64 -18
- package/lib/formbuilder/objectform/heatmapLayerForm.d.ts +11 -0
- package/lib/formbuilder/objectform/heatmapLayerForm.js +60 -0
- package/lib/formbuilder/objectform/vectorlayerform.d.ts +0 -2
- package/lib/formbuilder/objectform/vectorlayerform.js +0 -59
- package/lib/mainview/TemporalSlider.d.ts +8 -0
- package/lib/mainview/TemporalSlider.js +303 -0
- package/lib/mainview/mainView.d.ts +25 -4
- package/lib/mainview/mainView.js +213 -75
- package/lib/mainview/mainviewmodel.d.ts +4 -0
- package/lib/mainview/mainviewmodel.js +4 -0
- package/lib/mainview/mainviewwidget.d.ts +0 -2
- package/lib/mainview/mainviewwidget.js +0 -2
- package/lib/panelview/annotationPanel.js +5 -5
- package/lib/panelview/components/filter-panel/Filter.js +4 -25
- package/lib/panelview/components/identify-panel/IdentifyPanel.js +1 -1
- package/lib/panelview/components/layers.js +2 -2
- package/lib/panelview/components/sources.js +1 -1
- package/lib/panelview/leftpanel.d.ts +3 -0
- package/lib/panelview/leftpanel.js +5 -1
- package/lib/panelview/model.js +8 -8
- package/lib/panelview/objectproperties.js +10 -10
- package/lib/panelview/rightpanel.d.ts +1 -1
- package/lib/panelview/rightpanel.js +10 -10
- package/lib/toolbar/widget.d.ts +1 -1
- package/lib/toolbar/widget.js +44 -32
- package/lib/tools.d.ts +10 -6
- package/lib/tools.js +89 -15
- package/lib/types.d.ts +2 -0
- package/lib/widget.d.ts +29 -5
- package/lib/widget.js +41 -7
- package/package.json +4 -3
- package/style/base.css +10 -0
- package/style/symbologyDialog.css +7 -1
- package/style/temporalSlider.css +47 -0
|
@@ -104,8 +104,8 @@ function LayersBodyComponent(props) {
|
|
|
104
104
|
*/
|
|
105
105
|
(_b = props.model) === null || _b === void 0 ? void 0 : _b.documentChanged.connect((_, widget) => {
|
|
106
106
|
var _a;
|
|
107
|
-
setModel(widget === null || widget === void 0 ? void 0 : widget.
|
|
108
|
-
setLayerTree(((_a = widget === null || widget === void 0 ? void 0 : widget.
|
|
107
|
+
setModel(widget === null || widget === void 0 ? void 0 : widget.model);
|
|
108
|
+
setLayerTree(((_a = widget === null || widget === void 0 ? void 0 : widget.model) === null || _a === void 0 ? void 0 : _a.getLayerTree()) || []);
|
|
109
109
|
});
|
|
110
110
|
return (React.createElement("div", { id: "jp-gis-layer-tree" }, layerTree
|
|
111
111
|
.slice()
|
|
@@ -55,7 +55,7 @@ function SourcesBodyComponent(props) {
|
|
|
55
55
|
* Update the model when it changes.
|
|
56
56
|
*/
|
|
57
57
|
(_b = props.model) === null || _b === void 0 ? void 0 : _b.documentChanged.connect((_, widget) => {
|
|
58
|
-
setModel(widget === null || widget === void 0 ? void 0 : widget.
|
|
58
|
+
setModel(widget === null || widget === void 0 ? void 0 : widget.model);
|
|
59
59
|
});
|
|
60
60
|
return (React.createElement("div", { id: "jp-gis-sources" }, sourceIds.map(sourceId => {
|
|
61
61
|
return (React.createElement(SourceComponent, { key: `source-${sourceId}`, gisModel: model, sourceId: sourceId, onClick: onItemClick }));
|
|
@@ -4,6 +4,7 @@ import { SidePanel } from '@jupyterlab/ui-components';
|
|
|
4
4
|
import { Message } from '@lumino/messaging';
|
|
5
5
|
import { MouseEvent as ReactMouseEvent } from 'react';
|
|
6
6
|
import { IControlPanelModel } from '../types';
|
|
7
|
+
import { CommandRegistry } from '@lumino/commands';
|
|
7
8
|
/**
|
|
8
9
|
* Options of the left panel widget.
|
|
9
10
|
*/
|
|
@@ -37,12 +38,14 @@ export declare class LeftPanelWidget extends SidePanel {
|
|
|
37
38
|
private _lastSelectedNodeId;
|
|
38
39
|
private _model;
|
|
39
40
|
private _state;
|
|
41
|
+
private _commands;
|
|
40
42
|
}
|
|
41
43
|
export declare namespace LeftPanelWidget {
|
|
42
44
|
interface IOptions {
|
|
43
45
|
model: IControlPanelModel;
|
|
44
46
|
tracker: IJupyterGISTracker;
|
|
45
47
|
state: IStateDB;
|
|
48
|
+
commands: CommandRegistry;
|
|
46
49
|
}
|
|
47
50
|
interface IProps {
|
|
48
51
|
filePath?: string;
|
|
@@ -3,6 +3,7 @@ import { LayersPanel } from './components/layers';
|
|
|
3
3
|
import { SourcesPanel } from './components/sources';
|
|
4
4
|
import { ControlPanelHeader } from './header';
|
|
5
5
|
import { FilterPanel } from './components/filter-panel/Filter';
|
|
6
|
+
import { CommandIDs } from '../constants';
|
|
6
7
|
export class LeftPanelWidget extends SidePanel {
|
|
7
8
|
constructor(options) {
|
|
8
9
|
super();
|
|
@@ -50,11 +51,13 @@ export class LeftPanelWidget extends SidePanel {
|
|
|
50
51
|
const updatedSelectedValue = Object.assign(Object.assign({}, selectedValue), { [item]: { type, selectedNodeId: nodeId } });
|
|
51
52
|
this._lastSelectedNodeId = nodeId;
|
|
52
53
|
jGISModel.syncSelected(updatedSelectedValue, this.id);
|
|
54
|
+
this._commands.notifyCommandChanged(CommandIDs.temporalController);
|
|
53
55
|
}
|
|
54
56
|
};
|
|
55
57
|
this.addClass('jGIS-sidepanel-widget');
|
|
56
58
|
this._model = options.model;
|
|
57
59
|
this._state = options.state;
|
|
60
|
+
this._commands = options.commands;
|
|
58
61
|
const header = new ControlPanelHeader();
|
|
59
62
|
this.header.addWidget(header);
|
|
60
63
|
const sourcesPanel = new SourcesPanel({
|
|
@@ -81,7 +84,7 @@ export class LeftPanelWidget extends SidePanel {
|
|
|
81
84
|
this.addWidget(filterPanel);
|
|
82
85
|
options.tracker.currentChanged.connect((_, changed) => {
|
|
83
86
|
if (changed) {
|
|
84
|
-
header.title.label = changed.
|
|
87
|
+
header.title.label = changed.model.filePath;
|
|
85
88
|
}
|
|
86
89
|
else {
|
|
87
90
|
header.title.label = '-';
|
|
@@ -129,5 +132,6 @@ export class LeftPanelWidget extends SidePanel {
|
|
|
129
132
|
this._lastSelectedNodeId = nodeId;
|
|
130
133
|
}
|
|
131
134
|
(_b = (_a = this._model) === null || _a === void 0 ? void 0 : _a.jGISModel) === null || _b === void 0 ? void 0 : _b.syncSelected(selection, this.id);
|
|
135
|
+
this._commands.notifyCommandChanged(CommandIDs.temporalController);
|
|
132
136
|
}
|
|
133
137
|
}
|
package/lib/panelview/model.js
CHANGED
|
@@ -8,23 +8,23 @@ export class ControlPanelModel {
|
|
|
8
8
|
}
|
|
9
9
|
get filePath() {
|
|
10
10
|
var _a;
|
|
11
|
-
return (_a = this._tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.
|
|
11
|
+
return (_a = this._tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model.filePath;
|
|
12
12
|
}
|
|
13
13
|
get jGISModel() {
|
|
14
14
|
var _a;
|
|
15
|
-
return (_a = this._tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.
|
|
15
|
+
return (_a = this._tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model;
|
|
16
16
|
}
|
|
17
17
|
get sharedModel() {
|
|
18
18
|
var _a;
|
|
19
|
-
return (_a = this._tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.
|
|
19
|
+
return (_a = this._tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model.sharedModel;
|
|
20
20
|
}
|
|
21
21
|
disconnect(f) {
|
|
22
22
|
this._tracker.forEach(w => {
|
|
23
|
-
w.
|
|
24
|
-
w.
|
|
25
|
-
w.
|
|
23
|
+
w.model.sharedLayersChanged.disconnect(f);
|
|
24
|
+
w.model.sharedSourcesChanged.disconnect(f);
|
|
25
|
+
w.model.sharedOptionsChanged.disconnect(f);
|
|
26
26
|
});
|
|
27
|
-
this._tracker.forEach(w => w.
|
|
28
|
-
this._tracker.forEach(w => w.
|
|
27
|
+
this._tracker.forEach(w => w.model.themeChanged.disconnect(f));
|
|
28
|
+
this._tracker.forEach(w => w.model.clientStateChanged.disconnect(f));
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -45,7 +45,7 @@ class ObjectPropertiesReact extends React.Component {
|
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
this.state = {
|
|
48
|
-
|
|
48
|
+
model: (_a = props.tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model,
|
|
49
49
|
clientId: null,
|
|
50
50
|
id: uuid()
|
|
51
51
|
};
|
|
@@ -55,14 +55,14 @@ class ObjectPropertiesReact extends React.Component {
|
|
|
55
55
|
if (changed) {
|
|
56
56
|
this.props.cpModel.disconnect(this._sharedJGISModelChanged);
|
|
57
57
|
this.props.cpModel.disconnect(this._onClientSharedStateChanged);
|
|
58
|
-
changed.
|
|
59
|
-
changed.
|
|
60
|
-
changed.
|
|
61
|
-
this.setState(old => (Object.assign(Object.assign({}, old), {
|
|
58
|
+
changed.model.sharedLayersChanged.connect(this._sharedJGISModelChanged);
|
|
59
|
+
changed.model.sharedSourcesChanged.connect(this._sharedJGISModelChanged);
|
|
60
|
+
changed.model.clientStateChanged.connect(this._onClientSharedStateChanged);
|
|
61
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { model: changed.model, filePath: changed.model.filePath, clientId: changed.model.getClientId() })));
|
|
62
62
|
}
|
|
63
63
|
else {
|
|
64
64
|
this.setState({
|
|
65
|
-
|
|
65
|
+
model: undefined,
|
|
66
66
|
selectedObject: undefined
|
|
67
67
|
});
|
|
68
68
|
}
|
|
@@ -71,22 +71,22 @@ class ObjectPropertiesReact extends React.Component {
|
|
|
71
71
|
render() {
|
|
72
72
|
var _a;
|
|
73
73
|
const selectedObject = this.state.selectedObject;
|
|
74
|
-
if (!selectedObject || !this.state.
|
|
74
|
+
if (!selectedObject || !this.state.model) {
|
|
75
75
|
return React.createElement("div", null);
|
|
76
76
|
}
|
|
77
77
|
let layerId = undefined;
|
|
78
78
|
let sourceId = undefined;
|
|
79
|
-
const layer = this.state.
|
|
79
|
+
const layer = this.state.model.getLayer(selectedObject);
|
|
80
80
|
if (layer) {
|
|
81
81
|
layerId = selectedObject;
|
|
82
82
|
sourceId = (_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.source;
|
|
83
83
|
}
|
|
84
84
|
else {
|
|
85
|
-
const source = this.state.
|
|
85
|
+
const source = this.state.model.getSource(selectedObject);
|
|
86
86
|
if (source) {
|
|
87
87
|
sourceId = selectedObject;
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
|
-
return (React.createElement(EditForm, { layer: layerId, source: sourceId, formSchemaRegistry: this.props.formSchemaRegistry,
|
|
90
|
+
return (React.createElement(EditForm, { layer: layerId, source: sourceId, formSchemaRegistry: this.props.formSchemaRegistry, model: this.state.model }));
|
|
91
91
|
}
|
|
92
92
|
}
|
|
@@ -4,7 +4,7 @@ import { IControlPanelModel } from '../types';
|
|
|
4
4
|
export declare class RightPanelWidget extends SidePanel {
|
|
5
5
|
constructor(options: RightPanelWidget.IOptions);
|
|
6
6
|
dispose(): void;
|
|
7
|
-
private
|
|
7
|
+
private _currentModel;
|
|
8
8
|
private _model;
|
|
9
9
|
private _annotationModel;
|
|
10
10
|
}
|
|
@@ -31,12 +31,12 @@ export class RightPanelWidget extends SidePanel {
|
|
|
31
31
|
this.addWidget(identifyPanel);
|
|
32
32
|
this._model.documentChanged.connect((_, changed) => {
|
|
33
33
|
if (changed) {
|
|
34
|
-
if (changed.
|
|
35
|
-
header.title.label = changed.
|
|
34
|
+
if (changed.model.sharedModel.editable) {
|
|
35
|
+
header.title.label = changed.model.filePath;
|
|
36
36
|
properties.show();
|
|
37
37
|
}
|
|
38
38
|
else {
|
|
39
|
-
header.title.label = `${changed.
|
|
39
|
+
header.title.label = `${changed.model.filePath} - Read Only`;
|
|
40
40
|
properties.hide();
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -47,16 +47,16 @@ export class RightPanelWidget extends SidePanel {
|
|
|
47
47
|
options.tracker.currentChanged.connect(async (_, changed) => {
|
|
48
48
|
var _a;
|
|
49
49
|
if (changed) {
|
|
50
|
-
this.
|
|
51
|
-
header.title.label = this.
|
|
52
|
-
this._annotationModel.
|
|
53
|
-
((_a = options.tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.
|
|
54
|
-
await changed.context.ready;
|
|
50
|
+
this._currentModel = changed.model;
|
|
51
|
+
header.title.label = this._currentModel.filePath;
|
|
52
|
+
this._annotationModel.model =
|
|
53
|
+
((_a = options.tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model) || undefined;
|
|
54
|
+
// await changed.context.ready;
|
|
55
55
|
}
|
|
56
56
|
else {
|
|
57
57
|
header.title.label = '-';
|
|
58
|
-
this.
|
|
59
|
-
this._annotationModel.
|
|
58
|
+
this._currentModel = null;
|
|
59
|
+
this._annotationModel.model = undefined;
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
62
|
}
|
package/lib/toolbar/widget.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IJGISExternalCommand, JupyterGISModel } from '@jupytergis/schema';
|
|
2
|
-
import {
|
|
2
|
+
import { ReactiveToolbar, Toolbar } from '@jupyterlab/ui-components';
|
|
3
3
|
import { CommandRegistry } from '@lumino/commands';
|
|
4
4
|
import { Widget } from '@lumino/widgets';
|
|
5
5
|
export declare const TOOLBAR_SEPARATOR_CLASS = "jGIS-Toolbar-Separator";
|
package/lib/toolbar/widget.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { CommandToolbarButton } from '@jupyterlab/apputils';
|
|
2
|
-
import { ReactWidget, ReactiveToolbar, ToolbarButton, addIcon, redoIcon,
|
|
2
|
+
import { MenuSvg, ReactWidget, ReactiveToolbar, ToolbarButton, addIcon, redoIcon, terminalIcon, undoIcon } from '@jupyterlab/ui-components';
|
|
3
3
|
import { Menu, Widget } from '@lumino/widgets';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { CommandIDs } from '../constants';
|
|
6
|
+
import { rasterIcon } from '../icons';
|
|
6
7
|
import { UsersItem } from './usertoolbaritem';
|
|
7
8
|
export const TOOLBAR_SEPARATOR_CLASS = 'jGIS-Toolbar-Separator';
|
|
8
9
|
export const TOOLBAR_GROUPNAME_CLASS = 'jGIS-Toolbar-GroupName';
|
|
@@ -55,52 +56,63 @@ export class ToolbarWidget extends ReactiveToolbar {
|
|
|
55
56
|
label: '',
|
|
56
57
|
commands: options.commands
|
|
57
58
|
}));
|
|
58
|
-
|
|
59
|
+
// vector sub menu
|
|
60
|
+
const vectorSubMenu = new Menu({ commands: options.commands });
|
|
61
|
+
vectorSubMenu.title.label = 'Add Vector Layer';
|
|
62
|
+
vectorSubMenu.title.iconClass = 'fa fa-vector-square';
|
|
63
|
+
vectorSubMenu.id = 'jp-gis-toolbar-vector-menu';
|
|
64
|
+
vectorSubMenu.addItem({
|
|
65
|
+
type: 'command',
|
|
66
|
+
command: CommandIDs.newGeoJSONEntry
|
|
67
|
+
});
|
|
68
|
+
vectorSubMenu.addItem({
|
|
69
|
+
type: 'command',
|
|
70
|
+
command: CommandIDs.newShapefileLayer
|
|
71
|
+
});
|
|
72
|
+
//raster submenu
|
|
73
|
+
const rasterSubMenu = new Menu({ commands: options.commands });
|
|
74
|
+
rasterSubMenu.title.label = 'Add Raster Layer';
|
|
75
|
+
rasterSubMenu.title.icon = rasterIcon;
|
|
76
|
+
rasterSubMenu.id = 'jp-gis-toolbar-raster-menu';
|
|
77
|
+
rasterSubMenu.addItem({
|
|
78
|
+
type: 'command',
|
|
79
|
+
command: CommandIDs.newHillshadeEntry
|
|
80
|
+
});
|
|
81
|
+
rasterSubMenu.addItem({
|
|
82
|
+
type: 'command',
|
|
83
|
+
command: CommandIDs.newImageEntry
|
|
84
|
+
});
|
|
85
|
+
rasterSubMenu.addItem({
|
|
86
|
+
type: 'command',
|
|
87
|
+
command: CommandIDs.newGeoTiffEntry
|
|
88
|
+
});
|
|
89
|
+
const NewSubMenu = new MenuSvg({ commands: options.commands });
|
|
90
|
+
NewSubMenu.title.label = 'Add Layer';
|
|
91
|
+
NewSubMenu.addItem({ type: 'submenu', submenu: rasterSubMenu });
|
|
92
|
+
NewSubMenu.addItem({ type: 'submenu', submenu: vectorSubMenu });
|
|
93
|
+
const NewEntryButton = new ToolbarButton({
|
|
59
94
|
icon: addIcon,
|
|
60
95
|
noFocusOnClick: false,
|
|
61
96
|
onClick: () => {
|
|
62
97
|
if (!options.commands) {
|
|
63
98
|
return;
|
|
64
99
|
}
|
|
65
|
-
const bbox =
|
|
66
|
-
const NewSubMenu = new Menu({ commands: options.commands });
|
|
67
|
-
NewSubMenu.title.label = 'New Layer';
|
|
68
|
-
NewSubMenu.addItem({
|
|
69
|
-
type: 'command',
|
|
70
|
-
command: CommandIDs.newHillshadeEntry
|
|
71
|
-
});
|
|
72
|
-
NewSubMenu.addItem({
|
|
73
|
-
type: 'separator'
|
|
74
|
-
});
|
|
75
|
-
NewSubMenu.addItem({
|
|
76
|
-
type: 'command',
|
|
77
|
-
command: CommandIDs.newImageEntry
|
|
78
|
-
});
|
|
79
|
-
NewSubMenu.addItem({
|
|
80
|
-
type: 'separator'
|
|
81
|
-
});
|
|
82
|
-
NewSubMenu.addItem({
|
|
83
|
-
type: 'command',
|
|
84
|
-
command: CommandIDs.newShapefileLayer
|
|
85
|
-
});
|
|
86
|
-
NewSubMenu.addItem({
|
|
87
|
-
type: 'command',
|
|
88
|
-
command: CommandIDs.newGeoTiffEntry
|
|
89
|
-
});
|
|
90
|
-
NewSubMenu.addItem({
|
|
91
|
-
type: 'command',
|
|
92
|
-
command: CommandIDs.newGeoJSONEntry
|
|
93
|
-
});
|
|
100
|
+
const bbox = NewEntryButton.node.getBoundingClientRect();
|
|
94
101
|
NewSubMenu.open(bbox.x, bbox.bottom);
|
|
95
102
|
}
|
|
96
103
|
});
|
|
97
|
-
this.addItem('New',
|
|
104
|
+
this.addItem('New', NewEntryButton);
|
|
98
105
|
this.addItem('separator2', new Separator());
|
|
99
106
|
this.addItem('identify', new CommandToolbarButton({
|
|
100
107
|
id: CommandIDs.identify,
|
|
101
108
|
label: '',
|
|
102
109
|
commands: options.commands
|
|
103
110
|
}));
|
|
111
|
+
this.addItem('temporalController', new CommandToolbarButton({
|
|
112
|
+
id: CommandIDs.temporalController,
|
|
113
|
+
label: '',
|
|
114
|
+
commands: options.commands
|
|
115
|
+
}));
|
|
104
116
|
this.addItem('spacer', ReactiveToolbar.createSpacerItem());
|
|
105
117
|
// Users
|
|
106
118
|
this.addItem('users', ReactWidget.create(React.createElement(UsersItem, { model: options.model })));
|
package/lib/tools.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { VectorTile } from '@mapbox/vector-tile';
|
|
2
|
+
import { Contents } from '@jupyterlab/services';
|
|
2
3
|
import { IDict, IJGISLayerBrowserRegistry, IJGISOptions, IJGISSource, IJupyterGISModel } from '@jupytergis/schema';
|
|
3
4
|
export declare const debounce: (func: CallableFunction, timeout?: number) => CallableFunction;
|
|
4
5
|
export declare function throttle<T extends (...args: any[]) => void>(callback: T, delay?: number): T;
|
|
@@ -22,7 +23,6 @@ export declare function deepCopy<T = IDict<any>>(value: T): T;
|
|
|
22
23
|
*/
|
|
23
24
|
export declare function createDefaultLayerRegistry(layerBrowserRegistry: IJGISLayerBrowserRegistry): void;
|
|
24
25
|
export declare function getLayerTileInfo(tileUrl: string, mapOptions: Pick<IJGISOptions, 'latitude' | 'longitude' | 'extent' | 'zoom'>, urlParameters?: IDict<string>): Promise<VectorTile>;
|
|
25
|
-
export declare function getSourceLayerNames(tileUrl: string, urlParameters?: IDict<string>): Promise<string[]>;
|
|
26
26
|
export interface IParsedStyle {
|
|
27
27
|
fillColor: string;
|
|
28
28
|
strokeColor: string;
|
|
@@ -46,24 +46,27 @@ export declare const openDatabase: () => Promise<IDBDatabase>;
|
|
|
46
46
|
* @param metadata metadata of file.
|
|
47
47
|
* @returns A promise that resolves once the data is successfully saved.
|
|
48
48
|
*/
|
|
49
|
-
export declare const saveToIndexedDB: (key: string, file:
|
|
49
|
+
export declare const saveToIndexedDB: (key: string, file: any, metadata?: any | undefined) => Promise<void>;
|
|
50
50
|
/**
|
|
51
51
|
* Retrieve a file and its metadata from the IndexedDB database.
|
|
52
52
|
*
|
|
53
53
|
* @param key fileID (sourceUrl).
|
|
54
54
|
* @returns A promise that resolves to the stored data object or undefined.
|
|
55
55
|
*/
|
|
56
|
-
export declare const getFromIndexedDB: (key: string) => Promise<
|
|
56
|
+
export declare const getFromIndexedDB: (key: string) => Promise<{
|
|
57
|
+
file: any;
|
|
58
|
+
metadata?: any | undefined;
|
|
59
|
+
} | undefined>;
|
|
57
60
|
/**
|
|
58
61
|
* Load a GeoTIFF file from IndexedDB database cache or fetch it .
|
|
59
62
|
*
|
|
60
63
|
* @param sourceInfo object containing the URL of the GeoTIFF file.
|
|
61
64
|
* @returns A promise that resolves to the file as a Blob, or undefined .
|
|
62
65
|
*/
|
|
63
|
-
export declare const
|
|
66
|
+
export declare const loadGeoTiff: (sourceInfo: {
|
|
64
67
|
url?: string | undefined;
|
|
65
|
-
}) => Promise<{
|
|
66
|
-
file:
|
|
68
|
+
}, file?: Contents.IModel | null) => Promise<{
|
|
69
|
+
file: any;
|
|
67
70
|
metadata: any;
|
|
68
71
|
sourceUrl: string;
|
|
69
72
|
} | null>;
|
|
@@ -106,3 +109,4 @@ export declare const getMimeType: (filename: string) => string;
|
|
|
106
109
|
* @returns An ArrayBuffer.
|
|
107
110
|
*/
|
|
108
111
|
export declare const stringToArrayBuffer: (content: string) => Promise<ArrayBuffer>;
|
|
112
|
+
export declare const getNumericFeatureAttributes: (featureProperties: Record<string, Set<any>>) => Record<string, Set<number>>;
|
package/lib/tools.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import Protobuf from 'pbf';
|
|
2
2
|
import { VectorTile } from '@mapbox/vector-tile';
|
|
3
|
-
import { URLExt } from '@jupyterlab/coreutils';
|
|
3
|
+
import { PathExt, URLExt } from '@jupyterlab/coreutils';
|
|
4
4
|
import { ServerConnection } from '@jupyterlab/services';
|
|
5
|
+
import { showErrorMessage } from '@jupyterlab/apputils';
|
|
5
6
|
import * as d3Color from 'd3-color';
|
|
6
|
-
import { PathExt } from '@jupyterlab/coreutils';
|
|
7
7
|
import shp from 'shpjs';
|
|
8
8
|
import RASTER_LAYER_GALLERY from '../rasterlayer_gallery/raster_layer_gallery.json';
|
|
9
9
|
import { getGdal } from './gdal';
|
|
@@ -215,11 +215,6 @@ export async function getLayerTileInfo(tileUrl, mapOptions, urlParameters) {
|
|
|
215
215
|
const tile = new VectorTile(new Protobuf(arrayBuffer));
|
|
216
216
|
return tile;
|
|
217
217
|
}
|
|
218
|
-
export async function getSourceLayerNames(tileUrl, urlParameters) {
|
|
219
|
-
const tile = await getLayerTileInfo(tileUrl, { latitude: 0, longitude: 0, zoom: 0, extent: [] }, urlParameters);
|
|
220
|
-
const layerNames = Object.keys(tile.layers);
|
|
221
|
-
return layerNames;
|
|
222
|
-
}
|
|
223
218
|
export function parseColor(type, style) {
|
|
224
219
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
225
220
|
if (!type || !style) {
|
|
@@ -312,23 +307,36 @@ export const getFromIndexedDB = async (key) => {
|
|
|
312
307
|
* @param sourceInfo object containing the URL of the GeoTIFF file.
|
|
313
308
|
* @returns A promise that resolves to the file as a Blob, or undefined .
|
|
314
309
|
*/
|
|
315
|
-
export const
|
|
310
|
+
export const loadGeoTiff = async (sourceInfo, file) => {
|
|
316
311
|
if (!(sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.url)) {
|
|
317
312
|
return null;
|
|
318
313
|
}
|
|
314
|
+
const mimeType = getMimeType(sourceInfo.url);
|
|
315
|
+
if (!mimeType || !mimeType.startsWith('image/tiff')) {
|
|
316
|
+
throw new Error('Invalid file type. Expected GeoTIFF (image/tiff).');
|
|
317
|
+
}
|
|
319
318
|
const cachedData = await getFromIndexedDB(sourceInfo.url);
|
|
320
319
|
if (cachedData) {
|
|
321
320
|
return {
|
|
322
|
-
file:
|
|
321
|
+
file: cachedData.file,
|
|
323
322
|
metadata: cachedData.metadata,
|
|
324
323
|
sourceUrl: sourceInfo.url
|
|
325
324
|
};
|
|
326
325
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
326
|
+
let fileBlob;
|
|
327
|
+
if (!file) {
|
|
328
|
+
const response = await fetch(`/jupytergis_core/proxy?url=${sourceInfo.url}`);
|
|
329
|
+
if (!response.ok) {
|
|
330
|
+
throw new Error(`Failed to fetch file. Status: ${response.status}`);
|
|
331
|
+
}
|
|
332
|
+
fileBlob = await response.blob();
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
fileBlob = await base64ToBlob(file.content, mimeType);
|
|
336
|
+
}
|
|
337
|
+
const geotiff = new File([fileBlob], 'loaded.tif');
|
|
330
338
|
const Gdal = await getGdal();
|
|
331
|
-
const result = await Gdal.open(
|
|
339
|
+
const result = await Gdal.open(geotiff);
|
|
332
340
|
const tifDataset = result.datasets[0];
|
|
333
341
|
const metadata = await Gdal.gdalinfo(tifDataset, ['-stats']);
|
|
334
342
|
Gdal.close(tifDataset);
|
|
@@ -369,24 +377,58 @@ export const loadFile = async (fileInfo) => {
|
|
|
369
377
|
}
|
|
370
378
|
}
|
|
371
379
|
case 'ShapefileSource': {
|
|
380
|
+
const cached = await getFromIndexedDB(filepath);
|
|
381
|
+
if (cached) {
|
|
382
|
+
return cached.file;
|
|
383
|
+
}
|
|
384
|
+
// First trying a direct fetch
|
|
385
|
+
try {
|
|
386
|
+
const response = await fetch(filepath);
|
|
387
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
388
|
+
const geojson = await shp(arrayBuffer);
|
|
389
|
+
await saveToIndexedDB(filepath, geojson);
|
|
390
|
+
return geojson;
|
|
391
|
+
}
|
|
392
|
+
catch (error) {
|
|
393
|
+
console.warn('Cannot load shapefile from ${filepath}: ${error}');
|
|
394
|
+
}
|
|
395
|
+
// Trying through our proxy server
|
|
372
396
|
try {
|
|
373
397
|
const response = await fetch(`/jupytergis_core/proxy?url=${filepath}`);
|
|
374
398
|
const arrayBuffer = await response.arrayBuffer();
|
|
375
399
|
const geojson = await shp(arrayBuffer);
|
|
400
|
+
await saveToIndexedDB(filepath, geojson);
|
|
376
401
|
return geojson;
|
|
377
402
|
}
|
|
378
403
|
catch (error) {
|
|
379
|
-
console.
|
|
380
|
-
throw error;
|
|
404
|
+
console.warn('Cannot communicate with the JupyterGIS proxy server:', error);
|
|
381
405
|
}
|
|
406
|
+
// Trying through an external proxy server
|
|
407
|
+
try {
|
|
408
|
+
const response = await fetch(`https://corsproxy.io/?url=${filepath}`);
|
|
409
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
410
|
+
const geojson = await shp(arrayBuffer);
|
|
411
|
+
await saveToIndexedDB(filepath, geojson);
|
|
412
|
+
return geojson;
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
console.warn('Cannot communicate with external proxy server', error);
|
|
416
|
+
}
|
|
417
|
+
showErrorMessage('Network error', 'Failed to fetch ${filepath}');
|
|
418
|
+
throw new Error('Failed to fetch ${filepath}');
|
|
382
419
|
}
|
|
383
420
|
case 'GeoJSONSource': {
|
|
421
|
+
const cached = await getFromIndexedDB(filepath);
|
|
422
|
+
if (cached) {
|
|
423
|
+
return cached.file;
|
|
424
|
+
}
|
|
384
425
|
try {
|
|
385
426
|
const response = await fetch(`/jupytergis_core/proxy?url=${filepath}`);
|
|
386
427
|
if (!response.ok) {
|
|
387
428
|
throw new Error(`Failed to fetch GeoJSON from URL: ${filepath}`);
|
|
388
429
|
}
|
|
389
430
|
const geojson = await response.json();
|
|
431
|
+
await saveToIndexedDB(filepath, geojson);
|
|
390
432
|
return geojson;
|
|
391
433
|
}
|
|
392
434
|
catch (error) {
|
|
@@ -394,6 +436,16 @@ export const loadFile = async (fileInfo) => {
|
|
|
394
436
|
throw error;
|
|
395
437
|
}
|
|
396
438
|
}
|
|
439
|
+
case 'GeoTiffSource': {
|
|
440
|
+
try {
|
|
441
|
+
const tiff = loadGeoTiff({ url: filepath });
|
|
442
|
+
return tiff;
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
console.error('Error loading remote GeoTIFF:', error);
|
|
446
|
+
throw error;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
397
449
|
default: {
|
|
398
450
|
throw new Error(`Unsupported URL handling for source type: ${type}`);
|
|
399
451
|
}
|
|
@@ -441,6 +493,15 @@ export const loadFile = async (fileInfo) => {
|
|
|
441
493
|
throw new Error('Invalid file format for image content.');
|
|
442
494
|
}
|
|
443
495
|
}
|
|
496
|
+
case 'GeoTiffSource': {
|
|
497
|
+
if (typeof file.content === 'string') {
|
|
498
|
+
const tiff = loadGeoTiff({ url: filepath }, file);
|
|
499
|
+
return tiff;
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
throw new Error('Invalid file format for tiff content.');
|
|
503
|
+
}
|
|
504
|
+
}
|
|
444
505
|
default: {
|
|
445
506
|
throw new Error(`Unsupported source type: ${type}`);
|
|
446
507
|
}
|
|
@@ -645,3 +706,16 @@ export const stringToArrayBuffer = async (content) => {
|
|
|
645
706
|
const base64Response = await fetch(`data:application/octet-stream;base64,${content}`);
|
|
646
707
|
return await base64Response.arrayBuffer();
|
|
647
708
|
};
|
|
709
|
+
export const getNumericFeatureAttributes = (featureProperties) => {
|
|
710
|
+
// We only want number values here
|
|
711
|
+
const filteredRecord = {};
|
|
712
|
+
for (const [key, set] of Object.entries(featureProperties)) {
|
|
713
|
+
const firstValue = set.values().next().value;
|
|
714
|
+
// Check if the first value is a string that cannot be parsed as a number
|
|
715
|
+
const isInvalidString = typeof firstValue === 'string' && isNaN(Number(firstValue));
|
|
716
|
+
if (!isInvalidString) {
|
|
717
|
+
filteredRecord[key] = set;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return filteredRecord;
|
|
721
|
+
};
|
package/lib/types.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { IDict, IJupyterGISDoc, IJupyterGISModel, IJupyterGISTracker, IJupyterGISWidget } from '@jupytergis/schema';
|
|
2
|
+
import { WidgetTracker } from '@jupyterlab/apputils';
|
|
2
3
|
import { ISignal } from '@lumino/signaling';
|
|
3
4
|
import { Map } from 'ol';
|
|
4
5
|
export { IDict };
|
|
5
6
|
export type ValueOf<T> = T[keyof T];
|
|
7
|
+
export type JupyterGISTracker = WidgetTracker<IJupyterGISWidget>;
|
|
6
8
|
export interface IControlPanelModel {
|
|
7
9
|
disconnect(f: any): void;
|
|
8
10
|
documentChanged: ISignal<IJupyterGISTracker, IJupyterGISWidget | null>;
|
package/lib/widget.d.ts
CHANGED
|
@@ -1,25 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ISignal } from '@lumino/signaling';
|
|
3
|
-
import { SplitPanel } from '@lumino/widgets';
|
|
1
|
+
import { MainAreaWidget } from '@jupyterlab/apputils';
|
|
4
2
|
import { ConsolePanel, IConsoleTracker } from '@jupyterlab/console';
|
|
5
3
|
import { DocumentWidget } from '@jupyterlab/docregistry';
|
|
6
4
|
import { IObservableMap, ObservableMap } from '@jupyterlab/observables';
|
|
7
|
-
import {
|
|
5
|
+
import { JSONValue } from '@lumino/coreutils';
|
|
6
|
+
import { ISignal } from '@lumino/signaling';
|
|
7
|
+
import { SplitPanel } from '@lumino/widgets';
|
|
8
|
+
import { IJupyterGISModel, IJupyterGISOutputWidget, IJupyterGISDocumentWidget } from '@jupytergis/schema';
|
|
8
9
|
import { JupyterGISMainViewPanel } from './mainview';
|
|
9
10
|
import { MainViewModel } from './mainview/mainviewmodel';
|
|
10
11
|
import { ConsoleView } from './console';
|
|
11
|
-
|
|
12
|
+
import { CommandRegistry } from '@lumino/commands';
|
|
13
|
+
export type JupyterGISWidget = JupyterGISDocumentWidget | JupyterGISOutputWidget;
|
|
14
|
+
export declare class JupyterGISDocumentWidget extends DocumentWidget<JupyterGISPanel, IJupyterGISModel> implements IJupyterGISDocumentWidget {
|
|
12
15
|
constructor(options: DocumentWidget.IOptions<JupyterGISPanel, IJupyterGISModel>);
|
|
16
|
+
get model(): IJupyterGISModel;
|
|
13
17
|
/**
|
|
14
18
|
* Dispose of the resources held by the widget.
|
|
15
19
|
*/
|
|
16
20
|
dispose(): void;
|
|
17
21
|
onResize: (msg: any) => void;
|
|
18
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* A main area widget designed to be used as Notebook cell output widget, to ease the
|
|
25
|
+
* integration of toolbar and tracking.
|
|
26
|
+
*/
|
|
27
|
+
export declare class JupyterGISOutputWidget extends MainAreaWidget<JupyterGISPanel> implements IJupyterGISOutputWidget {
|
|
28
|
+
constructor(options: JupyterGISOutputWidget.IOptions);
|
|
29
|
+
/**
|
|
30
|
+
* Dispose of the resources held by the widget.
|
|
31
|
+
*/
|
|
32
|
+
dispose(): void;
|
|
33
|
+
readonly model: IJupyterGISModel;
|
|
34
|
+
readonly resizeObserver: ResizeObserver;
|
|
35
|
+
}
|
|
36
|
+
export declare namespace JupyterGISOutputWidget {
|
|
37
|
+
interface IOptions extends MainAreaWidget.IOptions<JupyterGISPanel> {
|
|
38
|
+
model: IJupyterGISModel;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
19
41
|
export declare class JupyterGISPanel extends SplitPanel {
|
|
20
42
|
constructor(options: JupyterGISPanel.IOptions);
|
|
21
43
|
_initModel(options: {
|
|
22
44
|
model: IJupyterGISModel;
|
|
45
|
+
commandRegistry: CommandRegistry;
|
|
23
46
|
}): void;
|
|
24
47
|
_initView(): void;
|
|
25
48
|
get jupyterGISMainViewPanel(): JupyterGISMainViewPanel;
|
|
@@ -44,6 +67,7 @@ export declare class JupyterGISPanel extends SplitPanel {
|
|
|
44
67
|
export declare namespace JupyterGISPanel {
|
|
45
68
|
interface IOptions extends Partial<ConsoleView.IOptions> {
|
|
46
69
|
model: IJupyterGISModel;
|
|
70
|
+
commandRegistry: CommandRegistry;
|
|
47
71
|
consoleTracker?: IConsoleTracker;
|
|
48
72
|
}
|
|
49
73
|
}
|