Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions packages/base/src/dialogs/symbology/hooks/useGetBandInfo.ts
Original file line number Diff line number Diff line change
@@ -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<IJupyterGISModel>,
layer: IJGISLayer
) => {
const [bandRows, setBandRows] = useState<IBandRow[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(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;
25 changes: 22 additions & 3 deletions packages/base/src/dialogs/symbology/tiff_layer/TiffRendering.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react';
import { ISymbologyDialogProps } from '../symbologyDialog';
import SingleBandPseudoColor from './types/SingleBandPseudoColor';
import MultibandColor from './types/MultibandColor';

const TiffRendering = ({
context,
Expand All @@ -10,13 +11,20 @@ const TiffRendering = ({
layerId
}: ISymbologyDialogProps) => {
const renderTypes = ['Singleband Pseudocolor', 'Multiband Color'];
const [selectedRenderType, setSelectedRenderType] = useState(
'Singleband Pseudocolor'
);
const [selectedRenderType, setSelectedRenderType] = useState<string>();
const [componentToRender, setComponentToRender] = useState<any>(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;
Expand All @@ -34,6 +42,17 @@ const TiffRendering = ({
/>
);
break;
case 'Multiband Color':
RenderComponent = (
<MultibandColor
context={context}
state={state}
okSignalPromise={okSignalPromise}
cancel={cancel}
layerId={layerId}
/>
);
break;
default:
RenderComponent = <div>Render Type Not Implemented (yet)</div>;
}
Expand Down
132 changes: 80 additions & 52 deletions packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,37 @@
import React, { useState } from 'react';
import { IBandRow } from '../types/SingleBandPseudoColor';
import { IBandRow } from '../../hooks/useGetBandInfo';

const BandRow = ({
index,
bandRow,
bandRows,
setSelectedBand,
setBandRows
}: {
interface IBandRowProps {
label: string;
index: number;
bandRow: IBandRow;
bandRows: IBandRow[];
setSelectedBand: (band: number) => void;
setBandRows: (bandRows: IBandRow[]) => void;
}) => {
const [minValue, setMinValue] = useState(bandRow.stats.minimum);
const [maxValue, setMaxValue] = useState(bandRow.stats.maximum);
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,
bandRow,
bandRows,
setSelectedBand,
setBandRows,
isMultibandColor
}: IBandRowProps) => {
const [minValue, setMinValue] = useState(bandRow?.stats.minimum);
const [maxValue, setMaxValue] = useState(bandRow?.stats.maximum);

const handleMinValueChange = (event: {
target: { value: string | number };
Expand All @@ -41,7 +57,7 @@ const BandRow = ({
return (
<>
<div className="jp-gis-symbology-row">
<label htmlFor={`band-select-${index}`}>Band:</label>
<label htmlFor={`band-select-${index}`}>{label}:</label>
<div className="jp-select-wrapper">
<select
name={`band-select-${index}`}
Expand All @@ -52,55 +68,67 @@ const BandRow = ({
<option
key={bandIndex}
value={band.band}
selected={band.band === bandRow.band}
selected={band.band === bandRow?.band}
className="jp-mod-styled"
>
{`Band ${band.band} (${band.colorInterpretation})`}
</option>
))}
{isMultibandColor ? (
<option
key={'unset'}
value={0}
selected={!bandRow}
className="jp-mod-styled"
>
Unset
</option>
) : null}
</select>
</div>
</div>
<div className="jp-gis-symbology-row" style={{ gap: '0.5rem' }}>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
width: '50%'
}}
>
<label htmlFor="band-min" style={{ alignSelf: 'center' }}>
Min
</label>
<input
type="number"
className="jp-mod-styled"
style={{ marginRight: 15 }}
value={minValue}
onChange={handleMinValueChange}
/>
</div>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
width: '50%',
paddingRight: '2px'
}}
>
<label htmlFor="band-max" style={{ alignSelf: 'center' }}>
Max
</label>
<input
type="number"
className="jp-mod-styled"
// defaultValue={bandRow.stats.maximum}
value={maxValue}
onChange={handleMaxValueChange}
onBlur={setNewBands}
/>
{isMultibandColor ? null : (
<div className="jp-gis-symbology-row" style={{ gap: '0.5rem' }}>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
width: '50%'
}}
>
<label htmlFor="band-min" style={{ alignSelf: 'center' }}>
Min
</label>
<input
type="number"
className="jp-mod-styled"
style={{ marginRight: 15 }}
value={minValue}
onChange={handleMinValueChange}
/>
</div>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
width: '50%',
paddingRight: '2px'
}}
>
<label htmlFor="band-max" style={{ alignSelf: 'center' }}>
Max
</label>
<input
type="number"
className="jp-mod-styled"
// defaultValue={bandRow.stats.maximum}
value={maxValue}
onChange={handleMaxValueChange}
onBlur={setNewBands}
/>
</div>
</div>
</div>
)}
</>
);
};
Expand Down
Loading
Loading