@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.
Files changed (49) hide show
  1. package/lib/annotations/components/Annotation.js +1 -1
  2. package/lib/commands.js +30 -1
  3. package/lib/constants.d.ts +1 -0
  4. package/lib/constants.js +1 -0
  5. package/lib/dialogs/formdialog.d.ts +5 -0
  6. package/lib/dialogs/formdialog.js +2 -2
  7. package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.js +2 -1
  8. package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +27 -0
  9. package/lib/dialogs/symbology/hooks/useGetBandInfo.js +59 -0
  10. package/lib/dialogs/symbology/hooks/useGetProperties.js +6 -1
  11. package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +1 -1
  12. package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +14 -1
  13. package/lib/dialogs/symbology/tiff_layer/components/BandRow.d.ts +16 -3
  14. package/lib/dialogs/symbology/tiff_layer/components/BandRow.js +21 -7
  15. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.d.ts +4 -0
  16. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +84 -0
  17. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.d.ts +0 -19
  18. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +18 -59
  19. package/lib/formbuilder/creationform.d.ts +5 -0
  20. package/lib/formbuilder/creationform.js +2 -2
  21. package/lib/formbuilder/editform.d.ts +1 -0
  22. package/lib/formbuilder/editform.js +8 -3
  23. package/lib/formbuilder/formselectors.js +7 -0
  24. package/lib/formbuilder/objectform/baseform.d.ts +10 -0
  25. package/lib/formbuilder/objectform/baseform.js +39 -0
  26. package/lib/formbuilder/objectform/fileselectorwidget.d.ts +2 -0
  27. package/lib/formbuilder/objectform/fileselectorwidget.js +81 -0
  28. package/lib/formbuilder/objectform/geojsonsource.d.ts +5 -7
  29. package/lib/formbuilder/objectform/geojsonsource.js +8 -24
  30. package/lib/formbuilder/objectform/layerform.d.ts +2 -0
  31. package/lib/formbuilder/objectform/layerform.js +6 -0
  32. package/lib/formbuilder/objectform/pathbasedsource.d.ts +19 -0
  33. package/lib/formbuilder/objectform/pathbasedsource.js +98 -0
  34. package/lib/icons.d.ts +1 -0
  35. package/lib/icons.js +5 -0
  36. package/lib/keybindings.json +62 -0
  37. package/lib/mainview/mainView.d.ts +22 -5
  38. package/lib/mainview/mainView.js +224 -75
  39. package/lib/panelview/components/filter-panel/Filter.js +6 -1
  40. package/lib/statusbar/StatusBar.d.ts +13 -0
  41. package/lib/statusbar/StatusBar.js +52 -0
  42. package/lib/toolbar/widget.js +0 -5
  43. package/lib/tools.d.ts +40 -1
  44. package/lib/tools.js +308 -0
  45. package/package.json +16 -5
  46. package/style/base.css +1 -0
  47. package/style/icons/logo_mini_qgz.svg +31 -0
  48. package/style/leftPanel.css +8 -0
  49. 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.centerOnAnnotation(itemId);
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
- url: 'https://maplibre.org/maplibre-gl-js/docs/assets/radar.gif',
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) {
@@ -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("select", { name: "mode-select", onChange: event => setSelectedMode(event.target.value) }, modeOptions.map(mode => (React.createElement("option", { className: "jp-mod-styled", value: mode, selected: selectedMode === mode }, mode)))))));
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 model.readGeoJSON((_b = source.parameters) === null || _b === void 0 ? void 0 : _b.path);
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('Singleband Pseudocolor');
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 '../types/SingleBandPseudoColor';
3
- declare const BandRow: ({ index, bandRow, bandRows, setSelectedBand, setBandRows }: {
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
- }) => React.JSX.Element;
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
- const BandRow = ({ index, bandRow, bandRows, setSelectedBand, setBandRows }) => {
3
- const [minValue, setMinValue] = useState(bandRow.stats.minimum);
4
- const [maxValue, setMaxValue] = useState(bandRow.stats.maximum);
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}` }, "Band:"),
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" }, bandRows.map((band, bandIndex) => (React.createElement("option", { key: bandIndex, value: band.band, selected: band.band === bandRow.band, className: "jp-mod-styled" }, `Band ${band.band} (${band.colorInterpretation})`)))))),
24
- React.createElement("div", { className: "jp-gis-symbology-row", style: { gap: '0.5rem' } },
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,4 @@
1
+ import React from 'react';
2
+ import { ISymbologyDialogProps } from '../../symbologyDialog';
3
+ declare const MultibandColor: ({ context, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => React.JSX.Element | undefined;
4
+ export default MultibandColor;
@@ -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 { GeoTiffClassifications } from '../../classificationModes';
3
+ import { Spinner } from '../../../../mainview/spinner';
4
4
  import { GlobalStateDbManager } from '../../../../store';
5
- import BandRow from '../components/BandRow';
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 { Spinner } from '../../../../mainview/spinner';
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" }, bandRows.length === 0 ? (React.createElement(Spinner, { loading: bandRows.length === 0 })) : (React.createElement(BandRow
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
  }
@@ -21,4 +21,5 @@ export declare class EditForm extends React.Component<IEditFormProps, any> {
21
21
  [key: string]: any;
22
22
  }): Promise<void>;
23
23
  render(): React.JSX.Element | undefined;
24
+ private sourceFormChangedSignal;
24
25
  }