@jupytergis/base 0.3.0 → 0.4.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.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 +3 -8
- package/lib/dialogs/symbology/hooks/useGetBandInfo.js +16 -28
- 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/components/BandRow.js +3 -1
- 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 +51 -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 +26 -5
- package/lib/mainview/mainView.js +221 -108
- 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 +6 -16
- package/lib/tools.js +54 -56
- package/lib/types.d.ts +2 -0
- package/lib/widget.d.ts +30 -6
- package/lib/widget.js +43 -9
- package/package.json +4 -3
- package/style/base.css +10 -0
- package/style/symbologyDialog.css +7 -1
- package/style/temporalSlider.css +47 -0
|
@@ -9,12 +9,12 @@ export class AnnotationsPanel extends Component {
|
|
|
9
9
|
};
|
|
10
10
|
this._annotationModel = props.annotationModel;
|
|
11
11
|
this._rightPanelModel = props.rightPanelModel;
|
|
12
|
-
this._annotationModel.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
(
|
|
12
|
+
this._annotationModel.modelChanged.connect(async () => {
|
|
13
|
+
// await this._annotationModel?.context?.ready;
|
|
14
|
+
var _a, _b, _c, _d;
|
|
15
|
+
(_b = (_a = this._annotationModel) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.sharedMetadataChanged.disconnect(updateCallback);
|
|
16
16
|
this._annotationModel = props.annotationModel;
|
|
17
|
-
(
|
|
17
|
+
(_d = (_c = this._annotationModel) === null || _c === void 0 ? void 0 : _c.model) === null || _d === void 0 ? void 0 : _d.sharedMetadataChanged.connect(updateCallback);
|
|
18
18
|
this.forceUpdate();
|
|
19
19
|
});
|
|
20
20
|
}
|
|
@@ -2,9 +2,8 @@ import { Button, ReactWidget } from '@jupyterlab/ui-components';
|
|
|
2
2
|
import { Panel } from '@lumino/widgets';
|
|
3
3
|
import { cloneDeep } from 'lodash';
|
|
4
4
|
import React, { useEffect, useRef, useState } from 'react';
|
|
5
|
-
import { debounce,
|
|
5
|
+
import { debounce, loadFile } from '../../../tools';
|
|
6
6
|
import FilterRow from './FilterRow';
|
|
7
|
-
import { loadFile } from '../../../tools';
|
|
8
7
|
/**
|
|
9
8
|
* The filters panel widget.
|
|
10
9
|
*/
|
|
@@ -29,7 +28,7 @@ const FilterComponent = (props) => {
|
|
|
29
28
|
const [featuresInLayer, setFeaturesInLayer] = useState({});
|
|
30
29
|
const [model, setModel] = useState(props.model.jGISModel);
|
|
31
30
|
(_a = props.model) === null || _a === void 0 ? void 0 : _a.documentChanged.connect((_, widget) => {
|
|
32
|
-
setModel(widget === null || widget === void 0 ? void 0 : widget.
|
|
31
|
+
setModel(widget === null || widget === void 0 ? void 0 : widget.model);
|
|
33
32
|
});
|
|
34
33
|
// Reset state values when current widget changes
|
|
35
34
|
useEffect(() => {
|
|
@@ -108,13 +107,12 @@ const FilterComponent = (props) => {
|
|
|
108
107
|
featuresInLayerRef.current = featuresInLayer;
|
|
109
108
|
}, [featuresInLayer]);
|
|
110
109
|
const buildFilterObject = async (currentLayer) => {
|
|
111
|
-
var _a, _b
|
|
110
|
+
var _a, _b;
|
|
112
111
|
if (!model) {
|
|
113
112
|
return;
|
|
114
113
|
}
|
|
115
114
|
const layer = model.getLayer(currentLayer !== null && currentLayer !== void 0 ? currentLayer : selectedLayer);
|
|
116
115
|
const source = model.getSource((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.source);
|
|
117
|
-
const { latitude, longitude, extent, zoom } = model.getOptions();
|
|
118
116
|
if (!source || !layer) {
|
|
119
117
|
return;
|
|
120
118
|
}
|
|
@@ -131,28 +129,9 @@ const FilterComponent = (props) => {
|
|
|
131
129
|
});
|
|
132
130
|
}
|
|
133
131
|
switch (source.type) {
|
|
134
|
-
case 'VectorTileSource': {
|
|
135
|
-
try {
|
|
136
|
-
const tile = await getLayerTileInfo((_b = source === null || source === void 0 ? void 0 : source.parameters) === null || _b === void 0 ? void 0 : _b.url, {
|
|
137
|
-
latitude,
|
|
138
|
-
longitude,
|
|
139
|
-
extent,
|
|
140
|
-
zoom
|
|
141
|
-
});
|
|
142
|
-
const layerValue = tile.layers[(_c = layer.parameters) === null || _c === void 0 ? void 0 : _c.sourceLayer];
|
|
143
|
-
for (let i = 0; i < layerValue.length; i++) {
|
|
144
|
-
const feature = layerValue.feature(i);
|
|
145
|
-
addFeatureValue(feature.properties, aggregatedProperties);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
catch (error) {
|
|
149
|
-
console.warn(`Error fetching tile info: ${error}`);
|
|
150
|
-
}
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
132
|
case 'GeoJSONSource': {
|
|
154
133
|
const data = await loadFile({
|
|
155
|
-
filepath: (
|
|
134
|
+
filepath: (_b = source.parameters) === null || _b === void 0 ? void 0 : _b.path,
|
|
156
135
|
type: 'GeoJSONSource',
|
|
157
136
|
model: model
|
|
158
137
|
});
|
|
@@ -26,7 +26,7 @@ const IdentifyPanelComponent = ({ controlPanelModel, tracker }) => {
|
|
|
26
26
|
* Update the model when it changes.
|
|
27
27
|
*/
|
|
28
28
|
controlPanelModel === null || controlPanelModel === void 0 ? void 0 : controlPanelModel.documentChanged.connect((_, widget) => {
|
|
29
|
-
setJgisModel(widget === null || widget === void 0 ? void 0 : widget.
|
|
29
|
+
setJgisModel(widget === null || widget === void 0 ? void 0 : widget.model);
|
|
30
30
|
});
|
|
31
31
|
// Reset state values when current widget changes
|
|
32
32
|
useEffect(() => {
|
|
@@ -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
|
@@ -22,7 +22,6 @@ export declare function deepCopy<T = IDict<any>>(value: T): T;
|
|
|
22
22
|
*/
|
|
23
23
|
export declare function createDefaultLayerRegistry(layerBrowserRegistry: IJGISLayerBrowserRegistry): void;
|
|
24
24
|
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
25
|
export interface IParsedStyle {
|
|
27
26
|
fillColor: string;
|
|
28
27
|
strokeColor: string;
|
|
@@ -46,27 +45,17 @@ export declare const openDatabase: () => Promise<IDBDatabase>;
|
|
|
46
45
|
* @param metadata metadata of file.
|
|
47
46
|
* @returns A promise that resolves once the data is successfully saved.
|
|
48
47
|
*/
|
|
49
|
-
export declare const saveToIndexedDB: (key: string, file:
|
|
48
|
+
export declare const saveToIndexedDB: (key: string, file: any, metadata?: any | undefined) => Promise<void>;
|
|
50
49
|
/**
|
|
51
50
|
* Retrieve a file and its metadata from the IndexedDB database.
|
|
52
51
|
*
|
|
53
52
|
* @param key fileID (sourceUrl).
|
|
54
53
|
* @returns A promise that resolves to the stored data object or undefined.
|
|
55
54
|
*/
|
|
56
|
-
export declare const getFromIndexedDB: (key: string) => Promise<
|
|
57
|
-
|
|
58
|
-
|
|
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>;
|
|
55
|
+
export declare const getFromIndexedDB: (key: string) => Promise<{
|
|
56
|
+
file: any;
|
|
57
|
+
metadata?: any | undefined;
|
|
58
|
+
} | undefined>;
|
|
70
59
|
/**
|
|
71
60
|
* Generalized file reader for different source types.
|
|
72
61
|
*
|
|
@@ -106,3 +95,4 @@ export declare const getMimeType: (filename: string) => string;
|
|
|
106
95
|
* @returns An ArrayBuffer.
|
|
107
96
|
*/
|
|
108
97
|
export declare const stringToArrayBuffer: (content: string) => Promise<ArrayBuffer>;
|
|
98
|
+
export declare const getNumericFeatureAttributes: (featureProperties: Record<string, Set<any>>) => Record<string, Set<number>>;
|
package/lib/tools.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
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
|
-
import { getGdal } from './gdal';
|
|
10
9
|
export const debounce = (func, timeout = 100) => {
|
|
11
10
|
let timeoutId;
|
|
12
11
|
return (...args) => {
|
|
@@ -215,11 +214,6 @@ export async function getLayerTileInfo(tileUrl, mapOptions, urlParameters) {
|
|
|
215
214
|
const tile = new VectorTile(new Protobuf(arrayBuffer));
|
|
216
215
|
return tile;
|
|
217
216
|
}
|
|
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
217
|
export function parseColor(type, style) {
|
|
224
218
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
225
219
|
if (!type || !style) {
|
|
@@ -306,38 +300,26 @@ export const getFromIndexedDB = async (key) => {
|
|
|
306
300
|
request.onerror = () => reject(request.error);
|
|
307
301
|
});
|
|
308
302
|
};
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
303
|
+
const fetchWithProxies = async (url, parseResponse) => {
|
|
304
|
+
const proxyUrls = [
|
|
305
|
+
url, // Direct fetch
|
|
306
|
+
`/jupytergis_core/proxy?url=${encodeURIComponent(url)}`, // Internal proxy
|
|
307
|
+
`https://corsproxy.io/?url=${encodeURIComponent(url)}` // External proxy
|
|
308
|
+
];
|
|
309
|
+
for (const proxyUrl of proxyUrls) {
|
|
310
|
+
try {
|
|
311
|
+
const response = await fetch(proxyUrl);
|
|
312
|
+
if (!response.ok) {
|
|
313
|
+
console.warn(`Failed to fetch from ${proxyUrl}: ${response.statusText}`);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
return await parseResponse(response);
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
console.warn(`Error fetching from ${proxyUrl}:`, error);
|
|
320
|
+
}
|
|
326
321
|
}
|
|
327
|
-
|
|
328
|
-
const fileBlob = await response.blob();
|
|
329
|
-
const file = new File([fileBlob], 'loaded.tif');
|
|
330
|
-
const Gdal = await getGdal();
|
|
331
|
-
const result = await Gdal.open(file);
|
|
332
|
-
const tifDataset = result.datasets[0];
|
|
333
|
-
const metadata = await Gdal.gdalinfo(tifDataset, ['-stats']);
|
|
334
|
-
Gdal.close(tifDataset);
|
|
335
|
-
await saveToIndexedDB(sourceInfo.url, fileBlob, metadata);
|
|
336
|
-
return {
|
|
337
|
-
file: fileBlob,
|
|
338
|
-
metadata,
|
|
339
|
-
sourceUrl: sourceInfo.url
|
|
340
|
-
};
|
|
322
|
+
return null;
|
|
341
323
|
};
|
|
342
324
|
/**
|
|
343
325
|
* Generalized file reader for different source types.
|
|
@@ -369,30 +351,33 @@ export const loadFile = async (fileInfo) => {
|
|
|
369
351
|
}
|
|
370
352
|
}
|
|
371
353
|
case 'ShapefileSource': {
|
|
372
|
-
|
|
373
|
-
|
|
354
|
+
const cached = await getFromIndexedDB(filepath);
|
|
355
|
+
if (cached) {
|
|
356
|
+
return cached.file;
|
|
357
|
+
}
|
|
358
|
+
const geojson = await fetchWithProxies(filepath, async (response) => {
|
|
374
359
|
const arrayBuffer = await response.arrayBuffer();
|
|
375
|
-
|
|
360
|
+
return shp(arrayBuffer);
|
|
361
|
+
});
|
|
362
|
+
if (geojson) {
|
|
363
|
+
await saveToIndexedDB(filepath, geojson);
|
|
376
364
|
return geojson;
|
|
377
365
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
throw error;
|
|
381
|
-
}
|
|
366
|
+
showErrorMessage('Network error', `Failed to fetch ${filepath}`);
|
|
367
|
+
throw new Error(`Failed to fetch ${filepath}`);
|
|
382
368
|
}
|
|
383
369
|
case 'GeoJSONSource': {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
throw new Error(`Failed to fetch GeoJSON from URL: ${filepath}`);
|
|
388
|
-
}
|
|
389
|
-
const geojson = await response.json();
|
|
390
|
-
return geojson;
|
|
370
|
+
const cached = await getFromIndexedDB(filepath);
|
|
371
|
+
if (cached) {
|
|
372
|
+
return cached.file;
|
|
391
373
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
374
|
+
const geojson = await fetchWithProxies(filepath, async (response) => response.json());
|
|
375
|
+
if (geojson) {
|
|
376
|
+
await saveToIndexedDB(filepath, geojson);
|
|
377
|
+
return geojson;
|
|
395
378
|
}
|
|
379
|
+
showErrorMessage('Network error', `Failed to fetch ${filepath}`);
|
|
380
|
+
throw new Error(`Failed to fetch ${filepath}`);
|
|
396
381
|
}
|
|
397
382
|
default: {
|
|
398
383
|
throw new Error(`Unsupported URL handling for source type: ${type}`);
|
|
@@ -645,3 +630,16 @@ export const stringToArrayBuffer = async (content) => {
|
|
|
645
630
|
const base64Response = await fetch(`data:application/octet-stream;base64,${content}`);
|
|
646
631
|
return await base64Response.arrayBuffer();
|
|
647
632
|
};
|
|
633
|
+
export const getNumericFeatureAttributes = (featureProperties) => {
|
|
634
|
+
// We only want number values here
|
|
635
|
+
const filteredRecord = {};
|
|
636
|
+
for (const [key, set] of Object.entries(featureProperties)) {
|
|
637
|
+
const firstValue = set.values().next().value;
|
|
638
|
+
// Check if the first value is a string that cannot be parsed as a number
|
|
639
|
+
const isInvalidString = typeof firstValue === 'string' && isNaN(Number(firstValue));
|
|
640
|
+
if (!isInvalidString) {
|
|
641
|
+
filteredRecord[key] = set;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return filteredRecord;
|
|
645
|
+
};
|
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>;
|