From 9f15edaf35065e9fcdca4bb7a47a5b3024521e42 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 17 Jan 2025 12:24:25 +0100 Subject: [PATCH 01/11] Add label prop to BandRow --- .../tiff_layer/components/BandRow.tsx | 20 +++++++++++-------- .../types/SingleBandPseudoColor.tsx | 3 ++- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx b/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx index 50aeef8b6..a5bb2a300 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx @@ -1,19 +1,23 @@ import React, { useState } from 'react'; import { IBandRow } from '../types/SingleBandPseudoColor'; +interface IBandRowProps { + label: string; + index: number; + bandRow: IBandRow; + bandRows: IBandRow[]; + setSelectedBand: (band: number) => void; + setBandRows: (bandRows: IBandRow[]) => void; +} + const BandRow = ({ + label, index, bandRow, bandRows, setSelectedBand, setBandRows -}: { - index: number; - bandRow: IBandRow; - bandRows: IBandRow[]; - setSelectedBand: (band: number) => void; - setBandRows: (bandRows: IBandRow[]) => void; -}) => { +}: IBandRowProps) => { const [minValue, setMinValue] = useState(bandRow.stats.minimum); const [maxValue, setMaxValue] = useState(bandRow.stats.maximum); @@ -41,7 +45,7 @@ const BandRow = ({ return ( <>
- +
-
-
- - -
-
- - + {hideMinMax ? null : ( +
+
+ + +
+
+ + +
-
+ )} ); }; From a006acae60577a32616bdbb2450537926572d8e6 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 17 Jan 2025 14:50:32 +0100 Subject: [PATCH 04/11] Load renderType from params --- .../dialogs/symbology/tiff_layer/TiffRendering.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/base/src/dialogs/symbology/tiff_layer/TiffRendering.tsx b/packages/base/src/dialogs/symbology/tiff_layer/TiffRendering.tsx index 5d2422fd9..9e923a5a5 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/TiffRendering.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/TiffRendering.tsx @@ -11,13 +11,20 @@ const TiffRendering = ({ layerId }: ISymbologyDialogProps) => { const renderTypes = ['Singleband Pseudocolor', 'Multiband Color']; - const [selectedRenderType, setSelectedRenderType] = useState( - 'Singleband Pseudocolor' - ); + const [selectedRenderType, setSelectedRenderType] = useState(); const [componentToRender, setComponentToRender] = useState(null); let RenderComponent; + if (!layerId) { + return; + } + useEffect(() => { + const layer = context.model.getLayer(layerId); + const renderType = layer?.parameters?.symbologyState.renderType; + setSelectedRenderType(renderType ?? 'Singleband Pseudocolor'); + }, []); + useEffect(() => { if (!selectedRenderType) { return; From 8ee822b024960f7e408689070d8b3f01d4b8fe75 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 17 Jan 2025 14:51:34 +0100 Subject: [PATCH 05/11] Add multiband fields to webgllayer schema --- packages/schema/src/schema/webGlLayer.json | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/schema/src/schema/webGlLayer.json b/packages/schema/src/schema/webGlLayer.json index 86929b784..1ac92be8d 100644 --- a/packages/schema/src/schema/webGlLayer.json +++ b/packages/schema/src/schema/webGlLayer.json @@ -51,14 +51,7 @@ "symbologyState": { "type": "object", "description": "The state of the symbology panel options", - "required": [ - "renderType", - "band", - "interpolation", - "colorRamp", - "nClasses", - "mode" - ], + "required": ["renderType"], "properties": { "renderType": { "type": "string" @@ -66,6 +59,15 @@ "band": { "type": "number" }, + "redBand": { + "type": "number" + }, + "greenBand": { + "type": "number" + }, + "blueBand": { + "type": "number" + }, "interpolation": { "type": "string", "enum": ["discrete", "linear", "exact"] From 8a544b2fa3d152f5841ae7e278f9ac8988899eb5 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 17 Jan 2025 14:52:05 +0100 Subject: [PATCH 06/11] Add multiband color symbology panel --- .../tiff_layer/types/MultibandColor.tsx | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx diff --git a/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx b/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx new file mode 100644 index 000000000..25f11d57d --- /dev/null +++ b/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx @@ -0,0 +1,175 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { ISymbologyDialogProps } from '../../symbologyDialog'; +import BandRow from '../components/BandRow'; +import { IBandRow, TifBandData } from './SingleBandPseudoColor'; +import { IWebGlLayer } from '@jupytergis/schema'; +import { loadGeoTIFFWithCache } from '../../../../tools'; +import { Spinner } from '../../../../mainview/spinner'; +import { ExpressionValue } from 'ol/expr/expression'; + +const MultibandColor = ({ + context, + okSignalPromise, + cancel, + layerId +}: ISymbologyDialogProps) => { + const [selectedRedBand, setSelectedRedBand] = useState(1); + const [selectedGreenBand, setSelectedGreenBand] = useState(2); + const [selectedBlueBand, setSelectedBlueBand] = useState(3); + + const selectedRedBandRef = useRef(); + const selectedGreenBandRef = useRef(); + const selectedBlueBandRef = useRef(); + + const [bandRows, setBandRows] = useState([]); + + if (!layerId) { + return; + } + const layer = context.model.getLayer(layerId); + if (!layer?.parameters) { + return; + } + + useEffect(() => { + populateOptions(); + getBandInfo(); + + okSignalPromise.promise.then(okSignal => { + okSignal.connect(handleOk); + }); + + return () => { + okSignalPromise.promise.then(okSignal => { + okSignal.disconnect(handleOk, this); + }); + }; + }, []); + + useEffect(() => { + selectedRedBandRef.current = selectedRedBand; + selectedGreenBandRef.current = selectedGreenBand; + selectedBlueBandRef.current = selectedBlueBand; + }, [selectedRedBand, selectedGreenBand, selectedBlueBand]); + + const populateOptions = async () => { + const layerParams = layer.parameters as IWebGlLayer; + const redBand = layerParams.symbologyState?.redBand ?? 1; + const greenBand = layerParams.symbologyState?.greenBand ?? 2; + const blueBand = layerParams.symbologyState?.blueBand ?? 3; + + setSelectedRedBand(redBand); + setSelectedGreenBand(greenBand); + setSelectedBlueBand(blueBand); + }; + + const preloadGeoTiffFile = async (sourceInfo: { + url?: string | undefined; + }) => { + return await loadGeoTIFFWithCache(sourceInfo); + }; + + const getBandInfo = async () => { + const bandsArr: IBandRow[] = []; + const source = context.model.getSource(layer?.parameters?.source); + const sourceInfo = source?.parameters?.urls[0]; + + if (!sourceInfo?.url) { + return; + } + + // Preload the file only once + const preloadedFile = await preloadGeoTiffFile(sourceInfo); + const { file, metadata, sourceUrl } = { ...preloadedFile }; + + if (file && metadata && sourceUrl === sourceInfo.url) { + metadata['bands'].forEach((bandData: TifBandData) => { + bandsArr.push({ + band: bandData.band, + colorInterpretation: bandData.colorInterpretation, + stats: { + minimum: sourceInfo.min ?? bandData.minimum, + maximum: sourceInfo.max ?? bandData.maximum, + mean: bandData.mean, + stdDev: bandData.stdDev + }, + metadata: bandData.metadata, + histogram: bandData.histogram + }); + }); + setBandRows(bandsArr); + } + }; + + const handleOk = () => { + // Update layer + if (!layer.parameters) { + return; + } + + const colorExpr: ExpressionValue[] = ['array']; + + // Set NoData values to transparent + colorExpr.push(['band', selectedRedBandRef.current]); + colorExpr.push(['band', selectedGreenBandRef.current]); + colorExpr.push(['band', selectedBlueBandRef.current]); + colorExpr.push(['band', 5]); + + const symbologyState = { + renderType: 'Multiband Color', + redBand: selectedRedBandRef.current, + greenBand: selectedGreenBandRef.current, + blueBand: selectedBlueBandRef.current + }; + + layer.parameters.symbologyState = symbologyState; + layer.parameters.color = colorExpr; + + context.model.sharedModel.updateLayer(layerId, layer); + cancel(); + }; + + return ( +
+
+ {bandRows.length === 0 ? ( + + ) : ( + <> + + + + + + + )} +
+
+ ); +}; + +export default MultibandColor; From 43ed6b553d7c0309a0f7cc712613e9760e8f9243 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 17 Jan 2025 16:37:11 +0100 Subject: [PATCH 07/11] Condense --- .../tiff_layer/types/MultibandColor.tsx | 80 +++++++++++-------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx b/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx index 25f11d57d..409fd14ce 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx @@ -7,19 +7,29 @@ import { loadGeoTIFFWithCache } from '../../../../tools'; import { Spinner } from '../../../../mainview/spinner'; import { ExpressionValue } from 'ol/expr/expression'; +interface ISelectedBands { + red: number; + green: number; + blue: number; +} + const MultibandColor = ({ context, okSignalPromise, cancel, layerId }: ISymbologyDialogProps) => { - const [selectedRedBand, setSelectedRedBand] = useState(1); - const [selectedGreenBand, setSelectedGreenBand] = useState(2); - const [selectedBlueBand, setSelectedBlueBand] = useState(3); - - const selectedRedBandRef = useRef(); - const selectedGreenBandRef = useRef(); - const selectedBlueBandRef = useRef(); + const [selectedBands, setSelectedBands] = useState({ + red: 1, + green: 2, + blue: 3 + }); + + const selectedBandsRef = useRef({ + red: selectedBands.red, + green: selectedBands.green, + blue: selectedBands.blue + }); const [bandRows, setBandRows] = useState([]); @@ -46,21 +56,13 @@ const MultibandColor = ({ }; }, []); - useEffect(() => { - selectedRedBandRef.current = selectedRedBand; - selectedGreenBandRef.current = selectedGreenBand; - selectedBlueBandRef.current = selectedBlueBand; - }, [selectedRedBand, selectedGreenBand, selectedBlueBand]); - const populateOptions = async () => { const layerParams = layer.parameters as IWebGlLayer; - const redBand = layerParams.symbologyState?.redBand ?? 1; - const greenBand = layerParams.symbologyState?.greenBand ?? 2; - const blueBand = layerParams.symbologyState?.blueBand ?? 3; + const red = layerParams.symbologyState?.redBand ?? 1; + const green = layerParams.symbologyState?.greenBand ?? 2; + const blue = layerParams.symbologyState?.blueBand ?? 3; - setSelectedRedBand(redBand); - setSelectedGreenBand(greenBand); - setSelectedBlueBand(blueBand); + setSelectedBands({ red, green, blue }); }; const preloadGeoTiffFile = async (sourceInfo: { @@ -101,6 +103,15 @@ const MultibandColor = ({ } }; + const updateBand = (color: 'red' | 'green' | 'blue', value: number) => { + setSelectedBands(prevBands => ({ + ...prevBands, + [color]: value + })); + selectedBandsRef.current[color] = value; + console.log('updateing', value); + }; + const handleOk = () => { // Update layer if (!layer.parameters) { @@ -109,17 +120,18 @@ const MultibandColor = ({ const colorExpr: ExpressionValue[] = ['array']; + console.log('selectedBandsRef.current', selectedBandsRef.current); // Set NoData values to transparent - colorExpr.push(['band', selectedRedBandRef.current]); - colorExpr.push(['band', selectedGreenBandRef.current]); - colorExpr.push(['band', selectedBlueBandRef.current]); + colorExpr.push(['band', selectedBandsRef.current['red']]); + colorExpr.push(['band', selectedBandsRef.current['green']]); + colorExpr.push(['band', selectedBandsRef.current['blue']]); colorExpr.push(['band', 5]); const symbologyState = { renderType: 'Multiband Color', - redBand: selectedRedBandRef.current, - greenBand: selectedGreenBandRef.current, - blueBand: selectedBlueBandRef.current + redBand: selectedBandsRef.current['red'], + greenBand: selectedBandsRef.current['green'], + blueBand: selectedBandsRef.current['blue'] }; layer.parameters.symbologyState = symbologyState; @@ -138,30 +150,30 @@ const MultibandColor = ({ <> updateBand('red', val)} setBandRows={setBandRows} hideMinMax={true} /> updateBand('green', val)} setBandRows={setBandRows} hideMinMax={true} /> updateBand('blue', val)} setBandRows={setBandRows} hideMinMax={true} /> From 2e70a3e4e8109c38c2f132aae79b5c4aeb5e4afd Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 20 Jan 2025 14:47:34 +0100 Subject: [PATCH 08/11] Iterate --- .../tiff_layer/components/BandRow.tsx | 33 +++++++++++++---- .../tiff_layer/types/MultibandColor.tsx | 36 +++++++++++-------- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx b/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx index 4e094a280..b51746853 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx @@ -8,10 +8,19 @@ interface IBandRowProps { bandRows: IBandRow[]; setSelectedBand: (band: number) => void; setBandRows: (bandRows: IBandRow[]) => void; - // TODO: QGIS uses these values for contrast enhancement in multiband color - hideMinMax?: boolean; + isMultibandColor?: boolean; } +/** + * + * @param label Label displayed in symbology dialog + * @param index Index of current row in band row data + * @param bandRow Band from bands array, will be undefined when band is 'unset' in Multiband color + * @param bandRows Bands array from tiff data + * @param setSelectedBand Function to set selected band parent + * @param setBandRows Function to update band rows in parent + * @param isMultibandColor Used to hide min/max input and add 'Unset' option to drop down menu for MultiBand symbology + */ const BandRow = ({ label, index, @@ -19,10 +28,10 @@ const BandRow = ({ bandRows, setSelectedBand, setBandRows, - hideMinMax + isMultibandColor }: IBandRowProps) => { - const [minValue, setMinValue] = useState(bandRow.stats.minimum); - const [maxValue, setMaxValue] = useState(bandRow.stats.maximum); + const [minValue, setMinValue] = useState(bandRow?.stats.minimum); + const [maxValue, setMaxValue] = useState(bandRow?.stats.maximum); const handleMinValueChange = (event: { target: { value: string | number }; @@ -60,16 +69,26 @@ const BandRow = ({ ))} + {isMultibandColor ? ( + + ) : null}
- {hideMinMax ? null : ( + {isMultibandColor ? null : (
{ + useEffect(() => { + selectedBandsRef.current = selectedBands; + }, [selectedBands]); + + const updateBand = (color: rgbEnum, value: number) => { setSelectedBands(prevBands => ({ ...prevBands, [color]: value })); - selectedBandsRef.current[color] = value; - console.log('updateing', value); }; const handleOk = () => { @@ -121,10 +125,14 @@ const MultibandColor = ({ const colorExpr: ExpressionValue[] = ['array']; console.log('selectedBandsRef.current', selectedBandsRef.current); - // Set NoData values to transparent - colorExpr.push(['band', selectedBandsRef.current['red']]); - colorExpr.push(['band', selectedBandsRef.current['green']]); - colorExpr.push(['band', selectedBandsRef.current['blue']]); + const rgb: rgbEnum[] = ['red', 'green', 'blue']; + + rgb.forEach(color => { + const bandValue = selectedBandsRef.current[color]; + colorExpr.push(bandValue !== 0 ? ['band', bandValue] : 0); + }); + + // Array expression expects 4 values colorExpr.push(['band', 5]); const symbologyState = { @@ -155,7 +163,7 @@ const MultibandColor = ({ bandRows={bandRows} setSelectedBand={val => updateBand('red', val)} setBandRows={setBandRows} - hideMinMax={true} + isMultibandColor={true} /> updateBand('green', val)} setBandRows={setBandRows} - hideMinMax={true} + isMultibandColor={true} /> updateBand('blue', val)} setBandRows={setBandRows} - hideMinMax={true} + isMultibandColor={true} /> )} From e183bc73d7ffb7f011334ffd34c204d1cf35f8ad Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 20 Jan 2025 17:38:15 +0100 Subject: [PATCH 09/11] Factor out common stuff to hook --- .../dialogs/symbology/hooks/useGetBandInfo.ts | 101 ++++++++++++++ .../tiff_layer/components/BandRow.tsx | 3 +- .../tiff_layer/types/MultibandColor.tsx | 67 ++-------- .../types/SingleBandPseudoColor.tsx | 123 ++++-------------- 4 files changed, 142 insertions(+), 152 deletions(-) create mode 100644 packages/base/src/dialogs/symbology/hooks/useGetBandInfo.ts diff --git a/packages/base/src/dialogs/symbology/hooks/useGetBandInfo.ts b/packages/base/src/dialogs/symbology/hooks/useGetBandInfo.ts new file mode 100644 index 000000000..86f884f60 --- /dev/null +++ b/packages/base/src/dialogs/symbology/hooks/useGetBandInfo.ts @@ -0,0 +1,101 @@ +import { IDict, IJGISLayer, IJupyterGISModel } from '@jupytergis/schema'; +import { DocumentRegistry } from '@jupyterlab/docregistry'; +import { useEffect, useState } from 'react'; +import { loadGeoTIFFWithCache } from '../../../tools'; + +export interface IBandHistogram { + buckets: number[]; + count: number; + max: number; + min: number; +} + +export interface IBandRow { + band: number; + colorInterpretation: string; + stats: { + minimum: number; + maximum: number; + mean: number; + stdDev: number; + }; + metadata: IDict; + histogram: IBandHistogram; +} + +interface ITifBandData { + band: number; + colorInterpretation: string; + minimum: number; + maximum: number; + mean: number; + stdDev: number; + metadata: object; + histogram: any; +} + +const preloadGeoTiffFile = async (sourceInfo: { url?: string | undefined }) => { + return await loadGeoTIFFWithCache(sourceInfo); +}; + +const useGetBandInfo = ( + context: DocumentRegistry.IContext, + layer: IJGISLayer +) => { + const [bandRows, setBandRows] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const fetchBandInfo = async () => { + setLoading(true); + setError(null); + + try { + const bandsArr: IBandRow[] = []; + const source = context.model.getSource(layer?.parameters?.source); + const sourceInfo = source?.parameters?.urls[0]; + + if (!sourceInfo?.url) { + setError('No source URL found.'); + setLoading(false); + return; + } + + const preloadedFile = await preloadGeoTiffFile(sourceInfo); + const { file, metadata, sourceUrl } = { ...preloadedFile }; + + if (file && metadata && sourceUrl === sourceInfo.url) { + metadata['bands'].forEach((bandData: ITifBandData) => { + bandsArr.push({ + band: bandData.band, + colorInterpretation: bandData.colorInterpretation, + stats: { + minimum: sourceInfo.min ?? bandData.minimum, + maximum: sourceInfo.max ?? bandData.maximum, + mean: bandData.mean, + stdDev: bandData.stdDev + }, + metadata: bandData.metadata, + histogram: bandData.histogram + }); + }); + + setBandRows(bandsArr); + } else { + setError('Failed to preload the file or metadata mismatch.'); + } + } catch (err: any) { + setError(`Error fetching band info: ${err.message}`); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchBandInfo(); + }, []); + + return { bandRows, setBandRows, loading, error }; +}; + +export default useGetBandInfo; diff --git a/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx b/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx index b51746853..f65c0c6f7 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx @@ -1,5 +1,6 @@ import React, { useState } from 'react'; -import { IBandRow } from '../types/SingleBandPseudoColor'; +import { IBandRow } from '../../hooks/useGetBandInfo'; +// import { IBandRow } from '../types/SingleBandPseudoColor'; interface IBandRowProps { label: string; diff --git a/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx b/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx index a0161ff86..75c213fbf 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx @@ -2,10 +2,10 @@ import { IWebGlLayer } from '@jupytergis/schema'; import { ExpressionValue } from 'ol/expr/expression'; import React, { useEffect, useRef, useState } from 'react'; import { Spinner } from '../../../../mainview/spinner'; -import { loadGeoTIFFWithCache } from '../../../../tools'; +// import { loadGeoTIFFWithCache } from '../../../../tools'; +import useGetBandInfo from '../../hooks/useGetBandInfo'; import { ISymbologyDialogProps } from '../../symbologyDialog'; import BandRow from '../components/BandRow'; -import { IBandRow, TifBandData } from './SingleBandPseudoColor'; interface ISelectedBands { red: number; @@ -21,6 +21,16 @@ const MultibandColor = ({ cancel, layerId }: ISymbologyDialogProps) => { + if (!layerId) { + return; + } + const layer = context.model.getLayer(layerId); + if (!layer?.parameters) { + return; + } + + const { bandRows, setBandRows, loading } = useGetBandInfo(context, layer); + const [selectedBands, setSelectedBands] = useState({ red: 1, green: 2, @@ -33,19 +43,8 @@ const MultibandColor = ({ blue: selectedBands.blue }); - const [bandRows, setBandRows] = useState([]); - - if (!layerId) { - return; - } - const layer = context.model.getLayer(layerId); - if (!layer?.parameters) { - return; - } - useEffect(() => { populateOptions(); - getBandInfo(); okSignalPromise.promise.then(okSignal => { okSignal.connect(handleOk); @@ -67,44 +66,6 @@ const MultibandColor = ({ setSelectedBands({ red, green, blue }); }; - const preloadGeoTiffFile = async (sourceInfo: { - url?: string | undefined; - }) => { - return await loadGeoTIFFWithCache(sourceInfo); - }; - - const getBandInfo = async () => { - const bandsArr: IBandRow[] = []; - const source = context.model.getSource(layer?.parameters?.source); - const sourceInfo = source?.parameters?.urls[0]; - - if (!sourceInfo?.url) { - return; - } - - // Preload the file only once - const preloadedFile = await preloadGeoTiffFile(sourceInfo); - const { file, metadata, sourceUrl } = { ...preloadedFile }; - - if (file && metadata && sourceUrl === sourceInfo.url) { - metadata['bands'].forEach((bandData: TifBandData) => { - bandsArr.push({ - band: bandData.band, - colorInterpretation: bandData.colorInterpretation, - stats: { - minimum: sourceInfo.min ?? bandData.minimum, - maximum: sourceInfo.max ?? bandData.maximum, - mean: bandData.mean, - stdDev: bandData.stdDev - }, - metadata: bandData.metadata, - histogram: bandData.histogram - }); - }); - setBandRows(bandsArr); - } - }; - useEffect(() => { selectedBandsRef.current = selectedBands; }, [selectedBands]); @@ -152,8 +113,8 @@ const MultibandColor = ({ return (
- {bandRows.length === 0 ? ( - + {loading ? ( + ) : ( <> { + if (!layerId) { + return; + } + const layer = context.model.getLayer(layerId); + if (!layer?.parameters) { + return; + } + const functions = ['discrete', 'linear', 'exact']; const modeOptions = ['continuous', 'equal interval', 'quantile']; - const stopRowsRef = useRef(); - const bandRowsRef = useRef([]); - const selectedFunctionRef = useRef(); - const colorRampOptionsRef = useRef(); - const layerStateRef = useRef(); - const selectedBandRef = useRef(); + + const stateDb = GlobalStateDbManager.getInstance().getStateDb(); + + const { bandRows, setBandRows, loading } = useGetBandInfo(context, layer); const [layerState, setLayerState] = useState(); const [selectedBand, setSelectedBand] = useState(1); const [stopRows, setStopRows] = useState([]); - const [bandRows, setBandRows] = useState([]); const [selectedFunction, setSelectedFunction] = useState('linear'); const [colorRampOptions, setColorRampOptions] = useState< ColorRampOptions | undefined >(); - if (!layerId) { - return; - } - const layer = context.model.getLayer(layerId); - if (!layer?.parameters) { - return; - } - const stateDb = GlobalStateDbManager.getInstance().getStateDb(); + const stopRowsRef = useRef(); + const bandRowsRef = useRef([]); + const selectedFunctionRef = useRef(); + const colorRampOptionsRef = useRef(); + const selectedBandRef = useRef(); useEffect(() => { populateOptions(); @@ -96,11 +67,6 @@ const SingleBandPseudoColor = ({ }; }, []); - useEffect(() => { - layerStateRef.current = layerState; - getBandInfo(); - }, [layerState]); - useEffect(() => { bandRowsRef.current = bandRows; buildColorInfo(); @@ -111,7 +77,6 @@ const SingleBandPseudoColor = ({ selectedFunctionRef.current = selectedFunction; colorRampOptionsRef.current = colorRampOptions; selectedBandRef.current = selectedBand; - layerStateRef.current = layerState; }, [stopRows, selectedFunction, colorRampOptions, selectedBand, layerState]); const populateOptions = async () => { @@ -129,44 +94,6 @@ const SingleBandPseudoColor = ({ setSelectedFunction(interpolation); }; - const preloadGeoTiffFile = async (sourceInfo: { - url?: string | undefined; - }) => { - return await loadGeoTIFFWithCache(sourceInfo); - }; - - const getBandInfo = async () => { - const bandsArr: IBandRow[] = []; - const source = context.model.getSource(layer?.parameters?.source); - const sourceInfo = source?.parameters?.urls[0]; - - if (!sourceInfo?.url) { - return; - } - - // Preload the file only once - const preloadedFile = await preloadGeoTiffFile(sourceInfo); - const { file, metadata, sourceUrl } = { ...preloadedFile }; - - if (file && metadata && sourceUrl === sourceInfo.url) { - metadata['bands'].forEach((bandData: TifBandData) => { - bandsArr.push({ - band: bandData.band, - colorInterpretation: bandData.colorInterpretation, - stats: { - minimum: sourceInfo.min ?? bandData.minimum, - maximum: sourceInfo.max ?? bandData.maximum, - mean: bandData.mean, - stdDev: bandData.stdDev - }, - metadata: bandData.metadata, - histogram: bandData.histogram - }); - }); - setBandRows(bandsArr); - } - }; - const buildColorInfo = () => { // This it to parse a color object on the layer if (!layer.parameters?.color || !layerState) { @@ -433,8 +360,8 @@ const SingleBandPseudoColor = ({ return (
- {bandRows.length === 0 ? ( - + {loading ? ( + ) : ( Date: Mon, 20 Jan 2025 17:47:14 +0100 Subject: [PATCH 10/11] Clean up --- .../src/dialogs/symbology/tiff_layer/components/BandRow.tsx | 2 -- .../src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx | 3 --- 2 files changed, 5 deletions(-) diff --git a/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx b/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx index f65c0c6f7..cc9666861 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx @@ -1,6 +1,5 @@ import React, { useState } from 'react'; import { IBandRow } from '../../hooks/useGetBandInfo'; -// import { IBandRow } from '../types/SingleBandPseudoColor'; interface IBandRowProps { label: string; @@ -52,7 +51,6 @@ const BandRow = ({ const newBandRows = [...bandRows]; newBandRows[index].stats.minimum = minValue; newBandRows[index].stats.maximum = maxValue; - console.log('newBandRows', newBandRows); setBandRows(newBandRows); }; diff --git a/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx b/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx index 75c213fbf..f2e8f7d95 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx @@ -2,7 +2,6 @@ import { IWebGlLayer } from '@jupytergis/schema'; import { ExpressionValue } from 'ol/expr/expression'; import React, { useEffect, useRef, useState } from 'react'; import { Spinner } from '../../../../mainview/spinner'; -// import { loadGeoTIFFWithCache } from '../../../../tools'; import useGetBandInfo from '../../hooks/useGetBandInfo'; import { ISymbologyDialogProps } from '../../symbologyDialog'; import BandRow from '../components/BandRow'; @@ -84,8 +83,6 @@ const MultibandColor = ({ } const colorExpr: ExpressionValue[] = ['array']; - - console.log('selectedBandsRef.current', selectedBandsRef.current); const rgb: rgbEnum[] = ['red', 'green', 'blue']; rgb.forEach(color => { From 3081abc37ca6029e6d48d39b72b8ac43acf69557 Mon Sep 17 00:00:00 2001 From: Greg Date: Tue, 21 Jan 2025 11:51:54 +0100 Subject: [PATCH 11/11] Use correct alpha band --- .../tiff_layer/types/MultibandColor.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx b/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx index f2e8f7d95..0353a87de 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/types/MultibandColor.tsx @@ -36,6 +36,7 @@ const MultibandColor = ({ blue: 3 }); + const numOfBandsRef = useRef(0); const selectedBandsRef = useRef({ red: selectedBands.red, green: selectedBands.green, @@ -56,6 +57,14 @@ const MultibandColor = ({ }; }, []); + useEffect(() => { + numOfBandsRef.current = bandRows.length; + }, [bandRows]); + + useEffect(() => { + selectedBandsRef.current = selectedBands; + }, [selectedBands]); + const populateOptions = async () => { const layerParams = layer.parameters as IWebGlLayer; const red = layerParams.symbologyState?.redBand ?? 1; @@ -65,10 +74,6 @@ const MultibandColor = ({ setSelectedBands({ red, green, blue }); }; - useEffect(() => { - selectedBandsRef.current = selectedBands; - }, [selectedBands]); - const updateBand = (color: rgbEnum, value: number) => { setSelectedBands(prevBands => ({ ...prevBands, @@ -91,7 +96,8 @@ const MultibandColor = ({ }); // Array expression expects 4 values - colorExpr.push(['band', 5]); + // Last band should be alpha band added by OpenLayers + colorExpr.push(['band', numOfBandsRef.current + 1]); const symbologyState = { renderType: 'Multiband Color',