///////////////////////////////////////////////////////////////////////////////// // // 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 // 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 "RimCalculatedSummaryCase.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 "cafPdmUiListEditor.h" #include "cafPdmUiTreeSelectionEditor.h" #include #include #include namespace caf { template <> void caf::AppEnum::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::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& 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& timeSteps ) { m_selectedTimeSteps.v().clear(); for ( auto time : timeSteps ) { m_selectedTimeSteps.v().push_back( RiaQDateTimeTools::fromTime_t( time ) ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RimAnalysisPlot::unfilteredAddresses() { std::set addresses; RiaSummaryCurveDefinitionAnalyser* analyserOfSelectedCurveDefs = getOrCreateSelectedCurveDefAnalyser(); for ( RimSummaryCase* sumCase : analyserOfSelectedCurveDefs->m_singleSummaryCases ) { const std::set& caseAddrs = sumCase->summaryReader()->allResultAddresses(); addresses.insert( caseAddrs.begin(), caseAddrs.end() ); } return addresses; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RimAnalysisPlot::ensembleParameters() { std::set ensembleParms; RiaSummaryCurveDefinitionAnalyser* analyserOfSelectedCurveDefs = getOrCreateSelectedCurveDefAnalyser(); std::set ensembles; for ( RimSummaryCase* sumCase : analyserOfSelectedCurveDefs->m_singleSummaryCases ) { if ( sumCase->ensemble() ) { ensembles.insert( sumCase->ensemble() ); } } for ( RimSummaryCaseCollection* ensemble : ensembles ) { std::vector parameters = ensemble->variationSortedEnsembleParameters(); ensembleParms.insert( parameters.begin(), parameters.end() ); } return ensembleParms; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigEnsembleParameter RimAnalysisPlot::ensembleParameter( const QString& ensembleParameterName ) { std::set 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& timeRangeOrSelection, bool useAbsValue, double* minVal, double* maxVal ) { double min = std::numeric_limits::infinity(); double max = useAbsValue ? 0.0 : -std::numeric_limits::infinity(); std::function minOrAbsMin; std::function 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 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 allSumCases = allSourceCases(); for ( RimSummaryCase* sumCase : allSumCases ) { RifSummaryReaderInterface* reader = sumCase->summaryReader(); if ( !reader ) continue; if ( reader->hasAddress( address ) ) { std::vector values; reader->values( address, &values ); const std::vector& 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& 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 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 RimAnalysisPlot::selectedTimeSteps() { std::vector 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 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 RimAnalysisPlot::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) { QList options = RimPlot::calculateValueOptions( fieldNeedingOptions ); if ( !options.isEmpty() ) return options; if ( fieldNeedingOptions == &m_selectedTimeSteps ) { std::set allTimeSteps = allAvailableTimeSteps(); std::set currentlySelectedTimeSteps( m_selectedTimeSteps().begin(), m_selectedTimeSteps().end() ); if ( allTimeSteps.empty() ) { return options; } std::set currentlySelectedTimeStepIndices; std::vector 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 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 allSummaryCases = RimProject::current()->allSummaryCases(); options.push_back( { "None", nullptr } ); for ( auto sumCase : allSummaryCases ) { QString displayName = sumCase->displayCaseName(); auto caseColl = dynamic_cast( 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 RimAnalysisPlot::allAvailableTimeSteps() { std::set timeStepUnion; for ( RimSummaryCase* sumCase : timestepDefiningSourceCases() ) { if ( !sumCase || !sumCase->summaryReader() ) continue; const std::vector& timeSteps = sumCase->summaryReader()->timeSteps( RifEclipseSummaryAddress() ); for ( time_t t : timeSteps ) { timeStepUnion.insert( t ); } } return timeStepUnion; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RimAnalysisPlot::timestepDefiningSourceCases() { RiaSummaryCurveDefinitionAnalyser* analyserOfSelectedCurveDefs = getOrCreateSelectedCurveDefAnalyser(); std::set timeStepDefiningSumCases = analyserOfSelectedCurveDefs->m_singleSummaryCases; for ( auto ensemble : analyserOfSelectedCurveDefs->m_ensembles ) { auto allSumCases = ensemble->allSummaryCases(); timeStepDefiningSumCases.insert( allSumCases.begin(), allSumCases.end() ); } return timeStepDefiningSumCases; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RimAnalysisPlot::allSourceCases() { RiaSummaryCurveDefinitionAnalyser* analyserOfSelectedCurveDefs = getOrCreateSelectedCurveDefAnalyser(); std::set 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 RimAnalysisPlot::findTimestepIndices( std::vector selectedTimesteps, const std::vector& timesteps ) { std::vector 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 RimAnalysisPlot::filteredCurveDefs() { std::vector dataDefinitions = curveDefinitions(); // Split out the filter targets std::set filteredSumCases; std::set 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 filters = m_plotDataFilterCollection->filters(); for ( RimPlotDataFilterItem* filter : filters ) { applyFilter( filter, &filteredSumCases, &filteredSummaryItems ); } // Remove all std::vector 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* filteredSumCases, std::set* filteredSummaryItems ) { if ( !filter->isActive() || !filter->isValid() ) return; std::set casesToKeep; std::set sumItemsToKeep; std::map casesToKeepWithValue; std::map 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 selectedTimesteps; if ( filter->consideredTimeStepsType() == RimPlotDataFilterItem::SELECT_TIMESTEPS ) { selectedTimesteps = filter->explicitlySelectedTimeSteps(); } else if ( filter->consideredTimeStepsType() == RimPlotDataFilterItem::PLOT_SOURCE_TIMESTEPS ) { selectedTimesteps = this->selectedTimeSteps(); } std::function storeResultCoreLambda; RimSummaryCase* sumCaseInEvaluation = nullptr; // clang-format off std::function 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 values; reader->values( addrToFilterValue, &values ); const std::vector& 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 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 historyTimesteps = reader->timeSteps( historyAddr ); if ( historyTimesteps.size() ) { selectedTimestepIndices = RimAnalysisPlot::findTimestepIndices( { historyTimesteps.back() }, timesteps ); } } else if ( filter->consideredTimeStepsType() == RimPlotDataFilterItem::SELECT_TIMESTEP_RANGE ) { std::pair 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 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 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 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 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 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 barDataDefinitions = filteredCurveDefs(); for ( const RiaSummaryCurveDefinition& curveDef : barDataDefinitions ) { if ( !curveDef.summaryCase() ) continue; RifSummaryReaderInterface* reader = curveDef.summaryCase()->summaryReader(); if ( !reader ) continue; // Todo: // If curveDef.summaryCase() is a RimGridSummaryCase and we are using summary item as legend and the summary // items are wells, then: /// use color from eclCase->defaultWellColor( wellName ); std::vector timeSteps; std::vector values; if ( referenceCaseReader ) { std::pair, std::vector> 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 selectedTimestepIndices; for ( time_t tt : selectedTimesteps ) { for ( int timestepIdx = 0; static_cast( timestepIdx ) < timeSteps.size(); ++timestepIdx ) { if ( timeSteps[timestepIdx] == tt ) { selectedTimestepIndices.push_back( timestepIdx ); break; } } } for ( int timestepIdx : selectedTimestepIndices ) { double sortValue = std::numeric_limits::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 ) { if ( !dynamic_cast( summaryCase ) ) { 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( new RiaSummaryCurveDefinitionAnalyser ); } m_analyserOfSelectedCurveDefs->setCurveDefinitions( this->curveDefinitions() ); return m_analyserOfSelectedCurveDefs.get(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimAnalysisPlot::curveDefinitions() const { std::vector curveDefs; for ( auto dataEntry : m_analysisPlotDataSelection ) { curveDefs.push_back( dataEntry->curveDefinition() ); } return curveDefs; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set 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::infinity(), "R1", "", 0.4 ); chartBuilder.addBarEntry( "T1_The_red_Fox", "", "", std::numeric_limits::infinity(), "R2", "", 0.45 ); chartBuilder.addBarEntry( "T1_The_red_Fox", "W1", "", std::numeric_limits::infinity(), "R1", "", 0.5 ); chartBuilder.addBarEntry( "T1_The_red_Fox", "W1", "", std::numeric_limits::infinity(), "R2", "", 0.55 ); chartBuilder.addBarEntry( "T1_The_red_Fox", "W3", "", std::numeric_limits::infinity(), "R1", "", 0.7 ); chartBuilder.addBarEntry( "T1_The_red_Fox", "W3", "", std::numeric_limits::infinity(), "R2", "", 0.75 ); chartBuilder.addBarEntry( "T1_The_red_Fox", "W2", "", std::numeric_limits::infinity(), "R1", "", 1.05 ); chartBuilder.addBarEntry( "T1_The_red_Fox", "W2", "", std::numeric_limits::infinity(), "R2", "", 1.0 ); chartBuilder.addBarEntry( "T2", "W1", "", std::numeric_limits::infinity(), "R1", "", 1.5 ); chartBuilder.addBarEntry( "T2", "W1", "", std::numeric_limits::infinity(), "R2", "", 1.5 ); chartBuilder.addBarEntry( "T2", "W2", "", std::numeric_limits::infinity(), "R1", "", 2.0 ); chartBuilder.addBarEntry( "T2", "W2", "", std::numeric_limits::infinity(), "R2", "", 2.0 ); chartBuilder.addBarEntry( "T3", "W1", "1", std::numeric_limits::infinity(), "R1", "", 1.5 ); chartBuilder.addBarEntry( "T3", "W1", "2", std::numeric_limits::infinity(), "R2", "", 1.5 ); chartBuilder.addBarEntry( "T3", "W2", "3", std::numeric_limits::infinity(), "R1", "", 2.0 ); chartBuilder.addBarEntry( "T3", "W2", "4", std::numeric_limits::infinity(), "R1", "", 2.0 ); chartBuilder.addBarEntry( "T3", "W2", "5", std::numeric_limits::infinity(), "R1", "", 2.0 ); chartBuilder.addBarEntry( "T4", "W1", "1", std::numeric_limits::infinity(), "R1", "", 1.5 ); chartBuilder.addBarEntry( "T4", "W1", "2", std::numeric_limits::infinity(), "R2", "", 1.5 ); chartBuilder.addBarEntry( "T4", "W2", "3", std::numeric_limits::infinity(), "R1", "", 2.0 ); chartBuilder.addBarEntry( "T4", "W2", "4", std::numeric_limits::infinity(), "R2", "", 2.0 ); chartBuilder.addBarEntry( "T4", "W1", "1", std::numeric_limits::infinity(), "R1", "", 1.6 ); chartBuilder.addBarEntry( "T4", "W1", "2", std::numeric_limits::infinity(), "R2", "", 1.6 ); chartBuilder.addBarEntry( "T4", "W2", "3", std::numeric_limits::infinity(), "R1", "", 2.6 ); chartBuilder.addBarEntry( "T4", "W2", "4", std::numeric_limits::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 ); } } }