From df05094144c9ba2a6cb061533d41e4abd95d45da Mon Sep 17 00:00:00 2001 From: Nakul Date: Tue, 23 Sep 2025 22:46:13 +0530 Subject: [PATCH 1/5] Adding type to hooks --- .../dialogs/symbology/components/color_ramp/ColorRamp.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx index 70af1d808..f8602aa7f 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -33,9 +33,9 @@ const ColorRamp: React.FC = ({ showRampSelector, }) => { const [selectedRamp, setSelectedRamp] = useState(''); - const [selectedMode, setSelectedMode] = useState(''); - const [numberOfShades, setNumberOfShades] = useState(''); - const [isLoading, setIsLoading] = useState(false); + const [selectedMode, setSelectedMode] = useState(''); + const [numberOfShades, setNumberOfShades] = useState(''); + const [isLoading, setIsLoading] = useState(false); useEffect(() => { if (selectedRamp === '' && selectedMode === '' && numberOfShades === '') { From 7101462e0674e293963df94244dc432aa5a532fc Mon Sep 17 00:00:00 2001 From: Nakul Date: Wed, 24 Sep 2025 20:41:18 +0530 Subject: [PATCH 2/5] Adding new ClassificationMode type --- .../components/color_ramp/ColorRamp.tsx | 21 +++++++++------- .../components/color_ramp/ModeSelectRow.tsx | 25 ++++++++++++------- packages/base/src/types.ts | 11 ++++++++ 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx index f8602aa7f..a3a26349b 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -3,15 +3,16 @@ import { Button } from '@jupyterlab/ui-components'; import React, { useEffect, useState } from 'react'; import { LoadingIcon } from '@/src/shared/components/loading'; +import { ClassificationMode } from '@/src/types'; import CanvasSelectComponent from './CanvasSelectComponent'; import ModeSelectRow from './ModeSelectRow'; interface IColorRampProps { - modeOptions: string[]; + modeOptions: ClassificationMode[]; layerParams: IDict; classifyFunc: ( - selectedMode: string, - numberOfShades: string, + selectedMode: ClassificationMode | undefined, + numberOfShades: number | undefined, selectedRamp: string, setIsLoading: (isLoading: boolean) => void, ) => void; @@ -21,8 +22,8 @@ interface IColorRampProps { export type ColorRampOptions = { selectedRamp: string; - numberOfShades: string; - selectedMode: string; + numberOfShades: number | undefined; + selectedMode: ClassificationMode | undefined; }; const ColorRamp: React.FC = ({ @@ -33,12 +34,14 @@ const ColorRamp: React.FC = ({ showRampSelector, }) => { const [selectedRamp, setSelectedRamp] = useState(''); - const [selectedMode, setSelectedMode] = useState(''); - const [numberOfShades, setNumberOfShades] = useState(''); + const [selectedMode, setSelectedMode] = useState< + ClassificationMode | undefined + >(); + const [numberOfShades, setNumberOfShades] = useState(); const [isLoading, setIsLoading] = useState(false); useEffect(() => { - if (selectedRamp === '' && selectedMode === '' && numberOfShades === '') { + if (selectedRamp === '') { populateOptions(); } }, [layerParams]); @@ -52,7 +55,7 @@ const ColorRamp: React.FC = ({ colorRamp = layerParams.symbologyState.colorRamp; } setNumberOfShades(nClasses ? nClasses : '9'); - setSelectedMode(singleBandMode ? singleBandMode : 'equal interval'); + setSelectedMode((singleBandMode as ClassificationMode) ?? 'equal interval'); setSelectedRamp(colorRamp ? colorRamp : 'viridis'); }; diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ModeSelectRow.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ModeSelectRow.tsx index c6be6814d..cafbbae70 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ModeSelectRow.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ModeSelectRow.tsx @@ -1,10 +1,12 @@ import React from 'react'; + +import { ClassificationMode } from '@/src/types'; interface IModeSelectRowProps { - numberOfShades: string; - setNumberOfShades: (value: string) => void; - selectedMode: string; - setSelectedMode: (value: string) => void; - modeOptions: string[]; + numberOfShades: number | undefined; + setNumberOfShades: (value: number | undefined) => void; + selectedMode: ClassificationMode | undefined; + setSelectedMode: (value: ClassificationMode | undefined) => void; + modeOptions: ClassificationMode[]; } const ModeSelectRow: React.FC = ({ numberOfShades, @@ -21,8 +23,11 @@ const ModeSelectRow: React.FC = ({ className="jp-mod-styled" name="class-number-input" type="number" - value={selectedMode === 'continuous' ? 52 : numberOfShades} - onChange={event => setNumberOfShades(event.target.value)} + value={selectedMode === 'continuous' ? 52 : (numberOfShades ?? '')} + onChange={event => { + const value = event.target.value; + setNumberOfShades(value === '' ? undefined : Number(value)); + }} disabled={selectedMode === 'continuous'} /> @@ -34,10 +39,12 @@ const ModeSelectRow: React.FC = ({ id="mode-select" className="jp-mod-styled" value={selectedMode} - onChange={event => setSelectedMode(event.target.value)} + onChange={event => + setSelectedMode(event.target.value as ClassificationMode) + } > {modeOptions.map(mode => ( - ))} diff --git a/packages/base/src/types.ts b/packages/base/src/types.ts index 6ae72d4b2..87c11d5b4 100644 --- a/packages/base/src/types.ts +++ b/packages/base/src/types.ts @@ -40,3 +40,14 @@ declare global { jupytergisMaps: { [name: string]: Map }; } } + +const classificationModes = [ + 'quantile', + 'equal interval', + 'jenks', + 'pretty', + 'logarithmic', + 'continuous', +] as const; + +export type ClassificationMode = (typeof classificationModes)[number]; From 0f37850407ea50323cba09754f437b3d02d03c96 Mon Sep 17 00:00:00 2001 From: Nakul Date: Wed, 24 Sep 2025 20:44:47 +0530 Subject: [PATCH 3/5] Refactor classification mode handling to use ClassificationMode type in SingleBandPseudoColor, Categorized, and Graduated components --- .../tiff_layer/types/SingleBandPseudoColor.tsx | 15 ++++++++++++--- .../symbology/vector_layer/types/Categorized.tsx | 6 +++--- .../symbology/vector_layer/types/Graduated.tsx | 12 +++++++++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx b/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx index 0cc58f61d..547ff34c4 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx @@ -20,6 +20,7 @@ import { Utils } from '@/src/dialogs/symbology/symbologyUtils'; import BandRow from '@/src/dialogs/symbology/tiff_layer/components/BandRow'; import { LoadingOverlay } from '@/src/shared/components/loading'; import { GlobalStateDbManager } from '@/src/store'; +import { ClassificationMode } from '@/src/types'; export type InterpolationType = 'discrete' | 'linear' | 'exact'; @@ -38,7 +39,11 @@ const SingleBandPseudoColor: React.FC = ({ } const functions = ['discrete', 'linear', 'exact']; - const modeOptions = ['continuous', 'equal interval', 'quantile']; + const modeOptions = [ + 'continuous', + 'equal interval', + 'quantile', + ] as ClassificationMode[]; const stateDb = GlobalStateDbManager.getInstance().getStateDb(); @@ -282,8 +287,8 @@ const SingleBandPseudoColor: React.FC = ({ }; const buildColorInfoFromClassification = async ( - selectedMode: string, - numberOfShades: string, + selectedMode: ClassificationMode | undefined, + numberOfShades: number | undefined, selectedRamp: string, setIsLoading: (isLoading: boolean) => void, ) => { @@ -299,6 +304,10 @@ const SingleBandPseudoColor: React.FC = ({ const currentBand = bandRows[selectedBand - 1]; const source = model.getSource(layer?.parameters?.source); const sourceInfo = source?.parameters?.urls[0]; + if (!numberOfShades) { + console.warn('No number of shades provided'); + return; + } const nClasses = selectedMode === 'continuous' ? 52 : +numberOfShades; setIsLoading(true); diff --git a/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx b/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx index 4e0e36a52..52fcf93ee 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx @@ -11,7 +11,7 @@ import { } from '@/src/dialogs/symbology/symbologyDialog'; import { Utils, VectorUtils } from '@/src/dialogs/symbology/symbologyUtils'; import ValueSelect from '@/src/dialogs/symbology/vector_layer/components/ValueSelect'; -import { SymbologyTab } from '@/src/types'; +import { SymbologyTab, ClassificationMode } from '@/src/types'; const Categorized: React.FC = ({ model, @@ -117,8 +117,8 @@ const Categorized: React.FC = ({ }, [selectedAttribute, stopRows, colorRampOptions]); const buildColorInfoFromClassification = ( - selectedMode: string, - numberOfShades: string, + selectedMode: ClassificationMode | undefined, + numberOfShades: number | undefined, selectedRamp: string, setIsLoading: (isLoading: boolean) => void, ) => { diff --git a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx index 3da611ab5..55eedc8a3 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx @@ -13,6 +13,7 @@ import { } from '@/src/dialogs/symbology/symbologyDialog'; import { Utils, VectorUtils } from '@/src/dialogs/symbology/symbologyUtils'; import ValueSelect from '@/src/dialogs/symbology/vector_layer/components/ValueSelect'; +import { ClassificationMode } from '@/src/types'; const Graduated: React.FC = ({ model, @@ -29,7 +30,7 @@ const Graduated: React.FC = ({ 'jenks', 'pretty', 'logarithmic', - ]; + ] as ClassificationMode[]; const selectableAttributeRef = useRef(); const symbologyTabRef = useRef(); @@ -215,8 +216,8 @@ const Graduated: React.FC = ({ }; const buildColorInfoFromClassification = ( - selectedMode: string, - numberOfShades: string, + selectedMode: ClassificationMode | undefined, + numberOfShades: number | undefined, selectedRamp: string, ) => { setColorRampOptions({ @@ -229,6 +230,11 @@ const Graduated: React.FC = ({ const values = Array.from(selectableAttributesAndValues[selectedAttribute]); + if (!numberOfShades) { + console.warn('No number of shades provided'); + return; + } + switch (selectedMode) { case 'quantile': stops = VectorClassifications.calculateQuantileBreaks( From da56e22e50896063237d4c55cf29ede79097bcc2 Mon Sep 17 00:00:00 2001 From: Nakul Date: Wed, 24 Sep 2025 21:44:11 +0530 Subject: [PATCH 4/5] Add validation for number of shades in ColorRamp and update usage in SingleBandPseudoColor and Graduated components --- .../components/color_ramp/ColorRamp.tsx | 5 +++++ .../tiff_layer/types/SingleBandPseudoColor.tsx | 7 ++----- .../symbology/vector_layer/types/Graduated.tsx | 17 ++++++----------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx index a3a26349b..6e7ce2749 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -26,6 +26,10 @@ export type ColorRampOptions = { selectedMode: ClassificationMode | undefined; }; +const isValidNumberOfShades = (value: number | undefined) => { + return value !== undefined && !isNaN(value) && value > 0; +}; + const ColorRamp: React.FC = ({ layerParams, modeOptions, @@ -84,6 +88,7 @@ const ColorRamp: React.FC = ({ ) : (