@jupytergis/base 0.2.1 → 0.3.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/commands.js +30 -1
- package/lib/constants.d.ts +1 -0
- package/lib/constants.js +1 -0
- package/lib/dialogs/formdialog.d.ts +5 -0
- package/lib/dialogs/formdialog.js +2 -2
- package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.js +2 -1
- package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +27 -0
- package/lib/dialogs/symbology/hooks/useGetBandInfo.js +59 -0
- package/lib/dialogs/symbology/hooks/useGetProperties.js +6 -1
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +1 -1
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +14 -1
- 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 +84 -0
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.d.ts +0 -19
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +18 -59
- package/lib/formbuilder/creationform.d.ts +5 -0
- package/lib/formbuilder/creationform.js +2 -2
- package/lib/formbuilder/editform.d.ts +1 -0
- package/lib/formbuilder/editform.js +8 -3
- package/lib/formbuilder/formselectors.js +7 -0
- package/lib/formbuilder/objectform/baseform.d.ts +10 -0
- package/lib/formbuilder/objectform/baseform.js +39 -0
- package/lib/formbuilder/objectform/fileselectorwidget.d.ts +2 -0
- package/lib/formbuilder/objectform/fileselectorwidget.js +81 -0
- package/lib/formbuilder/objectform/geojsonsource.d.ts +5 -7
- package/lib/formbuilder/objectform/geojsonsource.js +8 -24
- 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/icons.d.ts +1 -0
- package/lib/icons.js +5 -0
- package/lib/keybindings.json +62 -0
- package/lib/mainview/mainView.d.ts +22 -5
- package/lib/mainview/mainView.js +224 -75
- package/lib/panelview/components/filter-panel/Filter.js +6 -1
- package/lib/statusbar/StatusBar.d.ts +13 -0
- package/lib/statusbar/StatusBar.js +52 -0
- package/lib/toolbar/widget.js +0 -5
- package/lib/tools.d.ts +40 -1
- package/lib/tools.js +308 -0
- package/package.json +16 -5
- package/style/base.css +1 -0
- package/style/icons/logo_mini_qgz.svg +31 -0
- package/style/leftPanel.css +8 -0
- package/style/statusBar.css +16 -0
|
@@ -36,7 +36,7 @@ const Annotation = ({ itemId, annotationModel, rightPanelModel, children }) => {
|
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
38
|
const centerOnAnnotation = () => {
|
|
39
|
-
jgisModel === null || jgisModel === void 0 ? void 0 : jgisModel.
|
|
39
|
+
jgisModel === null || jgisModel === void 0 ? void 0 : jgisModel.centerOnPosition(itemId);
|
|
40
40
|
};
|
|
41
41
|
return (React.createElement("div", { className: "jGIS-Annotation" },
|
|
42
42
|
children,
|
package/lib/commands.js
CHANGED
|
@@ -3,6 +3,16 @@ import { CommandIDs, icons } from './constants';
|
|
|
3
3
|
import { CreationFormDialog } from './dialogs/formdialog';
|
|
4
4
|
import { LayerBrowserWidget } from './dialogs/layerBrowserDialog';
|
|
5
5
|
import { SymbologyWidget } from './dialogs/symbology/symbologyDialog';
|
|
6
|
+
import keybindings from './keybindings.json';
|
|
7
|
+
function loadKeybindings(commands, keybindings) {
|
|
8
|
+
keybindings.forEach(binding => {
|
|
9
|
+
commands.addKeyBinding({
|
|
10
|
+
command: binding.command,
|
|
11
|
+
keys: binding.keys,
|
|
12
|
+
selector: binding.selector
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
}
|
|
6
16
|
/**
|
|
7
17
|
* Add the commands to the application's command registry.
|
|
8
18
|
*/
|
|
@@ -164,7 +174,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
164
174
|
createSource: true,
|
|
165
175
|
sourceData: {
|
|
166
176
|
name: 'Custom Image Source',
|
|
167
|
-
|
|
177
|
+
path: 'https://maplibre.org/maplibre-gl-js/docs/assets/radar.gif',
|
|
168
178
|
coordinates: [
|
|
169
179
|
[-80.425, 46.437],
|
|
170
180
|
[-71.516, 46.437],
|
|
@@ -638,6 +648,25 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
638
648
|
}
|
|
639
649
|
}
|
|
640
650
|
});
|
|
651
|
+
commands.addCommand(CommandIDs.zoomToLayer, {
|
|
652
|
+
label: trans.__('Zoom to Layer'),
|
|
653
|
+
execute: () => {
|
|
654
|
+
var _a;
|
|
655
|
+
const currentWidget = tracker.currentWidget;
|
|
656
|
+
if (!currentWidget || !completionProviderManager) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
console.log('zooming');
|
|
660
|
+
const model = tracker.currentWidget.context.model;
|
|
661
|
+
const selectedItems = (_a = model.localState) === null || _a === void 0 ? void 0 : _a.selected.value;
|
|
662
|
+
if (!selectedItems) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const layerId = Object.keys(selectedItems)[0];
|
|
666
|
+
model.centerOnPosition(layerId);
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
loadKeybindings(commands, keybindings);
|
|
641
670
|
}
|
|
642
671
|
var Private;
|
|
643
672
|
(function (Private) {
|
package/lib/constants.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export declare namespace CommandIDs {
|
|
|
45
45
|
const executeConsole = "jupytergis:executeConsole";
|
|
46
46
|
const selectCompleter = "jupytergis:selectConsoleCompleter";
|
|
47
47
|
const addAnnotation = "jupytergis:addAnnotation";
|
|
48
|
+
const zoomToLayer = "jupytergis:zoomToLayer";
|
|
48
49
|
}
|
|
49
50
|
interface IRegisteredIcon {
|
|
50
51
|
icon?: LabIcon;
|
package/lib/constants.js
CHANGED
|
@@ -55,6 +55,7 @@ export var CommandIDs;
|
|
|
55
55
|
CommandIDs.selectCompleter = 'jupytergis:selectConsoleCompleter';
|
|
56
56
|
// Map Commands
|
|
57
57
|
CommandIDs.addAnnotation = 'jupytergis:addAnnotation';
|
|
58
|
+
CommandIDs.zoomToLayer = 'jupytergis:zoomToLayer';
|
|
58
59
|
})(CommandIDs || (CommandIDs = {}));
|
|
59
60
|
const iconObject = {
|
|
60
61
|
RasterSource: { icon: rasterIcon },
|
|
@@ -16,6 +16,11 @@ export interface ICreationFormWrapperProps extends ICreationFormProps {
|
|
|
16
16
|
* some extra errors or not.
|
|
17
17
|
*/
|
|
18
18
|
formErrorSignalPromise?: PromiseDelegate<Signal<Dialog<any>, boolean>>;
|
|
19
|
+
/**
|
|
20
|
+
* Configuration options for the dialog, including settings for layer data, source data,
|
|
21
|
+
* and other form-related parameters.
|
|
22
|
+
*/
|
|
23
|
+
dialogOptions?: any;
|
|
19
24
|
}
|
|
20
25
|
export interface ICreationFormDialogOptions extends ICreationFormProps {
|
|
21
26
|
title: string;
|
|
@@ -16,7 +16,7 @@ export const CreationFormWrapper = (props) => {
|
|
|
16
16
|
formErrorSignal.current = formChanged;
|
|
17
17
|
setReady(true);
|
|
18
18
|
});
|
|
19
|
-
return (ready && (React.createElement(CreationForm, { context: props.context, formSchemaRegistry: props.formSchemaRegistry, createLayer: props.createLayer, createSource: props.createSource, layerType: props.layerType, sourceType: props.sourceType, sourceData: props.sourceData, layerData: props.layerData, ok: okSignal.current, cancel: props.cancel, formErrorSignal: formErrorSignal.current })));
|
|
19
|
+
return (ready && (React.createElement(CreationForm, { context: props.context, formSchemaRegistry: props.formSchemaRegistry, createLayer: props.createLayer, createSource: props.createSource, layerType: props.layerType, sourceType: props.sourceType, sourceData: props.sourceData, layerData: props.layerData, ok: okSignal.current, cancel: props.cancel, formErrorSignal: formErrorSignal.current, dialogOptions: props.dialogOptions })));
|
|
20
20
|
};
|
|
21
21
|
/**
|
|
22
22
|
* Form for creating a source, a layer or both at the same time
|
|
@@ -29,7 +29,7 @@ export class CreationFormDialog extends Dialog {
|
|
|
29
29
|
const okSignalPromise = new PromiseDelegate();
|
|
30
30
|
const formErrorSignalPromise = new PromiseDelegate();
|
|
31
31
|
const body = (React.createElement("div", { style: { overflow: 'auto' } },
|
|
32
|
-
React.createElement(CreationFormWrapper, { context: options.context, formSchemaRegistry: options.formSchemaRegistry, createLayer: options.createLayer, createSource: options.createSource, layerType: options.layerType, sourceType: options.sourceType, sourceData: options.sourceData, layerData: options.layerData, okSignalPromise: okSignalPromise, cancel: cancelCallback, formErrorSignalPromise: formErrorSignalPromise })));
|
|
32
|
+
React.createElement(CreationFormWrapper, { context: options.context, formSchemaRegistry: options.formSchemaRegistry, createLayer: options.createLayer, createSource: options.createSource, layerType: options.layerType, sourceType: options.sourceType, sourceData: options.sourceData, layerData: options.layerData, okSignalPromise: okSignalPromise, cancel: cancelCallback, formErrorSignalPromise: formErrorSignalPromise, dialogOptions: options })));
|
|
33
33
|
super({
|
|
34
34
|
title: options.title,
|
|
35
35
|
body,
|
|
@@ -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,27 @@
|
|
|
1
|
+
import { IDict, IJGISLayer, IJupyterGISModel } from '@jupytergis/schema';
|
|
2
|
+
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
3
|
+
export interface IBandHistogram {
|
|
4
|
+
buckets: number[];
|
|
5
|
+
count: number;
|
|
6
|
+
max: number;
|
|
7
|
+
min: number;
|
|
8
|
+
}
|
|
9
|
+
export interface IBandRow {
|
|
10
|
+
band: number;
|
|
11
|
+
colorInterpretation: string;
|
|
12
|
+
stats: {
|
|
13
|
+
minimum: number;
|
|
14
|
+
maximum: number;
|
|
15
|
+
mean: number;
|
|
16
|
+
stdDev: number;
|
|
17
|
+
};
|
|
18
|
+
metadata: IDict;
|
|
19
|
+
histogram: IBandHistogram;
|
|
20
|
+
}
|
|
21
|
+
declare const useGetBandInfo: (context: DocumentRegistry.IContext<IJupyterGISModel>, layer: IJGISLayer) => {
|
|
22
|
+
bandRows: IBandRow[];
|
|
23
|
+
setBandRows: import("react").Dispatch<import("react").SetStateAction<IBandRow[]>>;
|
|
24
|
+
loading: boolean;
|
|
25
|
+
error: string | null;
|
|
26
|
+
};
|
|
27
|
+
export default useGetBandInfo;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { loadGeoTIFFWithCache } from '../../../tools';
|
|
3
|
+
const preloadGeoTiffFile = async (sourceInfo) => {
|
|
4
|
+
return await loadGeoTIFFWithCache(sourceInfo);
|
|
5
|
+
};
|
|
6
|
+
const useGetBandInfo = (context, layer) => {
|
|
7
|
+
const [bandRows, setBandRows] = useState([]);
|
|
8
|
+
const [loading, setLoading] = useState(false);
|
|
9
|
+
const [error, setError] = useState(null);
|
|
10
|
+
const fetchBandInfo = async () => {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
setLoading(true);
|
|
13
|
+
setError(null);
|
|
14
|
+
try {
|
|
15
|
+
const bandsArr = [];
|
|
16
|
+
const source = context.model.getSource((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.source);
|
|
17
|
+
const sourceInfo = (_b = source === null || source === void 0 ? void 0 : source.parameters) === null || _b === void 0 ? void 0 : _b.urls[0];
|
|
18
|
+
if (!(sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.url)) {
|
|
19
|
+
setError('No source URL found.');
|
|
20
|
+
setLoading(false);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const preloadedFile = await preloadGeoTiffFile(sourceInfo);
|
|
24
|
+
const { file, metadata, sourceUrl } = Object.assign({}, preloadedFile);
|
|
25
|
+
if (file && metadata && sourceUrl === sourceInfo.url) {
|
|
26
|
+
metadata['bands'].forEach((bandData) => {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
bandsArr.push({
|
|
29
|
+
band: bandData.band,
|
|
30
|
+
colorInterpretation: bandData.colorInterpretation,
|
|
31
|
+
stats: {
|
|
32
|
+
minimum: (_a = sourceInfo.min) !== null && _a !== void 0 ? _a : bandData.minimum,
|
|
33
|
+
maximum: (_b = sourceInfo.max) !== null && _b !== void 0 ? _b : bandData.maximum,
|
|
34
|
+
mean: bandData.mean,
|
|
35
|
+
stdDev: bandData.stdDev
|
|
36
|
+
},
|
|
37
|
+
metadata: bandData.metadata,
|
|
38
|
+
histogram: bandData.histogram
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
setBandRows(bandsArr);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
setError('Failed to preload the file or metadata mismatch.');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
setError(`Error fetching band info: ${err.message}`);
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
setLoading(false);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
fetchBandInfo();
|
|
56
|
+
}, []);
|
|
57
|
+
return { bandRows, setBandRows, loading, error };
|
|
58
|
+
};
|
|
59
|
+
export default useGetBandInfo;
|
|
@@ -1,5 +1,6 @@
|
|
|
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
5
|
const [featureProps, setFeatureProps] = useState({});
|
|
5
6
|
const [isLoading, setIsLoading] = useState(true);
|
|
@@ -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
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ISymbologyDialogProps } from '../symbologyDialog';
|
|
3
|
-
declare const TiffRendering: ({ context, state, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element;
|
|
3
|
+
declare const TiffRendering: ({ context, state, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element | undefined;
|
|
4
4
|
export default TiffRendering;
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import SingleBandPseudoColor from './types/SingleBandPseudoColor';
|
|
3
|
+
import MultibandColor from './types/MultibandColor';
|
|
3
4
|
const TiffRendering = ({ context, 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;
|
|
14
|
+
const layer = context.model.getLayer(layerId);
|
|
15
|
+
const renderType = (_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.symbologyState.renderType;
|
|
16
|
+
setSelectedRenderType(renderType !== null && renderType !== void 0 ? renderType : 'Singleband Pseudocolor');
|
|
17
|
+
}, []);
|
|
8
18
|
useEffect(() => {
|
|
9
19
|
if (!selectedRenderType) {
|
|
10
20
|
return;
|
|
@@ -13,6 +23,9 @@ const TiffRendering = ({ context, state, okSignalPromise, cancel, layerId }) =>
|
|
|
13
23
|
case 'Singleband Pseudocolor':
|
|
14
24
|
RenderComponent = (React.createElement(SingleBandPseudoColor, { context: context, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: layerId }));
|
|
15
25
|
break;
|
|
26
|
+
case 'Multiband Color':
|
|
27
|
+
RenderComponent = (React.createElement(MultibandColor, { context: context, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: layerId }));
|
|
28
|
+
break;
|
|
16
29
|
default:
|
|
17
30
|
RenderComponent = React.createElement("div", null, "Render Type Not Implemented (yet)");
|
|
18
31
|
}
|
|
@@ -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,84 @@
|
|
|
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 = ({ context, okSignalPromise, cancel, layerId }) => {
|
|
6
|
+
if (!layerId) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const layer = context.model.getLayer(layerId);
|
|
10
|
+
if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const { bandRows, setBandRows, loading } = useGetBandInfo(context, 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
|
+
context.model.sharedModel.updateLayer(layerId, layer);
|
|
76
|
+
cancel();
|
|
77
|
+
};
|
|
78
|
+
return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
|
|
79
|
+
React.createElement("div", { className: "jp-gis-band-container" }, loading ? (React.createElement(Spinner, { loading: loading })) : (React.createElement(React.Fragment, null,
|
|
80
|
+
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 }),
|
|
81
|
+
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 }),
|
|
82
|
+
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 }))))));
|
|
83
|
+
};
|
|
84
|
+
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
4
|
declare const SingleBandPseudoColor: ({ context, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element | undefined;
|
|
24
5
|
export default SingleBandPseudoColor;
|
|
@@ -1,28 +1,14 @@
|
|
|
1
1
|
import { Button } from '@jupyterlab/ui-components';
|
|
2
2
|
import React, { useEffect, useRef, useState } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { Spinner } from '../../../../mainview/spinner';
|
|
4
4
|
import { GlobalStateDbManager } from '../../../../store';
|
|
5
|
-
import
|
|
5
|
+
import { GeoTiffClassifications } from '../../classificationModes';
|
|
6
6
|
import ColorRamp from '../../components/color_ramp/ColorRamp';
|
|
7
7
|
import StopRow from '../../components/color_stops/StopRow';
|
|
8
|
+
import useGetBandInfo from '../../hooks/useGetBandInfo';
|
|
8
9
|
import { Utils } from '../../symbologyUtils';
|
|
9
|
-
import
|
|
10
|
-
import { loadGeoTIFFWithCache } from '../../../../tools';
|
|
10
|
+
import BandRow from '../components/BandRow';
|
|
11
11
|
const SingleBandPseudoColor = ({ context, okSignalPromise, cancel, layerId }) => {
|
|
12
|
-
const functions = ['discrete', 'linear', 'exact'];
|
|
13
|
-
const modeOptions = ['continuous', 'equal interval', 'quantile'];
|
|
14
|
-
const stopRowsRef = useRef();
|
|
15
|
-
const bandRowsRef = useRef([]);
|
|
16
|
-
const selectedFunctionRef = useRef();
|
|
17
|
-
const colorRampOptionsRef = useRef();
|
|
18
|
-
const layerStateRef = useRef();
|
|
19
|
-
const selectedBandRef = useRef();
|
|
20
|
-
const [layerState, setLayerState] = useState();
|
|
21
|
-
const [selectedBand, setSelectedBand] = useState(1);
|
|
22
|
-
const [stopRows, setStopRows] = useState([]);
|
|
23
|
-
const [bandRows, setBandRows] = useState([]);
|
|
24
|
-
const [selectedFunction, setSelectedFunction] = useState('linear');
|
|
25
|
-
const [colorRampOptions, setColorRampOptions] = useState();
|
|
26
12
|
if (!layerId) {
|
|
27
13
|
return;
|
|
28
14
|
}
|
|
@@ -30,7 +16,20 @@ const SingleBandPseudoColor = ({ context, okSignalPromise, cancel, layerId }) =>
|
|
|
30
16
|
if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
|
|
31
17
|
return;
|
|
32
18
|
}
|
|
19
|
+
const functions = ['discrete', 'linear', 'exact'];
|
|
20
|
+
const modeOptions = ['continuous', 'equal interval', 'quantile'];
|
|
33
21
|
const stateDb = GlobalStateDbManager.getInstance().getStateDb();
|
|
22
|
+
const { bandRows, setBandRows, loading } = useGetBandInfo(context, layer);
|
|
23
|
+
const [layerState, setLayerState] = useState();
|
|
24
|
+
const [selectedBand, setSelectedBand] = useState(1);
|
|
25
|
+
const [stopRows, setStopRows] = useState([]);
|
|
26
|
+
const [selectedFunction, setSelectedFunction] = useState('linear');
|
|
27
|
+
const [colorRampOptions, setColorRampOptions] = useState();
|
|
28
|
+
const stopRowsRef = useRef();
|
|
29
|
+
const bandRowsRef = useRef([]);
|
|
30
|
+
const selectedFunctionRef = useRef();
|
|
31
|
+
const colorRampOptionsRef = useRef();
|
|
32
|
+
const selectedBandRef = useRef();
|
|
34
33
|
useEffect(() => {
|
|
35
34
|
populateOptions();
|
|
36
35
|
okSignalPromise.promise.then(okSignal => {
|
|
@@ -42,10 +41,6 @@ const SingleBandPseudoColor = ({ context, okSignalPromise, cancel, layerId }) =>
|
|
|
42
41
|
});
|
|
43
42
|
};
|
|
44
43
|
}, []);
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
layerStateRef.current = layerState;
|
|
47
|
-
getBandInfo();
|
|
48
|
-
}, [layerState]);
|
|
49
44
|
useEffect(() => {
|
|
50
45
|
bandRowsRef.current = bandRows;
|
|
51
46
|
buildColorInfo();
|
|
@@ -55,7 +50,6 @@ const SingleBandPseudoColor = ({ context, okSignalPromise, cancel, layerId }) =>
|
|
|
55
50
|
selectedFunctionRef.current = selectedFunction;
|
|
56
51
|
colorRampOptionsRef.current = colorRampOptions;
|
|
57
52
|
selectedBandRef.current = selectedBand;
|
|
58
|
-
layerStateRef.current = layerState;
|
|
59
53
|
}, [stopRows, selectedFunction, colorRampOptions, selectedBand, layerState]);
|
|
60
54
|
const populateOptions = async () => {
|
|
61
55
|
var _a, _b, _c, _d;
|
|
@@ -67,39 +61,6 @@ const SingleBandPseudoColor = ({ context, okSignalPromise, cancel, layerId }) =>
|
|
|
67
61
|
setSelectedBand(band);
|
|
68
62
|
setSelectedFunction(interpolation);
|
|
69
63
|
};
|
|
70
|
-
const preloadGeoTiffFile = async (sourceInfo) => {
|
|
71
|
-
return await loadGeoTIFFWithCache(sourceInfo);
|
|
72
|
-
};
|
|
73
|
-
const getBandInfo = async () => {
|
|
74
|
-
var _a, _b;
|
|
75
|
-
const bandsArr = [];
|
|
76
|
-
const source = context.model.getSource((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.source);
|
|
77
|
-
const sourceInfo = (_b = source === null || source === void 0 ? void 0 : source.parameters) === null || _b === void 0 ? void 0 : _b.urls[0];
|
|
78
|
-
if (!(sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.url)) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
// Preload the file only once
|
|
82
|
-
const preloadedFile = await preloadGeoTiffFile(sourceInfo);
|
|
83
|
-
const { file, metadata, sourceUrl } = Object.assign({}, preloadedFile);
|
|
84
|
-
if (file && metadata && sourceUrl === sourceInfo.url) {
|
|
85
|
-
metadata['bands'].forEach((bandData) => {
|
|
86
|
-
var _a, _b;
|
|
87
|
-
bandsArr.push({
|
|
88
|
-
band: bandData.band,
|
|
89
|
-
colorInterpretation: bandData.colorInterpretation,
|
|
90
|
-
stats: {
|
|
91
|
-
minimum: (_a = sourceInfo.min) !== null && _a !== void 0 ? _a : bandData.minimum,
|
|
92
|
-
maximum: (_b = sourceInfo.max) !== null && _b !== void 0 ? _b : bandData.maximum,
|
|
93
|
-
mean: bandData.mean,
|
|
94
|
-
stdDev: bandData.stdDev
|
|
95
|
-
},
|
|
96
|
-
metadata: bandData.metadata,
|
|
97
|
-
histogram: bandData.histogram
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
setBandRows(bandsArr);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
64
|
const buildColorInfo = () => {
|
|
104
65
|
var _a;
|
|
105
66
|
// This it to parse a color object on the layer
|
|
@@ -297,9 +258,7 @@ const SingleBandPseudoColor = ({ context, okSignalPromise, cancel, layerId }) =>
|
|
|
297
258
|
return (value * (1 - 0) - min * (1 - 0)) / (max - min);
|
|
298
259
|
};
|
|
299
260
|
return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
|
|
300
|
-
React.createElement("div", { className: "jp-gis-band-container" },
|
|
301
|
-
// Band numbers are 1 indexed
|
|
302
|
-
, {
|
|
261
|
+
React.createElement("div", { className: "jp-gis-band-container" }, loading ? (React.createElement(Spinner, { loading: loading })) : (React.createElement(BandRow, { label: "Band",
|
|
303
262
|
// Band numbers are 1 indexed
|
|
304
263
|
index: selectedBand - 1, bandRow: bandRows[selectedBand - 1], bandRows: bandRows, setSelectedBand: setSelectedBand, setBandRows: setBandRows }))),
|
|
305
264
|
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
@@ -43,6 +43,11 @@ export interface ICreationFormProps {
|
|
|
43
43
|
* extra errors or not.
|
|
44
44
|
*/
|
|
45
45
|
formErrorSignal?: Signal<Dialog<any>, boolean>;
|
|
46
|
+
/**
|
|
47
|
+
* Configuration options for the dialog, including settings for layer data, source data,
|
|
48
|
+
* and other form-related parameters.
|
|
49
|
+
*/
|
|
50
|
+
dialogOptions?: any;
|
|
46
51
|
}
|
|
47
52
|
/**
|
|
48
53
|
* Form for creating a source, a layer or both at the same time
|
|
@@ -107,11 +107,11 @@ export class CreationForm extends React.Component {
|
|
|
107
107
|
React.createElement("h3", null, "Source Properties"),
|
|
108
108
|
React.createElement(SourceForm, { formContext: "create", model: this.jGISModel, filePath: `${this.filePath}::panel`, schema: sourceSchema, sourceData: this.props.sourceData, syncData: (properties) => {
|
|
109
109
|
sourceCreationPromise === null || sourceCreationPromise === void 0 ? void 0 : sourceCreationPromise.resolve(properties);
|
|
110
|
-
}, ok: this.props.ok, cancel: this.props.cancel, formChangedSignal: this.sourceFormChangedSignal, formErrorSignal: this.props.formErrorSignal }))),
|
|
110
|
+
}, ok: this.props.ok, cancel: this.props.cancel, formChangedSignal: this.sourceFormChangedSignal, formErrorSignal: this.props.formErrorSignal, dialogOptions: this.props.dialogOptions, sourceType: this.props.sourceType }))),
|
|
111
111
|
this.props.createLayer && (React.createElement("div", null,
|
|
112
112
|
React.createElement("h3", null, "Layer Properties"),
|
|
113
113
|
React.createElement(LayerForm, { formContext: "create", sourceType: this.props.sourceType, model: this.jGISModel, filePath: `${this.filePath}::panel`, schema: layerSchema, sourceData: layerData, syncData: (properties) => {
|
|
114
114
|
layerCreationPromise === null || layerCreationPromise === void 0 ? void 0 : layerCreationPromise.resolve(properties);
|
|
115
|
-
}, ok: this.props.ok, cancel: this.props.cancel, sourceFormChangedSignal: this.sourceFormChangedSignal, formErrorSignal: this.props.formErrorSignal })))));
|
|
115
|
+
}, ok: this.props.ok, cancel: this.props.cancel, sourceFormChangedSignal: this.sourceFormChangedSignal, formErrorSignal: this.props.formErrorSignal, dialogOptions: this.props.dialogOptions })))));
|
|
116
116
|
}
|
|
117
117
|
}
|