ResInsight/ApplicationLibCode/ProjectDataModel/AnalysisPlots/RimAnalysisPlot.cpp
Magne Sjaastad 40080a99de
9978 Improve UI for long drop-down lists, use tree selection more
* Improve tree selection editor
- always call defineEditorAttributes
- use heightHint in editor attributes 
- use tree selection editor as default editor for std::vector

* Use tree selection editor instead of list selection editor
List selection editor must be used when editing std::vector<cvf::vec3d> and similar. Replace other use of list selection editor with tree selection editor.

* Set checked state based on text string for integer only models
For models with only integer values, use text string to define the items to be selected. The full list will always be visible, and the checked state will be updated when editing the filter text.

Example: "1, 5-7" will set items 1, 5, 6, 7 checked and all other items unchecked

* Minor fixes
- Set placeholder text after content is added (to ensure correct data type)
- Fix check of integers. `canConvert<int>()`returns true for both QString and int. Thus convert to string and then check for int conversion.

* Activate filtering when unchecking all items in list with only integers
- Reactivate filtering when uncheck of all items for a list with only integer values (to keep consistency between filter and list)
- Update function name for clarity

---------

Co-authored-by: Jørgen Herje <jorgen.herje@ceetronsolutions.com>
2023-05-22 15:44:37 +02:00

1778 lines
72 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2020 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 "RimAnalysisPlot.h"
#include "RiaDefines.h"
#include "RiaPlotDefines.h"
#include "RiaPreferences.h"
#include "RiaQDateTimeTools.h"
#include "RiaSummaryCurveDefinition.h"
#include "RiaTextStringTools.h"
#include "RiuGroupedBarChartBuilder.h"
#include "RiuPlotMainWindowTools.h"
#include "RiuQwtPlotTools.h"
#include "RiuSummaryQwtPlot.h"
#include "RiuSummaryVectorSelectionDialog.h"
#include "RifSummaryReaderInterface.h"
#include "RimAnalysisPlotDataEntry.h"
#include "RimDerivedSummaryCase.h"
#include "RimPlotAxisProperties.h"
#include "RimPlotAxisPropertiesInterface.h"
#include "RimPlotDataFilterCollection.h"
#include "RimProject.h"
#include "RimSummaryCase.h"
#include "RimSummaryCaseCollection.h"
#include "RimSummaryPlotAxisFormatter.h"
#include "qwt_column_symbol.h"
#include "qwt_legend.h"
#include "qwt_painter.h"
#include "qwt_plot_barchart.h"
#include "qwt_scale_draw.h"
#include "cafPdmUiActionPushButtonEditor.h"
#include "cafPdmUiCheckBoxEditor.h"
#include "cafPdmUiComboBoxEditor.h"
#include "cafPdmUiGroup.h"
#include "cafPdmUiTreeSelectionEditor.h"
#include <cmath>
#include <limits>
#include <map>
namespace caf
{
template <>
void caf::AppEnum<RimAnalysisPlot::SortGroupType>::setUp()
{
addItem( RimAnalysisPlot::NONE, "NONE", "None" );
addItem( RimAnalysisPlot::SUMMARY_ITEM, "SUMMARY_ITEM", "Summary Item" );
addItem( RimAnalysisPlot::VECTOR, "VECTOR", "Vector" );
addItem( RimAnalysisPlot::CASE, "CASE", "Case" );
addItem( RimAnalysisPlot::ENSEMBLE, "ENSEMBLE", "Ensemble" );
addItem( RimAnalysisPlot::VALUE, "VALUE", "Value" );
addItem( RimAnalysisPlot::ABS_VALUE, "ABS_VALUE", "abs(Value)" );
addItem( RimAnalysisPlot::OTHER_VALUE, "OTHER_VALUE", "Other Value" );
addItem( RimAnalysisPlot::ABS_OTHER_VALUE, "ABS_OTHER_VALUE", "abs(Other Value)" );
addItem( RimAnalysisPlot::TIME_STEP, "TIME_STEP", "Time Step" );
setDefault( RimAnalysisPlot::NONE );
}
template <>
void caf::AppEnum<RimAnalysisPlot::BarOrientation>::setUp()
{
addItem( RimAnalysisPlot::BARS_HORIZONTAL, "BARS_HORIZONTAL", "Horizontal" );
addItem( RimAnalysisPlot::BARS_VERTICAL, "BARS_VERTICAL", "Vertical" );
setDefault( RimAnalysisPlot::BARS_VERTICAL );
}
} // namespace caf
CAF_PDM_SOURCE_INIT( RimAnalysisPlot, "AnalysisPlot" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimAnalysisPlot::RimAnalysisPlot()
: RimPlot()
{
CAF_PDM_InitObject( "Analysis Plot", ":/AnalysisPlot16x16.png" );
// Variable selection
CAF_PDM_InitFieldNoDefault( &m_selectedVarsUiField, "selectedVarsUiField", "Selected Vectors" );
m_selectedVarsUiField.xmlCapability()->disableIO();
m_selectedVarsUiField.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
m_selectedVarsUiField.uiCapability()->setUiReadOnly( true );
CAF_PDM_InitField( &m_selectVariablesButtonField, "BrowseButton", false, "..." );
caf::PdmUiActionPushButtonEditor::configureEditorForField( &m_selectVariablesButtonField );
CAF_PDM_InitFieldNoDefault( &m_analysisPlotDataSelection, "AnalysisPlotData", "" );
m_analysisPlotDataSelection.uiCapability()->setUiTreeChildrenHidden( true );
m_analysisPlotDataSelection.uiCapability()->setUiTreeHidden( true );
// Time Step Selection
CAF_PDM_InitFieldNoDefault( &m_timeStepFilter, "TimeStepFilter", "Available Time Steps" );
CAF_PDM_InitFieldNoDefault( &m_selectedTimeSteps, "TimeSteps", "Select Time Steps" );
m_selectedTimeSteps.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
m_selectedTimeSteps.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::TOP );
// Options
CAF_PDM_InitFieldNoDefault( &m_referenceCase, "ReferenceCase", "Reference Case" );
CAF_PDM_InitField( &m_useAutoPlotTitle, "IsUsingAutoName", true, "Auto" );
caf::PdmUiNativeCheckBoxEditor::configureFieldForEditor( &m_useAutoPlotTitle );
CAF_PDM_InitField( &m_description, "PlotDescription", QString( "Analysis Plot" ), "Title" );
m_description.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
CAF_PDM_InitFieldNoDefault( &m_barOrientation, "BarOrientation", "Bar Orientation" );
// Grouping
CAF_PDM_InitFieldNoDefault( &m_majorGroupType, "MajorGroupType", "Major Grouping" );
CAF_PDM_InitFieldNoDefault( &m_mediumGroupType, "MediumGroupType", "Medium Grouping" );
CAF_PDM_InitFieldNoDefault( &m_minorGroupType, "MinorGroupType", "Minor Grouping" );
CAF_PDM_InitFieldNoDefault( &m_valueSortOperation, "ValueSortOperation", "Sort by Value" );
CAF_PDM_InitFieldNoDefault( &m_sortGroupForColors, "groupForColors", "Coloring Using" );
m_sortGroupForColors = RimAnalysisPlot::CASE;
m_showPlotLegends = false;
CAF_PDM_InitField( &m_useTopBarsFilter, "UseTopBarsFilter", false, "Show Only Top" );
caf::PdmUiNativeCheckBoxEditor::configureFieldForEditor( &m_useTopBarsFilter );
CAF_PDM_InitField( &m_maxBarCount, "MaxBarCount", 20, "Bar Count" );
m_maxBarCount.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
// Bar text
CAF_PDM_InitField( &m_useBarText, "UseBarText", true, "Activate Bar Labels" );
caf::PdmUiNativeCheckBoxEditor::configureFieldForEditor( &m_useBarText );
CAF_PDM_InitField( &m_useCaseInBarText, "UseCaseInBarText", true, "Case Name" );
CAF_PDM_InitField( &m_useEnsembleInBarText, "UseEnsembleInBarText", false, "Ensemble" );
CAF_PDM_InitField( &m_useSummaryItemInBarText, "UseSummaryItemInBarText", false, "Summary Item" );
CAF_PDM_InitField( &m_useTimeStepInBarText, "UseTimeStepInBarText", false, "Time Step" );
CAF_PDM_InitField( &m_useVectorNameInBarText, "UseQuantityInBarText", false, "Vector" );
CAF_PDM_InitFieldNoDefault( &m_barTextFontSize, "BarTextFontSize", "Font Size" );
CAF_PDM_InitFieldNoDefault( &m_valueAxisProperties, "ValueAxisProperties", "ValueAxisProperties" );
m_valueAxisProperties.uiCapability()->setUiTreeHidden( true );
m_valueAxisProperties = new RimPlotAxisProperties;
m_valueAxisProperties->setNameAndAxis( "Value-Axis", "Value-Axis", RiuQwtPlotTools::fromQwtPlotAxis( QwtAxis::YLeft ) );
m_valueAxisProperties->enableRangeSettings( false );
CAF_PDM_InitFieldNoDefault( &m_plotDataFilterCollection, "PlotDataFilterCollection", "PlotDataFilterCollection" );
m_plotDataFilterCollection.uiCapability()->setUiTreeHidden( true );
m_plotDataFilterCollection = new RimPlotDataFilterCollection;
connectAxisSignals( m_valueAxisProperties() );
m_plotDataFilterCollection->filtersChanged.connect( this, &RimAnalysisPlot::onFiltersChanged );
setDeletable( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimAnalysisPlot::~RimAnalysisPlot()
{
removeMdiWindowFromMdiArea();
cleanupBeforeClose();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::updateCaseNameHasChanged()
{
this->onLoadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimPlotDataFilterCollection* RimAnalysisPlot::plotDataFilterCollection() const
{
return m_plotDataFilterCollection;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::setCurveDefinitions( const std::vector<RiaSummaryCurveDefinition>& curveDefinitions )
{
m_analysisPlotDataSelection.deleteChildren();
for ( auto curveDef : curveDefinitions )
{
auto dataEntry = new RimAnalysisPlotDataEntry();
dataEntry->setFromCurveDefinition( curveDef );
m_analysisPlotDataSelection.push_back( dataEntry );
}
connectAllCaseSignals();
auto timeSteps = allAvailableTimeSteps();
if ( m_selectedTimeSteps().empty() && !timeSteps.empty() )
{
m_selectedTimeSteps.v().push_back( RiaQDateTimeTools::fromTime_t( *timeSteps.rbegin() ) );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::setTimeSteps( const std::vector<time_t>& timeSteps )
{
m_selectedTimeSteps.v().clear();
for ( auto time : timeSteps )
{
m_selectedTimeSteps.v().push_back( RiaQDateTimeTools::fromTime_t( time ) );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<RifEclipseSummaryAddress> RimAnalysisPlot::unfilteredAddresses()
{
std::set<RifEclipseSummaryAddress> addresses;
RiaSummaryCurveDefinitionAnalyser* analyserOfSelectedCurveDefs = getOrCreateSelectedCurveDefAnalyser();
for ( RimSummaryCase* sumCase : analyserOfSelectedCurveDefs->m_singleSummaryCases )
{
const std::set<RifEclipseSummaryAddress>& caseAddrs = sumCase->summaryReader()->allResultAddresses();
addresses.insert( caseAddrs.begin(), caseAddrs.end() );
}
return addresses;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<RigEnsembleParameter> RimAnalysisPlot::ensembleParameters()
{
std::set<RigEnsembleParameter> ensembleParms;
RiaSummaryCurveDefinitionAnalyser* analyserOfSelectedCurveDefs = getOrCreateSelectedCurveDefAnalyser();
std::set<RimSummaryCaseCollection*> ensembles;
for ( RimSummaryCase* sumCase : analyserOfSelectedCurveDefs->m_singleSummaryCases )
{
if ( sumCase->ensemble() )
{
ensembles.insert( sumCase->ensemble() );
}
}
for ( RimSummaryCaseCollection* ensemble : ensembles )
{
std::vector<RigEnsembleParameter> parameters = ensemble->variationSortedEnsembleParameters();
ensembleParms.insert( parameters.begin(), parameters.end() );
}
return ensembleParms;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigEnsembleParameter RimAnalysisPlot::ensembleParameter( const QString& ensembleParameterName )
{
std::set<RigEnsembleParameter> ensembleParms = ensembleParameters();
for ( const RigEnsembleParameter& eParam : ensembleParms )
{
if ( eParam.name == ensembleParameterName ) return eParam;
}
return RigEnsembleParameter();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::maxMinValueFromAddress( const RifEclipseSummaryAddress& address,
RimPlotDataFilterItem::TimeStepSourceType timeStepSourceType,
const std::vector<QDateTime>& timeRangeOrSelection,
bool useAbsValue,
double* minVal,
double* maxVal )
{
double min = std::numeric_limits<double>::infinity();
double max = useAbsValue ? 0.0 : -std::numeric_limits<double>::infinity();
std::function<double( double, double )> minOrAbsMin;
std::function<double( double, double )> maxOrAbsMax;
if ( useAbsValue )
{
minOrAbsMin = []( double v1, double v2 ) { return std::min( fabs( v1 ), fabs( v2 ) ); };
maxOrAbsMax = []( double v1, double v2 ) { return std::max( fabs( v1 ), fabs( v2 ) ); };
}
else
{
minOrAbsMin = []( double v1, double v2 ) { return std::min( v1, v2 ); };
maxOrAbsMax = []( double v1, double v2 ) { return std::max( v1, v2 ); };
}
std::vector<time_t> selectedTimesteps;
if ( timeStepSourceType == RimPlotDataFilterItem::SELECT_TIMESTEPS )
{
for ( const QDateTime& dateTime : timeRangeOrSelection )
{
selectedTimesteps.push_back( dateTime.toSecsSinceEpoch() );
}
}
else if ( timeStepSourceType == RimPlotDataFilterItem::PLOT_SOURCE_TIMESTEPS )
{
selectedTimesteps = selectedTimeSteps();
}
std::set<RimSummaryCase*> allSumCases = allSourceCases();
for ( RimSummaryCase* sumCase : allSumCases )
{
RifSummaryReaderInterface* reader = sumCase->summaryReader();
if ( !reader ) continue;
if ( reader->hasAddress( address ) )
{
std::vector<double> values;
reader->values( address, &values );
const std::vector<time_t>& timesteps = reader->timeSteps( address );
if ( timesteps.size() && values.size() )
{
if ( timeStepSourceType == RimPlotDataFilterItem::LAST_TIMESTEP )
{
min = minOrAbsMin( min, values[timesteps.size() - 1] );
max = maxOrAbsMax( max, values[timesteps.size() - 1] );
}
else if ( timeStepSourceType == RimPlotDataFilterItem::FIRST_TIMESTEP )
{
min = minOrAbsMin( min, values[0] );
max = maxOrAbsMax( max, values[0] );
}
else if ( timeStepSourceType == RimPlotDataFilterItem::ALL_TIMESTEPS )
{
for ( size_t tIdx = 0; tIdx < timesteps.size(); ++tIdx )
{
min = minOrAbsMin( min, values[tIdx] );
max = maxOrAbsMax( max, values[tIdx] );
}
}
else if ( timeStepSourceType == RimPlotDataFilterItem::SELECT_TIMESTEP_RANGE )
{
if ( timeRangeOrSelection.size() >= 2 )
{
time_t minTime = timeRangeOrSelection.front().toSecsSinceEpoch();
time_t maxTime = timeRangeOrSelection.back().toSecsSinceEpoch();
for ( size_t tIdx = 0; tIdx < timesteps.size(); ++tIdx )
{
time_t dateTime = timesteps[tIdx];
if ( minTime <= dateTime && dateTime <= maxTime )
{
min = minOrAbsMin( min, values[tIdx] );
max = maxOrAbsMax( max, values[tIdx] );
}
}
}
}
else if ( timeStepSourceType == RimPlotDataFilterItem::LAST_TIMESTEP_WITH_HISTORY )
{
RifEclipseSummaryAddress historyAddr = address;
if ( !historyAddr.isHistoryVector() ) historyAddr.setVectorName( address.vectorName() + "H" );
const std::vector<time_t>& historyTimesteps = reader->timeSteps( historyAddr );
if ( historyTimesteps.size() )
{
min = minOrAbsMin( min, values[historyTimesteps.size() - 1] );
max = maxOrAbsMax( max, values[historyTimesteps.size() - 1] );
}
}
else if ( selectedTimesteps.size() )
{
std::vector<size_t> selectedTimestepIndices = RimAnalysisPlot::findTimestepIndices( selectedTimesteps, timesteps );
for ( size_t tsIdx : selectedTimestepIndices )
{
min = minOrAbsMin( min, values[tsIdx] );
max = maxOrAbsMax( max, values[tsIdx] );
}
}
}
}
}
( *minVal ) = min;
( *maxVal ) = max;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::onFiltersChanged( const caf::SignalEmitter* emitter )
{
this->loadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<time_t> RimAnalysisPlot::selectedTimeSteps()
{
std::vector<time_t> selectedTimeTTimeSteps;
for ( const QDateTime& dateTime : m_selectedTimeSteps.v() )
{
selectedTimeTTimeSteps.push_back( dateTime.toSecsSinceEpoch() );
}
return selectedTimeTTimeSteps;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
RimPlot::fieldChangedByUi( changedField, oldValue, newValue );
if ( changedField == &m_selectVariablesButtonField )
{
// Do select variables
RiuSummaryVectorSelectionDialog dlg( nullptr );
dlg.enableMultiSelect( true );
dlg.enableIndividualEnsembleCaseSelection( true );
dlg.hideEnsembles();
dlg.setCurveSelection( this->curveDefinitions() );
if ( dlg.exec() == QDialog::Accepted )
{
std::vector<RiaSummaryCurveDefinition> summaryVectorDefinitions = dlg.curveSelection();
m_analysisPlotDataSelection.deleteChildren();
for ( const RiaSummaryCurveDefinition& vectorDef : summaryVectorDefinitions )
{
auto dataEntry = new RimAnalysisPlotDataEntry();
dataEntry->setFromCurveDefinition( vectorDef );
m_analysisPlotDataSelection.push_back( dataEntry );
}
connectAllCaseSignals();
}
m_selectVariablesButtonField = false;
}
else if ( changedField == &m_timeStepFilter )
{
m_selectedTimeSteps.v().clear();
this->updateConnectedEditors();
}
this->loadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
caf::PdmUiGroup* selVectorsGrp = uiOrdering.addNewGroup( "Selected Vectors" );
selVectorsGrp->add( &m_selectedVarsUiField );
selVectorsGrp->add( &m_selectVariablesButtonField, { false } );
selVectorsGrp->add( &m_referenceCase, { true, 3, 2 } );
QString vectorNames;
if ( getOrCreateSelectedCurveDefAnalyser() )
{
for ( const std::string& vectorName : getOrCreateSelectedCurveDefAnalyser()->m_vectorNames )
{
vectorNames += QString::fromStdString( vectorName ) + ", ";
}
if ( !vectorNames.isEmpty() )
{
vectorNames.chop( 2 );
}
}
if ( !vectorNames.isEmpty() )
{
m_selectedVarsUiField = vectorNames;
}
else
{
m_selectedVarsUiField = "Select Data Sources -->";
}
caf::PdmUiGroup* timeStepGrp = uiOrdering.addNewGroup( "Time Steps" );
timeStepGrp->add( &m_timeStepFilter );
timeStepGrp->add( &m_selectedTimeSteps );
caf::PdmUiGroup* titleGrp = uiOrdering.addNewGroup( "Title and Legend" );
titleGrp->add( &m_showPlotTitle );
titleGrp->add( &m_useAutoPlotTitle, { false } );
titleGrp->add( &m_description, { false } );
m_description.uiCapability()->setUiReadOnly( m_useAutoPlotTitle() );
titleGrp->add( &m_showPlotLegends );
titleGrp->add( &m_legendFontSize );
m_legendFontSize.uiCapability()->setUiReadOnly( !m_showPlotLegends() );
caf::PdmUiGroup* chartSettings = uiOrdering.addNewGroup( "Bar Settings" );
chartSettings->add( &m_barOrientation, { true, 3, 2 } );
chartSettings->add( &m_majorGroupType );
chartSettings->add( &m_mediumGroupType );
chartSettings->add( &m_minorGroupType );
chartSettings->add( &m_valueSortOperation );
chartSettings->add( &m_useTopBarsFilter );
chartSettings->add( &m_maxBarCount, { false } );
m_maxBarCount.uiCapability()->setUiReadOnly( !m_useTopBarsFilter() );
chartSettings->add( &m_sortGroupForColors );
caf::PdmUiGroup* barLabelGrp = uiOrdering.addNewGroup( "Bar Labels" );
barLabelGrp->add( &m_useBarText );
barLabelGrp->add( &m_barTextFontSize );
barLabelGrp->add( &m_useVectorNameInBarText );
barLabelGrp->add( &m_useSummaryItemInBarText );
barLabelGrp->add( &m_useCaseInBarText );
barLabelGrp->add( &m_useEnsembleInBarText );
barLabelGrp->add( &m_useTimeStepInBarText );
m_barTextFontSize.uiCapability()->setUiReadOnly( !m_useBarText );
m_useVectorNameInBarText.uiCapability()->setUiReadOnly( !m_useBarText );
m_useSummaryItemInBarText.uiCapability()->setUiReadOnly( !m_useBarText );
m_useCaseInBarText.uiCapability()->setUiReadOnly( !m_useBarText );
m_useEnsembleInBarText.uiCapability()->setUiReadOnly( !m_useBarText );
m_useTimeStepInBarText.uiCapability()->setUiReadOnly( !m_useBarText );
uiOrdering.skipRemainingFields( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmFieldHandle* RimAnalysisPlot::userDescriptionField()
{
return &m_description;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<caf::PdmOptionItemInfo> RimAnalysisPlot::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
{
QList<caf::PdmOptionItemInfo> options = RimPlot::calculateValueOptions( fieldNeedingOptions );
if ( !options.isEmpty() ) return options;
if ( fieldNeedingOptions == &m_selectedTimeSteps )
{
std::set<time_t> allTimeSteps = allAvailableTimeSteps();
std::set<QDateTime> currentlySelectedTimeSteps( m_selectedTimeSteps().begin(), m_selectedTimeSteps().end() );
if ( allTimeSteps.empty() )
{
return options;
}
std::set<int> currentlySelectedTimeStepIndices;
std::vector<QDateTime> allDateTimes;
for ( time_t timeStep : allTimeSteps )
{
QDateTime dateTime = RiaQDateTimeTools::fromTime_t( timeStep );
if ( currentlySelectedTimeSteps.count( dateTime ) )
{
currentlySelectedTimeStepIndices.insert( (int)allDateTimes.size() );
}
allDateTimes.push_back( dateTime );
}
std::vector<int> filteredTimeStepIndices =
RimTimeStepFilter::filteredTimeStepIndices( allDateTimes, 0, (int)allDateTimes.size() - 1, m_timeStepFilter(), 1 );
// Add existing time steps to list of options to avoid removing them when changing filter.
filteredTimeStepIndices.insert( filteredTimeStepIndices.end(),
currentlySelectedTimeStepIndices.begin(),
currentlySelectedTimeStepIndices.end() );
std::sort( filteredTimeStepIndices.begin(), filteredTimeStepIndices.end() );
filteredTimeStepIndices.erase( std::unique( filteredTimeStepIndices.begin(), filteredTimeStepIndices.end() ),
filteredTimeStepIndices.end() );
QString dateFormatString = RiaQDateTimeTools::dateFormatString( RiaPreferences::current()->dateFormat(),
RiaDefines::DateFormatComponents::DATE_FORMAT_YEAR_MONTH_DAY );
QString timeFormatString = RiaQDateTimeTools::timeFormatString( RiaPreferences::current()->timeFormat(),
RiaDefines::TimeFormatComponents::TIME_FORMAT_HOUR_MINUTE );
QString dateTimeFormatString = QString( "%1 %2" ).arg( dateFormatString ).arg( timeFormatString );
bool showTime = m_timeStepFilter() == RimTimeStepFilter::TS_ALL || m_timeStepFilter() == RimTimeStepFilter::TS_INTERVAL_DAYS;
for ( auto timeStepIndex : filteredTimeStepIndices )
{
QDateTime dateTime = allDateTimes[timeStepIndex];
if ( showTime && dateTime.time() != QTime( 0, 0, 0 ) )
{
options.push_back(
caf::PdmOptionItemInfo( RiaQDateTimeTools::toStringUsingApplicationLocale( dateTime, dateTimeFormatString ), dateTime ) );
}
else
{
options.push_back(
caf::PdmOptionItemInfo( RiaQDateTimeTools::toStringUsingApplicationLocale( dateTime, dateFormatString ), dateTime ) );
}
}
}
else if ( fieldNeedingOptions == &m_valueSortOperation )
{
options.push_back( caf::PdmOptionItemInfo( SortGroupAppEnum::uiText( NONE ), NONE ) );
options.push_back( caf::PdmOptionItemInfo( SortGroupAppEnum::uiText( VALUE ), VALUE ) );
options.push_back( caf::PdmOptionItemInfo( SortGroupAppEnum::uiText( ABS_VALUE ), ABS_VALUE ) );
}
else if ( fieldNeedingOptions == &m_majorGroupType || fieldNeedingOptions == &m_mediumGroupType ||
fieldNeedingOptions == &m_minorGroupType || fieldNeedingOptions == &m_sortGroupForColors )
{
options.push_back( caf::PdmOptionItemInfo( SortGroupAppEnum::uiText( NONE ), NONE ) );
QStringList currentSummaryItems;
for ( auto summaryAddr : getOrCreateSelectedCurveDefAnalyser()->m_summaryAdresses )
{
currentSummaryItems.push_back( QString::fromStdString( summaryAddr.itemUiText() ) );
}
currentSummaryItems.removeDuplicates();
if ( !currentSummaryItems.isEmpty() )
{
QString exampleString = currentSummaryItems.join( ", " );
if ( exampleString.length() > 16 )
{
exampleString = exampleString.left( 13 ) + "...";
}
QString summaryItemText = QString( "%1 (%2)" ).arg( SortGroupAppEnum::uiText( SUMMARY_ITEM ) ).arg( exampleString );
options.push_back( caf::PdmOptionItemInfo( summaryItemText, SUMMARY_ITEM ) );
}
options.push_back( caf::PdmOptionItemInfo( SortGroupAppEnum::uiText( VECTOR ), VECTOR ) );
options.push_back( caf::PdmOptionItemInfo( SortGroupAppEnum::uiText( CASE ), CASE ) );
options.push_back( caf::PdmOptionItemInfo( SortGroupAppEnum::uiText( ENSEMBLE ), ENSEMBLE ) );
options.push_back( caf::PdmOptionItemInfo( SortGroupAppEnum::uiText( TIME_STEP ), TIME_STEP ) );
}
else if ( fieldNeedingOptions == &m_referenceCase )
{
std::vector<RimSummaryCase*> allSummaryCases = RimProject::current()->allSummaryCases();
options.push_back( { "None", nullptr } );
for ( auto sumCase : allSummaryCases )
{
QString displayName = sumCase->displayCaseName();
auto caseColl = dynamic_cast<RimSummaryCaseCollection*>( sumCase->parentField()->ownerObject() );
if ( caseColl )
{
displayName = caseColl->name() + "/" + displayName;
}
options.push_back( { displayName, sumCase } );
}
}
else if ( fieldNeedingOptions == &m_barTextFontSize )
{
options = caf::FontTools::relativeSizeValueOptions( RiaPreferences::current()->defaultPlotFontSize() );
}
return options;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<time_t> RimAnalysisPlot::allAvailableTimeSteps()
{
std::set<time_t> timeStepUnion;
for ( RimSummaryCase* sumCase : timestepDefiningSourceCases() )
{
if ( !sumCase || !sumCase->summaryReader() ) continue;
const std::vector<time_t>& timeSteps = sumCase->summaryReader()->timeSteps( RifEclipseSummaryAddress() );
for ( time_t t : timeSteps )
{
timeStepUnion.insert( t );
}
}
return timeStepUnion;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<RimSummaryCase*> RimAnalysisPlot::timestepDefiningSourceCases()
{
RiaSummaryCurveDefinitionAnalyser* analyserOfSelectedCurveDefs = getOrCreateSelectedCurveDefAnalyser();
std::set<RimSummaryCase*> timeStepDefiningSumCases = analyserOfSelectedCurveDefs->m_singleSummaryCases;
for ( auto ensemble : analyserOfSelectedCurveDefs->m_ensembles )
{
auto allSumCases = ensemble->allSummaryCases();
timeStepDefiningSumCases.insert( allSumCases.begin(), allSumCases.end() );
}
return timeStepDefiningSumCases;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<RimSummaryCase*> RimAnalysisPlot::allSourceCases()
{
RiaSummaryCurveDefinitionAnalyser* analyserOfSelectedCurveDefs = getOrCreateSelectedCurveDefAnalyser();
std::set<RimSummaryCase*> allSumCases = analyserOfSelectedCurveDefs->m_singleSummaryCases;
return allSumCases;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QWidget* RimAnalysisPlot::viewWidget()
{
return m_plotWidget;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::deleteViewWidget()
{
cleanupBeforeClose();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::onLoadDataAndUpdate()
{
updateMdiWindowVisibility();
getOrCreateSelectedCurveDefAnalyser();
if ( m_plotWidget )
{
m_plotWidget->qwtPlot()->detachItems( QwtPlotItem::Rtti_PlotBarChart );
m_plotWidget->qwtPlot()->detachItems( QwtPlotItem::Rtti_PlotScale );
RiuGroupedBarChartBuilder chartBuilder;
chartBuilder.setLabelFontSize( barTextFontSize() );
// buildTestPlot( chartBuilder );
addDataToChartBuilder( chartBuilder );
chartBuilder.addBarChartToPlot( m_plotWidget->qwtPlot(),
m_barOrientation == BARS_HORIZONTAL ? Qt::Horizontal : Qt::Vertical,
m_useTopBarsFilter() ? m_maxBarCount : -1 );
if ( m_showPlotLegends && m_plotWidget->qwtPlot()->legend() == nullptr )
{
QwtLegend* legend = new QwtLegend( m_plotWidget );
m_plotWidget->qwtPlot()->insertLegend( legend, QwtPlot::RightLegend );
}
else if ( !m_showPlotLegends )
{
m_plotWidget->qwtPlot()->insertLegend( nullptr );
}
m_plotWidget->setLegendFontSize( legendFontSize() );
m_plotWidget->updateLegend();
}
this->updateAxes();
this->updatePlotTitle();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QImage RimAnalysisPlot::snapshotWindowContent()
{
QImage image;
if ( m_plotWidget )
{
QPixmap pix = m_plotWidget->grab();
image = pix.toImage();
}
return image;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimAnalysisPlot::description() const
{
return m_description();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotWidget* RimAnalysisPlot::doCreatePlotViewWidget( QWidget* mainWindowParent /*= nullptr */ )
{
if ( !m_plotWidget )
{
m_plotWidget = new RiuQwtPlotWidget( this, mainWindowParent );
}
return m_plotWidget;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuQwtPlotWidget* RimAnalysisPlot::viewer()
{
return m_plotWidget;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotWidget* RimAnalysisPlot::plotWidget()
{
return m_plotWidget;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::detachAllCurves()
{
if ( m_plotWidget ) m_plotWidget->qwtPlot()->detachItems();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::updateAxes()
{
if ( !m_plotWidget ) return;
RiuPlotAxis axis = RiuPlotAxis::defaultLeft();
if ( m_barOrientation == BARS_HORIZONTAL )
{
axis = RiuPlotAxis::defaultBottom();
m_plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultLeft(), false );
}
else
{
m_plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultBottom(), false );
}
RimPlotAxisProperties* valAxisProperties = m_valueAxisProperties();
if ( valAxisProperties->isActive() )
{
m_plotWidget->enableAxis( axis, true );
m_valueAxisProperties->setNameAndAxis( "Value-Axis", "Value-Axis", axis.axis() );
RimSummaryPlotAxisFormatter calc( valAxisProperties, {}, curveDefinitions(), {}, {} );
calc.applyAxisPropertiesToPlot( m_plotWidget );
}
else
{
m_plotWidget->enableAxis( axis, false );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::onAxisSelected( RiuPlotAxis axis, bool toggle )
{
RiuPlotMainWindowTools::showPlotMainWindow();
caf::PdmObject* itemToSelect = nullptr;
if ( axis.axis() == RiaDefines::PlotAxis::PLOT_AXIS_LEFT )
{
if ( m_barOrientation == BARS_VERTICAL )
{
itemToSelect = m_valueAxisProperties;
}
else
{
itemToSelect = this;
}
}
else if ( axis.axis() == RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM )
{
if ( m_barOrientation == BARS_HORIZONTAL )
{
itemToSelect = m_valueAxisProperties;
}
else
{
itemToSelect = this;
}
}
RiuPlotMainWindowTools::selectOrToggleObject( itemToSelect, toggle );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::cleanupBeforeClose()
{
detachAllCurves();
if ( m_plotWidget )
{
m_plotWidget->setParent( nullptr );
delete m_plotWidget;
m_plotWidget = nullptr;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimAnalysisPlot::assignGroupingText( RimAnalysisPlot::SortGroupType sortGroup,
const RiaSummaryCurveDefinition dataEntry,
const QString& timestepString ) const
{
QString groupingText = "";
switch ( sortGroup )
{
case RimAnalysisPlot::SUMMARY_ITEM:
{
RifEclipseSummaryAddress addr = dataEntry.summaryAddress();
groupingText = QString::fromStdString( addr.itemUiText() );
}
break;
case RimAnalysisPlot::CASE:
{
if ( dataEntry.summaryCase() )
{
groupingText = dataEntry.summaryCase()->displayCaseName();
}
}
break;
case RimAnalysisPlot::ENSEMBLE:
{
if ( dataEntry.ensemble() )
{
groupingText = dataEntry.ensemble()->name();
}
}
break;
case RimAnalysisPlot::VECTOR:
{
RifEclipseSummaryAddress addr = dataEntry.summaryAddress();
groupingText = QString::fromStdString( addr.vectorName() );
}
break;
case RimAnalysisPlot::TIME_STEP:
{
groupingText = timestepString;
}
break;
default:
{
// Return empty string
}
break;
}
return groupingText;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<size_t> RimAnalysisPlot::findTimestepIndices( std::vector<time_t> selectedTimesteps, const std::vector<time_t>& timesteps )
{
std::vector<size_t> selectedTimestepIndices;
for ( time_t tt : selectedTimesteps )
{
for ( size_t timestepIdx = 0; timestepIdx < timesteps.size(); ++timestepIdx )
{
if ( timesteps[timestepIdx] == tt )
{
selectedTimestepIndices.push_back( timestepIdx );
break;
}
}
}
return selectedTimestepIndices;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RiaSummaryCurveDefinition> RimAnalysisPlot::filteredCurveDefs()
{
std::vector<RiaSummaryCurveDefinition> dataDefinitions = curveDefinitions();
// Split out the filter targets
std::set<RimSummaryCase*> filteredSumCases;
std::set<RifEclipseSummaryAddress> filteredSummaryItems; // Stores only the unique summary items
for ( const auto& curveDef : dataDefinitions )
{
if ( curveDef.summaryCase() )
{
filteredSumCases.insert( curveDef.summaryCase() );
RifEclipseSummaryAddress address = curveDef.summaryAddress();
address.setVectorName( "" ); // Vector name set to "" in order to store only unique summary items
filteredSummaryItems.insert( address );
}
}
std::vector<RimPlotDataFilterItem*> filters = m_plotDataFilterCollection->filters();
for ( RimPlotDataFilterItem* filter : filters )
{
applyFilter( filter, &filteredSumCases, &filteredSummaryItems );
}
// Remove all
std::vector<RiaSummaryCurveDefinition> filteredDataDefinitions;
for ( const RiaSummaryCurveDefinition& curveDefCandidate : dataDefinitions )
{
RimSummaryCase* sumCase = curveDefCandidate.summaryCase();
RifEclipseSummaryAddress addr = curveDefCandidate.summaryAddress();
addr.setVectorName( "" );
if ( filteredSumCases.count( sumCase ) && filteredSummaryItems.count( addr ) )
{
filteredDataDefinitions.push_back( curveDefCandidate );
}
}
return filteredDataDefinitions;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::applyFilter( const RimPlotDataFilterItem* filter,
std::set<RimSummaryCase*>* filteredSumCases,
std::set<RifEclipseSummaryAddress>* filteredSummaryItems )
{
if ( !filter->isActive() || !filter->isValid() ) return;
std::set<RimSummaryCase*> casesToKeep;
std::set<RifEclipseSummaryAddress> sumItemsToKeep;
std::map<RimSummaryCase*, double> casesToKeepWithValue;
std::map<RifEclipseSummaryAddress, double> sumItemsToKeepWithValue;
if ( filter->filterTarget() == RimPlotDataFilterItem::ENSEMBLE_CASE )
{
if ( !filter->ensembleParameterName().isEmpty() )
{
sumItemsToKeep = ( *filteredSummaryItems ); // Not filtering items
RigEnsembleParameter eParam = this->ensembleParameter( filter->ensembleParameterName() );
for ( auto sumCase : ( *filteredSumCases ) )
{
if ( !eParam.isValid() ) continue;
if ( !sumCase->caseRealizationParameters() ) continue;
RigCaseRealizationParameters::Value crpValue =
sumCase->caseRealizationParameters()->parameterValue( filter->ensembleParameterName() );
if ( eParam.isNumeric() && crpValue.isNumeric() )
{
double value = crpValue.numericValue();
if ( filter->filterOperation() == RimPlotDataFilterItem::RANGE )
{
auto [min, max] = filter->filterRangeMinMax();
if ( min <= value && value <= max )
{
casesToKeep.insert( sumCase );
}
}
else if ( filter->filterOperation() == RimPlotDataFilterItem::TOP_N ||
filter->filterOperation() == RimPlotDataFilterItem::BOTTOM_N )
{
bool useLargest = filter->filterOperation() == RimPlotDataFilterItem::TOP_N;
auto itIsInsertedPair = casesToKeepWithValue.insert( { sumCase, value } );
if ( !itIsInsertedPair.second ) // Already exists in map
{
double& insertedValue = itIsInsertedPair.first->second;
if ( ( useLargest && ( insertedValue < value ) ) || ( !useLargest && ( value < insertedValue ) ) )
{
insertedValue = value;
}
}
}
}
else if ( eParam.isText() && crpValue.isText() )
{
const auto& filterCategories = filter->selectedEnsembleParameterCategories();
if ( crpValue.isText() && std::count( filterCategories.begin(), filterCategories.end(), crpValue.textValue() ) == 0 )
{
casesToKeep.insert( sumCase );
}
}
}
}
else
{
casesToKeep = ( *filteredSumCases );
}
}
else
{
std::vector<time_t> selectedTimesteps;
if ( filter->consideredTimeStepsType() == RimPlotDataFilterItem::SELECT_TIMESTEPS )
{
selectedTimesteps = filter->explicitlySelectedTimeSteps();
}
else if ( filter->consideredTimeStepsType() == RimPlotDataFilterItem::PLOT_SOURCE_TIMESTEPS )
{
selectedTimesteps = this->selectedTimeSteps();
}
std::function<void( double )> storeResultCoreLambda;
RimSummaryCase* sumCaseInEvaluation = nullptr;
// clang-format off
std::function<void( RifEclipseSummaryAddress )> evaluateFilterForAllCases =
[&]( RifEclipseSummaryAddress addrToFilterValue ) // clang-format on
{
for ( auto sumCase : *filteredSumCases )
{
sumCaseInEvaluation = sumCase;
RifSummaryReaderInterface* reader = sumCase->summaryReader();
if ( !reader ) continue;
if ( reader->hasAddress( addrToFilterValue ) )
{
std::vector<double> values;
reader->values( addrToFilterValue, &values );
const std::vector<time_t>& timesteps = reader->timeSteps( addrToFilterValue );
if ( filter->consideredTimeStepsType() == RimPlotDataFilterItem::ALL_TIMESTEPS )
{
for ( size_t tIdx = 0; tIdx < timesteps.size(); ++tIdx )
{
double value = values[tIdx];
storeResultCoreLambda( value );
}
}
else if ( timesteps.size() )
{
std::vector<size_t> selectedTimestepIndices;
if ( filter->consideredTimeStepsType() == RimPlotDataFilterItem::FIRST_TIMESTEP )
{
selectedTimestepIndices.push_back( 0 );
}
else if ( filter->consideredTimeStepsType() == RimPlotDataFilterItem::LAST_TIMESTEP )
{
size_t timeStepIdx = timesteps.size() - 1;
selectedTimestepIndices.push_back( timeStepIdx );
}
else if ( selectedTimesteps.size() )
{
selectedTimestepIndices = RimAnalysisPlot::findTimestepIndices( selectedTimesteps, timesteps );
}
else if ( filter->consideredTimeStepsType() == RimPlotDataFilterItem::LAST_TIMESTEP_WITH_HISTORY )
{
RifEclipseSummaryAddress historyAddr = addrToFilterValue;
if ( !historyAddr.isHistoryVector() ) historyAddr.setVectorName( addrToFilterValue.vectorName() + "H" );
std::vector<time_t> historyTimesteps = reader->timeSteps( historyAddr );
if ( historyTimesteps.size() )
{
selectedTimestepIndices = RimAnalysisPlot::findTimestepIndices( { historyTimesteps.back() }, timesteps );
}
}
else if ( filter->consideredTimeStepsType() == RimPlotDataFilterItem::SELECT_TIMESTEP_RANGE )
{
std::pair<time_t, time_t> timeMinMax = filter->timeRangeMinMax();
for ( size_t tIdx = 0; tIdx < timesteps.size(); ++tIdx )
{
time_t dateTime = timesteps[tIdx];
if ( timeMinMax.first <= dateTime && dateTime <= timeMinMax.second )
{
selectedTimestepIndices.push_back( tIdx );
}
}
}
for ( size_t timeStepIdx : selectedTimestepIndices )
{
double value = values[timeStepIdx];
storeResultCoreLambda( value );
}
}
}
}
};
if ( filter->filterTarget() == RimPlotDataFilterItem::SUMMARY_CASE )
{
sumItemsToKeep = ( *filteredSummaryItems ); // Not filtering items
RifEclipseSummaryAddress addrToFilterValue = filter->summaryAddress();
if ( filter->filterOperation() == RimPlotDataFilterItem::RANGE )
{
std::pair<double, double> minMax = filter->filterRangeMinMax();
// clang-format off
storeResultCoreLambda = [&]( double value ) // clang-format on
{
if ( minMax.first <= value && value <= minMax.second )
{
casesToKeep.insert( sumCaseInEvaluation );
}
};
}
else if ( filter->filterOperation() == RimPlotDataFilterItem::TOP_N || filter->filterOperation() == RimPlotDataFilterItem::BOTTOM_N )
{
// clang-format off
storeResultCoreLambda = [&]( double value ) // clang-format on
{
bool useLargest = filter->filterOperation() == RimPlotDataFilterItem::TOP_N;
auto itIsInsertedPair = casesToKeepWithValue.insert( { sumCaseInEvaluation, value } );
if ( !itIsInsertedPair.second ) // Already exists in map
{
double& insertedValue = itIsInsertedPair.first->second;
if ( ( useLargest && ( insertedValue < value ) ) || ( !useLargest && ( value < insertedValue ) ) )
{
insertedValue = value;
}
}
};
}
evaluateFilterForAllCases( addrToFilterValue );
}
else if ( filter->filterTarget() == RimPlotDataFilterItem::SUMMARY_ITEM )
{
casesToKeep = ( *filteredSumCases ); // Not filtering cases
std::string quantityName;
{
RifEclipseSummaryAddress addrToFilterValue = filter->summaryAddress();
quantityName = addrToFilterValue.vectorName();
}
for ( auto sumItem : *filteredSummaryItems )
{
RifEclipseSummaryAddress addrToFilterValue = sumItem;
addrToFilterValue.setVectorName( quantityName );
if ( filter->filterOperation() == RimPlotDataFilterItem::RANGE )
{
std::pair<double, double> minMax = filter->filterRangeMinMax();
// clang-format off
storeResultCoreLambda = [&]( double value ) // clang-format on
{
if ( minMax.first <= value && value <= minMax.second )
{
sumItemsToKeep.insert( sumItem );
}
};
}
else if ( filter->filterOperation() == RimPlotDataFilterItem::TOP_N ||
filter->filterOperation() == RimPlotDataFilterItem::BOTTOM_N )
{
// clang-format off
storeResultCoreLambda = [&]( double value ) // clang-format on
{
bool useLargest = filter->filterOperation() == RimPlotDataFilterItem::TOP_N;
auto itIsInsertedPair = sumItemsToKeepWithValue.insert( { sumItem, value } );
if ( !itIsInsertedPair.second ) // Already exists in map
{
double& insertedValue = itIsInsertedPair.first->second;
if ( ( useLargest && ( insertedValue < value ) ) || ( !useLargest && ( value < insertedValue ) ) )
{
insertedValue = value;
}
}
};
}
evaluateFilterForAllCases( addrToFilterValue );
}
}
}
// Handle top/bottom n filter
if ( filter->filterOperation() == RimPlotDataFilterItem::TOP_N || filter->filterOperation() == RimPlotDataFilterItem::BOTTOM_N )
{
if ( filter->filterTarget() == RimPlotDataFilterItem::SUMMARY_ITEM )
{
std::multimap<double, RifEclipseSummaryAddress> valueSortedSumItems;
for ( const auto& itemValPair : sumItemsToKeepWithValue )
{
valueSortedSumItems.insert( { itemValPair.second, itemValPair.first } );
}
if ( filter->filterOperation() == RimPlotDataFilterItem::TOP_N )
{
int count = 0;
for ( auto it = valueSortedSumItems.rbegin(); count < filter->topBottomN() && it != valueSortedSumItems.rend(); ++it )
{
sumItemsToKeep.insert( it->second );
++count;
}
}
else if ( filter->filterOperation() == RimPlotDataFilterItem::BOTTOM_N )
{
int count = 0;
for ( auto it = valueSortedSumItems.begin(); count < filter->topBottomN() && it != valueSortedSumItems.end(); ++it )
{
sumItemsToKeep.insert( it->second );
++count;
}
}
}
else
{
std::multimap<double, RimSummaryCase*> valueSortedSumCases;
for ( const auto& caseValPair : casesToKeepWithValue )
{
valueSortedSumCases.insert( { caseValPair.second, caseValPair.first } );
}
if ( filter->filterOperation() == RimPlotDataFilterItem::TOP_N )
{
int count = 0;
for ( auto it = valueSortedSumCases.rbegin(); count < filter->topBottomN() && it != valueSortedSumCases.rend(); ++it )
{
casesToKeep.insert( it->second );
++count;
}
}
else if ( filter->filterOperation() == RimPlotDataFilterItem::BOTTOM_N )
{
int count = 0;
for ( auto it = valueSortedSumCases.begin(); count < filter->topBottomN() && it != valueSortedSumCases.end(); ++it )
{
casesToKeep.insert( it->second );
++count;
}
}
}
}
( *filteredSumCases ) = casesToKeep;
( *filteredSummaryItems ) = sumItemsToKeep;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::addDataToChartBuilder( RiuGroupedBarChartBuilder& chartBuilder )
{
std::vector<time_t> selectedTimesteps;
for ( const QDateTime& dateTime : m_selectedTimeSteps.v() )
{
selectedTimesteps.push_back( dateTime.toSecsSinceEpoch() );
}
RifSummaryReaderInterface* referenceCaseReader = nullptr;
if ( m_referenceCase ) referenceCaseReader = m_referenceCase->summaryReader();
// Unpack ensemble curves and make one curve definition for each individual curve.
// Store both ensemble and summary case in the definition
std::vector<RiaSummaryCurveDefinition> barDataDefinitions = filteredCurveDefs();
for ( const RiaSummaryCurveDefinition& curveDef : barDataDefinitions )
{
if ( !curveDef.summaryCase() ) continue;
RifSummaryReaderInterface* reader = curveDef.summaryCase()->summaryReader();
if ( !reader ) continue;
std::vector<time_t> timeSteps;
std::vector<double> values;
if ( referenceCaseReader )
{
std::pair<std::vector<time_t>, std::vector<double>> timeAndValues =
RimDerivedSummaryCase::calculateDerivedValues( reader,
-1,
referenceCaseReader,
-1,
DerivedSummaryOperator::DERIVED_OPERATOR_SUB,
curveDef.summaryAddress() );
timeSteps.swap( timeAndValues.first );
values.swap( timeAndValues.second );
}
else
{
timeSteps = reader->timeSteps( curveDef.summaryAddress() );
reader->values( curveDef.summaryAddress(), &values );
}
if ( !( timeSteps.size() && values.size() ) ) continue;
// Find selected timestep indices
std::vector<int> selectedTimestepIndices;
for ( time_t tt : selectedTimesteps )
{
for ( int timestepIdx = 0; static_cast<unsigned>( timestepIdx ) < timeSteps.size(); ++timestepIdx )
{
if ( timeSteps[timestepIdx] == tt )
{
selectedTimestepIndices.push_back( timestepIdx );
break;
}
}
}
for ( int timestepIdx : selectedTimestepIndices )
{
double sortValue = std::numeric_limits<double>::infinity();
QDateTime dateTime = RiaQDateTimeTools::fromTime_t( timeSteps[timestepIdx] );
QString formatString = RiaQDateTimeTools::dateFormatString( RiaPreferences::current()->dateFormat() );
QString timestepString = dateTime.toString( formatString );
QString majorText = assignGroupingText( m_majorGroupType(), curveDef, timestepString );
QString medText = assignGroupingText( m_mediumGroupType(), curveDef, timestepString );
QString minText = assignGroupingText( m_minorGroupType(), curveDef, timestepString );
QString legendText = assignGroupingText( m_sortGroupForColors(), curveDef, timestepString );
double value = values[timestepIdx];
switch ( m_valueSortOperation() )
{
case VALUE:
sortValue = value;
break;
case ABS_VALUE:
sortValue = fabs( value );
break;
}
QString barText;
QString separator = ", ";
if ( m_useBarText() )
{
QStringList barTextComponents;
if ( m_useVectorNameInBarText )
{
barTextComponents += QString::fromStdString( curveDef.summaryAddress().vectorName() );
}
if ( m_useSummaryItemInBarText )
{
barTextComponents += QString::fromStdString( curveDef.summaryAddress().itemUiText() );
}
if ( m_useCaseInBarText && curveDef.summaryCase() )
{
barTextComponents += curveDef.summaryCase()->displayCaseName();
}
if ( m_useEnsembleInBarText && curveDef.ensemble() )
{
barTextComponents += curveDef.ensemble()->name();
}
if ( m_useTimeStepInBarText )
{
barTextComponents += timestepString;
}
barText = barTextComponents.join( separator );
}
chartBuilder.addBarEntry( majorText, medText, minText, sortValue, legendText, barText, value );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::updatePlotTitle()
{
if ( m_useAutoPlotTitle )
{
QString autoTitle;
QString separator = ", ";
if ( getOrCreateSelectedCurveDefAnalyser()->m_ensembles.size() == 1 )
{
autoTitle += ( *getOrCreateSelectedCurveDefAnalyser()->m_ensembles.begin() )->name();
}
if ( getOrCreateSelectedCurveDefAnalyser()->m_singleSummaryCases.size() == 1 )
{
if ( !autoTitle.isEmpty() ) autoTitle += separator;
autoTitle += ( *getOrCreateSelectedCurveDefAnalyser()->m_singleSummaryCases.begin() )->displayCaseName();
}
else if ( getOrCreateSelectedCurveDefAnalyser()->m_singleSummaryCases.size() > 1 )
{
if ( !autoTitle.isEmpty() ) autoTitle += separator;
QStringList caseNameList;
for ( auto summaryCase : getOrCreateSelectedCurveDefAnalyser()->m_singleSummaryCases )
{
caseNameList.push_back( summaryCase->displayCaseName() );
}
QString root = RiaTextStringTools::commonRoot( caseNameList );
QString trimmedRoot = RiaTextStringTools::trimNonAlphaNumericCharacters( root );
if ( !trimmedRoot.isEmpty() )
{
autoTitle += trimmedRoot;
}
else
{
autoTitle += "Several Summary Cases";
}
}
if ( getOrCreateSelectedCurveDefAnalyser()->m_summaryAdresses.size() == 1 )
{
if ( !autoTitle.isEmpty() ) autoTitle += separator;
autoTitle += QString::fromStdString( getOrCreateSelectedCurveDefAnalyser()->m_summaryAdresses.begin()->itemUiText() );
}
for ( std::string quantName : getOrCreateSelectedCurveDefAnalyser()->m_vectorNames )
{
if ( !autoTitle.isEmpty() ) autoTitle += separator;
autoTitle += QString::fromStdString( quantName );
}
if ( m_referenceCase() )
{
if ( !autoTitle.isEmpty() ) autoTitle += separator;
autoTitle += "Compared to " + m_referenceCase->displayCaseName();
}
if ( m_useTopBarsFilter )
{
if ( !autoTitle.isEmpty() ) autoTitle += " - ";
autoTitle += "Top " + QString::number( m_maxBarCount() );
}
if ( m_selectedTimeSteps().size() == 1 )
{
if ( !autoTitle.isEmpty() ) autoTitle += " @ ";
QString formatString = RiaPreferences::current()->dateTimeFormat( RiaDefines::DateFormatComponents::DATE_FORMAT_YEAR_MONTH_DAY,
RiaDefines::TimeFormatComponents::TIME_FORMAT_NONE );
autoTitle += m_selectedTimeSteps()[0].toString( formatString );
}
m_description = autoTitle;
}
if ( m_plotWidget )
{
QString plotTitle = description();
m_plotWidget->setPlotTitle( plotTitle );
m_plotWidget->setPlotTitleEnabled( m_showPlotTitle && !isSubPlot() );
m_plotWidget->scheduleReplot();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaSummaryCurveDefinitionAnalyser* RimAnalysisPlot::getOrCreateSelectedCurveDefAnalyser()
{
if ( !m_analyserOfSelectedCurveDefs )
{
m_analyserOfSelectedCurveDefs = std::unique_ptr<RiaSummaryCurveDefinitionAnalyser>( new RiaSummaryCurveDefinitionAnalyser );
}
m_analyserOfSelectedCurveDefs->setCurveDefinitions( this->curveDefinitions() );
return m_analyserOfSelectedCurveDefs.get();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RiaSummaryCurveDefinition> RimAnalysisPlot::curveDefinitions() const
{
std::vector<RiaSummaryCurveDefinition> curveDefs;
for ( auto dataEntry : m_analysisPlotDataSelection )
{
curveDefs.push_back( dataEntry->curveDefinition() );
}
return curveDefs;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<RimPlotAxisPropertiesInterface*> RimAnalysisPlot::allPlotAxes() const
{
return { m_valueAxisProperties };
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::connectAxisSignals( RimPlotAxisProperties* axis )
{
axis->settingsChanged.connect( this, &RimAnalysisPlot::axisSettingsChanged );
axis->logarithmicChanged.connect( this, &RimAnalysisPlot::axisLogarithmicChanged );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::axisSettingsChanged( const caf::SignalEmitter* emitter )
{
updateAxes();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::axisLogarithmicChanged( const caf::SignalEmitter* emitter, bool isLogarithmic )
{
loadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::buildTestPlot( RiuGroupedBarChartBuilder& chartBuilder )
{
chartBuilder.addBarEntry( "T1_The_red_Fox", "", "", std::numeric_limits<double>::infinity(), "R1", "", 0.4 );
chartBuilder.addBarEntry( "T1_The_red_Fox", "", "", std::numeric_limits<double>::infinity(), "R2", "", 0.45 );
chartBuilder.addBarEntry( "T1_The_red_Fox", "W1", "", std::numeric_limits<double>::infinity(), "R1", "", 0.5 );
chartBuilder.addBarEntry( "T1_The_red_Fox", "W1", "", std::numeric_limits<double>::infinity(), "R2", "", 0.55 );
chartBuilder.addBarEntry( "T1_The_red_Fox", "W3", "", std::numeric_limits<double>::infinity(), "R1", "", 0.7 );
chartBuilder.addBarEntry( "T1_The_red_Fox", "W3", "", std::numeric_limits<double>::infinity(), "R2", "", 0.75 );
chartBuilder.addBarEntry( "T1_The_red_Fox", "W2", "", std::numeric_limits<double>::infinity(), "R1", "", 1.05 );
chartBuilder.addBarEntry( "T1_The_red_Fox", "W2", "", std::numeric_limits<double>::infinity(), "R2", "", 1.0 );
chartBuilder.addBarEntry( "T2", "W1", "", std::numeric_limits<double>::infinity(), "R1", "", 1.5 );
chartBuilder.addBarEntry( "T2", "W1", "", std::numeric_limits<double>::infinity(), "R2", "", 1.5 );
chartBuilder.addBarEntry( "T2", "W2", "", std::numeric_limits<double>::infinity(), "R1", "", 2.0 );
chartBuilder.addBarEntry( "T2", "W2", "", std::numeric_limits<double>::infinity(), "R2", "", 2.0 );
chartBuilder.addBarEntry( "T3", "W1", "1", std::numeric_limits<double>::infinity(), "R1", "", 1.5 );
chartBuilder.addBarEntry( "T3", "W1", "2", std::numeric_limits<double>::infinity(), "R2", "", 1.5 );
chartBuilder.addBarEntry( "T3", "W2", "3", std::numeric_limits<double>::infinity(), "R1", "", 2.0 );
chartBuilder.addBarEntry( "T3", "W2", "4", std::numeric_limits<double>::infinity(), "R1", "", 2.0 );
chartBuilder.addBarEntry( "T3", "W2", "5", std::numeric_limits<double>::infinity(), "R1", "", 2.0 );
chartBuilder.addBarEntry( "T4", "W1", "1", std::numeric_limits<double>::infinity(), "R1", "", 1.5 );
chartBuilder.addBarEntry( "T4", "W1", "2", std::numeric_limits<double>::infinity(), "R2", "", 1.5 );
chartBuilder.addBarEntry( "T4", "W2", "3", std::numeric_limits<double>::infinity(), "R1", "", 2.0 );
chartBuilder.addBarEntry( "T4", "W2", "4", std::numeric_limits<double>::infinity(), "R2", "", 2.0 );
chartBuilder.addBarEntry( "T4", "W1", "1", std::numeric_limits<double>::infinity(), "R1", "", 1.6 );
chartBuilder.addBarEntry( "T4", "W1", "2", std::numeric_limits<double>::infinity(), "R2", "", 1.6 );
chartBuilder.addBarEntry( "T4", "W2", "3", std::numeric_limits<double>::infinity(), "R1", "", 2.6 );
chartBuilder.addBarEntry( "T4", "W2", "4", std::numeric_limits<double>::infinity(), "R2", "", -0.3 );
chartBuilder.addBarEntry( "T5", "", "", 1.5, "R3", "G1", 1.5 );
chartBuilder.addBarEntry( "T5", "", "", 1.5, "R3", "G2", 1.5 );
chartBuilder.addBarEntry( "T5", "", "", 2.0, "R3", "G3", 2.0 );
chartBuilder.addBarEntry( "T5", "", "", 2.0, "R3", "G4", 2.0 );
chartBuilder.addBarEntry( "T5", "", "", 1.6, "R3", "G5", 1.6 );
chartBuilder.addBarEntry( "T5", "", "", 1.6, "R3", "G6", 1.6 );
chartBuilder.addBarEntry( "T5", "", "", 2.6, "R3", "G7", 2.6 );
chartBuilder.addBarEntry( "T5", "", "", -0.1, "R3", "G8", -0.1 );
chartBuilder.addBarEntry( "", "", "", 1.2, "", "A", 1.2 );
chartBuilder.addBarEntry( "", "", "", 1.5, "", "B", 1.5 );
chartBuilder.addBarEntry( "", "", "", 2.3, "", "C", 2.3 );
chartBuilder.addBarEntry( "", "", "", 2.0, "", "D", 2.0 );
chartBuilder.addBarEntry( "", "", "", 1.6, "", "E", 1.6 );
chartBuilder.addBarEntry( "", "", "", 2.4, "", "F", -2.4 );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimAnalysisPlot::barTextFontSize() const
{
return caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_barTextFontSize() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::initAfterRead()
{
connectAllCaseSignals();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::onCaseRemoved( const SignalEmitter* emitter, RimSummaryCase* summaryCase )
{
for ( auto existingEntry : m_analysisPlotDataSelection )
{
if ( existingEntry->summaryCase() == summaryCase )
{
m_analysisPlotDataSelection.removeChild( existingEntry );
delete existingEntry;
break;
}
}
loadDataAndUpdate();
if ( m_plotWidget ) m_plotWidget->scheduleReplot();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::connectAllCaseSignals()
{
for ( auto dataEntry : m_analysisPlotDataSelection )
{
if ( dataEntry->ensemble() )
{
dataEntry->ensemble()->caseRemoved.connect( this, &RimAnalysisPlot::onCaseRemoved );
}
}
}