@jupytergis/base 0.2.1 → 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 +2 -2
- 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 +146 -62
- package/lib/constants.d.ts +3 -0
- package/lib/constants.js +5 -1
- package/lib/dialogs/formdialog.d.ts +5 -0
- 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/components/color_ramp/ModeSelectRow.js +2 -1
- package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +26 -0
- package/lib/dialogs/symbology/hooks/useGetBandInfo.js +64 -0
- package/lib/dialogs/symbology/hooks/useGetProperties.d.ts +1 -1
- package/lib/dialogs/symbology/hooks/useGetProperties.js +12 -9
- 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 +16 -3
- package/lib/dialogs/symbology/tiff_layer/components/BandRow.d.ts +16 -3
- package/lib/dialogs/symbology/tiff_layer/components/BandRow.js +21 -7
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.d.ts +4 -0
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +85 -0
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.d.ts +1 -20
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +25 -65
- 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 +6 -2
- package/lib/formbuilder/creationform.js +6 -6
- package/lib/formbuilder/editform.d.ts +2 -2
- package/lib/formbuilder/editform.js +14 -9
- package/lib/formbuilder/formselectors.js +11 -1
- package/lib/formbuilder/objectform/baseform.d.ts +12 -3
- package/lib/formbuilder/objectform/baseform.js +39 -0
- package/lib/formbuilder/objectform/fileselectorwidget.d.ts +2 -0
- package/lib/formbuilder/objectform/fileselectorwidget.js +88 -0
- package/lib/formbuilder/objectform/geojsonsource.d.ts +5 -7
- package/lib/formbuilder/objectform/geojsonsource.js +8 -24
- 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/layerform.d.ts +2 -0
- package/lib/formbuilder/objectform/layerform.js +6 -0
- package/lib/formbuilder/objectform/pathbasedsource.d.ts +19 -0
- package/lib/formbuilder/objectform/pathbasedsource.js +98 -0
- package/lib/formbuilder/objectform/vectorlayerform.d.ts +0 -2
- package/lib/formbuilder/objectform/vectorlayerform.js +0 -59
- package/lib/icons.d.ts +1 -0
- package/lib/icons.js +5 -0
- package/lib/keybindings.json +62 -0
- package/lib/mainview/TemporalSlider.d.ts +8 -0
- package/lib/mainview/TemporalSlider.js +303 -0
- package/lib/mainview/mainView.d.ts +46 -8
- package/lib/mainview/mainView.js +431 -144
- 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 +8 -24
- 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/statusbar/StatusBar.d.ts +13 -0
- package/lib/statusbar/StatusBar.js +52 -0
- package/lib/toolbar/widget.d.ts +1 -1
- package/lib/toolbar/widget.js +44 -37
- package/lib/tools.d.ts +50 -7
- package/lib/tools.js +394 -12
- package/lib/types.d.ts +2 -0
- package/lib/widget.d.ts +29 -5
- package/lib/widget.js +41 -7
- package/package.json +17 -5
- package/style/base.css +11 -0
- package/style/icons/logo_mini_qgz.svg +31 -0
- package/style/leftPanel.css +8 -0
- package/style/statusBar.css +16 -0
- package/style/symbologyDialog.css +7 -1
- package/style/temporalSlider.css +47 -0
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { IJGISFormSchemaRegistry, IJupyterGISModel, IRasterLayerGalleryEntry } from '@jupytergis/schema';
|
|
2
2
|
import { Dialog } from '@jupyterlab/apputils';
|
|
3
3
|
import { PromiseDelegate } from '@lumino/coreutils';
|
|
4
|
-
import React from 'react';
|
|
5
|
-
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
6
4
|
import { Signal } from '@lumino/signaling';
|
|
5
|
+
import React from 'react';
|
|
7
6
|
interface ILayerBrowserDialogProps {
|
|
8
|
-
|
|
7
|
+
model: IJupyterGISModel;
|
|
9
8
|
registry: IRasterLayerGalleryEntry[];
|
|
10
9
|
formSchemaRegistry: IJGISFormSchemaRegistry;
|
|
11
10
|
okSignalPromise: PromiseDelegate<Signal<Dialog<any>, number>>;
|
|
12
11
|
cancel: () => void;
|
|
13
12
|
}
|
|
14
|
-
export declare const LayerBrowserComponent: ({
|
|
13
|
+
export declare const LayerBrowserComponent: ({ model, registry, formSchemaRegistry, okSignalPromise, cancel }: ILayerBrowserDialogProps) => React.JSX.Element;
|
|
15
14
|
export interface ILayerBrowserOptions {
|
|
16
|
-
|
|
15
|
+
model: IJupyterGISModel;
|
|
17
16
|
registry: IRasterLayerGalleryEntry[];
|
|
18
17
|
formSchemaRegistry: IJGISFormSchemaRegistry;
|
|
19
18
|
}
|
|
@@ -2,11 +2,11 @@ import { faCheck, faPlus } from '@fortawesome/free-solid-svg-icons';
|
|
|
2
2
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
3
3
|
import { Dialog } from '@jupyterlab/apputils';
|
|
4
4
|
import { PromiseDelegate, UUID } from '@lumino/coreutils';
|
|
5
|
+
import { Signal } from '@lumino/signaling';
|
|
5
6
|
import React, { useEffect, useState } from 'react';
|
|
6
7
|
import CUSTOM_RASTER_IMAGE from '../../rasterlayer_gallery/custom_raster.png';
|
|
7
8
|
import { CreationFormWrapper } from './formdialog';
|
|
8
|
-
|
|
9
|
-
export const LayerBrowserComponent = ({ context, registry, formSchemaRegistry, okSignalPromise, cancel }) => {
|
|
9
|
+
export const LayerBrowserComponent = ({ model, registry, formSchemaRegistry, okSignalPromise, cancel }) => {
|
|
10
10
|
const [searchTerm, setSearchTerm] = useState('');
|
|
11
11
|
const [activeLayers, setActiveLayers] = useState([]);
|
|
12
12
|
const [selectedCategory, setSelectedCategory] = useState();
|
|
@@ -15,9 +15,9 @@ export const LayerBrowserComponent = ({ context, registry, formSchemaRegistry, o
|
|
|
15
15
|
const providers = [...new Set(registry.map(item => item.source.provider))];
|
|
16
16
|
const filteredGallery = galleryWithCategory.filter(item => item.name.toLowerCase().includes(searchTerm));
|
|
17
17
|
useEffect(() => {
|
|
18
|
-
|
|
18
|
+
model.sharedModel.layersChanged.connect(handleLayerChange);
|
|
19
19
|
return () => {
|
|
20
|
-
|
|
20
|
+
model.sharedModel.layersChanged.disconnect(handleLayerChange);
|
|
21
21
|
};
|
|
22
22
|
}, []);
|
|
23
23
|
/**
|
|
@@ -25,7 +25,7 @@ export const LayerBrowserComponent = ({ context, registry, formSchemaRegistry, o
|
|
|
25
25
|
*/
|
|
26
26
|
const handleLayerChange = (_, change) => {
|
|
27
27
|
// The split is to get rid of the 'Layer' part of the name to match the names in the gallery
|
|
28
|
-
setActiveLayers(Object.values(
|
|
28
|
+
setActiveLayers(Object.values(model.sharedModel.layers).map(layer => layer.name.split(' ')[0]));
|
|
29
29
|
};
|
|
30
30
|
const handleSearchInput = (event) => {
|
|
31
31
|
setSearchTerm(event.target.value.toLowerCase());
|
|
@@ -64,8 +64,8 @@ export const LayerBrowserComponent = ({ context, registry, formSchemaRegistry, o
|
|
|
64
64
|
visible: true,
|
|
65
65
|
name: tile.name + ' Layer'
|
|
66
66
|
};
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
model.sharedModel.addSource(sourceId, sourceModel);
|
|
68
|
+
model.addLayer(UUID.uuid4(), layerModel);
|
|
69
69
|
};
|
|
70
70
|
if (creatingCustomRaster) {
|
|
71
71
|
// Disconnect any previous handler
|
|
@@ -73,7 +73,7 @@ export const LayerBrowserComponent = ({ context, registry, formSchemaRegistry, o
|
|
|
73
73
|
value.disconnect(cancel, this);
|
|
74
74
|
});
|
|
75
75
|
return (React.createElement("div", { className: "jGIS-customlayer-form" },
|
|
76
|
-
React.createElement(CreationFormWrapper, {
|
|
76
|
+
React.createElement(CreationFormWrapper, { model: model, formSchemaRegistry: formSchemaRegistry, createLayer: true, createSource: true, layerType: 'RasterLayer', sourceType: 'RasterSource', layerData: {
|
|
77
77
|
name: 'Custom Raster'
|
|
78
78
|
}, sourceData: {
|
|
79
79
|
url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
|
@@ -122,7 +122,7 @@ export class LayerBrowserWidget extends Dialog {
|
|
|
122
122
|
this.resolve(0);
|
|
123
123
|
};
|
|
124
124
|
const okSignalPromise = new PromiseDelegate();
|
|
125
|
-
const body = (React.createElement(LayerBrowserComponent, {
|
|
125
|
+
const body = (React.createElement(LayerBrowserComponent, { model: options.model, registry: options.registry, formSchemaRegistry: options.formSchemaRegistry, okSignalPromise: okSignalPromise, cancel: cancelCallback }));
|
|
126
126
|
super({ body, buttons: [Dialog.cancelButton(), Dialog.okButton()] });
|
|
127
127
|
this.id = 'jupytergis::layerBrowser';
|
|
128
128
|
this.okSignal = new Signal(this);
|
|
@@ -6,6 +6,7 @@ const ModeSelectRow = ({ numberOfShades, setNumberOfShades, selectedMode, setSel
|
|
|
6
6
|
React.createElement("input", { className: "jp-mod-styled", name: "class-number-input", type: "number", value: selectedMode === 'continuous' ? 52 : numberOfShades, onChange: event => setNumberOfShades(event.target.value), disabled: selectedMode === 'continuous' })),
|
|
7
7
|
React.createElement("div", { className: "jp-gis-color-ramp-div" },
|
|
8
8
|
React.createElement("label", { htmlFor: "mode-select" }, "Mode:"),
|
|
9
|
-
React.createElement("
|
|
9
|
+
React.createElement("div", { className: "jp-select-wrapper" },
|
|
10
|
+
React.createElement("select", { name: "mode-select", id: "mode-select", className: "jp-mod-styled", value: selectedMode, onChange: event => setSelectedMode(event.target.value) }, modeOptions.map(mode => (React.createElement("option", { key: mode, value: mode, selected: selectedMode === mode }, mode))))))));
|
|
10
11
|
};
|
|
11
12
|
export default ModeSelectRow;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { IDict, IJGISLayer, IJupyterGISModel } from '@jupytergis/schema';
|
|
2
|
+
export interface IBandHistogram {
|
|
3
|
+
buckets: number[];
|
|
4
|
+
count: number;
|
|
5
|
+
max: number;
|
|
6
|
+
min: number;
|
|
7
|
+
}
|
|
8
|
+
export interface IBandRow {
|
|
9
|
+
band: number;
|
|
10
|
+
colorInterpretation: string;
|
|
11
|
+
stats: {
|
|
12
|
+
minimum: number;
|
|
13
|
+
maximum: number;
|
|
14
|
+
mean: number;
|
|
15
|
+
stdDev: number;
|
|
16
|
+
};
|
|
17
|
+
metadata: IDict;
|
|
18
|
+
histogram: IBandHistogram;
|
|
19
|
+
}
|
|
20
|
+
declare const useGetBandInfo: (model: IJupyterGISModel, layer: IJGISLayer) => {
|
|
21
|
+
bandRows: IBandRow[];
|
|
22
|
+
setBandRows: import("react").Dispatch<import("react").SetStateAction<IBandRow[]>>;
|
|
23
|
+
loading: boolean;
|
|
24
|
+
error: string | null;
|
|
25
|
+
};
|
|
26
|
+
export default useGetBandInfo;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { loadFile } from '../../../tools';
|
|
3
|
+
const preloadGeoTiffFile = async (sourceInfo, model) => {
|
|
4
|
+
var _a;
|
|
5
|
+
return await loadFile({
|
|
6
|
+
filepath: (_a = sourceInfo.url) !== null && _a !== void 0 ? _a : '',
|
|
7
|
+
type: 'GeoTiffSource',
|
|
8
|
+
model: model
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
const useGetBandInfo = (model, layer) => {
|
|
12
|
+
const [bandRows, setBandRows] = useState([]);
|
|
13
|
+
const [loading, setLoading] = useState(false);
|
|
14
|
+
const [error, setError] = useState(null);
|
|
15
|
+
const fetchBandInfo = async () => {
|
|
16
|
+
var _a, _b;
|
|
17
|
+
setLoading(true);
|
|
18
|
+
setError(null);
|
|
19
|
+
try {
|
|
20
|
+
const bandsArr = [];
|
|
21
|
+
const source = model.getSource((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.source);
|
|
22
|
+
const sourceInfo = (_b = source === null || source === void 0 ? void 0 : source.parameters) === null || _b === void 0 ? void 0 : _b.urls[0];
|
|
23
|
+
if (!(sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.url)) {
|
|
24
|
+
setError('No source URL found.');
|
|
25
|
+
setLoading(false);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const preloadedFile = await preloadGeoTiffFile(sourceInfo, model);
|
|
29
|
+
const { file, metadata, sourceUrl } = Object.assign({}, preloadedFile);
|
|
30
|
+
if (file && metadata && sourceUrl === sourceInfo.url) {
|
|
31
|
+
metadata['bands'].forEach((bandData) => {
|
|
32
|
+
var _a, _b;
|
|
33
|
+
bandsArr.push({
|
|
34
|
+
band: bandData.band,
|
|
35
|
+
colorInterpretation: bandData.colorInterpretation,
|
|
36
|
+
stats: {
|
|
37
|
+
minimum: (_a = sourceInfo.min) !== null && _a !== void 0 ? _a : bandData.minimum,
|
|
38
|
+
maximum: (_b = sourceInfo.max) !== null && _b !== void 0 ? _b : bandData.maximum,
|
|
39
|
+
mean: bandData.mean,
|
|
40
|
+
stdDev: bandData.stdDev
|
|
41
|
+
},
|
|
42
|
+
metadata: bandData.metadata,
|
|
43
|
+
histogram: bandData.histogram
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
setBandRows(bandsArr);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
setError('Failed to preload the file or metadata mismatch.');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
setError(`Error fetching band info: ${err.message}`);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
setLoading(false);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
fetchBandInfo();
|
|
61
|
+
}, []);
|
|
62
|
+
return { bandRows, setBandRows, loading, error };
|
|
63
|
+
};
|
|
64
|
+
export default useGetBandInfo;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// import { GeoJSONFeature } from 'geojson';
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
|
+
import { loadFile } from '../../../tools';
|
|
3
4
|
export const useGetProperties = ({ layerId, model }) => {
|
|
4
|
-
const [
|
|
5
|
+
const [featureProperties, setFeatureProperties] = useState({});
|
|
5
6
|
const [isLoading, setIsLoading] = useState(true);
|
|
6
7
|
const [error, setError] = useState(undefined);
|
|
7
8
|
const getProperties = async () => {
|
|
@@ -15,7 +16,11 @@ export const useGetProperties = ({ layerId, model }) => {
|
|
|
15
16
|
if (!source) {
|
|
16
17
|
throw new Error('Source not found');
|
|
17
18
|
}
|
|
18
|
-
const data = await
|
|
19
|
+
const data = await loadFile({
|
|
20
|
+
filepath: (_b = source.parameters) === null || _b === void 0 ? void 0 : _b.path,
|
|
21
|
+
type: 'GeoJSONSource',
|
|
22
|
+
model: model
|
|
23
|
+
});
|
|
19
24
|
if (!data) {
|
|
20
25
|
throw new Error('Failed to read GeoJSON data');
|
|
21
26
|
}
|
|
@@ -23,16 +28,14 @@ export const useGetProperties = ({ layerId, model }) => {
|
|
|
23
28
|
data.features.forEach((feature) => {
|
|
24
29
|
if (feature.properties) {
|
|
25
30
|
Object.entries(feature.properties).forEach(([key, value]) => {
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
result[key] = new Set();
|
|
29
|
-
}
|
|
30
|
-
result[key].add(value);
|
|
31
|
+
if (!(key in result)) {
|
|
32
|
+
result[key] = new Set();
|
|
31
33
|
}
|
|
34
|
+
result[key].add(value);
|
|
32
35
|
});
|
|
33
36
|
}
|
|
34
37
|
});
|
|
35
|
-
|
|
38
|
+
setFeatureProperties(result);
|
|
36
39
|
setIsLoading(false);
|
|
37
40
|
}
|
|
38
41
|
catch (err) {
|
|
@@ -43,5 +46,5 @@ export const useGetProperties = ({ layerId, model }) => {
|
|
|
43
46
|
useEffect(() => {
|
|
44
47
|
getProperties();
|
|
45
48
|
}, [model, layerId]);
|
|
46
|
-
return {
|
|
49
|
+
return { featureProperties, isLoading, error };
|
|
47
50
|
};
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { IJupyterGISModel } from '@jupytergis/schema';
|
|
2
2
|
import { Dialog } from '@jupyterlab/apputils';
|
|
3
|
-
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
4
3
|
import { IStateDB } from '@jupyterlab/statedb';
|
|
5
4
|
import { PromiseDelegate } from '@lumino/coreutils';
|
|
6
5
|
import { Signal } from '@lumino/signaling';
|
|
7
6
|
export interface ISymbologyDialogProps {
|
|
8
|
-
|
|
7
|
+
model: IJupyterGISModel;
|
|
9
8
|
state: IStateDB;
|
|
10
9
|
okSignalPromise: PromiseDelegate<Signal<SymbologyWidget, null>>;
|
|
11
10
|
cancel: () => void;
|
|
12
11
|
layerId?: string;
|
|
13
12
|
}
|
|
14
13
|
export interface ISymbologyWidgetOptions {
|
|
15
|
-
|
|
14
|
+
model: IJupyterGISModel;
|
|
16
15
|
state: IStateDB;
|
|
17
16
|
}
|
|
18
17
|
export interface IStopRow {
|
|
@@ -4,31 +4,31 @@ import { Signal } from '@lumino/signaling';
|
|
|
4
4
|
import React, { useEffect, useState } from 'react';
|
|
5
5
|
import TiffRendering from './tiff_layer/TiffRendering';
|
|
6
6
|
import VectorRendering from './vector_layer/VectorRendering';
|
|
7
|
-
const SymbologyDialog = ({
|
|
7
|
+
const SymbologyDialog = ({ model, state, okSignalPromise, cancel }) => {
|
|
8
8
|
const [selectedLayer, setSelectedLayer] = useState(null);
|
|
9
9
|
const [componentToRender, setComponentToRender] = useState(null);
|
|
10
10
|
let LayerSymbology;
|
|
11
11
|
useEffect(() => {
|
|
12
12
|
const handleClientStateChanged = () => {
|
|
13
13
|
var _a, _b;
|
|
14
|
-
if (!((_b = (_a =
|
|
14
|
+
if (!((_b = (_a = model.localState) === null || _a === void 0 ? void 0 : _a.selected) === null || _b === void 0 ? void 0 : _b.value)) {
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
|
-
const currentLayer = Object.keys(
|
|
17
|
+
const currentLayer = Object.keys(model.localState.selected.value)[0];
|
|
18
18
|
setSelectedLayer(currentLayer);
|
|
19
19
|
};
|
|
20
20
|
// Initial state
|
|
21
21
|
handleClientStateChanged();
|
|
22
|
-
|
|
22
|
+
model.clientStateChanged.connect(handleClientStateChanged);
|
|
23
23
|
return () => {
|
|
24
|
-
|
|
24
|
+
model.clientStateChanged.disconnect(handleClientStateChanged);
|
|
25
25
|
};
|
|
26
26
|
}, []);
|
|
27
27
|
useEffect(() => {
|
|
28
28
|
if (!selectedLayer) {
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
|
-
const layer =
|
|
31
|
+
const layer = model.getLayer(selectedLayer);
|
|
32
32
|
if (!layer) {
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
@@ -36,10 +36,11 @@ const SymbologyDialog = ({ context, state, okSignalPromise, cancel }) => {
|
|
|
36
36
|
switch (layer.type) {
|
|
37
37
|
case 'VectorLayer':
|
|
38
38
|
case 'VectorTileLayer':
|
|
39
|
-
|
|
39
|
+
case 'HeatmapLayer':
|
|
40
|
+
LayerSymbology = (React.createElement(VectorRendering, { model: model, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: selectedLayer }));
|
|
40
41
|
break;
|
|
41
42
|
case 'WebGlLayer':
|
|
42
|
-
LayerSymbology = (React.createElement(TiffRendering, {
|
|
43
|
+
LayerSymbology = (React.createElement(TiffRendering, { model: model, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: selectedLayer }));
|
|
43
44
|
break;
|
|
44
45
|
default:
|
|
45
46
|
LayerSymbology = React.createElement("div", null, "Layer Type Not Supported");
|
|
@@ -54,7 +55,7 @@ export class SymbologyWidget extends Dialog {
|
|
|
54
55
|
this.resolve(0);
|
|
55
56
|
};
|
|
56
57
|
const okSignalPromise = new PromiseDelegate();
|
|
57
|
-
const body = (React.createElement(SymbologyDialog, {
|
|
58
|
+
const body = (React.createElement(SymbologyDialog, { model: options.model, okSignalPromise: okSignalPromise, cancel: cancelCallback, state: options.state }));
|
|
58
59
|
super({ title: 'Symbology', body });
|
|
59
60
|
this.id = 'jupytergis::symbologyWidget';
|
|
60
61
|
this.okSignal = new Signal(this);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ISymbologyDialogProps } from '../symbologyDialog';
|
|
3
|
-
declare const TiffRendering: ({
|
|
3
|
+
declare const TiffRendering: ({ model, state, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element | undefined;
|
|
4
4
|
export default TiffRendering;
|
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import SingleBandPseudoColor from './types/SingleBandPseudoColor';
|
|
3
|
-
|
|
3
|
+
import MultibandColor from './types/MultibandColor';
|
|
4
|
+
const TiffRendering = ({ model, state, okSignalPromise, cancel, layerId }) => {
|
|
4
5
|
const renderTypes = ['Singleband Pseudocolor', 'Multiband Color'];
|
|
5
|
-
const [selectedRenderType, setSelectedRenderType] = useState(
|
|
6
|
+
const [selectedRenderType, setSelectedRenderType] = useState();
|
|
6
7
|
const [componentToRender, setComponentToRender] = useState(null);
|
|
7
8
|
let RenderComponent;
|
|
9
|
+
if (!layerId) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
var _a, _b;
|
|
14
|
+
const layer = model.getLayer(layerId);
|
|
15
|
+
const renderType = (_b = (_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.symbologyState) === null || _b === void 0 ? void 0 : _b.renderType;
|
|
16
|
+
setSelectedRenderType(renderType !== null && renderType !== void 0 ? renderType : 'Singleband Pseudocolor');
|
|
17
|
+
}, []);
|
|
8
18
|
useEffect(() => {
|
|
9
19
|
if (!selectedRenderType) {
|
|
10
20
|
return;
|
|
11
21
|
}
|
|
12
22
|
switch (selectedRenderType) {
|
|
13
23
|
case 'Singleband Pseudocolor':
|
|
14
|
-
RenderComponent = (React.createElement(SingleBandPseudoColor, {
|
|
24
|
+
RenderComponent = (React.createElement(SingleBandPseudoColor, { model: model, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: layerId }));
|
|
25
|
+
break;
|
|
26
|
+
case 'Multiband Color':
|
|
27
|
+
RenderComponent = (React.createElement(MultibandColor, { model: model, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: layerId }));
|
|
15
28
|
break;
|
|
16
29
|
default:
|
|
17
30
|
RenderComponent = React.createElement("div", null, "Render Type Not Implemented (yet)");
|
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { IBandRow } from '
|
|
3
|
-
|
|
2
|
+
import { IBandRow } from '../../hooks/useGetBandInfo';
|
|
3
|
+
interface IBandRowProps {
|
|
4
|
+
label: string;
|
|
4
5
|
index: number;
|
|
5
6
|
bandRow: IBandRow;
|
|
6
7
|
bandRows: IBandRow[];
|
|
7
8
|
setSelectedBand: (band: number) => void;
|
|
8
9
|
setBandRows: (bandRows: IBandRow[]) => void;
|
|
9
|
-
|
|
10
|
+
isMultibandColor?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param label Label displayed in symbology dialog
|
|
15
|
+
* @param index Index of current row in band row data
|
|
16
|
+
* @param bandRow Band from bands array, will be undefined when band is 'unset' in Multiband color
|
|
17
|
+
* @param bandRows Bands array from tiff data
|
|
18
|
+
* @param setSelectedBand Function to set selected band parent
|
|
19
|
+
* @param setBandRows Function to update band rows in parent
|
|
20
|
+
* @param isMultibandColor Used to hide min/max input and add 'Unset' option to drop down menu for MultiBand symbology
|
|
21
|
+
*/
|
|
22
|
+
declare const BandRow: ({ label, index, bandRow, bandRows, setSelectedBand, setBandRows, isMultibandColor }: IBandRowProps) => React.JSX.Element;
|
|
10
23
|
export default BandRow;
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @param label Label displayed in symbology dialog
|
|
5
|
+
* @param index Index of current row in band row data
|
|
6
|
+
* @param bandRow Band from bands array, will be undefined when band is 'unset' in Multiband color
|
|
7
|
+
* @param bandRows Bands array from tiff data
|
|
8
|
+
* @param setSelectedBand Function to set selected band parent
|
|
9
|
+
* @param setBandRows Function to update band rows in parent
|
|
10
|
+
* @param isMultibandColor Used to hide min/max input and add 'Unset' option to drop down menu for MultiBand symbology
|
|
11
|
+
*/
|
|
12
|
+
const BandRow = ({ label, index, bandRow, bandRows, setSelectedBand, setBandRows, isMultibandColor }) => {
|
|
13
|
+
const [minValue, setMinValue] = useState(bandRow === null || bandRow === void 0 ? void 0 : bandRow.stats.minimum);
|
|
14
|
+
const [maxValue, setMaxValue] = useState(bandRow === null || bandRow === void 0 ? void 0 : bandRow.stats.maximum);
|
|
5
15
|
const handleMinValueChange = (event) => {
|
|
6
16
|
setMinValue(+event.target.value);
|
|
7
17
|
setNewBands();
|
|
@@ -18,10 +28,14 @@ const BandRow = ({ index, bandRow, bandRows, setSelectedBand, setBandRows }) =>
|
|
|
18
28
|
};
|
|
19
29
|
return (React.createElement(React.Fragment, null,
|
|
20
30
|
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
21
|
-
React.createElement("label", { htmlFor: `band-select-${index}` },
|
|
31
|
+
React.createElement("label", { htmlFor: `band-select-${index}` },
|
|
32
|
+
label,
|
|
33
|
+
":"),
|
|
22
34
|
React.createElement("div", { className: "jp-select-wrapper" },
|
|
23
|
-
React.createElement("select", { name: `band-select-${index}`, onChange: event => setSelectedBand(+event.target.value), className: "jp-mod-styled" },
|
|
24
|
-
|
|
35
|
+
React.createElement("select", { name: `band-select-${index}`, onChange: event => setSelectedBand(+event.target.value), className: "jp-mod-styled" },
|
|
36
|
+
bandRows.map((band, bandIndex) => (React.createElement("option", { key: bandIndex, value: band.band, selected: band.band === (bandRow === null || bandRow === void 0 ? void 0 : bandRow.band), className: "jp-mod-styled" }, `Band ${band.band} (${band.colorInterpretation})`))),
|
|
37
|
+
isMultibandColor ? (React.createElement("option", { key: 'unset', value: 0, selected: !bandRow, className: "jp-mod-styled" }, "Unset")) : null))),
|
|
38
|
+
isMultibandColor ? null : (React.createElement("div", { className: "jp-gis-symbology-row", style: { gap: '0.5rem' } },
|
|
25
39
|
React.createElement("div", { style: {
|
|
26
40
|
display: 'flex',
|
|
27
41
|
justifyContent: 'space-between',
|
|
@@ -38,6 +52,6 @@ const BandRow = ({ index, bandRow, bandRows, setSelectedBand, setBandRows }) =>
|
|
|
38
52
|
React.createElement("label", { htmlFor: "band-max", style: { alignSelf: 'center' } }, "Max"),
|
|
39
53
|
React.createElement("input", { type: "number", className: "jp-mod-styled",
|
|
40
54
|
// defaultValue={bandRow.stats.maximum}
|
|
41
|
-
value: maxValue, onChange: handleMaxValueChange, onBlur: setNewBands })))));
|
|
55
|
+
value: maxValue, onChange: handleMaxValueChange, onBlur: setNewBands }))))));
|
|
42
56
|
};
|
|
43
57
|
export default BandRow;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { Spinner } from '../../../../mainview/spinner';
|
|
3
|
+
import useGetBandInfo from '../../hooks/useGetBandInfo';
|
|
4
|
+
import BandRow from '../components/BandRow';
|
|
5
|
+
const MultibandColor = ({ model, okSignalPromise, cancel, layerId }) => {
|
|
6
|
+
if (!layerId) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const layer = model.getLayer(layerId);
|
|
10
|
+
if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const { bandRows, setBandRows, loading } = useGetBandInfo(model, layer);
|
|
14
|
+
const [selectedBands, setSelectedBands] = useState({
|
|
15
|
+
red: 1,
|
|
16
|
+
green: 2,
|
|
17
|
+
blue: 3
|
|
18
|
+
});
|
|
19
|
+
const numOfBandsRef = useRef(0);
|
|
20
|
+
const selectedBandsRef = useRef({
|
|
21
|
+
red: selectedBands.red,
|
|
22
|
+
green: selectedBands.green,
|
|
23
|
+
blue: selectedBands.blue
|
|
24
|
+
});
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
populateOptions();
|
|
27
|
+
okSignalPromise.promise.then(okSignal => {
|
|
28
|
+
okSignal.connect(handleOk);
|
|
29
|
+
});
|
|
30
|
+
return () => {
|
|
31
|
+
okSignalPromise.promise.then(okSignal => {
|
|
32
|
+
okSignal.disconnect(handleOk, this);
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
}, []);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
numOfBandsRef.current = bandRows.length;
|
|
38
|
+
}, [bandRows]);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
selectedBandsRef.current = selectedBands;
|
|
41
|
+
}, [selectedBands]);
|
|
42
|
+
const populateOptions = async () => {
|
|
43
|
+
var _a, _b, _c, _d, _e, _f;
|
|
44
|
+
const layerParams = layer.parameters;
|
|
45
|
+
const red = (_b = (_a = layerParams.symbologyState) === null || _a === void 0 ? void 0 : _a.redBand) !== null && _b !== void 0 ? _b : 1;
|
|
46
|
+
const green = (_d = (_c = layerParams.symbologyState) === null || _c === void 0 ? void 0 : _c.greenBand) !== null && _d !== void 0 ? _d : 2;
|
|
47
|
+
const blue = (_f = (_e = layerParams.symbologyState) === null || _e === void 0 ? void 0 : _e.blueBand) !== null && _f !== void 0 ? _f : 3;
|
|
48
|
+
setSelectedBands({ red, green, blue });
|
|
49
|
+
};
|
|
50
|
+
const updateBand = (color, value) => {
|
|
51
|
+
setSelectedBands(prevBands => (Object.assign(Object.assign({}, prevBands), { [color]: value })));
|
|
52
|
+
};
|
|
53
|
+
const handleOk = () => {
|
|
54
|
+
// Update layer
|
|
55
|
+
if (!layer.parameters) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const colorExpr = ['array'];
|
|
59
|
+
const rgb = ['red', 'green', 'blue'];
|
|
60
|
+
rgb.forEach(color => {
|
|
61
|
+
const bandValue = selectedBandsRef.current[color];
|
|
62
|
+
colorExpr.push(bandValue !== 0 ? ['band', bandValue] : 0);
|
|
63
|
+
});
|
|
64
|
+
// Array expression expects 4 values
|
|
65
|
+
// Last band should be alpha band added by OpenLayers
|
|
66
|
+
colorExpr.push(['band', numOfBandsRef.current + 1]);
|
|
67
|
+
const symbologyState = {
|
|
68
|
+
renderType: 'Multiband Color',
|
|
69
|
+
redBand: selectedBandsRef.current['red'],
|
|
70
|
+
greenBand: selectedBandsRef.current['green'],
|
|
71
|
+
blueBand: selectedBandsRef.current['blue']
|
|
72
|
+
};
|
|
73
|
+
layer.parameters.symbologyState = symbologyState;
|
|
74
|
+
layer.parameters.color = colorExpr;
|
|
75
|
+
layer.type = 'WebGlLayer';
|
|
76
|
+
model.sharedModel.updateLayer(layerId, layer);
|
|
77
|
+
cancel();
|
|
78
|
+
};
|
|
79
|
+
return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
|
|
80
|
+
React.createElement("div", { className: "jp-gis-band-container" }, loading ? (React.createElement(Spinner, { loading: loading })) : (React.createElement(React.Fragment, null,
|
|
81
|
+
React.createElement(BandRow, { label: "Red Band", index: selectedBands['red'] - 1, bandRow: bandRows[selectedBands['red'] - 1], bandRows: bandRows, setSelectedBand: val => updateBand('red', val), setBandRows: setBandRows, isMultibandColor: true }),
|
|
82
|
+
React.createElement(BandRow, { label: "Green Band", index: selectedBands['green'] - 1, bandRow: bandRows[selectedBands['green'] - 1], bandRows: bandRows, setSelectedBand: val => updateBand('green', val), setBandRows: setBandRows, isMultibandColor: true }),
|
|
83
|
+
React.createElement(BandRow, { label: "Blue Band", index: selectedBands['blue'] - 1, bandRow: bandRows[selectedBands['blue'] - 1], bandRows: bandRows, setSelectedBand: val => updateBand('blue', val), setBandRows: setBandRows, isMultibandColor: true }))))));
|
|
84
|
+
};
|
|
85
|
+
export default MultibandColor;
|
|
@@ -1,24 +1,5 @@
|
|
|
1
|
-
import { IDict } from '@jupytergis/schema';
|
|
2
1
|
import React from 'react';
|
|
3
2
|
import { ISymbologyDialogProps } from '../../symbologyDialog';
|
|
4
|
-
export interface IBandRow {
|
|
5
|
-
band: number;
|
|
6
|
-
colorInterpretation: string;
|
|
7
|
-
stats: {
|
|
8
|
-
minimum: number;
|
|
9
|
-
maximum: number;
|
|
10
|
-
mean: number;
|
|
11
|
-
stdDev: number;
|
|
12
|
-
};
|
|
13
|
-
metadata: IDict;
|
|
14
|
-
histogram: IBandHistogram;
|
|
15
|
-
}
|
|
16
|
-
export interface IBandHistogram {
|
|
17
|
-
buckets: number[];
|
|
18
|
-
count: number;
|
|
19
|
-
max: number;
|
|
20
|
-
min: number;
|
|
21
|
-
}
|
|
22
3
|
export type InterpolationType = 'discrete' | 'linear' | 'exact';
|
|
23
|
-
declare const SingleBandPseudoColor: ({
|
|
4
|
+
declare const SingleBandPseudoColor: ({ model, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element | undefined;
|
|
24
5
|
export default SingleBandPseudoColor;
|