From 78e9966d38605eaeefc70ac46172f01f53d93d28 Mon Sep 17 00:00:00 2001 From: Nakul Date: Fri, 29 Aug 2025 19:52:44 +0530 Subject: [PATCH 01/73] Supporting divergent colomaps --- .../src/dialogs/symbology/colorRampUtils.ts | 48 ++++++++ .../components/color_ramp/ColorRamp.tsx | 13 ++- .../components/color_stops/StopRow.tsx | 38 ++++++- .../src/dialogs/symbology/symbologyUtils.ts | 20 +++- .../src/panelview/components/legendItem.tsx | 107 +++++++++++++----- packages/base/style/base.css | 23 ++++ 6 files changed, 211 insertions(+), 38 deletions(-) diff --git a/packages/base/src/dialogs/symbology/colorRampUtils.ts b/packages/base/src/dialogs/symbology/colorRampUtils.ts index 19b95908e..5a431104f 100644 --- a/packages/base/src/dialogs/symbology/colorRampUtils.ts +++ b/packages/base/src/dialogs/symbology/colorRampUtils.ts @@ -83,6 +83,10 @@ export const getColorMapList = (): IColorMap[] => { format: 'rgbaString', }); + if (COLOR_RAMP_DEFINITIONS[name]) { + COLOR_RAMP_DEFINITIONS[name].colors = colorRamp; + } + colorMapList.push({ name, colors: colorRamp }); }); @@ -134,3 +138,47 @@ export function hexToRgb(hex: string): [number, number, number, number] { 255, // TODO: Make alpha customizable? ]; } + +export type ColorRampType = 'Sequential' | 'Divergent' | 'Cyclic'; + +export interface IColorRampDefinition { + name: ColorRampName; + type: ColorRampType; + colors: string[]; + criticalValue?: number[]; // Only for divergent ramps +} + +export const COLOR_RAMP_DEFINITIONS: Partial< + Record +> = { + balance: { + name: 'balance', + type: 'Divergent', + colors: [], + criticalValue: [0], + }, + delta: { + name: 'delta', + type: 'Divergent', + colors: [], + criticalValue: [0], + }, + curl: { + name: 'curl', + type: 'Divergent', + colors: [], + criticalValue: [0], + }, + diff: { + name: 'diff', + type: 'Divergent', + colors: [], + criticalValue: [0], + }, + tarn: { + name: 'tarn', + type: 'Divergent', + colors: [], + criticalValue: [0], + }, +}; 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..05a5993a9 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -5,7 +5,10 @@ import React, { useEffect, useState } from 'react'; import { LoadingIcon } from '@/src/shared/components/loading'; import CanvasSelectComponent from './CanvasSelectComponent'; import ModeSelectRow from './ModeSelectRow'; - +import { + COLOR_RAMP_DEFINITIONS, + ColorRampName, +} from '../../../symbology/colorRampUtils'; interface IColorRampProps { modeOptions: string[]; layerParams: IDict; @@ -56,6 +59,9 @@ const ColorRamp: React.FC = ({ setSelectedRamp(colorRamp ? colorRamp : 'viridis'); }; + const rampType = + COLOR_RAMP_DEFINITIONS[selectedRamp as ColorRampName]?.type || 'Unknown'; + return (
{showRampSelector && ( @@ -65,6 +71,11 @@ const ColorRamp: React.FC = ({ selectedRamp={selectedRamp} setSelected={setSelectedRamp} /> + {selectedRamp && ( +
+ Color Ramp Type: {rampType} +
+ )}
)} {showModeRow && ( diff --git a/packages/base/src/dialogs/symbology/components/color_stops/StopRow.tsx b/packages/base/src/dialogs/symbology/components/color_stops/StopRow.tsx index c8543d881..39aeaa451 100644 --- a/packages/base/src/dialogs/symbology/components/color_stops/StopRow.tsx +++ b/packages/base/src/dialogs/symbology/components/color_stops/StopRow.tsx @@ -6,6 +6,8 @@ import React, { useEffect, useRef } from 'react'; import { ensureHexColorCode, hexToRgb, + COLOR_RAMP_DEFINITIONS, + ColorRampName, } from '@/src/dialogs/symbology/colorRampUtils'; import { IStopRow } from '@/src/dialogs/symbology/symbologyDialog'; import { SymbologyValue, SizeValue, ColorValue } from '@/src/types'; @@ -18,6 +20,7 @@ const StopRow: React.FC<{ setStopRows: (stopRows: IStopRow[]) => void; deleteRow: () => void; useNumber?: boolean; + rampName?: string; }> = ({ index, dataValue, @@ -26,9 +29,16 @@ const StopRow: React.FC<{ setStopRows, deleteRow, useNumber, + rampName, }) => { const inputRef = useRef(null); + const ramp = rampName + ? COLOR_RAMP_DEFINITIONS[rampName as ColorRampName] + : undefined; + const isCritical = + ramp?.type === 'Divergent' && ramp?.criticalValue?.includes(dataValue); + useEffect(() => { if (inputRef.current === document.activeElement) { inputRef.current?.focus(); @@ -36,12 +46,18 @@ const StopRow: React.FC<{ }, [stopRows]); const handleStopChange = (event: { target: { value: string } }) => { + if (isCritical) { + return; + } const newRows = [...stopRows]; newRows[index].stop = +event.target.value; setStopRows(newRows); }; const handleBlur = () => { + if (isCritical) { + return; + } const newRows = [...stopRows]; newRows.sort((a, b) => { if (a.stop < b.stop) { @@ -56,6 +72,9 @@ const StopRow: React.FC<{ }; const handleOutputChange = (event: { target: { value: any } }) => { + if (isCritical) { + return; + } const newRows = [...stopRows]; useNumber ? (newRows[index].output = +event.target.value) @@ -71,6 +90,7 @@ const StopRow: React.FC<{ value={dataValue} onChange={handleStopChange} onBlur={handleBlur} + disabled={isCritical} className="jp-mod-styled jp-gis-color-row-value-input" /> @@ -80,6 +100,7 @@ const StopRow: React.FC<{ ref={inputRef} value={symbologyValue as SizeValue} onChange={handleOutputChange} + disabled={isCritical} className="jp-mod-styled jp-gis-color-row-output-input" /> ) : ( @@ -89,16 +110,21 @@ const StopRow: React.FC<{ value={ensureHexColorCode(symbologyValue as ColorValue)} type="color" onChange={handleOutputChange} + disabled={isCritical} className="jp-mod-styled jp-gis-color-row-output-input" /> )} - + {!isCritical && ( + + )} + {/* Critical label */} + {isCritical && (critical)} ); }; diff --git a/packages/base/src/dialogs/symbology/symbologyUtils.ts b/packages/base/src/dialogs/symbology/symbologyUtils.ts index afc3216ae..65be8a886 100644 --- a/packages/base/src/dialogs/symbology/symbologyUtils.ts +++ b/packages/base/src/dialogs/symbology/symbologyUtils.ts @@ -1,6 +1,7 @@ import { IJGISLayer } from '@jupytergis/schema'; import colormap from 'colormap'; +import { COLOR_RAMP_DEFINITIONS, ColorRampName } from './colorRampUtils'; import { IStopRow } from './symbologyDialog'; const COLOR_EXPR_STOPS_START = 3; @@ -105,6 +106,21 @@ export namespace Utils { nClasses: number, reverse = false, ) => { + const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp as ColorRampName]; + + let effectiveStops = [...stops]; + + if (rampDef?.type === 'Divergent') { + const min = Math.min(...stops); + const max = Math.max(...stops); + const maxAbs = Math.max(Math.abs(min), Math.abs(max)); + + effectiveStops = Array.from({ length: nClasses }, (_, i) => { + const t = i / (nClasses - 1); + return -maxAbs + t * (2 * maxAbs); + }); + } + let colorMap = colormap({ colormap: selectedRamp, nshades: nClasses > 9 ? nClasses : 9, @@ -127,7 +143,7 @@ export namespace Utils { // Get the last n/2 elements from the second array const secondPart = colorMap.slice( - colorMap.length - (stops.length - firstPart.length), + colorMap.length - (effectiveStops.length - firstPart.length), ); // Create the new array by combining the first and last parts @@ -135,7 +151,7 @@ export namespace Utils { } for (let i = 0; i < nClasses; i++) { - valueColorPairs.push({ stop: stops[i], output: colorMap[i] }); + valueColorPairs.push({ stop: effectiveStops[i], output: colorMap[i] }); } return valueColorPairs; diff --git a/packages/base/src/panelview/components/legendItem.tsx b/packages/base/src/panelview/components/legendItem.tsx index 50fa2fbd2..ee650726e 100644 --- a/packages/base/src/panelview/components/legendItem.tsx +++ b/packages/base/src/panelview/components/legendItem.tsx @@ -1,6 +1,10 @@ import { IJupyterGISModel } from '@jupytergis/schema'; import React, { useEffect, useState } from 'react'; +import { + COLOR_RAMP_DEFINITIONS, + ColorRampName, +} from '@/src/dialogs/symbology/colorRampUtils'; import { useGetSymbology } from '@/src/dialogs/symbology/hooks/useGetSymbology'; export const LegendItem: React.FC<{ @@ -121,6 +125,11 @@ export const LegendItem: React.FC<{ return; } + const rampName = symbology.symbologyState?.colorRamp; + const rampDef = rampName + ? COLOR_RAMP_DEFINITIONS[rampName as ColorRampName] + : undefined; + const segments = stops .map((s, i) => { const pct = (i / (stops.length - 1)) * 100; @@ -129,6 +138,11 @@ export const LegendItem: React.FC<{ .join(', '); const gradient = `linear-gradient(to right, ${segments})`; + const minValue = stops[0].value; + const maxValue = stops[stops.length - 1].value; + const criticalValue = + rampDef?.type === 'Divergent' ? rampDef.criticalValue : undefined; + setContent(
{property && ( @@ -147,40 +161,75 @@ export const LegendItem: React.FC<{ marginTop: 10, }} > - {stops.map((s, i) => { - const left = (i / (stops.length - 1)) * 100; - const up = i % 2 === 0; - return ( + {/* Min */} +
+
+
+ {minValue.toFixed(2)} +
+
+ + {/* Max */} +
+
+
+ {maxValue.toFixed(2)} +
+
+ + {/* Critical (divergent only) */} + {criticalValue !== undefined && ( +
+
-
-
- {s.value.toFixed(2)} -
+ {criticalValue}
- ); - })} +
+ )}
, ); diff --git a/packages/base/style/base.css b/packages/base/style/base.css index 946f692e2..ceb6708c9 100644 --- a/packages/base/style/base.css +++ b/packages/base/style/base.css @@ -121,3 +121,26 @@ button.jp-mod-styled.jp-mod-reject { margin: 0; } } + +.jp-gis-ramp-type { + display: flex; + align-items: center; +} + +.jp-gis-ramp-type-label { + padding: 2px 8px; + border-radius: 6px; + background: var(--jp-layout-color2, #f3f4f6); + font-size: 0.85rem; + font-weight: 600; + color: var(--jp-ui-font-color1, #374151); + border: 1px solid var(--jp-border-color2, #d1d5db); + line-height: 1.3; + white-space: nowrap; + user-select: none; + transition: background 0.2s ease; +} + +.jp-gis-ramp-type-label:hover { + background: var(--jp-layout-color3, #e5e7eb); +} From 63bcb153a98783cdd5df5f388873165ef5adc01b Mon Sep 17 00:00:00 2001 From: Nakul Date: Fri, 29 Aug 2025 20:48:17 +0530 Subject: [PATCH 02/73] critical value only for divergent --- .../components/color_ramp/ColorRamp.tsx | 4 +- .../src/panelview/components/legendItem.tsx | 168 +++++++++++------- 2 files changed, 110 insertions(+), 62 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 05a5993a9..54546682e 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -73,7 +73,9 @@ const ColorRamp: React.FC = ({ /> {selectedRamp && (
- Color Ramp Type: {rampType} + + Color Ramp Type: {rampType} +
)}
diff --git a/packages/base/src/panelview/components/legendItem.tsx b/packages/base/src/panelview/components/legendItem.tsx index ee650726e..686ca00b0 100644 --- a/packages/base/src/panelview/components/legendItem.tsx +++ b/packages/base/src/panelview/components/legendItem.tsx @@ -161,74 +161,120 @@ export const LegendItem: React.FC<{ marginTop: 10, }} > - {/* Min */} -
-
-
- {minValue.toFixed(2)} -
-
- - {/* Max */} -
-
-
- {maxValue.toFixed(2)} -
-
+ {criticalValue === undefined ? ( + stops.map((s, i) => { + const left = (i / (stops.length - 1)) * 100; + const up = i % 2 === 0; + return ( +
+
+
+ {s.value.toFixed(2)} +
+
+ ); + }) + ) : ( + <> + {/* Min */} +
+
+
+ {minValue.toFixed(2)} +
+
- {/* Critical (divergent only) */} - {criticalValue !== undefined && ( -
+ {/* Max */}
+ > +
+
+ {maxValue.toFixed(2)} +
+
+ + {/* Critical */}
- {criticalValue} +
+
+ {criticalValue} +
-
+ )}
, From e9ecb4000725eaa0ccdc5621acdba87bcfd54d3a Mon Sep 17 00:00:00 2001 From: Nakul Date: Tue, 2 Sep 2025 17:44:03 +0530 Subject: [PATCH 03/73] Divergent with min-max --- .../src/dialogs/symbology/colorRampUtils.ts | 14 ++- .../components/color_ramp/ColorRamp.tsx | 84 +++++++++++++- .../components/color_stops/StopRow.tsx | 38 +------ .../src/dialogs/symbology/symbologyUtils.ts | 37 ++++-- .../vector_layer/types/Categorized.tsx | 1 + .../vector_layer/types/Graduated.tsx | 13 +++ .../src/panelview/components/legendItem.tsx | 107 +++++++++++++----- 7 files changed, 213 insertions(+), 81 deletions(-) diff --git a/packages/base/src/dialogs/symbology/colorRampUtils.ts b/packages/base/src/dialogs/symbology/colorRampUtils.ts index 5a431104f..b532a58b4 100644 --- a/packages/base/src/dialogs/symbology/colorRampUtils.ts +++ b/packages/base/src/dialogs/symbology/colorRampUtils.ts @@ -145,7 +145,9 @@ export interface IColorRampDefinition { name: ColorRampName; type: ColorRampType; colors: string[]; - criticalValue?: number[]; // Only for divergent ramps + criticalValue?: number; // Only for divergent ramps + userMin?: number; + userMax?: number; } export const COLOR_RAMP_DEFINITIONS: Partial< @@ -155,30 +157,30 @@ export const COLOR_RAMP_DEFINITIONS: Partial< name: 'balance', type: 'Divergent', colors: [], - criticalValue: [0], + criticalValue: 0, }, delta: { name: 'delta', type: 'Divergent', colors: [], - criticalValue: [0], + criticalValue: 0, }, curl: { name: 'curl', type: 'Divergent', colors: [], - criticalValue: [0], + criticalValue: 0, }, diff: { name: 'diff', type: 'Divergent', colors: [], - criticalValue: [0], + criticalValue: 0, }, tarn: { name: 'tarn', type: 'Divergent', colors: [], - criticalValue: [0], + criticalValue: 0, }, }; 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 54546682e..f2b15035f 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -17,15 +17,22 @@ interface IColorRampProps { numberOfShades: string, selectedRamp: string, setIsLoading: (isLoading: boolean) => void, + criticalValue?: number, + minValue?: number, + maxValue?: number, ) => void; showModeRow: boolean; showRampSelector: boolean; + layerType?: 'graduated' | 'categorized'; } export type ColorRampOptions = { selectedRamp: string; numberOfShades: string; selectedMode: string; + minValue?: number; + maxValue?: number; + criticalValue?: number; }; const ColorRamp: React.FC = ({ @@ -34,10 +41,14 @@ const ColorRamp: React.FC = ({ classifyFunc, showModeRow, showRampSelector, + layerType, }) => { const [selectedRamp, setSelectedRamp] = useState(''); const [selectedMode, setSelectedMode] = useState(''); const [numberOfShades, setNumberOfShades] = useState(''); + const [criticalValue, setCriticalValue] = useState(0); + const [minValue, setMinValue] = useState(-5); + const [maxValue, setMaxValue] = useState(5); const [isLoading, setIsLoading] = useState(false); useEffect(() => { @@ -47,20 +58,26 @@ const ColorRamp: React.FC = ({ }, [layerParams]); const populateOptions = () => { - let nClasses, singleBandMode, colorRamp; + let nClasses, singleBandMode, colorRamp, min, max, crit; if (layerParams.symbologyState) { nClasses = layerParams.symbologyState.nClasses; singleBandMode = layerParams.symbologyState.mode; colorRamp = layerParams.symbologyState.colorRamp; + min = layerParams.symbologyState.minValue; + max = layerParams.symbologyState.maxValue; + crit = layerParams.symbologyState.criticalValue; } setNumberOfShades(nClasses ? nClasses : '9'); setSelectedMode(singleBandMode ? singleBandMode : 'equal interval'); setSelectedRamp(colorRamp ? colorRamp : 'viridis'); + setMinValue(min !== undefined ? min : -5); + setMaxValue(max !== undefined ? max : 5); + setCriticalValue(crit !== undefined ? crit : 0); }; - const rampType = - COLOR_RAMP_DEFINITIONS[selectedRamp as ColorRampName]?.type || 'Unknown'; + const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp as ColorRampName]; + const rampType = rampDef?.type || 'Unknown'; return (
@@ -89,6 +106,64 @@ const ColorRamp: React.FC = ({ setSelectedMode={setSelectedMode} /> )} + {/* 🔹 Divergent colormap controls */} + {layerType === 'graduated' && rampType === 'Divergent' && ( + <> +
+ + + setMinValue( + e.target.value !== '' + ? parseFloat(e.target.value) + : undefined, + ) + } + className="jp-mod-styled" + placeholder="Enter min value" + /> +
+ +
+ + + setCriticalValue( + e.target.value !== '' + ? parseFloat(e.target.value) + : undefined, + ) + } + className="jp-mod-styled" + placeholder="Enter critical value" + /> +
+ +
+ + + setMaxValue( + e.target.value !== '' + ? parseFloat(e.target.value) + : undefined, + ) + } + className="jp-mod-styled" + placeholder="Enter max value" + /> +
+ + )} {isLoading ? ( ) : ( @@ -100,6 +175,9 @@ const ColorRamp: React.FC = ({ numberOfShades, selectedRamp, setIsLoading, + criticalValue, + minValue, + maxValue, ) } > diff --git a/packages/base/src/dialogs/symbology/components/color_stops/StopRow.tsx b/packages/base/src/dialogs/symbology/components/color_stops/StopRow.tsx index 39aeaa451..c8543d881 100644 --- a/packages/base/src/dialogs/symbology/components/color_stops/StopRow.tsx +++ b/packages/base/src/dialogs/symbology/components/color_stops/StopRow.tsx @@ -6,8 +6,6 @@ import React, { useEffect, useRef } from 'react'; import { ensureHexColorCode, hexToRgb, - COLOR_RAMP_DEFINITIONS, - ColorRampName, } from '@/src/dialogs/symbology/colorRampUtils'; import { IStopRow } from '@/src/dialogs/symbology/symbologyDialog'; import { SymbologyValue, SizeValue, ColorValue } from '@/src/types'; @@ -20,7 +18,6 @@ const StopRow: React.FC<{ setStopRows: (stopRows: IStopRow[]) => void; deleteRow: () => void; useNumber?: boolean; - rampName?: string; }> = ({ index, dataValue, @@ -29,16 +26,9 @@ const StopRow: React.FC<{ setStopRows, deleteRow, useNumber, - rampName, }) => { const inputRef = useRef(null); - const ramp = rampName - ? COLOR_RAMP_DEFINITIONS[rampName as ColorRampName] - : undefined; - const isCritical = - ramp?.type === 'Divergent' && ramp?.criticalValue?.includes(dataValue); - useEffect(() => { if (inputRef.current === document.activeElement) { inputRef.current?.focus(); @@ -46,18 +36,12 @@ const StopRow: React.FC<{ }, [stopRows]); const handleStopChange = (event: { target: { value: string } }) => { - if (isCritical) { - return; - } const newRows = [...stopRows]; newRows[index].stop = +event.target.value; setStopRows(newRows); }; const handleBlur = () => { - if (isCritical) { - return; - } const newRows = [...stopRows]; newRows.sort((a, b) => { if (a.stop < b.stop) { @@ -72,9 +56,6 @@ const StopRow: React.FC<{ }; const handleOutputChange = (event: { target: { value: any } }) => { - if (isCritical) { - return; - } const newRows = [...stopRows]; useNumber ? (newRows[index].output = +event.target.value) @@ -90,7 +71,6 @@ const StopRow: React.FC<{ value={dataValue} onChange={handleStopChange} onBlur={handleBlur} - disabled={isCritical} className="jp-mod-styled jp-gis-color-row-value-input" /> @@ -100,7 +80,6 @@ const StopRow: React.FC<{ ref={inputRef} value={symbologyValue as SizeValue} onChange={handleOutputChange} - disabled={isCritical} className="jp-mod-styled jp-gis-color-row-output-input" /> ) : ( @@ -110,21 +89,16 @@ const StopRow: React.FC<{ value={ensureHexColorCode(symbologyValue as ColorValue)} type="color" onChange={handleOutputChange} - disabled={isCritical} className="jp-mod-styled jp-gis-color-row-output-input" /> )} - {!isCritical && ( - - )} - {/* Critical label */} - {isCritical && (critical)} +
); }; diff --git a/packages/base/src/dialogs/symbology/symbologyUtils.ts b/packages/base/src/dialogs/symbology/symbologyUtils.ts index 65be8a886..6a9a7117e 100644 --- a/packages/base/src/dialogs/symbology/symbologyUtils.ts +++ b/packages/base/src/dialogs/symbology/symbologyUtils.ts @@ -105,20 +105,33 @@ export namespace Utils { selectedRamp: string, nClasses: number, reverse = false, + layerType: 'categorized' | 'graduated' = 'graduated', + minValue?: number, + maxValue?: number, ) => { const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp as ColorRampName]; - - let effectiveStops = [...stops]; - - if (rampDef?.type === 'Divergent') { - const min = Math.min(...stops); - const max = Math.max(...stops); - const maxAbs = Math.max(Math.abs(min), Math.abs(max)); - - effectiveStops = Array.from({ length: nClasses }, (_, i) => { - const t = i / (nClasses - 1); - return -maxAbs + t * (2 * maxAbs); - }); + let effectiveStops: number[] = []; + + if (layerType === 'categorized') { + effectiveStops = stops; + } else { + if (rampDef?.type === 'Divergent') { + const min = minValue ?? Math.min(...stops); + const max = maxValue ?? Math.max(...stops); + + effectiveStops = Array.from( + { length: nClasses }, + (_, i) => min + (i / (nClasses - 1)) * (max - min), + ); + } else { + const min = Math.min(...stops); + const max = Math.max(...stops); + + effectiveStops = Array.from( + { length: nClasses }, + (_, i) => min + (i / (nClasses - 1)) * (max - min), + ); + } } let colorMap = colormap({ 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..82b53d3c1 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx @@ -138,6 +138,7 @@ const Categorized: React.FC = ({ selectedRamp, stops.length, reverseRamp, + 'categorized', ); setStopRows(valueColorPairs); 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..8bed6714c 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx @@ -218,11 +218,18 @@ const Graduated: React.FC = ({ selectedMode: string, numberOfShades: string, selectedRamp: string, + setIsLoading: (isLoading: boolean) => void, + criticalValue?: number, + minValue?: number, + maxValue?: number, ) => { setColorRampOptions({ selectedRamp, numberOfShades, selectedMode, + minValue, + maxValue, + criticalValue, }); let stops: number[]; @@ -273,6 +280,9 @@ const Graduated: React.FC = ({ selectedRamp, +numberOfShades, reverseRamp, + 'graduated', + minValue, + maxValue, ); if (symbologyTab === 'radius') { @@ -280,6 +290,8 @@ const Graduated: React.FC = ({ } else { setColorStopRows(stopOutputPairs); } + + setIsLoading(false); }; const handleReset = (method: string) => { @@ -408,6 +420,7 @@ const Graduated: React.FC = ({ classifyFunc={buildColorInfoFromClassification} showModeRow={true} showRampSelector={symbologyTab === 'color'} + layerType="graduated" /> @@ -161,7 +164,7 @@ export const LegendItem: React.FC<{ marginTop: 10, }} > - {criticalValue === undefined ? ( + {!isDivergent ? ( stops.map((s, i) => { const left = (i / (stops.length - 1)) * 100; const up = i % 2 === 0; @@ -245,38 +248,59 @@ export const LegendItem: React.FC<{
{/* Critical */} -
-
+ {isDivergent && (
= maxValue + ? 100 + : ((criticalValue - minValue) / + (maxValue - minValue)) * + 100 + }%`, + transform: 'translateX(-50%)', }} > - {criticalValue} +
+
+ {criticalValue.toFixed(2)} +
-
+ )} )}
+ + {/* Labels below bar */} +
+ {minValue !== undefined &&
Min: {minValue.toFixed(2)}
} + {maxValue !== undefined &&
Max: {maxValue.toFixed(2)}
} + {isDivergent && criticalValue !== undefined && ( +
+ Critical: {criticalValue.toFixed(2)} +
+ )} +
, ); return; @@ -292,6 +316,17 @@ export const LegendItem: React.FC<{ return; } + const numericCats = cats + .map(c => (typeof c.category === 'number' ? c.category : NaN)) + .filter(v => !isNaN(v)); + + const minValue = numericCats.length + ? Math.min(...numericCats) + : undefined; + const maxValue = numericCats.length + ? Math.max(...numericCats) + : undefined; + setContent(
{property && ( @@ -299,6 +334,7 @@ export const LegendItem: React.FC<{ {property}
)} +
{cats.map((c, i) => ( @@ -322,10 +359,24 @@ export const LegendItem: React.FC<{ borderRadius: 2, }} /> - {String(c.category)} + + {typeof c.category === 'number' + ? c.category.toFixed(2) + : String(c.category)} +
))}
+ + {/* Min/Max */} + {(minValue !== undefined || maxValue !== undefined) && ( +
+ {minValue !== undefined &&
Min: {minValue}
} + {maxValue !== undefined &&
Max: {maxValue}
} +
+ )}
, ); return; From 2e2f37bbe52913a78d63423359230b045cf04d8a Mon Sep 17 00:00:00 2001 From: Nakul Date: Fri, 5 Sep 2025 14:31:47 +0530 Subject: [PATCH 04/73] changes --- .../src/dialogs/symbology/colorRampUtils.ts | 183 +++++++----------- .../color_ramp/CanvasSelectComponent.tsx | 22 ++- .../components/color_ramp/ColorRamp.tsx | 13 +- .../components/color_ramp/ColorRampEntry.tsx | 9 +- .../src/dialogs/symbology/symbologyUtils.ts | 4 +- .../vector_layer/types/Graduated.tsx | 2 +- .../src/panelview/components/legendItem.tsx | 24 +-- packages/base/src/tools.ts | 1 + packages/base/src/types.ts | 42 ++++ packages/base/style/base.css | 23 --- 10 files changed, 148 insertions(+), 175 deletions(-) diff --git a/packages/base/src/dialogs/symbology/colorRampUtils.ts b/packages/base/src/dialogs/symbology/colorRampUtils.ts index b532a58b4..afb29d5f1 100644 --- a/packages/base/src/dialogs/symbology/colorRampUtils.ts +++ b/packages/base/src/dialogs/symbology/colorRampUtils.ts @@ -3,91 +3,90 @@ import colorScale from 'colormap/colorScale.js'; import { useEffect } from 'react'; import rawCmocean from '@/src/dialogs/symbology/components/color_ramp/cmocean.json'; +import { objectKeys } from '@/src/tools'; +import { + IColorMap, + IColorRampDefinition, +} from '@/src/types'; -export interface IColorMap { - name: ColorRampName; - colors: string[]; -} const { __license__: _, ...cmocean } = rawCmocean; Object.assign(colorScale, cmocean); -export const COLOR_RAMP_NAMES = [ - 'jet', - // 'hsv', 11 steps min - 'hot', - 'cool', - 'spring', - 'summer', - 'autumn', - 'winter', - 'bone', - 'copper', - 'greys', - 'YiGnBu', - 'greens', - 'YiOrRd', - 'bluered', - 'RdBu', - // 'picnic', 11 steps min - 'rainbow', - 'portland', - 'blackbody', - 'earth', - 'electric', - 'viridis', - 'inferno', - 'magma', - 'plasma', - 'warm', - // 'rainbow-soft', 11 steps min - 'bathymetry', - 'cdom', - 'chlorophyll', - 'density', - 'freesurface-blue', - 'freesurface-red', - 'oxygen', - 'par', - 'phase', - 'salinity', - 'temperature', - 'turbidity', - 'velocity-blue', - 'velocity-green', - // 'cubehelix' 16 steps min - 'ice', - 'oxy', - 'matter', - 'amp', - 'tempo', - 'rain', - 'topo', - 'balance', - 'delta', - 'curl', - 'diff', - 'tarn', -] as const; - +export const COLOR_RAMP_DEFINITIONS = { + 'jet': {type: 'Sequential'}, + // 'hsv': {type: 'Sequential'}, 11 steps min + 'hot': {type: 'Sequential'}, + 'cool': {type: 'Sequential'}, + 'spring': {type: 'Sequential'}, + 'summer': {type: 'Sequential'}, + 'autumn': {type: 'Sequential'}, + 'winter': {type: 'Sequential'}, + 'bone': {type: 'Sequential'}, + 'copper': {type: 'Sequential'}, + 'greys': {type: 'Sequential'}, + 'YiGnBu': {type: 'Sequential'}, + 'greens': {type: 'Sequential'}, + 'YiOrRd': {type: 'Sequential'}, + 'bluered': {type: 'Sequential'}, + 'RdBu': {type: 'Sequential'}, + // 'picnic': {type: 'Sequential'}, 11 steps min + 'rainbow': {type: 'Sequential'}, + 'portland': {type: 'Sequential'}, + 'blackbody': {type: 'Sequential'}, + 'earth': {type: 'Sequential'}, + 'electric': {type: 'Sequential'}, + 'viridis': {type: 'Sequential'}, + 'inferno': {type: 'Sequential'}, + 'magma': {type: 'Sequential'}, + 'plasma': {type: 'Sequential'}, + 'warm': {type: 'Sequential'}, + // 'rainbow-soft': {type: 'Sequential'}, 11 steps min + 'bathymetry': {type: 'Sequential'}, + 'cdom': {type: 'Sequential'}, + 'chlorophyll': {type: 'Sequential'}, + 'density': {type: 'Sequential'}, + 'freesurface-blue': {type: 'Sequential'}, + 'freesurface-red': {type: 'Sequential'}, + 'oxygen': {type: 'Sequential'}, + 'par': {type: 'Sequential'}, + 'phase': {type: 'Cyclic'}, + 'salinity': {type: 'Sequential'}, + 'temperature': {type: 'Sequential'}, + 'turbidity': {type: 'Sequential'}, + 'velocity-blue': {type: 'Sequential'}, + 'velocity-green': {type: 'Sequential'}, + // 'cubehelix': {type: 'Sequential'}, 16 steps min + 'ice': {type: 'Sequential'}, + 'oxy': {type: 'Sequential'}, + 'matter': {type: 'Sequential'}, + 'amp': {type: 'Sequential'}, + 'tempo': {type: 'Sequential'}, + 'rain': {type: 'Sequential'}, + 'topo': {type: 'Sequential'}, + 'balance': {type: 'Divergent', criticalValue: 0.5}, + 'delta': {type: 'Divergent', criticalValue: 0.5}, + 'curl': {type: 'Divergent', criticalValue: 0.5}, + 'diff': {type: 'Divergent', criticalValue: 0.5}, + 'tarn': {type: 'Divergent', criticalValue: 0.5}, + } as const satisfies { [key: string]: IColorRampDefinition }; + +export const COLOR_RAMP_NAMES = objectKeys(COLOR_RAMP_DEFINITIONS); export type ColorRampName = (typeof COLOR_RAMP_NAMES)[number]; export const getColorMapList = (): IColorMap[] => { const colorMapList: IColorMap[] = []; - COLOR_RAMP_NAMES.forEach(name => { - const colorRamp = colormap({ + COLOR_RAMP_NAMES.forEach(name => { + const definition = COLOR_RAMP_DEFINITIONS[name]; + const colors = colormap({ colormap: name, nshades: 255, format: 'rgbaString', }); - if (COLOR_RAMP_DEFINITIONS[name]) { - COLOR_RAMP_DEFINITIONS[name].colors = colorRamp; - } - - colorMapList.push({ name, colors: colorRamp }); + colorMapList.push({ name, colors, definition }); }); return colorMapList; @@ -138,49 +137,3 @@ export function hexToRgb(hex: string): [number, number, number, number] { 255, // TODO: Make alpha customizable? ]; } - -export type ColorRampType = 'Sequential' | 'Divergent' | 'Cyclic'; - -export interface IColorRampDefinition { - name: ColorRampName; - type: ColorRampType; - colors: string[]; - criticalValue?: number; // Only for divergent ramps - userMin?: number; - userMax?: number; -} - -export const COLOR_RAMP_DEFINITIONS: Partial< - Record -> = { - balance: { - name: 'balance', - type: 'Divergent', - colors: [], - criticalValue: 0, - }, - delta: { - name: 'delta', - type: 'Divergent', - colors: [], - criticalValue: 0, - }, - curl: { - name: 'curl', - type: 'Divergent', - colors: [], - criticalValue: 0, - }, - diff: { - name: 'diff', - type: 'Divergent', - colors: [], - criticalValue: 0, - }, - tarn: { - name: 'tarn', - type: 'Divergent', - colors: [], - criticalValue: 0, - }, -}; diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx index 062c21577..8bdd3c9b3 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx @@ -1,12 +1,17 @@ import { Button } from '@jupyterlab/ui-components'; import React, { useEffect, useRef, useState } from 'react'; -import { useColorMapList } from '@/src/dialogs/symbology/colorRampUtils'; +import { + useColorMapList, + COLOR_RAMP_DEFINITIONS, + ColorRampName, +} from '@/src/dialogs/symbology/colorRampUtils'; import ColorRampEntry from './ColorRampEntry'; export interface IColorMap { name: string; colors: string[]; + type?: string; } interface ICanvasSelectComponentProps { @@ -22,7 +27,13 @@ const CanvasSelectComponent: React.FC = ({ const [isOpen, setIsOpen] = useState(false); const [colorMaps, setColorMaps] = useState([]); - useColorMapList(setColorMaps); + useColorMapList((maps: IColorMap[]) => { + const withTypes = maps.map(m => { + const def = COLOR_RAMP_DEFINITIONS[m.name as ColorRampName]; + return { ...m, type: def?.type ?? 'Unknown' }; + }); + setColorMaps(withTypes); + }); useEffect(() => { if (colorMaps.length > 0) { @@ -99,7 +110,12 @@ const CanvasSelectComponent: React.FC = ({ className={`jp-gis-color-ramp-dropdown ${isOpen ? 'jp-gis-open' : ''}`} > {colorMaps.map((item, index) => ( - + ))}
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 f2b15035f..d299b3756 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -23,7 +23,7 @@ interface IColorRampProps { ) => void; showModeRow: boolean; showRampSelector: boolean; - layerType?: 'graduated' | 'categorized'; + renderType?: 'graduated' | 'categorized'; } export type ColorRampOptions = { @@ -41,7 +41,7 @@ const ColorRamp: React.FC = ({ classifyFunc, showModeRow, showRampSelector, - layerType, + renderType, }) => { const [selectedRamp, setSelectedRamp] = useState(''); const [selectedMode, setSelectedMode] = useState(''); @@ -88,13 +88,6 @@ const ColorRamp: React.FC = ({ selectedRamp={selectedRamp} setSelected={setSelectedRamp} /> - {selectedRamp && ( -
- - Color Ramp Type: {rampType} - -
- )}
)} {showModeRow && ( @@ -107,7 +100,7 @@ const ColorRamp: React.FC = ({ /> )} {/* 🔹 Divergent colormap controls */} - {layerType === 'graduated' && rampType === 'Divergent' && ( + {renderType === 'graduated' && rampType === 'Divergent' && ( <>
diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampEntry.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampEntry.tsx index e6da51cb5..d1216bb5c 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampEntry.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampEntry.tsx @@ -42,7 +42,14 @@ const ColorRampEntry: React.FC = ({ onClick={() => onClick(colorMap.name)} className="jp-gis-color-ramp-entry" > - {colorMap.name} + + {colorMap.name}{' '} + + ({colorMap.type}) + + { const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp as ColorRampName]; let effectiveStops: number[] = []; - if (layerType === 'categorized') { + if (renderType === 'categorized') { effectiveStops = stops; } else { if (rampDef?.type === 'Divergent') { 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 8bed6714c..6de48cf07 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx @@ -420,7 +420,7 @@ const Graduated: React.FC = ({ classifyFunc={buildColorInfoFromClassification} showModeRow={true} showRampSelector={symbologyTab === 'color'} - layerType="graduated" + renderType="graduated" /> c.name === rampName) : undefined; const segments = stops @@ -141,8 +138,8 @@ export const LegendItem: React.FC<{ const minValue = rampDef?.userMin ?? stops[0].value; const maxValue = rampDef?.userMax ?? stops[stops.length - 1].value; const criticalValue = - rampDef?.type === 'Divergent' - ? (rampDef.criticalValue ?? 0) + rampDef?.definition.type === 'Divergent' + ? (rampDef.criticalValue ?? 0.5) : undefined; const isDivergent = criticalValue !== undefined; @@ -288,19 +285,6 @@ export const LegendItem: React.FC<{ )}
- - {/* Labels below bar */} -
- {minValue !== undefined &&
Min: {minValue.toFixed(2)}
} - {maxValue !== undefined &&
Max: {maxValue.toFixed(2)}
} - {isDivergent && criticalValue !== undefined && ( -
- Critical: {criticalValue.toFixed(2)} -
- )} -
, ); return; diff --git a/packages/base/src/tools.ts b/packages/base/src/tools.ts index 0660b3d00..fced6140d 100644 --- a/packages/base/src/tools.ts +++ b/packages/base/src/tools.ts @@ -31,6 +31,7 @@ export const debounce = ( }; }; +export const objectKeys = Object.keys as >(obj: T) => Array; export function throttle void>( callback: T, delay = 100, diff --git a/packages/base/src/types.ts b/packages/base/src/types.ts index 6ae72d4b2..e18b0959b 100644 --- a/packages/base/src/types.ts +++ b/packages/base/src/types.ts @@ -2,6 +2,8 @@ import { IDict, IJupyterGISWidget } from '@jupytergis/schema'; import { WidgetTracker } from '@jupyterlab/apputils'; import { Map } from 'ol'; +import { COLOR_RAMP_DEFINITIONS } from './dialogs/symbology/colorRampUtils'; + export { IDict }; export type ValueOf = T[keyof T]; @@ -40,3 +42,43 @@ declare global { jupytergisMaps: { [name: string]: Map }; } } + +/** + * Color ramp types and definitions + */ +export type ColorRampType = 'Sequential' | 'Divergent' | 'Cyclic'; + +export interface IBaseColorRampDefinition { + type: ColorRampType; +} + +export interface ISequentialColorRampDefinition + extends IBaseColorRampDefinition { + type: 'Sequential'; +} + +export interface IDivergentColorRampDefinition + extends IBaseColorRampDefinition { + type: 'Divergent'; + criticalValue: number; +} + +export interface ICyclicColorRampDefinition extends IBaseColorRampDefinition { + type: 'Cyclic'; +} + +export type IColorRampDefinition = + | ISequentialColorRampDefinition + | IDivergentColorRampDefinition + | ICyclicColorRampDefinition; + +export interface IColorMap { + name: ColorRampName; + colors: string[]; + definition: IColorRampDefinition; + userMin?: number; + userMax?: number; + criticalValue?: number; +} + +export type ColorRampName = keyof typeof COLOR_RAMP_DEFINITIONS; diff --git a/packages/base/style/base.css b/packages/base/style/base.css index ceb6708c9..946f692e2 100644 --- a/packages/base/style/base.css +++ b/packages/base/style/base.css @@ -121,26 +121,3 @@ button.jp-mod-styled.jp-mod-reject { margin: 0; } } - -.jp-gis-ramp-type { - display: flex; - align-items: center; -} - -.jp-gis-ramp-type-label { - padding: 2px 8px; - border-radius: 6px; - background: var(--jp-layout-color2, #f3f4f6); - font-size: 0.85rem; - font-weight: 600; - color: var(--jp-ui-font-color1, #374151); - border: 1px solid var(--jp-border-color2, #d1d5db); - line-height: 1.3; - white-space: nowrap; - user-select: none; - transition: background 0.2s ease; -} - -.jp-gis-ramp-type-label:hover { - background: var(--jp-layout-color3, #e5e7eb); -} From 67faef4be82d51a7c907ee63982cb7ba90fe8faa Mon Sep 17 00:00:00 2001 From: Nakul Date: Fri, 5 Sep 2025 16:11:12 +0530 Subject: [PATCH 05/73] lint fix --- .../src/dialogs/symbology/colorRampUtils.ts | 112 +++++++++--------- .../components/color_ramp/ColorRamp.tsx | 25 ++-- packages/base/src/tools.ts | 4 +- 3 files changed, 68 insertions(+), 73 deletions(-) diff --git a/packages/base/src/dialogs/symbology/colorRampUtils.ts b/packages/base/src/dialogs/symbology/colorRampUtils.ts index afb29d5f1..7be062149 100644 --- a/packages/base/src/dialogs/symbology/colorRampUtils.ts +++ b/packages/base/src/dialogs/symbology/colorRampUtils.ts @@ -4,73 +4,69 @@ import { useEffect } from 'react'; import rawCmocean from '@/src/dialogs/symbology/components/color_ramp/cmocean.json'; import { objectKeys } from '@/src/tools'; -import { - IColorMap, - IColorRampDefinition, -} from '@/src/types'; - +import { IColorMap, IColorRampDefinition } from '@/src/types'; const { __license__: _, ...cmocean } = rawCmocean; Object.assign(colorScale, cmocean); export const COLOR_RAMP_DEFINITIONS = { - 'jet': {type: 'Sequential'}, + jet: { type: 'Sequential' }, // 'hsv': {type: 'Sequential'}, 11 steps min - 'hot': {type: 'Sequential'}, - 'cool': {type: 'Sequential'}, - 'spring': {type: 'Sequential'}, - 'summer': {type: 'Sequential'}, - 'autumn': {type: 'Sequential'}, - 'winter': {type: 'Sequential'}, - 'bone': {type: 'Sequential'}, - 'copper': {type: 'Sequential'}, - 'greys': {type: 'Sequential'}, - 'YiGnBu': {type: 'Sequential'}, - 'greens': {type: 'Sequential'}, - 'YiOrRd': {type: 'Sequential'}, - 'bluered': {type: 'Sequential'}, - 'RdBu': {type: 'Sequential'}, + hot: { type: 'Sequential' }, + cool: { type: 'Sequential' }, + spring: { type: 'Sequential' }, + summer: { type: 'Sequential' }, + autumn: { type: 'Sequential' }, + winter: { type: 'Sequential' }, + bone: { type: 'Sequential' }, + copper: { type: 'Sequential' }, + greys: { type: 'Sequential' }, + YiGnBu: { type: 'Sequential' }, + greens: { type: 'Sequential' }, + YiOrRd: { type: 'Sequential' }, + bluered: { type: 'Sequential' }, + RdBu: { type: 'Sequential' }, // 'picnic': {type: 'Sequential'}, 11 steps min - 'rainbow': {type: 'Sequential'}, - 'portland': {type: 'Sequential'}, - 'blackbody': {type: 'Sequential'}, - 'earth': {type: 'Sequential'}, - 'electric': {type: 'Sequential'}, - 'viridis': {type: 'Sequential'}, - 'inferno': {type: 'Sequential'}, - 'magma': {type: 'Sequential'}, - 'plasma': {type: 'Sequential'}, - 'warm': {type: 'Sequential'}, + rainbow: { type: 'Sequential' }, + portland: { type: 'Sequential' }, + blackbody: { type: 'Sequential' }, + earth: { type: 'Sequential' }, + electric: { type: 'Sequential' }, + viridis: { type: 'Sequential' }, + inferno: { type: 'Sequential' }, + magma: { type: 'Sequential' }, + plasma: { type: 'Sequential' }, + warm: { type: 'Sequential' }, // 'rainbow-soft': {type: 'Sequential'}, 11 steps min - 'bathymetry': {type: 'Sequential'}, - 'cdom': {type: 'Sequential'}, - 'chlorophyll': {type: 'Sequential'}, - 'density': {type: 'Sequential'}, - 'freesurface-blue': {type: 'Sequential'}, - 'freesurface-red': {type: 'Sequential'}, - 'oxygen': {type: 'Sequential'}, - 'par': {type: 'Sequential'}, - 'phase': {type: 'Cyclic'}, - 'salinity': {type: 'Sequential'}, - 'temperature': {type: 'Sequential'}, - 'turbidity': {type: 'Sequential'}, - 'velocity-blue': {type: 'Sequential'}, - 'velocity-green': {type: 'Sequential'}, + bathymetry: { type: 'Sequential' }, + cdom: { type: 'Sequential' }, + chlorophyll: { type: 'Sequential' }, + density: { type: 'Sequential' }, + 'freesurface-blue': { type: 'Sequential' }, + 'freesurface-red': { type: 'Sequential' }, + oxygen: { type: 'Sequential' }, + par: { type: 'Sequential' }, + phase: { type: 'Cyclic' }, + salinity: { type: 'Sequential' }, + temperature: { type: 'Sequential' }, + turbidity: { type: 'Sequential' }, + 'velocity-blue': { type: 'Sequential' }, + 'velocity-green': { type: 'Sequential' }, // 'cubehelix': {type: 'Sequential'}, 16 steps min - 'ice': {type: 'Sequential'}, - 'oxy': {type: 'Sequential'}, - 'matter': {type: 'Sequential'}, - 'amp': {type: 'Sequential'}, - 'tempo': {type: 'Sequential'}, - 'rain': {type: 'Sequential'}, - 'topo': {type: 'Sequential'}, - 'balance': {type: 'Divergent', criticalValue: 0.5}, - 'delta': {type: 'Divergent', criticalValue: 0.5}, - 'curl': {type: 'Divergent', criticalValue: 0.5}, - 'diff': {type: 'Divergent', criticalValue: 0.5}, - 'tarn': {type: 'Divergent', criticalValue: 0.5}, - } as const satisfies { [key: string]: IColorRampDefinition }; + ice: { type: 'Sequential' }, + oxy: { type: 'Sequential' }, + matter: { type: 'Sequential' }, + amp: { type: 'Sequential' }, + tempo: { type: 'Sequential' }, + rain: { type: 'Sequential' }, + topo: { type: 'Sequential' }, + balance: { type: 'Divergent', criticalValue: 0.5 }, + delta: { type: 'Divergent', criticalValue: 0.5 }, + curl: { type: 'Divergent', criticalValue: 0.5 }, + diff: { type: 'Divergent', criticalValue: 0.5 }, + tarn: { type: 'Divergent', criticalValue: 0.5 }, +} as const satisfies { [key: string]: IColorRampDefinition }; export const COLOR_RAMP_NAMES = objectKeys(COLOR_RAMP_DEFINITIONS); export type ColorRampName = (typeof COLOR_RAMP_NAMES)[number]; @@ -78,7 +74,7 @@ export type ColorRampName = (typeof COLOR_RAMP_NAMES)[number]; export const getColorMapList = (): IColorMap[] => { const colorMapList: IColorMap[] = []; - COLOR_RAMP_NAMES.forEach(name => { + COLOR_RAMP_NAMES.forEach(name => { const definition = COLOR_RAMP_DEFINITIONS[name]; const colors = colormap({ colormap: name, 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 d299b3756..97508f045 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -46,7 +46,6 @@ const ColorRamp: React.FC = ({ const [selectedRamp, setSelectedRamp] = useState(''); const [selectedMode, setSelectedMode] = useState(''); const [numberOfShades, setNumberOfShades] = useState(''); - const [criticalValue, setCriticalValue] = useState(0); const [minValue, setMinValue] = useState(-5); const [maxValue, setMaxValue] = useState(5); const [isLoading, setIsLoading] = useState(false); @@ -58,7 +57,7 @@ const ColorRamp: React.FC = ({ }, [layerParams]); const populateOptions = () => { - let nClasses, singleBandMode, colorRamp, min, max, crit; + let nClasses, singleBandMode, colorRamp, min, max; if (layerParams.symbologyState) { nClasses = layerParams.symbologyState.nClasses; @@ -66,18 +65,22 @@ const ColorRamp: React.FC = ({ colorRamp = layerParams.symbologyState.colorRamp; min = layerParams.symbologyState.minValue; max = layerParams.symbologyState.maxValue; - crit = layerParams.symbologyState.criticalValue; } setNumberOfShades(nClasses ? nClasses : '9'); setSelectedMode(singleBandMode ? singleBandMode : 'equal interval'); setSelectedRamp(colorRamp ? colorRamp : 'viridis'); setMinValue(min !== undefined ? min : -5); setMaxValue(max !== undefined ? max : 5); - setCriticalValue(crit !== undefined ? crit : 0); }; const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp as ColorRampName]; const rampType = rampDef?.type || 'Unknown'; + const normalizedCritical = + rampDef?.type === 'Divergent' ? (rampDef.criticalValue ?? 0.5) : 0.5; + const scaledCritical = + minValue !== undefined && maxValue !== undefined + ? minValue + normalizedCritical * (maxValue - minValue) + : undefined; return (
@@ -125,16 +128,10 @@ const ColorRamp: React.FC = ({ - setCriticalValue( - e.target.value !== '' - ? parseFloat(e.target.value) - : undefined, - ) - } + value={scaledCritical ?? ''} + readOnly className="jp-mod-styled" - placeholder="Enter critical value" + placeholder="Auto-calculated" />
@@ -168,7 +165,7 @@ const ColorRamp: React.FC = ({ numberOfShades, selectedRamp, setIsLoading, - criticalValue, + scaledCritical, minValue, maxValue, ) diff --git a/packages/base/src/tools.ts b/packages/base/src/tools.ts index fced6140d..6e1efee37 100644 --- a/packages/base/src/tools.ts +++ b/packages/base/src/tools.ts @@ -31,7 +31,9 @@ export const debounce = ( }; }; -export const objectKeys = Object.keys as >(obj: T) => Array; +export const objectKeys = Object.keys as >( + obj: T, +) => Array; export function throttle void>( callback: T, delay = 100, From 1af0c01f09a0222f6766886701a8e8b0ae949ff2 Mon Sep 17 00:00:00 2001 From: Nakul Date: Fri, 5 Sep 2025 22:21:58 +0530 Subject: [PATCH 06/73] support singleband psuedocolor --- .../components/color_ramp/ColorRamp.tsx | 94 +++++++------------ .../color_ramp/ColorRampValueControls.tsx | 61 ++++++++++++ .../dialogs/symbology/hooks/useGetBandInfo.ts | 4 +- .../types/SingleBandPseudoColor.tsx | 22 +++-- packages/base/src/mainview/mainView.tsx | 24 ++++- packages/base/src/types.ts | 8 ++ 6 files changed, 140 insertions(+), 73 deletions(-) create mode 100644 packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx 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 97508f045..c3701154c 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -2,13 +2,14 @@ import { IDict } from '@jupytergis/schema'; import { Button } from '@jupyterlab/ui-components'; import React, { useEffect, useState } from 'react'; -import { LoadingIcon } from '@/src/shared/components/loading'; -import CanvasSelectComponent from './CanvasSelectComponent'; -import ModeSelectRow from './ModeSelectRow'; import { COLOR_RAMP_DEFINITIONS, ColorRampName, -} from '../../../symbology/colorRampUtils'; +} from '@/src/dialogs/symbology/colorRampUtils'; +import { LoadingIcon } from '@/src/shared/components/loading'; +import CanvasSelectComponent from './CanvasSelectComponent'; +import { ColorRampValueControls } from './ColorRampValueControls'; +import ModeSelectRow from './ModeSelectRow'; interface IColorRampProps { modeOptions: string[]; layerParams: IDict; @@ -23,7 +24,7 @@ interface IColorRampProps { ) => void; showModeRow: boolean; showRampSelector: boolean; - renderType?: 'graduated' | 'categorized'; + renderType?: 'graduated' | 'categorized' | 'heatmap' | 'singleband'; } export type ColorRampOptions = { @@ -56,6 +57,24 @@ const ColorRamp: React.FC = ({ } }, [layerParams]); + useEffect(() => { + if (!layerParams.symbologyState) { + layerParams.symbologyState = {}; + } + + if (renderType !== 'heatmap') { + layerParams.symbologyState.min = minValue; + layerParams.symbologyState.max = maxValue; + layerParams.symbologyState.colorRamp = selectedRamp; + layerParams.symbologyState.nClasses = numberOfShades; + layerParams.symbologyState.mode = selectedMode; + + if (rampDef?.type === 'Divergent') { + layerParams.symbologyState.criticalValue = scaledCritical; + } + } + }, [minValue, maxValue, selectedRamp, selectedMode, numberOfShades]); + const populateOptions = () => { let nClasses, singleBandMode, colorRamp, min, max; @@ -63,8 +82,8 @@ const ColorRamp: React.FC = ({ nClasses = layerParams.symbologyState.nClasses; singleBandMode = layerParams.symbologyState.mode; colorRamp = layerParams.symbologyState.colorRamp; - min = layerParams.symbologyState.minValue; - max = layerParams.symbologyState.maxValue; + min = layerParams.symbologyState.min; + max = layerParams.symbologyState.max; } setNumberOfShades(nClasses ? nClasses : '9'); setSelectedMode(singleBandMode ? singleBandMode : 'equal interval'); @@ -74,7 +93,6 @@ const ColorRamp: React.FC = ({ }; const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp as ColorRampName]; - const rampType = rampDef?.type || 'Unknown'; const normalizedCritical = rampDef?.type === 'Divergent' ? (rampDef.criticalValue ?? 0.5) : 0.5; const scaledCritical = @@ -102,58 +120,16 @@ const ColorRamp: React.FC = ({ setSelectedMode={setSelectedMode} /> )} - {/* 🔹 Divergent colormap controls */} - {renderType === 'graduated' && rampType === 'Divergent' && ( - <> -
- - - setMinValue( - e.target.value !== '' - ? parseFloat(e.target.value) - : undefined, - ) - } - className="jp-mod-styled" - placeholder="Enter min value" - /> -
- -
- - -
- -
- - - setMaxValue( - e.target.value !== '' - ? parseFloat(e.target.value) - : undefined, - ) - } - className="jp-mod-styled" - placeholder="Enter max value" - /> -
- + {rampDef && ( + )} + {isLoading ? ( ) : ( diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx new file mode 100644 index 000000000..1b72dee2d --- /dev/null +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx @@ -0,0 +1,61 @@ +import React from 'react'; + +import { IColorRampValueControlsProps } from '@/src/types'; + +export const ColorRampValueControls: React.FC = ({ + min, + setMin, + max, + setMax, + rampDef, +}) => { + return ( + <> +
+ + + setMin( + e.target.value !== '' ? parseFloat(e.target.value) : undefined, + ) + } + className="jp-mod-styled" + placeholder="Enter min value" + /> +
+ + {rampDef.type === 'Divergent' && ( +
+ + +
+ )} + +
+ + + setMax( + e.target.value !== '' ? parseFloat(e.target.value) : undefined, + ) + } + className="jp-mod-styled" + placeholder="Enter max value" + /> +
+ + ); +}; diff --git a/packages/base/src/dialogs/symbology/hooks/useGetBandInfo.ts b/packages/base/src/dialogs/symbology/hooks/useGetBandInfo.ts index fd1074eb3..a719f2d63 100644 --- a/packages/base/src/dialogs/symbology/hooks/useGetBandInfo.ts +++ b/packages/base/src/dialogs/symbology/hooks/useGetBandInfo.ts @@ -71,8 +71,8 @@ const useGetBandInfo = (model: IJupyterGISModel, layer: IJGISLayer) => { bandsArr.push({ band: i, stats: { - minimum: sourceInfo.min ?? 0, - maximum: sourceInfo.max ?? 100, + minimum: layer.parameters?.symbologyState?.min ?? 0, + maximum: layer.parameters?.symbologyState?.max ?? 100, }, }); } 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..a498a68d7 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 { ColorRampValueControls } from '../../components/color_ramp/ColorRampValueControls'; export type InterpolationType = 'discrete' | 'linear' | 'exact'; @@ -46,6 +47,8 @@ const SingleBandPseudoColor: React.FC = ({ const [layerState, setLayerState] = useState(); const [selectedBand, setSelectedBand] = useState(1); + const [minValue, setMinValue] = useState(); + const [maxValue, setMaxValue] = useState(); const [stopRows, setStopRows] = useState([]); const [selectedFunction, setSelectedFunction] = useState('linear'); @@ -172,14 +175,6 @@ const SingleBandPseudoColor: React.FC = ({ const isQuantile = colorRampOptionsRef.current?.selectedMode === 'quantile'; - const sourceInfo = source.parameters.urls[0]; - sourceInfo.min = bandRow.stats.minimum; - sourceInfo.max = bandRow.stats.maximum; - - source.parameters.urls[0] = sourceInfo; - - model.sharedModel.updateSource(sourceId, source); - // Update layer if (!layer.parameters) { return; @@ -254,6 +249,8 @@ const SingleBandPseudoColor: React.FC = ({ colorRamp: colorRampOptionsRef.current?.selectedRamp, nClasses: colorRampOptionsRef.current?.numberOfShades, mode: colorRampOptionsRef.current?.selectedMode, + min: minValue ?? bandRow.stats.minimum, + max: maxValue ?? bandRow.stats.maximum, }; layer.parameters.symbologyState = symbologyState; @@ -403,6 +400,15 @@ const SingleBandPseudoColor: React.FC = ({ + {bandRows.length > 0 && ( { if (isRemote) { return { ...addNoData(sourceInfo), - min: sourceInfo.min, - max: sourceInfo.max, url: sourceInfo.url, }; } else { @@ -785,8 +783,6 @@ export class MainView extends React.Component { }); return { ...addNoData(sourceInfo), - min: sourceInfo.min, - max: sourceInfo.max, geotiff, url: URL.createObjectURL(geotiff.file), }; @@ -794,6 +790,26 @@ export class MainView extends React.Component { }), ); + const layerId = this._sourceToLayerMap.get(id); + const layer = layerId + ? this._model.sharedModel.getLayer(layerId) + : undefined; + + if (layer && layer.parameters) { + if (!layer.parameters.symbologyState) { + layer.parameters.symbologyState = { + renderType: 'Singleband Pseudocolor', + min: sourceParameters.urls[0]?.min, + max: sourceParameters.urls[0]?.max, + }; + } else { + layer.parameters.symbologyState.min ??= + sourceParameters.urls[0]?.min; + layer.parameters.symbologyState.max ??= + sourceParameters.urls[0]?.max; + } + } + newSource = new GeoTIFFSource({ interpolate: sourceParameters.interpolate, sources, diff --git a/packages/base/src/types.ts b/packages/base/src/types.ts index e18b0959b..24f622ca5 100644 --- a/packages/base/src/types.ts +++ b/packages/base/src/types.ts @@ -82,3 +82,11 @@ export interface IColorMap { } export type ColorRampName = keyof typeof COLOR_RAMP_DEFINITIONS; + +export interface IColorRampValueControlsProps { + min: number | undefined; + setMin: (v: number | undefined) => void; + max: number | undefined; + setMax: (v: number | undefined) => void; + rampDef: IColorRampDefinition; +} From 19ed962ace9257a93f3b104a2f39ba7ee33d9129 Mon Sep 17 00:00:00 2001 From: Nakul Date: Mon, 8 Sep 2025 18:54:19 +0530 Subject: [PATCH 07/73] pre-populating values --- .../components/color_ramp/ColorRamp.tsx | 70 ++++++++++++++----- .../color_ramp/ColorRampValueControls.tsx | 20 +++++- .../src/dialogs/symbology/symbologyUtils.ts | 23 ++++-- .../vector_layer/types/Categorized.tsx | 40 +++++++++-- .../vector_layer/types/Graduated.tsx | 17 ++++- packages/base/src/types.ts | 9 ++- 6 files changed, 146 insertions(+), 33 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 c3701154c..aecd61a60 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -24,13 +24,19 @@ interface IColorRampProps { ) => void; showModeRow: boolean; showRampSelector: boolean; - renderType?: 'graduated' | 'categorized' | 'heatmap' | 'singleband'; + renderType?: + | 'Graduated' + | 'Categorized' + | 'Heatmap' + | 'Singleband PseudoColor'; + initialMin?: number; + initialMax?: number; } export type ColorRampOptions = { - selectedRamp: string; - numberOfShades: string; - selectedMode: string; + selectedRamp?: string; + numberOfShades?: string; + selectedMode?: string; minValue?: number; maxValue?: number; criticalValue?: number; @@ -43,12 +49,14 @@ const ColorRamp: React.FC = ({ showModeRow, showRampSelector, renderType, + initialMin, + initialMax, }) => { const [selectedRamp, setSelectedRamp] = useState(''); const [selectedMode, setSelectedMode] = useState(''); const [numberOfShades, setNumberOfShades] = useState(''); - const [minValue, setMinValue] = useState(-5); - const [maxValue, setMaxValue] = useState(5); + const [minValue, setMinValue] = useState(initialMin); + const [maxValue, setMaxValue] = useState(initialMax); const [isLoading, setIsLoading] = useState(false); useEffect(() => { @@ -62,7 +70,7 @@ const ColorRamp: React.FC = ({ layerParams.symbologyState = {}; } - if (renderType !== 'heatmap') { + if (renderType !== 'Heatmap') { layerParams.symbologyState.min = minValue; layerParams.symbologyState.max = maxValue; layerParams.symbologyState.colorRamp = selectedRamp; @@ -75,21 +83,30 @@ const ColorRamp: React.FC = ({ } }, [minValue, maxValue, selectedRamp, selectedMode, numberOfShades]); + useEffect(() => { + if (renderType === 'Graduated') { + if (initialMin !== undefined) { + setMinValue(initialMin); + } + if (initialMax !== undefined) { + setMaxValue(initialMax); + } + } + }, [initialMin, initialMax, renderType]); + const populateOptions = () => { - let nClasses, singleBandMode, colorRamp, min, max; + let nClasses, singleBandMode, colorRamp; if (layerParams.symbologyState) { nClasses = layerParams.symbologyState.nClasses; singleBandMode = layerParams.symbologyState.mode; colorRamp = layerParams.symbologyState.colorRamp; - min = layerParams.symbologyState.min; - max = layerParams.symbologyState.max; } setNumberOfShades(nClasses ? nClasses : '9'); setSelectedMode(singleBandMode ? singleBandMode : 'equal interval'); setSelectedRamp(colorRamp ? colorRamp : 'viridis'); - setMinValue(min !== undefined ? min : -5); - setMaxValue(max !== undefined ? max : 5); + setMinValue(initialMin); + setMaxValue(initialMax); }; const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp as ColorRampName]; @@ -100,6 +117,23 @@ const ColorRamp: React.FC = ({ ? minValue + normalizedCritical * (maxValue - minValue) : undefined; + let displayMin = minValue; + let displayMax = maxValue; + + if ( + rampDef?.type === 'Divergent' && + renderType === 'Graduated' && + displayMin !== undefined && + displayMax !== undefined + ) { + const absMax = Math.max( + minValue ?? Math.abs(displayMin), + maxValue ?? Math.abs(displayMax), + ); + displayMin = -absMax; + displayMax = absMax; + } + return (
{showRampSelector && ( @@ -122,11 +156,14 @@ const ColorRamp: React.FC = ({ )} {rampDef && ( )} @@ -135,6 +172,7 @@ const ColorRamp: React.FC = ({ ) : (
- {rampDef.type === 'Divergent' && ( + {rampDef.type === 'Divergent' && renderType === 'Graduated' && (
= ({ placeholder="Enter max value" />
+ { +
+ +
+ } ); }; diff --git a/packages/base/src/dialogs/symbology/symbologyUtils.ts b/packages/base/src/dialogs/symbology/symbologyUtils.ts index 38618c470..8753433cf 100644 --- a/packages/base/src/dialogs/symbology/symbologyUtils.ts +++ b/packages/base/src/dialogs/symbology/symbologyUtils.ts @@ -105,27 +105,36 @@ export namespace Utils { selectedRamp: string, nClasses: number, reverse = false, - renderType: 'categorized' | 'graduated' = 'graduated', + renderType: + | 'Categorized' + | 'Graduated' + | 'Heatmap' + | 'Singleband PseudoColor' = 'Graduated', minValue?: number, maxValue?: number, ) => { const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp as ColorRampName]; let effectiveStops: number[] = []; - if (renderType === 'categorized') { + if (renderType === 'Categorized') { effectiveStops = stops; } else { - if (rampDef?.type === 'Divergent') { - const min = minValue ?? Math.min(...stops); - const max = maxValue ?? Math.max(...stops); + if (rampDef?.type === 'Divergent' && renderType === 'Graduated') { + const rawMin = minValue ?? Math.min(...stops); + const rawMax = maxValue ?? Math.max(...stops); + + const absMax = Math.max(Math.abs(rawMin), Math.abs(rawMax)); + + const min = -absMax; + const max = absMax; effectiveStops = Array.from( { length: nClasses }, (_, i) => min + (i / (nClasses - 1)) * (max - min), ); } else { - const min = Math.min(...stops); - const max = Math.max(...stops); + const min = minValue ?? Math.min(...stops); + const max = maxValue ?? Math.max(...stops); effectiveStops = Array.from( { length: nClasses }, 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 82b53d3c1..1402d30d6 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx @@ -1,9 +1,10 @@ import { IVectorLayer } from '@jupytergis/schema'; -import { ReadonlyJSONObject } from '@lumino/coreutils'; import { ExpressionValue } from 'ol/expr/expression'; import React, { useEffect, useRef, useState } from 'react'; -import ColorRamp from '@/src/dialogs/symbology/components/color_ramp/ColorRamp'; +import ColorRamp, { + ColorRampOptions, +} from '@/src/dialogs/symbology/components/color_ramp/ColorRamp'; import StopContainer from '@/src/dialogs/symbology/components/color_stops/StopContainer'; import { IStopRow, @@ -24,12 +25,12 @@ const Categorized: React.FC = ({ }) => { const selectedAttributeRef = useRef(); const stopRowsRef = useRef(); - const colorRampOptionsRef = useRef(); + const colorRampOptionsRef = useRef(); const [selectedAttribute, setSelectedAttribute] = useState(''); const [stopRows, setStopRows] = useState([]); const [colorRampOptions, setColorRampOptions] = useState< - ReadonlyJSONObject | undefined + ColorRampOptions | undefined >(); const [manualStyle, setManualStyle] = useState({ fillColor: '#3399CC', @@ -48,6 +49,22 @@ const Categorized: React.FC = ({ return; } + const getInitialAttribute = () => { + const layerParams = layer.parameters as IVectorLayer; + return ( + layerParams.symbologyState?.value ?? + Object.keys(selectableAttributesAndValues)[0] + ); + }; + const initialAttribute = getInitialAttribute(); + const initialValues = Array.from( + selectableAttributesAndValues[initialAttribute] ?? [], + ); + const computedInitialMin = + initialValues.length > 0 ? Math.min(...initialValues) : undefined; + const computedInitialMax = + initialValues.length > 0 ? Math.max(...initialValues) : undefined; + useEffect(() => { const valueColorPairs = VectorUtils.buildColorInfo(layer); @@ -107,6 +124,13 @@ const Categorized: React.FC = ({ layerParams.symbologyState?.value ?? Object.keys(selectableAttributesAndValues)[0]; + if (computedInitialMin !== undefined && computedInitialMax !== undefined) { + setColorRampOptions(prev => ({ + ...prev, + minValue: computedInitialMin, + maxValue: computedInitialMax, + })); + } setSelectedAttribute(attribute); }, [selectableAttributesAndValues]); @@ -123,10 +147,11 @@ const Categorized: React.FC = ({ setIsLoading: (isLoading: boolean) => void, ) => { setColorRampOptions({ - selectedFunction: '', selectedRamp, numberOfShades: '', selectedMode: '', + minValue: computedInitialMin, + maxValue: computedInitialMax, }); const stops = Array.from( @@ -138,7 +163,7 @@ const Categorized: React.FC = ({ selectedRamp, stops.length, reverseRamp, - 'categorized', + 'Categorized', ); setStopRows(valueColorPairs); @@ -337,6 +362,9 @@ const Categorized: React.FC = ({ classifyFunc={buildColorInfoFromClassification} showModeRow={false} showRampSelector={symbologyTab === 'color'} + renderType="Categorized" + initialMin={computedInitialMin} + initialMax={computedInitialMax} /> = ({ Object.keys(selectableAttributesAndValues)[0]; setSelectedAttribute(attribute); + + const values = Array.from(selectableAttributesAndValues[attribute] ?? []); + if (values.length > 0) { + const min = Math.min(...values); + const max = Math.max(...values); + setColorRampOptions(prev => ({ + ...prev, + minValue: min, + maxValue: max, + })); + } }, [selectableAttributesAndValues]); const updateStopRowsBasedOnLayer = () => { @@ -280,7 +291,7 @@ const Graduated: React.FC = ({ selectedRamp, +numberOfShades, reverseRamp, - 'graduated', + 'Graduated', minValue, maxValue, ); @@ -420,7 +431,9 @@ const Graduated: React.FC = ({ classifyFunc={buildColorInfoFromClassification} showModeRow={true} showRampSelector={symbologyTab === 'color'} - renderType="graduated" + renderType="Graduated" + initialMin={colorRampOptions?.minValue} + initialMax={colorRampOptions?.maxValue} /> = T[keyof T]; @@ -89,4 +89,11 @@ export interface IColorRampValueControlsProps { max: number | undefined; setMax: (v: number | undefined) => void; rampDef: IColorRampDefinition; + initialMin?: number; + initialMax?: number; + renderType?: + | 'Categorized' + | 'Graduated' + | 'Heatmap' + | 'Singleband PseudoColor'; } From dbeb7804f4ec61ddd4cb15cc645aa65c50954d22 Mon Sep 17 00:00:00 2001 From: Nakul Date: Tue, 9 Sep 2025 15:56:09 +0530 Subject: [PATCH 08/73] making radius work wit min and max --- .../symbology/vector_layer/types/Graduated.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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 af8797081..0281150a7 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx @@ -285,7 +285,16 @@ const Graduated: React.FC = ({ const stopOutputPairs = symbologyTab === 'radius' - ? stops.map(v => ({ stop: v, output: v })) + ? stops.map(v => { + const scaled = + minValue !== undefined && maxValue !== undefined + ? minValue + + ((v - Math.min(...stops)) / + (Math.max(...stops) - Math.min(...stops))) * + (maxValue - minValue) + : v; + return { stop: scaled, output: scaled }; + }) : Utils.getValueColorPairs( stops, selectedRamp, From c1fbede50928da048f6d40d765f9c729d71a6cd6 Mon Sep 17 00:00:00 2001 From: Nakul Date: Wed, 10 Sep 2025 20:53:49 +0530 Subject: [PATCH 09/73] gives colors a sequance --- packages/base/src/dialogs/symbology/colorRampUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/base/src/dialogs/symbology/colorRampUtils.ts b/packages/base/src/dialogs/symbology/colorRampUtils.ts index 7be062149..104afd384 100644 --- a/packages/base/src/dialogs/symbology/colorRampUtils.ts +++ b/packages/base/src/dialogs/symbology/colorRampUtils.ts @@ -11,8 +11,10 @@ const { __license__: _, ...cmocean } = rawCmocean; Object.assign(colorScale, cmocean); export const COLOR_RAMP_DEFINITIONS = { + // 'rainbow-soft': {type: 'Cyclic'}, 11 steps min + // 'hsv': {type: 'Cyclic'}, 11 steps min + phase: { type: 'Cyclic' }, jet: { type: 'Sequential' }, - // 'hsv': {type: 'Sequential'}, 11 steps min hot: { type: 'Sequential' }, cool: { type: 'Sequential' }, spring: { type: 'Sequential' }, @@ -27,7 +29,6 @@ export const COLOR_RAMP_DEFINITIONS = { YiOrRd: { type: 'Sequential' }, bluered: { type: 'Sequential' }, RdBu: { type: 'Sequential' }, - // 'picnic': {type: 'Sequential'}, 11 steps min rainbow: { type: 'Sequential' }, portland: { type: 'Sequential' }, blackbody: { type: 'Sequential' }, @@ -38,7 +39,6 @@ export const COLOR_RAMP_DEFINITIONS = { magma: { type: 'Sequential' }, plasma: { type: 'Sequential' }, warm: { type: 'Sequential' }, - // 'rainbow-soft': {type: 'Sequential'}, 11 steps min bathymetry: { type: 'Sequential' }, cdom: { type: 'Sequential' }, chlorophyll: { type: 'Sequential' }, @@ -47,7 +47,6 @@ export const COLOR_RAMP_DEFINITIONS = { 'freesurface-red': { type: 'Sequential' }, oxygen: { type: 'Sequential' }, par: { type: 'Sequential' }, - phase: { type: 'Cyclic' }, salinity: { type: 'Sequential' }, temperature: { type: 'Sequential' }, turbidity: { type: 'Sequential' }, @@ -61,6 +60,7 @@ export const COLOR_RAMP_DEFINITIONS = { tempo: { type: 'Sequential' }, rain: { type: 'Sequential' }, topo: { type: 'Sequential' }, + // 'picnic': {type: 'Divergent', criticalValue: 0.5 }, 11 steps min balance: { type: 'Divergent', criticalValue: 0.5 }, delta: { type: 'Divergent', criticalValue: 0.5 }, curl: { type: 'Divergent', criticalValue: 0.5 }, From b478a85e3f6ab83e4ed27683afcdbc6da274553c Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Fri, 12 Sep 2025 09:26:05 -0600 Subject: [PATCH 10/73] Simplify call to useColorMapList effect Co-authored-by: Nakul Verma --- .../src/dialogs/symbology/colorRampUtils.ts | 2 +- .../color_ramp/CanvasSelectComponent.tsx | 21 +++---------------- .../components/color_ramp/ColorRampEntry.tsx | 4 ++-- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/packages/base/src/dialogs/symbology/colorRampUtils.ts b/packages/base/src/dialogs/symbology/colorRampUtils.ts index 104afd384..629a13ab4 100644 --- a/packages/base/src/dialogs/symbology/colorRampUtils.ts +++ b/packages/base/src/dialogs/symbology/colorRampUtils.ts @@ -91,7 +91,7 @@ export const getColorMapList = (): IColorMap[] => { /** * Hook that loads and sets color maps. */ -export const useColorMapList = (setColorMaps: (maps: IColorMap[]) => void) => { +export const useColorMapList = (setColorMaps: React.Dispatch>) => { useEffect(() => { setColorMaps(getColorMapList()); }, [setColorMaps]); diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx index 8bdd3c9b3..c4fa65141 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx @@ -1,18 +1,9 @@ import { Button } from '@jupyterlab/ui-components'; import React, { useEffect, useRef, useState } from 'react'; -import { - useColorMapList, - COLOR_RAMP_DEFINITIONS, - ColorRampName, -} from '@/src/dialogs/symbology/colorRampUtils'; +import { useColorMapList } from '@/src/dialogs/symbology/colorRampUtils'; import ColorRampEntry from './ColorRampEntry'; - -export interface IColorMap { - name: string; - colors: string[]; - type?: string; -} +import { IColorMap } from '@/src/types'; interface ICanvasSelectComponentProps { selectedRamp: string; @@ -27,13 +18,7 @@ const CanvasSelectComponent: React.FC = ({ const [isOpen, setIsOpen] = useState(false); const [colorMaps, setColorMaps] = useState([]); - useColorMapList((maps: IColorMap[]) => { - const withTypes = maps.map(m => { - const def = COLOR_RAMP_DEFINITIONS[m.name as ColorRampName]; - return { ...m, type: def?.type ?? 'Unknown' }; - }); - setColorMaps(withTypes); - }); + useColorMapList(setColorMaps); useEffect(() => { if (colorMaps.length > 0) { diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampEntry.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampEntry.tsx index d1216bb5c..9777415fd 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampEntry.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampEntry.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react'; -import { IColorMap } from './CanvasSelectComponent'; +import { IColorMap } from '@/src/types'; interface IColorRampEntryProps { index: number; @@ -47,7 +47,7 @@ const ColorRampEntry: React.FC = ({ - ({colorMap.type}) + ({colorMap.definition.type}) Date: Fri, 12 Sep 2025 09:57:14 -0600 Subject: [PATCH 11/73] Remove type assertion Co-authored-by: Nakul Verma --- .../components/color_ramp/ColorRamp.tsx | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 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 aecd61a60..d1b80f130 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -52,7 +52,7 @@ const ColorRamp: React.FC = ({ initialMin, initialMax, }) => { - const [selectedRamp, setSelectedRamp] = useState(''); + const [selectedRamp, setSelectedRamp] = useState(); const [selectedMode, setSelectedMode] = useState(''); const [numberOfShades, setNumberOfShades] = useState(''); const [minValue, setMinValue] = useState(initialMin); @@ -60,7 +60,7 @@ const ColorRamp: React.FC = ({ const [isLoading, setIsLoading] = useState(false); useEffect(() => { - if (selectedRamp === '' && selectedMode === '' && numberOfShades === '') { + if (selectedRamp === undefined && selectedMode === '' && numberOfShades === '') { populateOptions(); } }, [layerParams]); @@ -109,7 +109,12 @@ const ColorRamp: React.FC = ({ setMaxValue(initialMax); }; - const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp as ColorRampName]; + if (selectedRamp === undefined) { + // This should be set at this point! + return; + } + const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp]; + const normalizedCritical = rampDef?.type === 'Divergent' ? (rampDef.criticalValue ?? 0.5) : 0.5; const scaledCritical = @@ -154,18 +159,17 @@ const ColorRamp: React.FC = ({ setSelectedMode={setSelectedMode} /> )} - {rampDef && ( - - )} + + {isLoading ? ( From 4c86aaf12a99e2337b203a7e1eeb76a5d9931308 Mon Sep 17 00:00:00 2001 From: Nakul Date: Sun, 14 Sep 2025 15:08:39 +0530 Subject: [PATCH 12/73] Update selectedRamp state initialization --- .../src/dialogs/symbology/colorRampUtils.ts | 4 +- .../color_ramp/CanvasSelectComponent.tsx | 4 +- .../components/color_ramp/ColorRamp.tsx | 44 +++++++++---------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/packages/base/src/dialogs/symbology/colorRampUtils.ts b/packages/base/src/dialogs/symbology/colorRampUtils.ts index 629a13ab4..86eddc9d5 100644 --- a/packages/base/src/dialogs/symbology/colorRampUtils.ts +++ b/packages/base/src/dialogs/symbology/colorRampUtils.ts @@ -91,7 +91,9 @@ export const getColorMapList = (): IColorMap[] => { /** * Hook that loads and sets color maps. */ -export const useColorMapList = (setColorMaps: React.Dispatch>) => { +export const useColorMapList = ( + setColorMaps: React.Dispatch>, +) => { useEffect(() => { setColorMaps(getColorMapList()); }, [setColorMaps]); diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx index c4fa65141..380ab5b27 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx @@ -2,8 +2,8 @@ import { Button } from '@jupyterlab/ui-components'; import React, { useEffect, useRef, useState } from 'react'; import { useColorMapList } from '@/src/dialogs/symbology/colorRampUtils'; -import ColorRampEntry from './ColorRampEntry'; import { IColorMap } from '@/src/types'; +import ColorRampEntry from './ColorRampEntry'; interface ICanvasSelectComponentProps { selectedRamp: string; @@ -24,7 +24,7 @@ const CanvasSelectComponent: React.FC = ({ if (colorMaps.length > 0) { updateCanvas(selectedRamp); } - }, [selectedRamp]); + }, [selectedRamp, colorMaps]); const toggleDropdown = () => { setIsOpen(!isOpen); 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 d1b80f130..e692e57d8 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -52,7 +52,7 @@ const ColorRamp: React.FC = ({ initialMin, initialMax, }) => { - const [selectedRamp, setSelectedRamp] = useState(); + const [selectedRamp, setSelectedRamp] = useState('viridis'); const [selectedMode, setSelectedMode] = useState(''); const [numberOfShades, setNumberOfShades] = useState(''); const [minValue, setMinValue] = useState(initialMin); @@ -60,29 +60,11 @@ const ColorRamp: React.FC = ({ const [isLoading, setIsLoading] = useState(false); useEffect(() => { - if (selectedRamp === undefined && selectedMode === '' && numberOfShades === '') { + if (selectedRamp && selectedMode === '' && numberOfShades === '') { populateOptions(); } }, [layerParams]); - useEffect(() => { - if (!layerParams.symbologyState) { - layerParams.symbologyState = {}; - } - - if (renderType !== 'Heatmap') { - layerParams.symbologyState.min = minValue; - layerParams.symbologyState.max = maxValue; - layerParams.symbologyState.colorRamp = selectedRamp; - layerParams.symbologyState.nClasses = numberOfShades; - layerParams.symbologyState.mode = selectedMode; - - if (rampDef?.type === 'Divergent') { - layerParams.symbologyState.criticalValue = scaledCritical; - } - } - }, [minValue, maxValue, selectedRamp, selectedMode, numberOfShades]); - useEffect(() => { if (renderType === 'Graduated') { if (initialMin !== undefined) { @@ -109,10 +91,6 @@ const ColorRamp: React.FC = ({ setMaxValue(initialMax); }; - if (selectedRamp === undefined) { - // This should be set at this point! - return; - } const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp]; const normalizedCritical = @@ -139,6 +117,24 @@ const ColorRamp: React.FC = ({ displayMax = absMax; } + useEffect(() => { + if (!layerParams.symbologyState) { + layerParams.symbologyState = {}; + } + + if (renderType !== 'Heatmap') { + layerParams.symbologyState.min = minValue; + layerParams.symbologyState.max = maxValue; + layerParams.symbologyState.colorRamp = selectedRamp; + layerParams.symbologyState.nClasses = numberOfShades; + layerParams.symbologyState.mode = selectedMode; + + if (rampDef?.type === 'Divergent') { + layerParams.symbologyState.criticalValue = scaledCritical; + } + } + }, [minValue, maxValue, selectedRamp, selectedMode, numberOfShades]); + return (
{showRampSelector && ( From 8374c71b4449fe2a1a4cc44c85c4fd5e09b81502 Mon Sep 17 00:00:00 2001 From: Nakul Date: Mon, 15 Sep 2025 17:33:18 +0530 Subject: [PATCH 13/73] Refactor getColorMapList to use objectEntries for improved readability --- packages/base/src/dialogs/symbology/colorRampUtils.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/base/src/dialogs/symbology/colorRampUtils.ts b/packages/base/src/dialogs/symbology/colorRampUtils.ts index 86eddc9d5..c228b43b4 100644 --- a/packages/base/src/dialogs/symbology/colorRampUtils.ts +++ b/packages/base/src/dialogs/symbology/colorRampUtils.ts @@ -3,7 +3,7 @@ import colorScale from 'colormap/colorScale.js'; import { useEffect } from 'react'; import rawCmocean from '@/src/dialogs/symbology/components/color_ramp/cmocean.json'; -import { objectKeys } from '@/src/tools'; +import { objectEntries, objectKeys } from '@/src/tools'; import { IColorMap, IColorRampDefinition } from '@/src/types'; const { __license__: _, ...cmocean } = rawCmocean; @@ -74,16 +74,15 @@ export type ColorRampName = (typeof COLOR_RAMP_NAMES)[number]; export const getColorMapList = (): IColorMap[] => { const colorMapList: IColorMap[] = []; - COLOR_RAMP_NAMES.forEach(name => { - const definition = COLOR_RAMP_DEFINITIONS[name]; + for (const [name, definition] of objectEntries(COLOR_RAMP_DEFINITIONS)) { const colors = colormap({ - colormap: name, + colormap: name as string, nshades: 255, format: 'rgbaString', }); colorMapList.push({ name, colors, definition }); - }); + } return colorMapList; }; From c5735c436c876dd906a73a14fa71cdc032344525 Mon Sep 17 00:00:00 2001 From: Nakul Date: Tue, 16 Sep 2025 19:03:32 +0530 Subject: [PATCH 14/73] Refactor ColorRamp components to improve prop naming and structure; add rampNames utility --- .../src/dialogs/symbology/colorRampUtils.ts | 63 +------------------ .../components/color_ramp/ColorRamp.tsx | 30 ++++----- .../components/color_ramp/ColorRampEntry.tsx | 7 +-- .../color_ramp/ColorRampValueControls.tsx | 53 +++++++++------- .../base/src/dialogs/symbology/rampNames.ts | 59 +++++++++++++++++ .../src/dialogs/symbology/symbologyUtils.ts | 5 +- .../types/SingleBandPseudoColor.tsx | 8 +-- packages/base/src/types.ts | 17 +---- 8 files changed, 115 insertions(+), 127 deletions(-) create mode 100644 packages/base/src/dialogs/symbology/rampNames.ts diff --git a/packages/base/src/dialogs/symbology/colorRampUtils.ts b/packages/base/src/dialogs/symbology/colorRampUtils.ts index c228b43b4..97a084737 100644 --- a/packages/base/src/dialogs/symbology/colorRampUtils.ts +++ b/packages/base/src/dialogs/symbology/colorRampUtils.ts @@ -3,71 +3,14 @@ import colorScale from 'colormap/colorScale.js'; import { useEffect } from 'react'; import rawCmocean from '@/src/dialogs/symbology/components/color_ramp/cmocean.json'; +import { COLOR_RAMP_DEFINITIONS } from '@/src/dialogs/symbology/rampNames'; import { objectEntries, objectKeys } from '@/src/tools'; -import { IColorMap, IColorRampDefinition } from '@/src/types'; +import { IColorMap } from '@/src/types'; const { __license__: _, ...cmocean } = rawCmocean; Object.assign(colorScale, cmocean); -export const COLOR_RAMP_DEFINITIONS = { - // 'rainbow-soft': {type: 'Cyclic'}, 11 steps min - // 'hsv': {type: 'Cyclic'}, 11 steps min - phase: { type: 'Cyclic' }, - jet: { type: 'Sequential' }, - hot: { type: 'Sequential' }, - cool: { type: 'Sequential' }, - spring: { type: 'Sequential' }, - summer: { type: 'Sequential' }, - autumn: { type: 'Sequential' }, - winter: { type: 'Sequential' }, - bone: { type: 'Sequential' }, - copper: { type: 'Sequential' }, - greys: { type: 'Sequential' }, - YiGnBu: { type: 'Sequential' }, - greens: { type: 'Sequential' }, - YiOrRd: { type: 'Sequential' }, - bluered: { type: 'Sequential' }, - RdBu: { type: 'Sequential' }, - rainbow: { type: 'Sequential' }, - portland: { type: 'Sequential' }, - blackbody: { type: 'Sequential' }, - earth: { type: 'Sequential' }, - electric: { type: 'Sequential' }, - viridis: { type: 'Sequential' }, - inferno: { type: 'Sequential' }, - magma: { type: 'Sequential' }, - plasma: { type: 'Sequential' }, - warm: { type: 'Sequential' }, - bathymetry: { type: 'Sequential' }, - cdom: { type: 'Sequential' }, - chlorophyll: { type: 'Sequential' }, - density: { type: 'Sequential' }, - 'freesurface-blue': { type: 'Sequential' }, - 'freesurface-red': { type: 'Sequential' }, - oxygen: { type: 'Sequential' }, - par: { type: 'Sequential' }, - salinity: { type: 'Sequential' }, - temperature: { type: 'Sequential' }, - turbidity: { type: 'Sequential' }, - 'velocity-blue': { type: 'Sequential' }, - 'velocity-green': { type: 'Sequential' }, - // 'cubehelix': {type: 'Sequential'}, 16 steps min - ice: { type: 'Sequential' }, - oxy: { type: 'Sequential' }, - matter: { type: 'Sequential' }, - amp: { type: 'Sequential' }, - tempo: { type: 'Sequential' }, - rain: { type: 'Sequential' }, - topo: { type: 'Sequential' }, - // 'picnic': {type: 'Divergent', criticalValue: 0.5 }, 11 steps min - balance: { type: 'Divergent', criticalValue: 0.5 }, - delta: { type: 'Divergent', criticalValue: 0.5 }, - curl: { type: 'Divergent', criticalValue: 0.5 }, - diff: { type: 'Divergent', criticalValue: 0.5 }, - tarn: { type: 'Divergent', criticalValue: 0.5 }, -} as const satisfies { [key: string]: IColorRampDefinition }; - export const COLOR_RAMP_NAMES = objectKeys(COLOR_RAMP_DEFINITIONS); export type ColorRampName = (typeof COLOR_RAMP_NAMES)[number]; @@ -76,7 +19,7 @@ export const getColorMapList = (): IColorMap[] => { for (const [name, definition] of objectEntries(COLOR_RAMP_DEFINITIONS)) { const colors = colormap({ - colormap: name as string, + colormap: name, nshades: 255, format: 'rgbaString', }); 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 e692e57d8..70ae3acbf 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -2,10 +2,8 @@ import { IDict } from '@jupytergis/schema'; import { Button } from '@jupyterlab/ui-components'; import React, { useEffect, useState } from 'react'; -import { - COLOR_RAMP_DEFINITIONS, - ColorRampName, -} from '@/src/dialogs/symbology/colorRampUtils'; +import { ColorRampName } from '@/src/dialogs/symbology/colorRampUtils'; +import { COLOR_RAMP_DEFINITIONS } from '@/src/dialogs/symbology/rampNames'; import { LoadingIcon } from '@/src/shared/components/loading'; import CanvasSelectComponent from './CanvasSelectComponent'; import { ColorRampValueControls } from './ColorRampValueControls'; @@ -28,7 +26,7 @@ interface IColorRampProps { | 'Graduated' | 'Categorized' | 'Heatmap' - | 'Singleband PseudoColor'; + | 'Singleband Pseudocolor'; initialMin?: number; initialMax?: number; } @@ -60,8 +58,8 @@ const ColorRamp: React.FC = ({ const [isLoading, setIsLoading] = useState(false); useEffect(() => { - if (selectedRamp && selectedMode === '' && numberOfShades === '') { - populateOptions(); + if (selectedMode === '' && numberOfShades === '') { + initializeState(); } }, [layerParams]); @@ -76,7 +74,7 @@ const ColorRamp: React.FC = ({ } }, [initialMin, initialMax, renderType]); - const populateOptions = () => { + const initializeState = () => { let nClasses, singleBandMode, colorRamp; if (layerParams.symbologyState) { @@ -87,8 +85,6 @@ const ColorRamp: React.FC = ({ setNumberOfShades(nClasses ? nClasses : '9'); setSelectedMode(singleBandMode ? singleBandMode : 'equal interval'); setSelectedRamp(colorRamp ? colorRamp : 'viridis'); - setMinValue(initialMin); - setMaxValue(initialMax); }; const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp]; @@ -130,7 +126,7 @@ const ColorRamp: React.FC = ({ layerParams.symbologyState.mode = selectedMode; if (rampDef?.type === 'Divergent') { - layerParams.symbologyState.criticalValue = scaledCritical; + layerParams.symbologyState.criticalValue = rampDef.criticalValue; } } }, [minValue, maxValue, selectedRamp, selectedMode, numberOfShades]); @@ -157,13 +153,13 @@ const ColorRamp: React.FC = ({ )} diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampEntry.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampEntry.tsx index 9777415fd..d89cad9d4 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampEntry.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampEntry.tsx @@ -43,12 +43,7 @@ const ColorRampEntry: React.FC = ({ className="jp-gis-color-ramp-entry" > - {colorMap.name}{' '} - - ({colorMap.definition.type}) - + {colorMap.name} ({colorMap.definition.type}) void; + selectedMax: number | undefined; + settedMax: (v: number | undefined) => void; + rampDef: IColorRampDefinition; + dataMin?: number; + dataMax?: number; + renderType?: + | 'Categorized' + | 'Graduated' + | 'Heatmap' + | 'Singleband Pseudocolor'; +} export const ColorRampValueControls: React.FC = ({ - min, - setMin, - max, - setMax, + selectedMin, + settedMin, + selectedMax, + settedMax, rampDef, - initialMin, - initialMax, + dataMin, + dataMax, renderType, }) => { return ( @@ -20,9 +34,9 @@ export const ColorRampValueControls: React.FC = ({ - setMin( + settedMin( e.target.value !== '' ? parseFloat(e.target.value) : undefined, ) } @@ -34,14 +48,9 @@ export const ColorRampValueControls: React.FC = ({ {rampDef.type === 'Divergent' && renderType === 'Graduated' && (
- + + {rampDef.criticalValue ?? 'Auto-calculated'} +
)} @@ -50,9 +59,9 @@ export const ColorRampValueControls: React.FC = ({ - setMax( + settedMax( e.target.value !== '' ? parseFloat(e.target.value) : undefined, ) } @@ -64,10 +73,10 @@ export const ColorRampValueControls: React.FC = ({
= T[keyof T]; @@ -82,18 +82,3 @@ export interface IColorMap { } export type ColorRampName = keyof typeof COLOR_RAMP_DEFINITIONS; - -export interface IColorRampValueControlsProps { - min: number | undefined; - setMin: (v: number | undefined) => void; - max: number | undefined; - setMax: (v: number | undefined) => void; - rampDef: IColorRampDefinition; - initialMin?: number; - initialMax?: number; - renderType?: - | 'Categorized' - | 'Graduated' - | 'Heatmap' - | 'Singleband PseudoColor'; -} From fe3465ab2f0d132ee83ccdbd2ff0ce50141d7ce6 Mon Sep 17 00:00:00 2001 From: Nakul Date: Wed, 17 Sep 2025 16:15:34 +0530 Subject: [PATCH 15/73] Refactor ColorRamp type usage to improve type safety and consistency across components --- .../dialogs/symbology/classificationModes.ts | 19 +++++------- .../src/dialogs/symbology/colorRampUtils.ts | 1 - .../components/color_ramp/ColorRamp.tsx | 6 ++-- .../src/dialogs/symbology/symbologyUtils.ts | 30 +++++++++---------- .../types/SingleBandPseudoColor.tsx | 5 ++-- .../vector_layer/types/Categorized.tsx | 28 ++++++++--------- .../vector_layer/types/Graduated.tsx | 19 ++++++------ 7 files changed, 52 insertions(+), 56 deletions(-) diff --git a/packages/base/src/dialogs/symbology/classificationModes.ts b/packages/base/src/dialogs/symbology/classificationModes.ts index b4ab85989..651d93785 100644 --- a/packages/base/src/dialogs/symbology/classificationModes.ts +++ b/packages/base/src/dialogs/symbology/classificationModes.ts @@ -49,20 +49,17 @@ export namespace VectorClassifications { export const calculateEqualIntervalBreaks = ( values: number[], nClasses: number, + selectedMin?: number, + selectedMax?: number, ) => { - const minimum = Math.min(...values); - const maximum = Math.max(...values); - - const breaks: number[] = []; - const step = (maximum - minimum) / nClasses; - - let value = minimum; + const minimum = selectedMin ?? Math.min(...values); + const maximum = selectedMax ?? Math.max(...values); - for (let i = 0; i < nClasses; i++) { - value += step; - breaks.push(value); - } + const breaks: number[] = Array.from({ length: nClasses }, (_, i) => { + return minimum + (i / (nClasses - 1)) * (maximum - minimum); + }); + breaks[0] = minimum; breaks[nClasses - 1] = maximum; return breaks; diff --git a/packages/base/src/dialogs/symbology/colorRampUtils.ts b/packages/base/src/dialogs/symbology/colorRampUtils.ts index 97a084737..2190af7f5 100644 --- a/packages/base/src/dialogs/symbology/colorRampUtils.ts +++ b/packages/base/src/dialogs/symbology/colorRampUtils.ts @@ -12,7 +12,6 @@ const { __license__: _, ...cmocean } = rawCmocean; Object.assign(colorScale, cmocean); export const COLOR_RAMP_NAMES = objectKeys(COLOR_RAMP_DEFINITIONS); -export type ColorRampName = (typeof COLOR_RAMP_NAMES)[number]; export const getColorMapList = (): IColorMap[] => { const colorMapList: IColorMap[] = []; 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 70ae3acbf..16cb2c4ad 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -2,9 +2,9 @@ import { IDict } from '@jupytergis/schema'; import { Button } from '@jupyterlab/ui-components'; import React, { useEffect, useState } from 'react'; -import { ColorRampName } from '@/src/dialogs/symbology/colorRampUtils'; import { COLOR_RAMP_DEFINITIONS } from '@/src/dialogs/symbology/rampNames'; import { LoadingIcon } from '@/src/shared/components/loading'; +import { ColorRampName } from '@/src/types'; import CanvasSelectComponent from './CanvasSelectComponent'; import { ColorRampValueControls } from './ColorRampValueControls'; import ModeSelectRow from './ModeSelectRow'; @@ -14,7 +14,7 @@ interface IColorRampProps { classifyFunc: ( selectedMode: string, numberOfShades: string, - selectedRamp: string, + selectedRamp: ColorRampName, setIsLoading: (isLoading: boolean) => void, criticalValue?: number, minValue?: number, @@ -32,7 +32,7 @@ interface IColorRampProps { } export type ColorRampOptions = { - selectedRamp?: string; + selectedRamp: ColorRampName; numberOfShades?: string; selectedMode?: string; minValue?: number; diff --git a/packages/base/src/dialogs/symbology/symbologyUtils.ts b/packages/base/src/dialogs/symbology/symbologyUtils.ts index 4fddf7eb0..071d42dd4 100644 --- a/packages/base/src/dialogs/symbology/symbologyUtils.ts +++ b/packages/base/src/dialogs/symbology/symbologyUtils.ts @@ -1,7 +1,8 @@ import { IJGISLayer } from '@jupytergis/schema'; import colormap from 'colormap'; -import { ColorRampName } from './colorRampUtils'; +import { ColorRampName } from '@/src/types'; +import { VectorClassifications } from './classificationModes'; import { COLOR_RAMP_DEFINITIONS } from './rampNames'; import { IStopRow } from './symbologyDialog'; @@ -103,7 +104,7 @@ export namespace VectorUtils { export namespace Utils { export const getValueColorPairs = ( stops: number[], - selectedRamp: string, + selectedRamp: ColorRampName, nClasses: number, reverse = false, renderType: @@ -114,7 +115,7 @@ export namespace Utils { minValue?: number, maxValue?: number, ) => { - const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp as ColorRampName]; + const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp]; let effectiveStops: number[] = []; if (renderType === 'Categorized') { @@ -123,23 +124,20 @@ export namespace Utils { if (rampDef?.type === 'Divergent' && renderType === 'Graduated') { const rawMin = minValue ?? Math.min(...stops); const rawMax = maxValue ?? Math.max(...stops); - const absMax = Math.max(Math.abs(rawMin), Math.abs(rawMax)); - const min = -absMax; - const max = absMax; - - effectiveStops = Array.from( - { length: nClasses }, - (_, i) => min + (i / (nClasses - 1)) * (max - min), + effectiveStops = VectorClassifications.calculateEqualIntervalBreaks( + stops, + nClasses, + -absMax, + absMax, ); } else { - const min = minValue ?? Math.min(...stops); - const max = maxValue ?? Math.max(...stops); - - effectiveStops = Array.from( - { length: nClasses }, - (_, i) => min + (i / (nClasses - 1)) * (max - min), + effectiveStops = VectorClassifications.calculateEqualIntervalBreaks( + stops, + nClasses, + minValue, + maxValue, ); } } 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 f3edb59a9..d4aad2e45 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 { ColorRampName } from '@/src/types'; import { ColorRampValueControls } from '../../components/color_ramp/ColorRampValueControls'; export type InterpolationType = 'discrete' | 'linear' | 'exact'; @@ -281,7 +282,7 @@ const SingleBandPseudoColor: React.FC = ({ const buildColorInfoFromClassification = async ( selectedMode: string, numberOfShades: string, - selectedRamp: string, + selectedRamp: ColorRampName, setIsLoading: (isLoading: boolean) => void, ) => { // Update layer state with selected options @@ -332,7 +333,7 @@ const SingleBandPseudoColor: React.FC = ({ const valueColorPairs = Utils.getValueColorPairs( stops, - selectedRamp, + selectedRamp as ColorRampName, nClasses, ); 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 1402d30d6..cffe5b5ae 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx @@ -12,7 +12,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 { ColorRampName, SymbologyTab } from '@/src/types'; const Categorized: React.FC = ({ model, @@ -25,28 +25,28 @@ const Categorized: React.FC = ({ }) => { const selectedAttributeRef = useRef(); const stopRowsRef = useRef(); - const colorRampOptionsRef = useRef(); const [selectedAttribute, setSelectedAttribute] = useState(''); const [stopRows, setStopRows] = useState([]); - const [colorRampOptions, setColorRampOptions] = useState< - ColorRampOptions | undefined - >(); + const [colorRampOptions, setColorRampOptions] = useState({ + selectedRamp: 'viridis', + }); const [manualStyle, setManualStyle] = useState({ fillColor: '#3399CC', strokeColor: '#3399CC', strokeWidth: 1.25, radius: 5, }); + const colorRampOptionsRef = useRef(colorRampOptions); const manualStyleRef = useRef(manualStyle); const [reverseRamp, setReverseRamp] = useState(false); if (!layerId) { - return; + return null; } const layer = model.getLayer(layerId); if (!layer?.parameters) { - return; + return null; } const getInitialAttribute = () => { @@ -143,7 +143,7 @@ const Categorized: React.FC = ({ const buildColorInfoFromClassification = ( selectedMode: string, numberOfShades: string, - selectedRamp: string, + selectedRamp: ColorRampName, setIsLoading: (isLoading: boolean) => void, ) => { setColorRampOptions({ @@ -206,9 +206,9 @@ const Categorized: React.FC = ({ const symbologyState = { renderType: 'Categorized', value: selectedAttributeRef.current, - colorRamp: colorRampOptionsRef.current?.selectedRamp, - nClasses: colorRampOptionsRef.current?.numberOfShades, - mode: colorRampOptionsRef.current?.selectedMode, + colorRamp: colorRampOptionsRef.current.selectedRamp, + nClasses: colorRampOptionsRef.current.numberOfShades, + mode: colorRampOptionsRef.current.selectedMode, symbologyTab, reverse: reverseRamp, }; @@ -242,9 +242,9 @@ const Categorized: React.FC = ({ // Reset color classification options if (layer.parameters.symbologyState) { - layer.parameters.symbologyState.colorRamp = undefined; - layer.parameters.symbologyState.nClasses = undefined; - layer.parameters.symbologyState.mode = undefined; + layer.parameters.symbologyState.colorRamp = 'viridis'; + layer.parameters.symbologyState.nClasses = ''; + layer.parameters.symbologyState.mode = ''; } } 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 0281150a7..ab72f886c 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 { ColorRampName } from '@/src/types'; const Graduated: React.FC = ({ model, @@ -35,14 +36,13 @@ const Graduated: React.FC = ({ const symbologyTabRef = useRef(); const colorStopRowsRef = useRef([]); const radiusStopRowsRef = useRef([]); - const colorRampOptionsRef = useRef(); const [selectedAttribute, setSelectedAttribute] = useState(''); const [colorStopRows, setColorStopRows] = useState([]); const [radiusStopRows, setRadiusStopRows] = useState([]); - const [colorRampOptions, setColorRampOptions] = useState< - ColorRampOptions | undefined - >(); + const [colorRampOptions, setColorRampOptions] = useState({ + selectedRamp: 'viridis', + }); const [colorManualStyle, setColorManualStyle] = useState({ fillColor: '#3399CC', strokeColor: '#3399CC', @@ -53,6 +53,7 @@ const Graduated: React.FC = ({ }); const [reverseRamp, setReverseRamp] = useState(false); + const colorRampOptionsRef = useRef(colorRampOptions); const colorManualStyleRef = useRef(colorManualStyle); const radiusManualStyleRef = useRef(radiusManualStyle); @@ -211,9 +212,9 @@ const Graduated: React.FC = ({ renderType: 'Graduated', value: selectableAttributeRef.current, method: symbologyTabRef.current, - colorRamp: colorRampOptionsRef.current?.selectedRamp, - nClasses: colorRampOptionsRef.current?.numberOfShades, - mode: colorRampOptionsRef.current?.selectedMode, + colorRamp: colorRampOptionsRef.current.selectedRamp, + nClasses: colorRampOptionsRef.current.numberOfShades, + mode: colorRampOptionsRef.current.selectedMode, reverse: reverseRamp, }; @@ -228,7 +229,7 @@ const Graduated: React.FC = ({ const buildColorInfoFromClassification = ( selectedMode: string, numberOfShades: string, - selectedRamp: string, + selectedRamp: ColorRampName, setIsLoading: (isLoading: boolean) => void, criticalValue?: number, minValue?: number, @@ -326,7 +327,7 @@ const Graduated: React.FC = ({ delete newStyle['stroke-color']; delete newStyle['circle-fill-color']; setColorStopRows([]); - setColorRampOptions(undefined); + setColorRampOptions({ selectedRamp: 'viridis' }); } if (method === 'radius') { From 5199cb4726b1b2616a29b9e3cf13f9bc07e016dc Mon Sep 17 00:00:00 2001 From: Nakul Date: Wed, 17 Sep 2025 16:19:35 +0530 Subject: [PATCH 16/73] Update ColorRampOptions to require numberOfShades and selectedMode for better consistency --- .../dialogs/symbology/components/color_ramp/ColorRamp.tsx | 4 ++-- .../dialogs/symbology/vector_layer/types/Categorized.tsx | 2 ++ .../src/dialogs/symbology/vector_layer/types/Graduated.tsx | 7 ++++++- 3 files changed, 10 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 16cb2c4ad..b1eff0e68 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -33,8 +33,8 @@ interface IColorRampProps { export type ColorRampOptions = { selectedRamp: ColorRampName; - numberOfShades?: string; - selectedMode?: string; + numberOfShades: string; + selectedMode: string; minValue?: number; maxValue?: number; criticalValue?: number; 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 cffe5b5ae..b5b0f884b 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx @@ -30,6 +30,8 @@ const Categorized: React.FC = ({ const [stopRows, setStopRows] = useState([]); const [colorRampOptions, setColorRampOptions] = useState({ selectedRamp: 'viridis', + numberOfShades: '', + selectedMode: '', }); const [manualStyle, setManualStyle] = useState({ fillColor: '#3399CC', 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 ab72f886c..53eb0da91 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx @@ -42,6 +42,8 @@ const Graduated: React.FC = ({ const [radiusStopRows, setRadiusStopRows] = useState([]); const [colorRampOptions, setColorRampOptions] = useState({ selectedRamp: 'viridis', + numberOfShades: '', + selectedMode: '', }); const [colorManualStyle, setColorManualStyle] = useState({ fillColor: '#3399CC', @@ -327,7 +329,10 @@ const Graduated: React.FC = ({ delete newStyle['stroke-color']; delete newStyle['circle-fill-color']; setColorStopRows([]); - setColorRampOptions({ selectedRamp: 'viridis' }); + setColorRampOptions({ selectedRamp: 'viridis', + numberOfShades: '', + selectedMode: '' + }); } if (method === 'radius') { From 63c1185c6c311e38a4102bdcfa831b7ed3a37155 Mon Sep 17 00:00:00 2001 From: Nakul Date: Wed, 17 Sep 2025 16:24:10 +0530 Subject: [PATCH 17/73] formatting --- .../src/dialogs/symbology/vector_layer/types/Graduated.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 53eb0da91..25059ab51 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx @@ -329,9 +329,10 @@ const Graduated: React.FC = ({ delete newStyle['stroke-color']; delete newStyle['circle-fill-color']; setColorStopRows([]); - setColorRampOptions({ selectedRamp: 'viridis', + setColorRampOptions({ + selectedRamp: 'viridis', numberOfShades: '', - selectedMode: '' + selectedMode: '', }); } From f9108cb6526d933001439276861ab8153a3d2f5e Mon Sep 17 00:00:00 2001 From: Nakul Date: Thu, 18 Sep 2025 18:34:37 +0530 Subject: [PATCH 18/73] Refactor ColorRamp and LegendItem component and critical value calculations; --- .../components/color_ramp/ColorRamp.tsx | 37 ++++++++----------- .../color_ramp/ColorRampValueControls.tsx | 30 ++++++++++----- .../src/dialogs/symbology/symbologyUtils.ts | 27 +++----------- .../src/panelview/components/legendItem.tsx | 34 +++++++++-------- 4 files changed, 60 insertions(+), 68 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 b1eff0e68..70f07874c 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -96,29 +96,14 @@ const ColorRamp: React.FC = ({ ? minValue + normalizedCritical * (maxValue - minValue) : undefined; - let displayMin = minValue; - let displayMax = maxValue; - - if ( - rampDef?.type === 'Divergent' && - renderType === 'Graduated' && - displayMin !== undefined && - displayMax !== undefined - ) { - const absMax = Math.max( - minValue ?? Math.abs(displayMin), - maxValue ?? Math.abs(displayMax), - ); - displayMin = -absMax; - displayMax = absMax; - } - useEffect(() => { if (!layerParams.symbologyState) { layerParams.symbologyState = {}; } if (renderType !== 'Heatmap') { + layerParams.symbologyState.dataMin = initialMin; + layerParams.symbologyState.dataMax = initialMax; layerParams.symbologyState.min = minValue; layerParams.symbologyState.max = maxValue; layerParams.symbologyState.colorRamp = selectedRamp; @@ -129,7 +114,15 @@ const ColorRamp: React.FC = ({ layerParams.symbologyState.criticalValue = rampDef.criticalValue; } } - }, [minValue, maxValue, selectedRamp, selectedMode, numberOfShades]); + }, [ + minValue, + maxValue, + selectedRamp, + selectedMode, + numberOfShades, + initialMin, + initialMax, + ]); return (
@@ -153,9 +146,9 @@ const ColorRamp: React.FC = ({ )} = ({ selectedRamp, setIsLoading, scaledCritical, - displayMin, - displayMax, + minValue, + maxValue, ) } > diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx index 73d2b2565..117500b9b 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx @@ -45,14 +45,24 @@ export const ColorRampValueControls: React.FC = ({ />
- {rampDef.type === 'Divergent' && renderType === 'Graduated' && ( -
- - - {rampDef.criticalValue ?? 'Auto-calculated'} - -
- )} + {rampDef.type === 'Divergent' && + renderType === 'Graduated' && + dataMin !== undefined && + dataMax !== undefined && ( +
+ + +
+ )}
@@ -75,8 +85,8 @@ export const ColorRampValueControls: React.FC = ({ className="jp-Dialog-button jp-mod-accept jp-mod-styled" disabled={selectedMin === dataMin && selectedMax === dataMax} onClick={() => { - settedMin(selectedMin); - settedMax(selectedMax); + settedMin(dataMin); + settedMax(dataMax); }} > Use Actual Range diff --git a/packages/base/src/dialogs/symbology/symbologyUtils.ts b/packages/base/src/dialogs/symbology/symbologyUtils.ts index 071d42dd4..0dc3e8774 100644 --- a/packages/base/src/dialogs/symbology/symbologyUtils.ts +++ b/packages/base/src/dialogs/symbology/symbologyUtils.ts @@ -3,7 +3,6 @@ import colormap from 'colormap'; import { ColorRampName } from '@/src/types'; import { VectorClassifications } from './classificationModes'; -import { COLOR_RAMP_DEFINITIONS } from './rampNames'; import { IStopRow } from './symbologyDialog'; const COLOR_EXPR_STOPS_START = 3; @@ -115,31 +114,17 @@ export namespace Utils { minValue?: number, maxValue?: number, ) => { - const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp]; let effectiveStops: number[] = []; if (renderType === 'Categorized') { effectiveStops = stops; } else { - if (rampDef?.type === 'Divergent' && renderType === 'Graduated') { - const rawMin = minValue ?? Math.min(...stops); - const rawMax = maxValue ?? Math.max(...stops); - const absMax = Math.max(Math.abs(rawMin), Math.abs(rawMax)); - - effectiveStops = VectorClassifications.calculateEqualIntervalBreaks( - stops, - nClasses, - -absMax, - absMax, - ); - } else { - effectiveStops = VectorClassifications.calculateEqualIntervalBreaks( - stops, - nClasses, - minValue, - maxValue, - ); - } + effectiveStops = VectorClassifications.calculateEqualIntervalBreaks( + stops, + nClasses, + minValue, + maxValue, + ); } let colorMap = colormap({ diff --git a/packages/base/src/panelview/components/legendItem.tsx b/packages/base/src/panelview/components/legendItem.tsx index cb99b2812..5183e8bcb 100644 --- a/packages/base/src/panelview/components/legendItem.tsx +++ b/packages/base/src/panelview/components/legendItem.tsx @@ -135,12 +135,15 @@ export const LegendItem: React.FC<{ .join(', '); const gradient = `linear-gradient(to right, ${segments})`; - const minValue = rampDef?.userMin ?? stops[0].value; - const maxValue = rampDef?.userMax ?? stops[stops.length - 1].value; - const criticalValue = - rampDef?.definition.type === 'Divergent' - ? (rampDef.criticalValue ?? 0.5) - : undefined; + const dataMin = symbology.symbologyState?.dataMin ?? stops[0].value; + const dataMax = + symbology.symbologyState?.dataMax ?? stops[stops.length - 1].value; + + let criticalValue: number | undefined = undefined; + if (rampDef?.definition.type === 'Divergent') { + const relativeCritical = rampDef.criticalValue ?? 0.5; + criticalValue = dataMin + relativeCritical * (dataMax - dataMin); + } const isDivergent = criticalValue !== undefined; setContent( @@ -204,6 +207,7 @@ export const LegendItem: React.FC<{ position: 'absolute', left: '0%', transform: 'translateX(0%)', + fontWeight: 'bold', }} >
- {minValue.toFixed(2)} + {dataMin.toFixed(2)}
@@ -227,6 +231,7 @@ export const LegendItem: React.FC<{ position: 'absolute', left: '100%', transform: 'translateX(-100%)', + fontWeight: 'bold', }} >
- {maxValue.toFixed(2)} + {dataMax.toFixed(2)}
{/* Critical */} - {isDivergent && ( + {isDivergent && criticalValue !== undefined && (
= maxValue + : criticalValue >= dataMax ? 100 - : ((criticalValue - minValue) / - (maxValue - minValue)) * + : ((criticalValue - dataMin) / + (dataMax - dataMin)) * 100 }%`, transform: 'translateX(-50%)', @@ -275,10 +280,9 @@ export const LegendItem: React.FC<{ top: -20, fontSize: '0.7em', fontWeight: 'bold', - color: 'red', }} > - {criticalValue.toFixed(2)} + {criticalValue.toFixed(1)}
)} From c643d523c9b9451a602e6beb1e1c0f36a66a6a5d Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Thu, 18 Sep 2025 09:28:59 -0600 Subject: [PATCH 19/73] Enable color ramp labels to display over light color ramps Co-authored-by: Nakul Verma --- packages/base/style/symbologyDialog.css | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/base/style/symbologyDialog.css b/packages/base/style/symbologyDialog.css index 557fb416c..a65f52cdf 100644 --- a/packages/base/style/symbologyDialog.css +++ b/packages/base/style/symbologyDialog.css @@ -183,6 +183,7 @@ select option { color: white; position: absolute; transition: transform 0.3s ease; + text-shadow: 0px 0px 4px black; } .jp-gis-color-ramp-entry:hover .jp-gis-color-label { From 437bcf050997dc711a6c6b946b10abf2fd622550 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Thu, 18 Sep 2025 09:44:53 -0600 Subject: [PATCH 20/73] Remove unnecessary cast Co-authored-by: Nakul Verma <173621577+nakul-py@users.noreply.github.com> --- .../symbology/tiff_layer/types/SingleBandPseudoColor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d4aad2e45..58770ba47 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx @@ -333,7 +333,7 @@ const SingleBandPseudoColor: React.FC = ({ const valueColorPairs = Utils.getValueColorPairs( stops, - selectedRamp as ColorRampName, + selectedRamp, nClasses, ); From 20ee9213eadbb6485bfa5977da0c50fdb1bc7a0a Mon Sep 17 00:00:00 2001 From: Nakul Date: Fri, 19 Sep 2025 19:54:11 +0530 Subject: [PATCH 21/73] making Critical value readonly and fixing colorRamp hover effect --- .../components/color_ramp/ColorRampValueControls.tsx | 12 ++++-------- packages/base/style/symbologyDialog.css | 2 ++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx index 117500b9b..54c4c5eb0 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx @@ -51,16 +51,12 @@ export const ColorRampValueControls: React.FC = ({ dataMax !== undefined && (
- + {`${( dataMin + (rampDef.criticalValue ?? 0.5) * (dataMax - dataMin) - ).toFixed(1)} (Colormap diverges at 50%)`} - readOnly - /> + ).toFixed(1)} (Colormap diverges at 50%)`} +
)} diff --git a/packages/base/style/symbologyDialog.css b/packages/base/style/symbologyDialog.css index a65f52cdf..148443148 100644 --- a/packages/base/style/symbologyDialog.css +++ b/packages/base/style/symbologyDialog.css @@ -184,6 +184,8 @@ select option { position: absolute; transition: transform 0.3s ease; text-shadow: 0px 0px 4px black; + transform: scale(1); + transform-origin: left bottom; } .jp-gis-color-ramp-entry:hover .jp-gis-color-label { From 422580b6cbe2868a3e845fbb41e697d010f16268 Mon Sep 17 00:00:00 2001 From: Nakul Date: Fri, 19 Sep 2025 20:22:28 +0530 Subject: [PATCH 22/73] using min and max instead of all values --- .../base/src/dialogs/symbology/classificationModes.ts | 9 ++++----- packages/base/src/dialogs/symbology/symbologyUtils.ts | 5 ++--- .../dialogs/symbology/vector_layer/types/Graduated.tsx | 3 ++- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/base/src/dialogs/symbology/classificationModes.ts b/packages/base/src/dialogs/symbology/classificationModes.ts index 651d93785..f4d555c81 100644 --- a/packages/base/src/dialogs/symbology/classificationModes.ts +++ b/packages/base/src/dialogs/symbology/classificationModes.ts @@ -47,13 +47,12 @@ export namespace VectorClassifications { }; export const calculateEqualIntervalBreaks = ( - values: number[], nClasses: number, - selectedMin?: number, - selectedMax?: number, + selectedMin: number, + selectedMax: number, ) => { - const minimum = selectedMin ?? Math.min(...values); - const maximum = selectedMax ?? Math.max(...values); + const minimum = selectedMin; + const maximum = selectedMax; const breaks: number[] = Array.from({ length: nClasses }, (_, i) => { return minimum + (i / (nClasses - 1)) * (maximum - minimum); diff --git a/packages/base/src/dialogs/symbology/symbologyUtils.ts b/packages/base/src/dialogs/symbology/symbologyUtils.ts index 0dc3e8774..90f9da54d 100644 --- a/packages/base/src/dialogs/symbology/symbologyUtils.ts +++ b/packages/base/src/dialogs/symbology/symbologyUtils.ts @@ -120,10 +120,9 @@ export namespace Utils { effectiveStops = stops; } else { effectiveStops = VectorClassifications.calculateEqualIntervalBreaks( - stops, nClasses, - minValue, - maxValue, + minValue ?? 0.95, + maxValue ?? 8.1, ); } 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 25059ab51..45480b824 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx @@ -259,8 +259,9 @@ const Graduated: React.FC = ({ break; case 'equal interval': stops = VectorClassifications.calculateEqualIntervalBreaks( - values, +numberOfShades, + minValue ?? 0.95, + maxValue ?? 8.1, ); break; case 'jenks': From 60e643fd8cf9dff54f989a0a04b3fc608b3ace3a Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Fri, 19 Sep 2025 10:05:03 -0600 Subject: [PATCH 23/73] Refactor: Extract reversal checkbox into CanvasSelectComponent Co-authored-by: Nakul Verma <173621577+nakul-py@users.noreply.github.com> --- .../color_ramp/CanvasSelectComponent.tsx | 15 +++++++++++++++ .../symbology/components/color_ramp/ColorRamp.tsx | 7 +++++++ .../tiff_layer/types/SingleBandPseudoColor.tsx | 6 ++++++ .../symbology/vector_layer/types/Categorized.tsx | 15 ++------------- .../symbology/vector_layer/types/Graduated.tsx | 15 ++------------- .../symbology/vector_layer/types/Heatmap.tsx | 14 +++----------- 6 files changed, 35 insertions(+), 37 deletions(-) diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx index 380ab5b27..c65654719 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx @@ -8,11 +8,15 @@ import ColorRampEntry from './ColorRampEntry'; interface ICanvasSelectComponentProps { selectedRamp: string; setSelected: (item: any) => void; + reverse: boolean; + setReverse: React.Dispatch>; } const CanvasSelectComponent: React.FC = ({ selectedRamp, setSelected, + reverse, + setReverse, }) => { const containerRef = useRef(null); const [isOpen, setIsOpen] = useState(false); @@ -103,6 +107,17 @@ const CanvasSelectComponent: React.FC = ({ /> ))} + +
+ +
); }; 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 70f07874c..9b6b55013 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -27,6 +27,8 @@ interface IColorRampProps { | 'Categorized' | 'Heatmap' | 'Singleband Pseudocolor'; + reverse: boolean; + setReverse: React.Dispatch>; initialMin?: number; initialMax?: number; } @@ -47,6 +49,8 @@ const ColorRamp: React.FC = ({ showModeRow, showRampSelector, renderType, + reverse = true, + setReverse, initialMin, initialMax, }) => { @@ -126,12 +130,15 @@ const ColorRamp: React.FC = ({ return (
+ {showRampSelector && (
)} 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 58770ba47..3ec1c6674 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx @@ -51,6 +51,7 @@ const SingleBandPseudoColor: React.FC = ({ const [minValue, setMinValue] = useState(); const [maxValue, setMaxValue] = useState(); const [stopRows, setStopRows] = useState([]); + const [reverseRamp, setReverseRamp] = useState(false); const [selectedFunction, setSelectedFunction] = useState('linear'); const [colorRampOptions, setColorRampOptions] = useState< @@ -252,6 +253,7 @@ const SingleBandPseudoColor: React.FC = ({ mode: colorRampOptionsRef.current?.selectedMode, min: minValue ?? bandRow.stats.minimum, max: maxValue ?? bandRow.stats.maximum, + reverse: reverseRamp, }; layer.parameters.symbologyState = symbologyState; @@ -335,6 +337,7 @@ const SingleBandPseudoColor: React.FC = ({ stops, selectedRamp, nClasses, + reverseRamp, ); setStopRows(valueColorPairs); @@ -417,8 +420,11 @@ const SingleBandPseudoColor: React.FC = ({ classifyFunc={buildColorInfoFromClassification} showModeRow={true} showRampSelector={true} + reverse={reverseRamp} + setReverse={setReverseRamp} /> )} +
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 b5b0f884b..a6f3bc8d1 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx @@ -344,19 +344,6 @@ const Categorized: React.FC = ({ )}
- {symbologyTab === 'color' && ( -
- -
- )} -
= ({ showModeRow={false} showRampSelector={symbologyTab === 'color'} renderType="Categorized" + reverse={reverseRamp} + setReverse={setReverseRamp} initialMin={computedInitialMin} initialMax={computedInitialMax} /> 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 45480b824..f5b1de989 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx @@ -429,19 +429,6 @@ const Graduated: React.FC = ({ )}
- {symbologyTab === 'color' && ( -
- -
- )} - = ({ showModeRow={true} showRampSelector={symbologyTab === 'color'} renderType="Graduated" + reverse={reverseRamp} + setReverse={setReverseRamp} initialMin={colorRampOptions?.minValue} initialMax={colorRampOptions?.maxValue} /> diff --git a/packages/base/src/dialogs/symbology/vector_layer/types/Heatmap.tsx b/packages/base/src/dialogs/symbology/vector_layer/types/Heatmap.tsx index 42e812536..bc072d124 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Heatmap.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Heatmap.tsx @@ -30,7 +30,7 @@ const Heatmap: React.FC = ({ radius: 8, blur: 15, }); - const reverseRampRef = useRef(false); + const reverseRampRef = useRef(false); // Do we need these refs here? Why not directly use the state? useEffect(() => { populateOptions(); @@ -102,18 +102,10 @@ const Heatmap: React.FC = ({
-
- -
Date: Fri, 19 Sep 2025 10:11:46 -0600 Subject: [PATCH 24/73] WIP: remove hardcoded min/max values This is in preparation for setting up the "use actual range" button to calculate the min/max from the data values. Co-authored-by: Nakul Verma <173621577+nakul-py@users.noreply.github.com> --- .../components/color_ramp/ColorRamp.tsx | 16 ++++++---- .../src/dialogs/symbology/symbologyUtils.ts | 10 +++---- .../types/SingleBandPseudoColor.tsx | 29 +++++++++++-------- .../vector_layer/types/Categorized.tsx | 4 +++ .../vector_layer/types/Graduated.tsx | 8 ++--- 5 files changed, 40 insertions(+), 27 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 9b6b55013..445e5b7c0 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -16,13 +16,13 @@ interface IColorRampProps { numberOfShades: string, selectedRamp: ColorRampName, setIsLoading: (isLoading: boolean) => void, + minValue: number, + maxValue: number, criticalValue?: number, - minValue?: number, - maxValue?: number, ) => void; showModeRow: boolean; showRampSelector: boolean; - renderType?: + renderType: | 'Graduated' | 'Categorized' | 'Heatmap' @@ -169,17 +169,21 @@ const ColorRamp: React.FC = ({ diff --git a/packages/base/src/dialogs/symbology/symbologyUtils.ts b/packages/base/src/dialogs/symbology/symbologyUtils.ts index 90f9da54d..e68ae2a86 100644 --- a/packages/base/src/dialogs/symbology/symbologyUtils.ts +++ b/packages/base/src/dialogs/symbology/symbologyUtils.ts @@ -110,9 +110,9 @@ export namespace Utils { | 'Categorized' | 'Graduated' | 'Heatmap' - | 'Singleband Pseudocolor' = 'Graduated', - minValue?: number, - maxValue?: number, + | 'Singleband Pseudocolor', + minValue: number, + maxValue: number, ) => { let effectiveStops: number[] = []; @@ -121,8 +121,8 @@ export namespace Utils { } else { effectiveStops = VectorClassifications.calculateEqualIntervalBreaks( nClasses, - minValue ?? 0.95, - maxValue ?? 8.1, + minValue, + maxValue, ); } 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 3ec1c6674..43a44fb97 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx @@ -21,7 +21,6 @@ import BandRow from '@/src/dialogs/symbology/tiff_layer/components/BandRow'; import { LoadingOverlay } from '@/src/shared/components/loading'; import { GlobalStateDbManager } from '@/src/store'; import { ColorRampName } from '@/src/types'; -import { ColorRampValueControls } from '../../components/color_ramp/ColorRampValueControls'; export type InterpolationType = 'discrete' | 'linear' | 'exact'; @@ -48,8 +47,16 @@ const SingleBandPseudoColor: React.FC = ({ const [layerState, setLayerState] = useState(); const [selectedBand, setSelectedBand] = useState(1); + + // TODO: New effect to set min/max. The min/max will be different per-band, + // so this effect will need to depend on the data values, and the selected band. + // The effect would be triggered on clicking "use actual range" or on + // component load if these values are not already initialized. + // This operation is expensive so we don't want to do it too many times; can + // we cache it in the global state db? const [minValue, setMinValue] = useState(); const [maxValue, setMaxValue] = useState(); + const [stopRows, setStopRows] = useState([]); const [reverseRamp, setReverseRamp] = useState(false); const [selectedFunction, setSelectedFunction] = @@ -286,6 +293,8 @@ const SingleBandPseudoColor: React.FC = ({ numberOfShades: string, selectedRamp: ColorRampName, setIsLoading: (isLoading: boolean) => void, + minValue: number, + maxValue: number, ) => { // Update layer state with selected options setColorRampOptions({ @@ -314,8 +323,8 @@ const SingleBandPseudoColor: React.FC = ({ case 'continuous': stops = GeoTiffClassifications.classifyContinuousBreaks( nClasses, - currentBand.stats.minimum, - currentBand.stats.maximum, + minValue, + maxValue, selectedFunction, ); break; @@ -338,6 +347,9 @@ const SingleBandPseudoColor: React.FC = ({ selectedRamp, nClasses, reverseRamp, + 'Singleband Pseudocolor', + minValue, + maxValue ); setStopRows(valueColorPairs); @@ -404,15 +416,7 @@ const SingleBandPseudoColor: React.FC = ({
- + {bandRows.length > 0 && ( = ({ classifyFunc={buildColorInfoFromClassification} showModeRow={true} showRampSelector={true} + renderType='Singleband Pseudocolor' reverse={reverseRamp} setReverse={setReverseRamp} /> 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 a6f3bc8d1..40ef28746 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx @@ -147,6 +147,8 @@ const Categorized: React.FC = ({ numberOfShades: string, selectedRamp: ColorRampName, setIsLoading: (isLoading: boolean) => void, + minValue: number, + maxValue: number, ) => { setColorRampOptions({ selectedRamp, @@ -166,6 +168,8 @@ const Categorized: React.FC = ({ stops.length, reverseRamp, 'Categorized', + minValue, + maxValue, ); setStopRows(valueColorPairs); 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 f5b1de989..8aa0385db 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx @@ -233,9 +233,9 @@ const Graduated: React.FC = ({ numberOfShades: string, selectedRamp: ColorRampName, setIsLoading: (isLoading: boolean) => void, + minValue: number, + maxValue: number, criticalValue?: number, - minValue?: number, - maxValue?: number, ) => { setColorRampOptions({ selectedRamp, @@ -260,8 +260,8 @@ const Graduated: React.FC = ({ case 'equal interval': stops = VectorClassifications.calculateEqualIntervalBreaks( +numberOfShades, - minValue ?? 0.95, - maxValue ?? 8.1, + minValue, + maxValue, ); break; case 'jenks': From 37d946a246afa99a6f2a0b4ee4060494f063b822 Mon Sep 17 00:00:00 2001 From: Nakul Date: Sat, 20 Sep 2025 17:32:08 +0530 Subject: [PATCH 25/73] Refactor: Improve layout and state management in color ramp components --- .../components/color_ramp/CanvasSelectComponent.tsx | 2 +- .../dialogs/symbology/components/color_ramp/ColorRamp.tsx | 3 +-- .../symbology/tiff_layer/types/SingleBandPseudoColor.tsx | 4 ++-- .../src/dialogs/symbology/vector_layer/types/Heatmap.tsx | 2 +- packages/base/style/symbologyDialog.css | 5 ++++- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx index c65654719..258f60b32 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/CanvasSelectComponent.tsx @@ -109,7 +109,7 @@ const CanvasSelectComponent: React.FC = ({
-
@@ -71,24 +76,32 @@ export const ColorRampValueControls: React.FC = ({ e.target.value !== '' ? parseFloat(e.target.value) : undefined, ) } - className="jp-mod-styled" + className={`jp-mod-styled ${ + selectedMode !== 'equal interval' ? 'jp-gis-disabled-input' : '' + }`} placeholder="Enter max value" + disabled={selectedMode !== 'equal interval'} /> - { -
- -
- } + + {renderType === 'Graduated' && selectedMode !== 'equal interval' && ( +

+ ⚠️Note: Min/Max values are only applied in Equal Interval mode. +

+ )} + +
+ +
); }; diff --git a/packages/base/src/dialogs/symbology/symbologyUtils.ts b/packages/base/src/dialogs/symbology/symbologyUtils.ts index c542b8f04..1b06a640e 100644 --- a/packages/base/src/dialogs/symbology/symbologyUtils.ts +++ b/packages/base/src/dialogs/symbology/symbologyUtils.ts @@ -116,8 +116,8 @@ export namespace Utils { ) => { let effectiveStops: number[] = []; - if (renderType === 'Categorized') { - effectiveStops = stops; + if (stops && stops.length > 0) { + effectiveStops = stops.map(v => parseFloat(v.toFixed(2))); } else { effectiveStops = VectorClassifications.calculateEqualIntervalBreaks( nClasses, diff --git a/packages/base/style/base.css b/packages/base/style/base.css index 946f692e2..ca514e8cf 100644 --- a/packages/base/style/base.css +++ b/packages/base/style/base.css @@ -121,3 +121,9 @@ button.jp-mod-styled.jp-mod-reject { margin: 0; } } + +.jp-gis-disabled-input { + opacity: 0.5; + pointer-events: none; + background-color: #dfdfdf; +} From e8f1db66ee07f1bbaaedc536398aa49ace2b123c Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Tue, 23 Sep 2025 10:20:17 -0600 Subject: [PATCH 35/73] Move the "Use actual range" button to right, display more prominent warning on same row --- .../color_ramp/ColorRampValueControls.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx index 8ebd46ab3..b53202cb8 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx @@ -29,6 +29,8 @@ export const ColorRampValueControls: React.FC = ({ renderType, selectedMode, }) => { + const applyMinMax = renderType === 'Graduated' && selectedMode !== 'equal interval'; + return ( <>
@@ -84,13 +86,16 @@ export const ColorRampValueControls: React.FC = ({ />
- {renderType === 'Graduated' && selectedMode !== 'equal interval' && ( -

- ⚠️Note: Min/Max values are only applied in Equal Interval mode. -

- )} -
+ + {applyMinMax ? ( +

+ ⚠️ Warning: User-specified min/max values are only applied in Equal Interval mode. +

+ ) : ( +
+ )} +
From 345af02411440bbab6183495e0dd0996b546efa3 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Wed, 1 Oct 2025 08:52:37 -0600 Subject: [PATCH 43/73] Improve conditions to disable min/max controls Co-authored-by: Yao-Ting Yao <94820616+YaoTingYao@users.noreply.github.com> Co-authored-by: Nakul Verma <173621577+nakul-py@users.noreply.github.com> --- .../components/color_ramp/ColorRampValueControls.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx index 087e1a7f5..afdfb7b08 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx @@ -29,9 +29,8 @@ export const ColorRampValueControls: React.FC = ({ renderType, selectedMode, }) => { - const applyMinMax = - renderType === 'Graduated' && selectedMode !== 'equal interval'; + const enableMinMax = selectedMode === 'equal interval'; return ( <>
@@ -47,12 +46,11 @@ export const ColorRampValueControls: React.FC = ({ } className={'jp-mod-styled'} placeholder="Enter min value" - disabled={selectedMode !== 'equal interval'} + disabled={!enableMinMax} />
{rampDef.type === 'Divergent' && - renderType === 'Graduated' && dataMin !== undefined && dataMax !== undefined && (
@@ -79,12 +77,12 @@ export const ColorRampValueControls: React.FC = ({ } className={'jp-mod-styled'} placeholder="Enter max value" - disabled={selectedMode !== 'equal interval'} + disabled={!enableMinMax} />
- {applyMinMax ? ( + {!enableMinMax ? (
⚠️ Warning: User-specified min/max values are only applied in Equal Interval mode. @@ -95,7 +93,7 @@ export const ColorRampValueControls: React.FC = ({
From 78c5ca96ffcb9db45e05b1133ea9dc4cc539478e Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Wed, 1 Oct 2025 09:45:25 -0600 Subject: [PATCH 46/73] Fixup critical value display * Only display when in equal interval mode -- harder to calculate critical value in other modes * Dynamically calculate critical value instead of displaying 50% * Move under color ramp selector Co-authored-by: Yao-Ting Yao <94820616+YaoTingYao@users.noreply.github.com> Co-authored-by: Nakul Verma <173621577+nakul-py@users.noreply.github.com> --- .../color_ramp/ColorRampValueControls.tsx | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx index 19b93aa42..1135b4e96 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx @@ -27,6 +27,21 @@ export const ColorRampValueControls: React.FC = (p const enableMinMax = props.selectedMode === 'equal interval'; return ( <> + {props.rampDef.type === 'Divergent' && + props.selectedMode === 'equal interval' && + props.selectedMin !== undefined && + props.selectedMax !== undefined && ( +
+ + + {`${( + props.selectedMin + + (props.rampDef.criticalValue) * (props.selectedMax - props.selectedMin) + ).toFixed(2)} (Colormap diverges at ${props.rampDef.criticalValue * 100}%)`} + +
+ )} +
= (p />
- {props.rampDef.type === 'Divergent' && - props.dataMin !== undefined && - props.dataMax !== undefined && ( -
- - - {`${( - props.dataMin + - (props.rampDef.criticalValue ?? 0.5) * (props.dataMax - props.dataMin) - ).toFixed(1)} (Colormap diverges at 50%)`} - -
- )} -
Date: Wed, 1 Oct 2025 09:47:05 -0600 Subject: [PATCH 47/73] Save "reverse", "min", and "max" into symbology state in shared model Co-authored-by: Yao-Ting Yao <94820616+YaoTingYao@users.noreply.github.com> Co-authored-by: Nakul Verma <173621577+nakul-py@users.noreply.github.com> --- .../dialogs/symbology/components/color_ramp/ColorRamp.tsx | 1 + .../components/color_ramp/ColorRampValueControls.tsx | 2 +- .../symbology/tiff_layer/types/SingleBandPseudoColor.tsx | 1 + .../src/dialogs/symbology/vector_layer/types/Categorized.tsx | 1 + .../src/dialogs/symbology/vector_layer/types/Graduated.tsx | 5 ++++- 5 files changed, 8 insertions(+), 2 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 d29a88925..d8bea4f8e 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -35,6 +35,7 @@ interface IColorRampProps { export type ColorRampOptions = { selectedRamp: ColorRampName; + reverseRamp: boolean; numberOfShades: string; selectedMode: string; minValue: number; diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx index 1135b4e96..e36b6cf82 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx @@ -16,7 +16,7 @@ export interface IColorRampValueControlsProps { | 'Singleband Pseudocolor'; dataMin?: number; dataMax?: number; - selectedMode: string; // TODO: should be ClssificationMode + selectedMode: string; // TODO: should be ClssificationMode (https://github.com/geojupyter/jupytergis/pull/937) } export const ColorRampValueControls: React.FC = (props) => { const permittedRenderTypes = ['Graduated', 'Singleband Pseudocolor']; 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 993e6b670..29ee7a748 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx @@ -315,6 +315,7 @@ const SingleBandPseudoColor: React.FC = ({ // Update layer state with selected options setColorRampOptions({ selectedRamp, + reverseRamp, numberOfShades, selectedMode, minValue, 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 be63adbe2..80aed849b 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx @@ -138,6 +138,7 @@ const Categorized: React.FC = ({ ) => { setColorRampOptions({ selectedRamp, + reverseRamp, numberOfShades, selectedMode, minValue, 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 b7306acb7..7c2f0bd7d 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx @@ -215,7 +215,9 @@ const Graduated: React.FC = ({ colorRamp: colorRampOptionsRef.current?.selectedRamp, nClasses: colorRampOptionsRef.current?.numberOfShades, mode: colorRampOptionsRef.current?.selectedMode, - reverse: reverseRamp, + reverse: colorRampOptionsRef.current?.reverseRamp, + min: colorRampOptionsRef.current?.minValue, + max: colorRampOptionsRef.current?.maxValue, }; if (layer.type === 'HeatmapLayer') { @@ -237,6 +239,7 @@ const Graduated: React.FC = ({ ) => { setColorRampOptions({ selectedRamp, + reverseRamp, numberOfShades, selectedMode, minValue, From 44b352e498694a2941d17d73ecd57c6d89efe2f1 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Wed, 1 Oct 2025 10:09:12 -0600 Subject: [PATCH 48/73] WIP Fix loading of reverse/min/max values from shared model Reverse is successfully being loaded from the shared model, but not min/max :( Co-authored-by: Yao-Ting Yao <94820616+YaoTingYao@users.noreply.github.com> Co-authored-by: Nakul Verma <173621577+nakul-py@users.noreply.github.com> --- .../components/color_ramp/ColorRamp.tsx | 24 +++++++++++-------- .../types/SingleBandPseudoColor.tsx | 10 ++++---- .../vector_layer/types/Categorized.tsx | 6 ++--- .../vector_layer/types/Graduated.tsx | 6 ++--- 4 files changed, 22 insertions(+), 24 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 d8bea4f8e..5238b981f 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -8,6 +8,7 @@ import { ColorRampName } from '@/src/types'; import CanvasSelectComponent from './CanvasSelectComponent'; import { ColorRampValueControls } from './ColorRampValueControls'; import ModeSelectRow from './ModeSelectRow'; + interface IColorRampProps { modeOptions: string[]; layerParams: IDict; @@ -15,6 +16,7 @@ interface IColorRampProps { selectedMode: string, numberOfShades: string, selectedRamp: ColorRampName, + reverseRamp: boolean, setIsLoading: (isLoading: boolean) => void, minValue: number, maxValue: number, @@ -27,8 +29,6 @@ interface IColorRampProps { | 'Categorized' | 'Heatmap' | 'Singleband Pseudocolor'; - reverse: boolean; - setReverse: React.Dispatch>; dataMin?: number; dataMax?: number; } @@ -50,12 +50,11 @@ const ColorRamp: React.FC = ({ showModeRow, showRampSelector, renderType, - reverse = true, - setReverse, dataMin, dataMax, }) => { const [selectedRamp, setSelectedRamp] = useState('viridis'); + const [reverseRamp, setReverseRamp] = useState(false); const [selectedMode, setSelectedMode] = useState(''); const [numberOfShades, setNumberOfShades] = useState(''); const [minValue, setMinValue] = useState(dataMin); @@ -80,16 +79,18 @@ const ColorRamp: React.FC = ({ }, [dataMin, dataMax, renderType]); const initializeState = () => { - let nClasses, singleBandMode, colorRamp; + let nClasses, singleBandMode, colorRamp, reverseRamp; if (layerParams.symbologyState) { nClasses = layerParams.symbologyState.nClasses; singleBandMode = layerParams.symbologyState.mode; colorRamp = layerParams.symbologyState.colorRamp; + reverseRamp = layerParams.symbologyState.reverse; } - setNumberOfShades(nClasses ? nClasses : '9'); - setSelectedMode(singleBandMode ? singleBandMode : 'equal interval'); - setSelectedRamp(colorRamp ? colorRamp : 'viridis'); + setNumberOfShades(nClasses ?? '9'); + setSelectedMode(singleBandMode ?? 'equal interval'); + setSelectedRamp(colorRamp ?? 'viridis'); + setReverseRamp(reverseRamp ?? false); }; const rampDef = COLOR_RAMP_DEFINITIONS[selectedRamp]; @@ -112,6 +113,7 @@ const ColorRamp: React.FC = ({ layerParams.symbologyState.min = minValue; layerParams.symbologyState.max = maxValue; layerParams.symbologyState.colorRamp = selectedRamp; + layerParams.symbologyState.reverse = reverseRamp; layerParams.symbologyState.nClasses = numberOfShades; layerParams.symbologyState.mode = selectedMode; @@ -123,6 +125,7 @@ const ColorRamp: React.FC = ({ minValue, maxValue, selectedRamp, + reverseRamp, selectedMode, numberOfShades, dataMin, @@ -137,8 +140,8 @@ const ColorRamp: React.FC = ({
)} @@ -180,6 +183,7 @@ const ColorRamp: React.FC = ({ selectedMode, numberOfShades, selectedRamp, + reverseRamp, setIsLoading, minValue, maxValue, 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 29ee7a748..2cd2f8622 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx @@ -56,7 +56,6 @@ const SingleBandPseudoColor: React.FC = ({ // we cache it in the global state db? const [stopRows, setStopRows] = useState([]); - const [reverseRamp, setReverseRamp] = useState(false); const [dataMin, setDataMin] = useState(); const [dataMax, setDataMax] = useState(); const [selectedFunction, setSelectedFunction] = @@ -272,11 +271,11 @@ const SingleBandPseudoColor: React.FC = ({ band: selectedBandRef.current, interpolation: selectedFunctionRef.current, colorRamp: colorRampOptionsRef.current?.selectedRamp, + reverse: colorRampOptionsRef.current?.reverseRamp, nClasses: colorRampOptionsRef.current?.numberOfShades, mode: colorRampOptionsRef.current?.selectedMode, - min: dataMin ?? bandRow.stats.minimum, - max: dataMax ?? bandRow.stats.maximum, - reverse: reverseRamp, + min: dataMin ?? bandRow.stats.minimum, // TODO: ? + max: dataMax ?? bandRow.stats.maximum, // TODO: ? }; layer.parameters.symbologyState = symbologyState; @@ -308,6 +307,7 @@ const SingleBandPseudoColor: React.FC = ({ selectedMode: string, numberOfShades: string, selectedRamp: ColorRampName, + reverseRamp: boolean, setIsLoading: (isLoading: boolean) => void, minValue: number, maxValue: number, @@ -444,8 +444,6 @@ const SingleBandPseudoColor: React.FC = ({ showModeRow={true} showRampSelector={true} renderType="Singleband Pseudocolor" - reverse={reverseRamp} - setReverse={setReverseRamp} dataMin={dataMin} dataMax={dataMax} /> 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 80aed849b..e9151e7fd 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Categorized.tsx @@ -39,7 +39,6 @@ const Categorized: React.FC = ({ radius: 5, }); const manualStyleRef = useRef(manualStyle); - const [reverseRamp, setReverseRamp] = useState(false); const [dataMin, setDataMin] = useState(); const [dataMax, setDataMax] = useState(); @@ -132,6 +131,7 @@ const Categorized: React.FC = ({ selectedMode: string, numberOfShades: string, selectedRamp: ColorRampName, + reverseRamp: boolean, setIsLoading: (isLoading: boolean) => void, minValue: number, maxValue: number, @@ -200,10 +200,10 @@ const Categorized: React.FC = ({ renderType: 'Categorized', value: selectedAttributeRef.current, colorRamp: colorRampOptionsRef.current?.selectedRamp, + reverse: colorRampOptionsRef.current?.reverseRamp, nClasses: colorRampOptionsRef.current?.numberOfShades, mode: colorRampOptionsRef.current?.selectedMode, symbologyTab, - reverse: reverseRamp, }; layer.parameters.symbologyState = symbologyState; @@ -343,8 +343,6 @@ const Categorized: React.FC = ({ showModeRow={false} showRampSelector={symbologyTab === 'color'} renderType="Categorized" - reverse={reverseRamp} - setReverse={setReverseRamp} dataMin={dataMin} dataMax={dataMax} /> 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 7c2f0bd7d..a588d9f62 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx @@ -51,7 +51,6 @@ const Graduated: React.FC = ({ const [radiusManualStyle, setRadiusManualStyle] = useState({ radius: 5, }); - const [reverseRamp, setReverseRamp] = useState(false); const [dataMin, setDataMin] = useState(); const [dataMax, setDataMax] = useState(); @@ -213,9 +212,9 @@ const Graduated: React.FC = ({ value: selectableAttributeRef.current, method: symbologyTabRef.current, colorRamp: colorRampOptionsRef.current?.selectedRamp, + reverse: colorRampOptionsRef.current?.reverseRamp, nClasses: colorRampOptionsRef.current?.numberOfShades, mode: colorRampOptionsRef.current?.selectedMode, - reverse: colorRampOptionsRef.current?.reverseRamp, min: colorRampOptionsRef.current?.minValue, max: colorRampOptionsRef.current?.maxValue, }; @@ -232,6 +231,7 @@ const Graduated: React.FC = ({ selectedMode: string, numberOfShades: string, selectedRamp: ColorRampName, + reverseRamp: boolean, setIsLoading: (isLoading: boolean) => void, minValue: number, maxValue: number, @@ -433,8 +433,6 @@ const Graduated: React.FC = ({ showModeRow={true} showRampSelector={symbologyTab === 'color'} renderType="Graduated" - reverse={reverseRamp} - setReverse={setReverseRamp} dataMin={dataMin} dataMax={dataMax} /> From 273b7f527c16abb99b28b11ce4f9d9134b42b844 Mon Sep 17 00:00:00 2001 From: Nakul Date: Thu, 2 Oct 2025 18:43:27 +0530 Subject: [PATCH 49/73] min and max succesfully loading from shared model or lint fix --- .../components/color_ramp/ColorRamp.tsx | 8 ++------ .../color_ramp/ColorRampValueControls.tsx | 19 ++++++++++++++----- .../types/SingleBandPseudoColor.tsx | 4 ++-- 3 files changed, 18 insertions(+), 13 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 5238b981f..69e0f6135 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRamp.tsx @@ -69,12 +69,8 @@ const ColorRamp: React.FC = ({ useEffect(() => { if (renderType) { - if (dataMin !== undefined) { - setMinValue(dataMin); - } - if (dataMax !== undefined) { - setMaxValue(dataMax); - } + setMinValue(layerParams.symbologyState.min ?? dataMin); + setMaxValue(layerParams.symbologyState.max ?? dataMax); } }, [dataMin, dataMax, renderType]); diff --git a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx index e36b6cf82..be09395c2 100644 --- a/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx +++ b/packages/base/src/dialogs/symbology/components/color_ramp/ColorRampValueControls.tsx @@ -16,9 +16,11 @@ export interface IColorRampValueControlsProps { | 'Singleband Pseudocolor'; dataMin?: number; dataMax?: number; - selectedMode: string; // TODO: should be ClssificationMode (https://github.com/geojupyter/jupytergis/pull/937) + selectedMode: string; // TODO: should be ClssificationMode (https://github.com/geojupyter/jupytergis/pull/937) } -export const ColorRampValueControls: React.FC = (props) => { +export const ColorRampValueControls: React.FC< + IColorRampValueControlsProps +> = props => { const permittedRenderTypes = ['Graduated', 'Singleband Pseudocolor']; if (!permittedRenderTypes.includes(props.renderType)) { return; @@ -36,8 +38,11 @@ export const ColorRampValueControls: React.FC = (p {`${( props.selectedMin + - (props.rampDef.criticalValue) * (props.selectedMax - props.selectedMin) - ).toFixed(2)} (Colormap diverges at ${props.rampDef.criticalValue * 100}%)`} + props.rampDef.criticalValue * + (props.selectedMax - props.selectedMin) + ).toFixed( + 2, + )} (Colormap diverges at ${props.rampDef.criticalValue * 100}%)`}
)} @@ -88,7 +93,11 @@ export const ColorRampValueControls: React.FC = (p