#13903 Harmonize correlation and tornado bar-chart behavior

Extract shared helpers into a new RimCorrelationBarChartTools
namespace so RimCorrelationPlot and RimRftTornadoPlot share a
single source of truth for:

- highlightSelectedParameterBar and the click-to-parameter-name
  lookup
- the axis-label format, abs/sort-value math, and the
  addBarEntry call (with the "legendText must equal parameter
  name" convention) via addCorrelationBar

Also enable click-to-select on RimCorrelationPlot: use the
parameter name as the bar chart title so click-to-select works,
and recolor the selected bar via a new HighlightBarColor field,
mirroring the pattern used in RimRftTornadoPlot.
This commit is contained in:
Magne Sjaastad
2026-04-20 11:41:26 +02:00
parent 4dfe238fef
commit 99910c3d7d
8 changed files with 183 additions and 75 deletions
@@ -8,6 +8,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimCorrelationReportPlot.h
${CMAKE_CURRENT_LIST_DIR}/RimRftCorrelationReportPlot.h
${CMAKE_CURRENT_LIST_DIR}/RimRftTornadoPlot.h
${CMAKE_CURRENT_LIST_DIR}/RimCorrelationBarChartTools.h
)
set(SOURCE_GROUP_SOURCE_FILES
@@ -20,6 +21,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimCorrelationReportPlot.cpp
${CMAKE_CURRENT_LIST_DIR}/RimRftCorrelationReportPlot.cpp
${CMAKE_CURRENT_LIST_DIR}/RimRftTornadoPlot.cpp
${CMAKE_CURRENT_LIST_DIR}/RimCorrelationBarChartTools.cpp
)
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
@@ -0,0 +1,89 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimCorrelationBarChartTools.h"
#include "RiuGroupedBarChartBuilder.h"
#include "RiuPlotItem.h"
#include "RiuQwtPlotItem.h"
#include "RiuQwtPlotWidget.h"
#include "qwt_column_symbol.h"
#include "qwt_plot.h"
#include "qwt_plot_barchart.h"
#include "qwt_text.h"
#include <cmath>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCorrelationBarChartTools::highlightSelectedParameterBar( RiuQwtPlotWidget* plotWidget,
const QString& selectedParamName,
const QColor& barColor,
const QColor& highlightColor )
{
if ( !plotWidget ) return;
for ( QwtPlotItem* item : plotWidget->qwtPlot()->itemList( QwtPlotItem::Rtti_PlotBarChart ) )
{
auto* barChart = static_cast<QwtPlotBarChart*>( item );
auto* symbol = const_cast<QwtColumnSymbol*>( barChart->symbol() );
if ( !symbol ) continue;
const QColor color = ( barChart->title().text() == selectedParamName ) ? highlightColor : barColor;
QPalette palette = symbol->palette();
palette.setColor( QPalette::Window, color );
palette.setColor( QPalette::Dark, color );
symbol->setPalette( palette );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimCorrelationBarChartTools::parameterNameFromPlotItem( std::shared_ptr<RiuPlotItem> plotItem )
{
auto* qwtPlotItem = dynamic_cast<RiuQwtPlotItem*>( plotItem.get() );
if ( !qwtPlotItem ) return {};
auto* barChart = dynamic_cast<QwtPlotBarChart*>( qwtPlotItem->qwtPlotItem() );
if ( !barChart ) return {};
return barChart->title().text();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCorrelationBarChartTools::addCorrelationBar( RiuGroupedBarChartBuilder& chartBuilder,
const QString& parameterName,
double correlation,
bool showAbsoluteValues,
bool sortByAbsoluteValues )
{
const double value = showAbsoluteValues ? std::abs( correlation ) : correlation;
const double sortValue = sortByAbsoluteValues ? std::abs( value ) : value;
const QString axisLabel = QString( "%1 (%2)" ).arg( parameterName ).arg( correlation, 5, 'f', 2 );
// legendText (5th arg) becomes barChart->title() and is used for click-to-select and
// selection highlight; it must equal parameterName.
// barText (6th arg) is shown on the axis label and can include the correlation value.
chartBuilder.addBarEntry( "", "", "", sortValue, parameterName, axisLabel, value );
}
@@ -0,0 +1,51 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include <QColor>
#include <QString>
#include <memory>
class RiuGroupedBarChartBuilder;
class RiuPlotItem;
class RiuQwtPlotWidget;
//==================================================================================================
///
/// Presentation helpers shared by the correlation bar/tornado plots (RimCorrelationPlot,
/// RimRftTornadoPlot). The plots rely on the convention that the legendText passed to
/// RiuGroupedBarChartBuilder::addBarEntry equals the parameter name, so the bar chart's
/// title can be used to identify which parameter was clicked or should be highlighted.
///
//==================================================================================================
namespace RimCorrelationBarChartTools
{
void highlightSelectedParameterBar( RiuQwtPlotWidget* plotWidget,
const QString& selectedParamName,
const QColor& barColor,
const QColor& highlightColor );
QString parameterNameFromPlotItem( std::shared_ptr<RiuPlotItem> plotItem );
void addCorrelationBar( RiuGroupedBarChartBuilder& chartBuilder,
const QString& parameterName,
double correlation,
bool showAbsoluteValues,
bool sortByAbsoluteValues );
} // namespace RimCorrelationBarChartTools
@@ -24,11 +24,11 @@
#include "RiuGroupedBarChartBuilder.h"
#include "RiuPlotMainWindowTools.h"
#include "RiuQwtPlotItem.h"
#include "RiuQwtPlotWidget.h"
#include "RifSummaryReaderInterface.h"
#include "RimCorrelationBarChartTools.h"
#include "RimDeltaSummaryCase.h"
#include "RimEnsembleCurveSet.h"
#include "RimPlotAxisProperties.h"
@@ -44,7 +44,6 @@
#include "cafPdmUiTreeSelectionEditor.h"
#include "qwt_plot.h"
#include "qwt_plot_barchart.h"
#include "qwt_text.h"
#include <limits>
@@ -75,6 +74,8 @@ RimCorrelationPlot::RimCorrelationPlot()
// Color taken from https://webviz-subsurface-example.azurewebsites.net/parameters-vs-rft
QColor qColor = QColor( "#3173b2" );
CAF_PDM_InitField( &m_barColor, "BarColor", RiaColorTools::fromQColorTo3f( qColor ), "Bar Color" );
QColor highlightColor = QColor( "#f5a623" );
CAF_PDM_InitField( &m_highlightBarColor, "HighlightBarColor", RiaColorTools::fromQColorTo3f( highlightColor ), "Bar Color (Selected)" );
setLegendsVisible( false );
setDeletable( true );
@@ -99,7 +100,8 @@ void RimCorrelationPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedFie
if ( changedField == &m_showAbsoluteValues || changedField == &m_sortByAbsoluteValues ||
changedField == &m_excludeParametersWithoutVariation || changedField == &m_selectedParametersList ||
changedField == &m_showOnlyTopNCorrelations || changedField == &m_topNFilterCount || changedField == &m_barColor )
changedField == &m_showOnlyTopNCorrelations || changedField == &m_topNFilterCount || changedField == &m_barColor ||
changedField == &m_highlightBarColor )
{
if ( changedField == &m_excludeParametersWithoutVariation )
{
@@ -142,6 +144,7 @@ void RimCorrelationPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrder
plotGroup->add( &m_axisTitleFontSize );
plotGroup->add( &m_axisValueFontSize );
plotGroup->add( &m_barColor );
plotGroup->add( &m_highlightBarColor );
m_description.uiCapability()->setUiReadOnly( m_useAutoPlotTitle() );
uiOrdering.skipRemainingFields( true );
@@ -191,6 +194,10 @@ void RimCorrelationPlot::onLoadDataAndUpdate()
chartBuilder.addBarChartToPlot( m_plotWidget->qwtPlot(), Qt::Horizontal, m_showOnlyTopNCorrelations() ? m_topNFilterCount() : -1 );
chartBuilder.setLabelFontSize( labelFontSize() );
RimCorrelationBarChartTools::highlightSelectedParameterBar( m_plotWidget,
m_selectedParameter,
RiaColorTools::toQColor( m_barColor() ),
RiaColorTools::toQColor( m_highlightBarColor() ) );
m_plotWidget->qwtPlot()->insertLegend( nullptr );
m_plotWidget->updateLegend();
@@ -247,11 +254,11 @@ void RimCorrelationPlot::addDataToChartBuilder( RiuGroupedBarChartBuilder& chart
for ( auto parameterCorrPair : correlations )
{
double value = m_showAbsoluteValues() ? std::abs( parameterCorrPair.second ) : parameterCorrPair.second;
double sortValue = m_sortByAbsoluteValues() ? std::abs( value ) : value;
QString barText = QString( "%1 (%2)" ).arg( parameterCorrPair.first.name ).arg( parameterCorrPair.second, 5, 'f', 2 );
QString majorText = "", medText = "", minText = "", legendText = barText;
chartBuilder.addBarEntry( majorText, medText, minText, sortValue, legendText, barText, value );
RimCorrelationBarChartTools::addCorrelationBar( chartBuilder,
parameterCorrPair.first.name,
parameterCorrPair.second,
m_showAbsoluteValues(),
m_sortByAbsoluteValues() );
}
}
@@ -275,24 +282,12 @@ void RimCorrelationPlot::updatePlotTitle()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCorrelationPlot::onPlotItemSelected( std::shared_ptr<RiuPlotItem> plotItem, bool toggle, int sampleIndex )
void RimCorrelationPlot::onPlotItemSelected( std::shared_ptr<RiuPlotItem> plotItem, bool /*toggle*/, int /*sampleIndex*/ )
{
RiuQwtPlotItem* qwtPlotItem = dynamic_cast<RiuQwtPlotItem*>( plotItem.get() );
if ( !qwtPlotItem ) return;
const QString paramName = RimCorrelationBarChartTools::parameterNameFromPlotItem( plotItem );
if ( paramName.isEmpty() || curveDefinitions().empty() ) return;
QwtPlotBarChart* barChart = dynamic_cast<QwtPlotBarChart*>( qwtPlotItem->qwtPlotItem() );
if ( barChart && !curveDefinitions().empty() )
{
auto curveDef = curveDefinitions().front();
auto barTitle = barChart->title();
for ( auto param : ensembleParameters() )
{
if ( barTitle.text() == param.name )
{
tornadoItemSelected.send( std::make_pair( param.name, curveDef ) );
}
}
}
tornadoItemSelected.send( std::make_pair( paramName, curveDefinitions().front() ) );
}
//--------------------------------------------------------------------------------------------------
@@ -369,3 +364,11 @@ void RimCorrelationPlot::setTopNFilterCount( int filterCount )
{
m_topNFilterCount = filterCount;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCorrelationPlot::setSelectedParameter( const QString& paramName )
{
m_selectedParameter = paramName;
}
@@ -55,6 +55,8 @@ public:
void setShowOnlyTopNCorrelations( bool showOnlyTopNCorrelations );
void setTopNFilterCount( int filterCount );
void setSelectedParameter( const QString& paramName );
private:
// Overridden PDM methods
@@ -79,4 +81,7 @@ private:
caf::PdmField<int> m_topNFilterCount;
caf::PdmField<std::vector<QString>> m_selectedParametersList;
caf::PdmField<cvf::Color3f> m_barColor;
caf::PdmField<cvf::Color3f> m_highlightBarColor;
QString m_selectedParameter;
};
@@ -714,6 +714,7 @@ void RimCorrelationReportPlot::onDataSelection( const caf::SignalEmitter*
auto curveDef = parameterAndCurveDef.second;
m_correlationPlot->setCurveDefinitions( { curveDef } );
m_correlationPlot->setSelectedParameter( paramName );
m_correlationPlot->loadDataAndUpdate();
m_parameterResultCrossPlot->setCurveDefinitions( { curveDef } );
m_parameterResultCrossPlot->setEnsembleParameter( paramName );
@@ -24,6 +24,7 @@
#include "RigEnsembleParameter.h"
#include "RigStatisticsTools.h"
#include "RimCorrelationBarChartTools.h"
#include "RimEclipseResultCase.h"
#include "RimParameterRftCrossPlot.h"
#include "RimSummaryEnsemble.h"
@@ -32,14 +33,11 @@
#include "RiuContextMenuLauncher.h"
#include "RiuGroupedBarChartBuilder.h"
#include "RiuPlotItem.h"
#include "RiuQwtPlotItem.h"
#include "RiuQwtPlotWidget.h"
#include "cafPdmUiCheckBoxEditor.h"
#include "qwt_column_symbol.h"
#include "qwt_plot.h"
#include "qwt_plot_barchart.h"
#include "qwt_text.h"
#include <QPaintDevice>
@@ -293,7 +291,10 @@ void RimRftTornadoPlot::onLoadDataAndUpdate()
const int labelSize = caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_labelFontSize() );
chartBuilder.setLabelFontSize( labelSize );
chartBuilder.addBarChartToPlot( m_plotWidget->qwtPlot(), Qt::Horizontal, m_showOnlyTopNCorrelations() ? m_topNFilterCount() : -1 );
highlightSelectedParameterBar();
RimCorrelationBarChartTools::highlightSelectedParameterBar( m_plotWidget,
m_selectedParameter,
RiaColorTools::toQColor( m_barColor() ),
RiaColorTools::toQColor( m_highlightBarColor() ) );
m_plotWidget->qwtPlot()->insertLegend( nullptr );
@@ -345,11 +346,8 @@ void RimRftTornadoPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedFiel
//--------------------------------------------------------------------------------------------------
void RimRftTornadoPlot::onPlotItemSelected( std::shared_ptr<RiuPlotItem> plotItem, bool /*toggle*/, int /*sampleIndex*/ )
{
auto* qwtPlotItem = dynamic_cast<RiuQwtPlotItem*>( plotItem.get() );
if ( !qwtPlotItem ) return;
auto* barChart = dynamic_cast<QwtPlotBarChart*>( qwtPlotItem->qwtPlotItem() );
if ( barChart && m_parameterSelectedCallback ) m_parameterSelectedCallback( barChart->title().text() );
const QString paramName = RimCorrelationBarChartTools::parameterNameFromPlotItem( plotItem );
if ( !paramName.isEmpty() && m_parameterSelectedCallback ) m_parameterSelectedCallback( paramName );
}
//--------------------------------------------------------------------------------------------------
@@ -397,52 +395,12 @@ std::map<QString, double> RimRftTornadoPlot::addDataToChartBuilder( RiuGroupedBa
correlations[param.name] = pearson;
double value = m_showAbsoluteValues() ? std::abs( pearson ) : pearson;
double sortValue = m_sortByAbsoluteValues() ? std::abs( value ) : value;
// legendText becomes barChart->title() and is used for click-to-select; must equal param.name.
// barText is shown on the axis label and can include the correlation value.
QString axisLabel = QString( "%1 (%2)" ).arg( param.name ).arg( pearson, 5, 'f', 2 );
chartBuilder.addBarEntry( "", "", "", sortValue, param.name, axisLabel, value );
RimCorrelationBarChartTools::addCorrelationBar( chartBuilder, param.name, pearson, m_showAbsoluteValues(), m_sortByAbsoluteValues() );
}
return correlations;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimRftTornadoPlot::highlightSelectedParameterBar()
{
if ( !m_plotWidget ) return;
const QColor highlightColor = RiaColorTools::toQColor( m_highlightBarColor() );
const QColor barColor = RiaColorTools::toQColor( m_barColor() );
for ( QwtPlotItem* item : m_plotWidget->qwtPlot()->itemList( QwtPlotItem::Rtti_PlotBarChart ) )
{
auto* barChart = static_cast<QwtPlotBarChart*>( item );
auto* symbol = const_cast<QwtColumnSymbol*>( barChart->symbol() );
if ( !symbol ) continue;
const QString paramName = barChart->title().text();
QColor color;
if ( paramName == m_selectedParameter )
{
color = highlightColor;
}
else
{
color = barColor;
}
QPalette palette = symbol->palette();
palette.setColor( QPalette::Window, color );
palette.setColor( QPalette::Dark, color );
symbol->setPalette( palette );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -88,7 +88,6 @@ private:
void onPlotItemSelected( std::shared_ptr<RiuPlotItem> plotItem, bool toggle, int sampleIndex ) override;
std::map<QString, double> addDataToChartBuilder( class RiuGroupedBarChartBuilder& chartBuilder ) const;
void highlightSelectedParameterBar();
void updatePlotTitle();
void cleanupBeforeClose();