diff --git a/frontend/src/modules/SimulationTimeSeriesMatrix/settings.tsx b/frontend/src/modules/SimulationTimeSeriesMatrix/settings.tsx index ba6704345..3f2a89d9e 100644 --- a/frontend/src/modules/SimulationTimeSeriesMatrix/settings.tsx +++ b/frontend/src/modules/SimulationTimeSeriesMatrix/settings.tsx @@ -17,6 +17,7 @@ import { Label } from "@lib/components/Label"; import { RadioGroup } from "@lib/components/RadioGroup"; import { SmartNodeSelectorSelection, TreeDataNode } from "@lib/components/SmartNodeSelector"; import { VectorSelector } from "@lib/components/VectorSelector"; +import { useValidState } from "@lib/hooks/useValidState"; import { resolveClassNames } from "@lib/utils/resolveClassNames"; import { createVectorSelectorDataFromVectors } from "@lib/utils/vectorSelectorUtils"; @@ -45,11 +46,11 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps([]); const [prevVisualizationMode, setPrevVisualizationMode] = React.useState(visualizationMode); - if (prevVisualizationMode !== visualizationMode) { + if (visualizationMode !== prevVisualizationMode) { setPrevVisualizationMode(visualizationMode); } + if (!isEqual(ensembleSet, previousEnsembleSet)) { + const newSelectedEnsembleIdents = selectedEnsembleIdents.filter( + (ensemble) => ensembleSet.findEnsemble(ensemble) !== null + ); + const validatedEnsembleIdents = fixupEnsembleIdents(newSelectedEnsembleIdents, ensembleSet) ?? []; + if (!isEqual(selectedEnsembleIdents, validatedEnsembleIdents)) { + setSelectedEnsembleIdents(validatedEnsembleIdents); + } + + setPreviousEnsembleSet(ensembleSet); + } + // Queries const vectorListQueries = useVectorListQueries(selectedEnsembleIdents); const ensembleVectorListsHelper = new EnsembleVectorListsHelper(selectedEnsembleIdents, vectorListQueries); - const vectorsUnion = ensembleVectorListsHelper.vectorsUnion(); const selectedVectorNamesHasHistorical = ensembleVectorListsHelper.hasAnyHistoricalVector(selectedVectorNames); - const currentVectorSelectorData = createVectorSelectorDataFromVectors(vectorsUnion); + const currentVectorSelectorData = createVectorSelectorDataFromVectors(ensembleVectorListsHelper.vectorsUnion()); - // Get union of continuous parameters for selected ensembles - const continuousParametersUnion: ParameterIdent[] = []; + // Get union of continuous and non-constant parameters for selected ensembles and set valid parameter ident str + const continuousAndNonConstantParametersUnion: ParameterIdent[] = []; for (const ensembleIdent of selectedEnsembleIdents) { const ensemble = ensembleSet.findEnsemble(ensembleIdent); if (ensemble === null) continue; for (const parameter of ensemble.getParameters().getParameterIdents(ParameterType.CONTINUOUS)) { - if (continuousParametersUnion.some((param) => param.equals(parameter))) continue; - - // TODO: Check if parameter is constant? + if (continuousAndNonConstantParametersUnion.some((param) => param.equals(parameter))) continue; if (ensemble.getParameters().getParameter(parameter).isConstant) continue; - continuousParametersUnion.push(parameter); + continuousAndNonConstantParametersUnion.push(parameter); } } + const [selectedParameterIdentStr, setSelectedParameterIdentStr] = useValidState(null, [ + continuousAndNonConstantParametersUnion, + (item: ParameterIdent) => item.toString(), + ]); - // Only update if all vector lists are retrieved before updating vectorSelectorData has changed + // Await update of vectorSelectorData until all vector lists are retrieved const hasVectorListQueriesErrorOrLoading = vectorListQueries.some((query) => query.isLoading || query.isError); if (!hasVectorListQueriesErrorOrLoading && !isEqual(currentVectorSelectorData, vectorSelectorData)) { setVectorSelectorData(currentVectorSelectorData); } - if (!isEqual(ensembleSet, previousEnsembleSet)) { - const newSelectedEnsembleIdents = selectedEnsembleIdents.filter( - (ensemble) => ensembleSet.findEnsemble(ensemble) !== null - ); - const validatedEnsembleIdents = fixupEnsembleIdents(newSelectedEnsembleIdents, ensembleSet) ?? []; - if (!isEqual(selectedEnsembleIdents, validatedEnsembleIdents)) { - setSelectedEnsembleIdents(validatedEnsembleIdents); - } - - setPreviousEnsembleSet(ensembleSet); - } - React.useEffect( function propagateVectorSpecsToView() { const newVectorSpecifications: VectorSpec[] = []; @@ -120,20 +122,36 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps parameter.equals(newParameterIdent)) + ? setParameterIdent(newParameterIdent) + : setParameterIdent(null); + } catch { + setParameterIdent(null); + } + }, + [selectedParameterIdentStr] + ); + function handleGroupByChange(event: React.ChangeEvent) { setGroupBy(event.target.value as GroupBy); } function handleColorByParameterChange(parameterIdentStr: string) { - const parameterIdent = ParameterIdent.fromString(parameterIdentStr); - const newParameterIdent = continuousParametersUnion.find((elm) => elm.equals(parameterIdent)) ?? null; - setParameterIdent(newParameterIdent); + setSelectedParameterIdentStr(parameterIdentStr); } function handleEnsembleSelectChange(ensembleIdentArr: EnsembleIdent[]) { @@ -263,7 +281,6 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps - + { @@ -291,10 +308,13 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps { - return { value: elm.toString(), label: elm.toString() }; + options={continuousAndNonConstantParametersUnion.map((elm) => { + return { + value: elm.toString(), + label: elm.groupName ? `${elm.groupName}:${elm.name}` : elm.name, + }; })} - value={parameterIdent?.toString() ?? undefined} + value={selectedParameterIdentStr?.toString() ?? undefined} onChange={handleColorByParameterChange} /> diff --git a/frontend/src/modules/SimulationTimeSeriesMatrix/utils/PlotlyTraceUtils/createVectorTracesUtils.ts b/frontend/src/modules/SimulationTimeSeriesMatrix/utils/PlotlyTraceUtils/createVectorTracesUtils.ts index c21cbbdb7..2bbb9dfc7 100644 --- a/frontend/src/modules/SimulationTimeSeriesMatrix/utils/PlotlyTraceUtils/createVectorTracesUtils.ts +++ b/frontend/src/modules/SimulationTimeSeriesMatrix/utils/PlotlyTraceUtils/createVectorTracesUtils.ts @@ -5,8 +5,6 @@ import { VectorStatisticData_api, } from "@api"; -import { type } from "os"; - import { FanchartData, FreeLineData, LowHighData, MinMaxData } from "./fanchartPlotting"; import { createFanchartTraces } from "./fanchartPlotting"; import { LineData, StatisticsData, createStatisticsTraces } from "./statisticsPlotting"; diff --git a/frontend/src/modules/SimulationTimeSeriesMatrix/utils/subplotBuilder.ts b/frontend/src/modules/SimulationTimeSeriesMatrix/utils/subplotBuilder.ts index f09e22e2a..14e6146a8 100644 --- a/frontend/src/modules/SimulationTimeSeriesMatrix/utils/subplotBuilder.ts +++ b/frontend/src/modules/SimulationTimeSeriesMatrix/utils/subplotBuilder.ts @@ -1,5 +1,5 @@ import { VectorHistoricalData_api, VectorRealizationData_api, VectorStatisticData_api } from "@api"; -import { ColorScale } from "@lib/utils/ColorScale"; +// import { ColorScale } from "@lib/utils/ColorScale"; import { ColorSet } from "@lib/utils/ColorSet"; import { PlotMarker } from "plotly.js"; @@ -58,7 +58,7 @@ export class SubplotBuilder { private _width = 0; private _height = 0; - private _defaultScatterType = "scatter"; + private _scatterType: "scatter" | "scattergl"; private _parameterColorScaleHelper: ContinuousParameterColorScaleHelper | null = null; private _parameterFallbackColor = "#808080"; @@ -69,7 +69,8 @@ export class SubplotBuilder { colorSet: ColorSet, width: number, height: number, - parameterColorScaleHelper?: ContinuousParameterColorScaleHelper + parameterColorScaleHelper?: ContinuousParameterColorScaleHelper, + scatterType: "scatter" | "scattergl" = "scatter" ) { this._selectedVectorSpecifications = selectedVectorSpecifications; this._width = width; @@ -97,6 +98,7 @@ export class SubplotBuilder { : this._uniqueEnsembleNames.length; this._parameterColorScaleHelper = parameterColorScaleHelper ?? null; + this._scatterType = scatterType; // TODO: // - Handle keep uirevision? @@ -253,8 +255,6 @@ export class SubplotBuilder { ): void { if (this._parameterColorScaleHelper === null) return; - this._defaultScatterType = "scattergl"; - // Only allow selected vectors const selectedVectorsRealizationData = vectorsRealizationData.filter((vec) => this._selectedVectorSpecifications.some( @@ -298,6 +298,7 @@ export class SubplotBuilder { hoverTemplate: hoverTemplate, showLegend: addLegendForTraces, yaxis: `y${subplotIndex + 1}`, + type: this._scatterType, }); this._plotData.push(vectorRealizationTrace); this._hasRealizationsTracesColoredByParameter = true; @@ -309,8 +310,6 @@ export class SubplotBuilder { vectorsRealizationData: { vectorSpecification: VectorSpec; data: VectorRealizationData_api[] }[], useIncreasedBrightness: boolean ): void { - this._defaultScatterType = "scattergl"; - // Only allow selected vectors const selectedVectorsRealizationData = vectorsRealizationData.filter((vec) => this._selectedVectorSpecifications.some( @@ -349,6 +348,7 @@ export class SubplotBuilder { hoverTemplate: hoverTemplate, showLegend: addLegendForTraces, yaxis: `y${subplotIndex + 1}`, + type: this._scatterType, }); this._plotData.push(...vectorRealizationTraces); @@ -380,6 +380,7 @@ export class SubplotBuilder { hexColor: color, legendGroup: legendGroup, yaxis: `y${subplotIndex + 1}`, + type: this._scatterType, }); this._plotData.push(...vectorFanchartTraces); @@ -414,6 +415,7 @@ export class SubplotBuilder { legendGroup: legendGroup, yaxis: `y${subplotIndex + 1}`, lineWidth: lineWidth, + type: this._scatterType, }); this._plotData.push(...vectorStatisticsTraces); @@ -443,6 +445,7 @@ export class SubplotBuilder { vectorHistoricalData: elm.data, color: this._historyVectorColor, yaxis: `y${subplotIndex + 1}`, + type: this._scatterType, }); this._plotData.push(vectorHistoryTrace); }); diff --git a/frontend/src/modules/SimulationTimeSeriesMatrix/view.tsx b/frontend/src/modules/SimulationTimeSeriesMatrix/view.tsx index 873d0fb27..22c28a326 100644 --- a/frontend/src/modules/SimulationTimeSeriesMatrix/view.tsx +++ b/frontend/src/modules/SimulationTimeSeriesMatrix/view.tsx @@ -2,13 +2,10 @@ import React from "react"; import Plot from "react-plotly.js"; import { Ensemble } from "@framework/Ensemble"; -import { EnsembleIdent } from "@framework/EnsembleIdent"; import { ModuleFCProps } from "@framework/Module"; import { useEnsembleSet } from "@framework/WorkbenchSession"; import { useElementSize } from "@lib/hooks/useElementSize"; import { ColorScaleGradientType } from "@lib/utils/ColorScale"; -// Note: Have for debug render count info -import { isDevMode } from "@lib/utils/devMode"; import { useHistoricalVectorDataQueries, useStatisticalVectorDataQueries, useVectorDataQueries } from "./queryHooks"; import { GroupBy, State, VisualizationMode } from "./state"; @@ -21,12 +18,6 @@ import { } from "./utils/vectorSpecificationsAndQueriesUtils"; export const view = ({ moduleContext, workbenchSession, workbenchSettings }: ModuleFCProps) => { - // Leave this in until we get a feeling for React18/Plotly - const renderCount = React.useRef(0); - React.useEffect(function incrementRenderCount() { - renderCount.current = renderCount.current + 1; - }); - const wrapperDivRef = React.useRef(null); const wrapperDivSize = useElementSize(wrapperDivRef); @@ -49,29 +40,6 @@ export const view = ({ moduleContext, workbenchSession, workbenchSettings }: Mod gradientType: ColorScaleGradientType.Diverging, }); - // Get range for color scale - const uniqueEnsembleIdents: EnsembleIdent[] = []; - vectorSpecifications?.forEach((specification) => { - if (uniqueEnsembleIdents.some((ensembleIdent) => ensembleIdent.equals(specification.ensembleIdent))) return; - - uniqueEnsembleIdents.push(specification.ensembleIdent); - }); - - // Get ensemble objects from ensemble set - const selectedEnsembles: Ensemble[] = []; - for (const ensembleIdent of uniqueEnsembleIdents) { - const ensemble = ensembleSet.findEnsemble(ensembleIdent); - if (ensemble === null) continue; - - selectedEnsembles.push(ensemble); - } - - // Create parameter color scale helper - const doColorByParameter = colorRealizationsByParameter && parameterIdent !== null && selectedEnsembles.length > 0; - const parameterColorScaleHelper = doColorByParameter - ? new ContinuousParameterColorScaleHelper(parameterIdent, selectedEnsembles, parameterColorScale) - : null; - // Queries const vectorDataQueries = useVectorDataQueries( vectorSpecifications, @@ -96,6 +64,9 @@ export const view = ({ moduleContext, workbenchSession, workbenchSettings }: Mod ); // Map vector specifications and queries with data + // TODO: + // - Add loading state if 1 or more queries are loading? + // - Can check for equal length of useQueries arrays and the loadedVectorSpecificationsAndData arrays? const loadedVectorSpecificationsAndRealizationData = vectorSpecifications ? createLoadedVectorSpecificationAndDataArray(vectorSpecifications, vectorDataQueries) : []; @@ -109,40 +80,52 @@ export const view = ({ moduleContext, workbenchSession, workbenchSettings }: Mod ) : []; - // TODO: - // - Add loading state if 1 or more queries are loading? - // - Can check for equal length of useQueries arrays and the loadedVectorSpecificationsAndData arrays? + // Retrieve selected ensembles from vector specifications + const selectedEnsembles: Ensemble[] = []; + vectorSpecifications?.forEach((vectorSpecification) => { + if (selectedEnsembles.some((ensemble) => ensemble.getIdent().equals(vectorSpecification.ensembleIdent))) { + return; + } - // Iterate over unique ensemble names and assign color from color palette - if (vectorSpecifications) { - const uniqueEnsembleNames: string[] = []; - vectorSpecifications.forEach((vectorSpec) => { - const ensembleName = vectorSpec.ensembleIdent.getEnsembleName(); - if (!uniqueEnsembleNames.includes(ensembleName)) { - uniqueEnsembleNames.push(vectorSpec.ensembleIdent.getEnsembleName()); - } - }); - } + const ensemble = ensembleSet.findEnsemble(vectorSpecification.ensembleIdent); + if (ensemble === null) return; + + selectedEnsembles.push(ensemble); + }); - // Plot builder - // NOTE: useRef? + // Create parameter color scale helper + const doColorByParameter = + colorRealizationsByParameter && + parameterIdent !== null && + selectedEnsembles.some((ensemble) => ensemble.getParameters().findParameter(parameterIdent)); + const parameterColorScaleHelper = doColorByParameter + ? new ContinuousParameterColorScaleHelper(parameterIdent, selectedEnsembles, parameterColorScale) + : null; + + // Create Plot Builder const subplotOwner = groupBy === GroupBy.TIME_SERIES ? SubplotOwner.VECTOR : SubplotOwner.ENSEMBLE; + const scatterType = + visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS || + visualizationMode === VisualizationMode.STATISTICS_AND_REALIZATIONS + ? "scattergl" + : "scatter"; const subplotBuilder = new SubplotBuilder( subplotOwner, vectorSpecifications ?? [], colorSet, wrapperDivSize.width, wrapperDivSize.height, - parameterColorScaleHelper ?? undefined + parameterColorScaleHelper ?? undefined, + scatterType ); - if (visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS) { - if (doColorByParameter) { - subplotBuilder.addRealizationTracesColoredByParameter(loadedVectorSpecificationsAndRealizationData); - } else { - const useIncreasedBrightness = false; - subplotBuilder.addRealizationsTraces(loadedVectorSpecificationsAndRealizationData, useIncreasedBrightness); - } + // Add traces based on visualization mode + if (doColorByParameter && visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS) { + subplotBuilder.addRealizationTracesColoredByParameter(loadedVectorSpecificationsAndRealizationData); + } + if (!doColorByParameter && visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS) { + const useIncreasedBrightness = false; + subplotBuilder.addRealizationsTraces(loadedVectorSpecificationsAndRealizationData, useIncreasedBrightness); } if (visualizationMode === VisualizationMode.STATISTICAL_FANCHART) { const selectedVectorsFanchartStatisticData = filterVectorSpecificationAndFanchartStatisticsDataArray( @@ -194,12 +177,6 @@ export const view = ({ moduleContext, workbenchSession, workbenchSettings }: Mod onHover={handleHover} onUnhover={handleUnHover} /> - {isDevMode() && ( - <> -
(rc={renderCount.current})
-
Traces: {plotData.length}
- - )} ); };