From ac7eccee8882676a97a1d2fd9dbb26072c1306ad Mon Sep 17 00:00:00 2001 From: Gaute Lindkvist Date: Mon, 4 Mar 2019 15:35:48 +0100 Subject: [PATCH] #4157 Grid Cross Plot: Make categorisation legend more like ensemble legend --- .../Tools/RiaGridCrossPlotCurveNameHelper.cpp | 9 +- .../Tools/RiaGridCrossPlotCurveNameHelper.h | 4 +- .../GridCrossPlots/RimGridCrossPlot.cpp | 37 +- .../GridCrossPlots/RimGridCrossPlot.h | 9 +- .../GridCrossPlots/RimGridCrossPlotCurve.cpp | 42 +-- .../GridCrossPlots/RimGridCrossPlotCurve.h | 11 +- .../RimGridCrossPlotCurveSet.cpp | 315 +++++++++++++++--- .../GridCrossPlots/RimGridCrossPlotCurveSet.h | 19 +- .../ProjectDataModel/RimPlotCurve.cpp | 46 ++- .../ProjectDataModel/RimPlotCurve.h | 6 +- .../RimRegularLegendConfig.cpp | 30 +- .../ProjectDataModel/RimRegularLegendConfig.h | 2 + .../RigEclipseCrossPlotDataExtractor.cpp | 61 +--- .../RigEclipseCrossPlotDataExtractor.h | 12 +- .../UserInterface/CMakeLists_files.cmake | 3 + .../UserInterface/RiuGridCrossQwtPlot.cpp | 140 ++++++++ .../UserInterface/RiuGridCrossQwtPlot.h | 52 +++ .../UserInterface/RiuSummaryQwtPlot.cpp | 8 +- .../UserInterface/RiuSummaryQwtPlot.h | 9 +- 19 files changed, 623 insertions(+), 192 deletions(-) create mode 100644 ApplicationCode/UserInterface/RiuGridCrossQwtPlot.cpp create mode 100644 ApplicationCode/UserInterface/RiuGridCrossQwtPlot.h diff --git a/ApplicationCode/Application/Tools/RiaGridCrossPlotCurveNameHelper.cpp b/ApplicationCode/Application/Tools/RiaGridCrossPlotCurveNameHelper.cpp index 6c5fc40b9a..d364539091 100644 --- a/ApplicationCode/Application/Tools/RiaGridCrossPlotCurveNameHelper.cpp +++ b/ApplicationCode/Application/Tools/RiaGridCrossPlotCurveNameHelper.cpp @@ -27,10 +27,11 @@ void RiaGridCrossPlotCurveNameHelper::addCurveSet(RimGridCrossPlotCurveSet* curv m_caseNameSet.insert(curveSet->caseNameString()); m_axisVariablesSet.insert(curveSet->axisVariableString()); m_timeStepSet.insert(curveSet->timeStepString()); - for (QString categoryName : curveSet->categoryStrings()) + for (auto category : curveSet->categoryStrings()) { - m_categoryNameSet.insert(categoryName); + m_categoriesSet.insert(category); } + m_curveSets.push_back(curveSet); } @@ -41,7 +42,7 @@ void RiaGridCrossPlotCurveNameHelper::applyCurveNames() { for (auto curveSet : m_curveSets) { - curveSet->updateCurveNames(m_caseNameSet.size() > 1u, m_axisVariablesSet.size() > 1u, m_timeStepSet.size() > 1u, m_categoryNameSet.size() > 1u); + curveSet->updateCurveNames(m_caseNameSet.size() > 1u, m_axisVariablesSet.size() > 1u, m_timeStepSet.size() > 1u, m_categoriesSet.size() > 1u); } } @@ -53,6 +54,6 @@ void RiaGridCrossPlotCurveNameHelper::reset() m_caseNameSet.clear(); m_axisVariablesSet.clear(); m_timeStepSet.clear(); - m_categoryNameSet.clear(); + m_categoriesSet.clear(); m_curveSets.clear(); } diff --git a/ApplicationCode/Application/Tools/RiaGridCrossPlotCurveNameHelper.h b/ApplicationCode/Application/Tools/RiaGridCrossPlotCurveNameHelper.h index 316ecbb887..e2cda05fc1 100644 --- a/ApplicationCode/Application/Tools/RiaGridCrossPlotCurveNameHelper.h +++ b/ApplicationCode/Application/Tools/RiaGridCrossPlotCurveNameHelper.h @@ -35,8 +35,8 @@ private: std::set m_caseNameSet; std::set m_axisVariablesSet; std::set m_timeStepSet; - std::set m_categoryNameSet; - + std::set m_categoriesSet; + std::vector m_curveSets; }; diff --git a/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlot.cpp b/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlot.cpp index a7d6623274..55b6652d10 100644 --- a/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlot.cpp +++ b/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlot.cpp @@ -19,7 +19,7 @@ #include "RiaGridCrossPlotCurveNameHelper.h" -#include "RiuSummaryQwtPlot.h" +#include "RiuGridCrossQwtPlot.h" #include "RiuPlotMainWindowTools.h" #include "RiuQwtPlotTools.h" @@ -106,6 +106,14 @@ int RimGridCrossPlot::indexOfCurveSet(const RimGridCrossPlotCurveSet* curveSet) return -1; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimGridCrossPlot::curveSets() const +{ + return m_crossPlotCurveSets.childObjects(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -168,7 +176,7 @@ void RimGridCrossPlot::reattachCurvesToQwtAndReplot() curveSet->setParentQwtPlotNoReplot(m_qwtPlot); } } - m_qwtPlot->replot(); + updateAxisDisplay(); } } @@ -188,7 +196,10 @@ QString RimGridCrossPlot::createAutoName() const QStringList dataSets; for (auto curveSet : m_crossPlotCurveSets) { - dataSets += curveSet->createAutoName(); + if (curveSet->isChecked()) + { + dataSets += curveSet->createAutoName(); + } } if (!dataSets.isEmpty()) { @@ -298,7 +309,7 @@ QWidget* RimGridCrossPlot::createViewWidget(QWidget* mainWindowParent) { if (!m_qwtPlot) { - m_qwtPlot = new RiuQwtPlot(this, mainWindowParent); + m_qwtPlot = new RiuGridCrossQwtPlot(this, mainWindowParent); for (auto curveSet : m_crossPlotCurveSets) { @@ -468,6 +479,14 @@ void RimGridCrossPlot::updateCurveNamesAndPlotTitle() } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RiuGridCrossQwtPlot* RimGridCrossPlot::qwtPlot() const +{ + return m_qwtPlot; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -476,7 +495,10 @@ QString RimGridCrossPlot::xAxisParameterString() const QStringList xAxisParams; for (auto curveSet : m_crossPlotCurveSets) { - xAxisParams.push_back(curveSet->xAxisName()); + if (curveSet->isChecked()) + { + xAxisParams.push_back(curveSet->xAxisName()); + } } xAxisParams.removeDuplicates(); @@ -497,7 +519,10 @@ QString RimGridCrossPlot::yAxisParameterString() const QStringList yAxisParams; for (auto curveSet : m_crossPlotCurveSets) { - yAxisParams.push_back(curveSet->yAxisName()); + if (curveSet->isChecked()) + { + yAxisParams.push_back(curveSet->yAxisName()); + } } yAxisParams.removeDuplicates(); diff --git a/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlot.h b/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlot.h index 7df054bf79..7f3a22f47b 100644 --- a/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlot.h +++ b/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlot.h @@ -31,7 +31,7 @@ class RimPlotAxisProperties; class RimGridCrossPlotCurveSet; -class RiuQwtPlot; +class RiuGridCrossQwtPlot; class RimGridCrossPlotNameConfig : public RimNameConfig { @@ -56,6 +56,8 @@ public: RimGridCrossPlotCurveSet* createCurveSet(); int indexOfCurveSet(const RimGridCrossPlotCurveSet* curveSet) const; + + std::vector curveSets() const; QWidget* viewWidget() override; QImage snapshotWindowContent() override; @@ -69,6 +71,7 @@ public: void performAutoNameUpdate() override; void updateCurveNamesAndPlotTitle(); + RiuGridCrossQwtPlot* qwtPlot() const; public: // Rim2dPlotInterface overrides void updateAxisScaling() override; @@ -97,7 +100,7 @@ protected: void updateAxisInQwt(RiaDefines::PlotAxis axisType); void updateAxisFromQwt(RiaDefines::PlotAxis axisType); - std::pair getAxisRangeFromData(RiaDefines::PlotAxis axisType); + private: caf::PdmField m_showLegend; caf::PdmField m_legendFontSize; @@ -108,7 +111,7 @@ private: caf::PdmChildArrayField m_crossPlotCurveSets; - QPointer m_qwtPlot; + QPointer m_qwtPlot; RiaGridCrossPlotCurveNameHelper m_curveNameHelper; }; diff --git a/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurve.cpp b/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurve.cpp index 1ec48c0906..65e459b907 100644 --- a/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurve.cpp +++ b/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurve.cpp @@ -48,7 +48,6 @@ CAF_PDM_SOURCE_INIT(RimGridCrossPlotCurve, "GridCrossPlotCurve"); RimGridCrossPlotCurve::RimGridCrossPlotCurve() : m_curveSetIndex(0) , m_categoryIndex(0) - , m_categoryCount(0) { CAF_PDM_InitObject("Cross Plot Points", ":/WellLogCurve16x16.png", "", ""); @@ -60,19 +59,10 @@ RimGridCrossPlotCurve::RimGridCrossPlotCurve() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimGridCrossPlotCurve::setCategoryInformation(int curveSetIndex, int categoryIndex, int categoryCount) +void RimGridCrossPlotCurve::setCategoryInformation(int curveSetIndex, int categoryIndex) { m_curveSetIndex = curveSetIndex; - m_categoryIndex = categoryIndex; - m_categoryCount = categoryCount; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimGridCrossPlotCurve::setUseContrastColors(bool useContrastColors) -{ - m_useContrastColors = useContrastColors; + m_categoryIndex = categoryIndex; } //-------------------------------------------------------------------------------------------------- @@ -90,7 +80,7 @@ void RimGridCrossPlotCurve::setSamples(const std::vector& xValues, const //-------------------------------------------------------------------------------------------------- void RimGridCrossPlotCurve::updateCurveAppearance() { - determineColorAndSymbol(); + determineSymbol(); RimPlotCurve::updateCurveAppearance(); } @@ -105,30 +95,10 @@ int RimGridCrossPlotCurve::categoryIndex() const //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimGridCrossPlotCurve::determineColorAndSymbol() +void RimGridCrossPlotCurve::determineSymbol() { - if (m_useContrastColors) - { - const caf::ColorTable& colors = RiaColorTables::categoryPaletteColors(); - int colorIndex = m_categoryIndex + m_curveSetIndex; // Offset cycle for each curve set - setColor(colors.cycledColor3f(colorIndex)); - int numColors = (int)colors.size(); - - // Retain same symbol until we've gone full cycle in colors - int symbolIndex = m_categoryIndex / numColors; - - RiuQwtSymbol::PointSymbolEnum symbol = RiuQwtSymbol::cycledSymbolStyle(m_curveSetIndex, symbolIndex); - setSymbol(symbol); - } - else - { - const caf::ColorTable& colors = RiaColorTables::contrastCategoryPaletteColors(); - cvf::Color3ub cycledBaseColor = colors.cycledColor3ub(m_curveSetIndex); - caf::ColorTable hueConstColTable = RiaColorTables::createBrightnessBasedColorTable(cycledBaseColor, m_categoryCount); - setColor(hueConstColTable.cycledColor3f(m_categoryIndex)); - RiuQwtSymbol::PointSymbolEnum symbol = RiuQwtSymbol::cycledFilledSymbolStyle(m_curveSetIndex); - setSymbol(symbol); - } + RiuQwtSymbol::PointSymbolEnum symbol = RiuQwtSymbol::cycledFilledSymbolStyle(m_curveSetIndex); + setSymbol(symbol); } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurve.h b/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurve.h index 3964e4e84b..7a14beb73a 100644 --- a/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurve.h +++ b/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurve.h @@ -40,15 +40,14 @@ class RimGridCrossPlotCurve : public RimPlotCurve public: RimGridCrossPlotCurve(); ~RimGridCrossPlotCurve() override = default; - void setCategoryInformation(int curveSetIndex, int categoryIndex, int categoryCount); - void setUseContrastColors(bool useContrastColors); + void setCategoryInformation(int curveSetIndex, int categoryIndex); void setSamples(const std::vector& xValues, const std::vector& yValues); void updateCurveAppearance() override; int categoryIndex() const; protected: - void determineColorAndSymbol(); + void determineSymbol(); void updateZoomInParentPlot() override; void updateLegendsInPlot() override; QString createCurveAutoName() override; @@ -56,9 +55,7 @@ protected: void defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) override; private: - int m_curveSetIndex; - int m_categoryIndex; - int m_categoryCount; - bool m_useContrastColors; + int m_curveSetIndex; + int m_categoryIndex; }; diff --git a/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurveSet.cpp b/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurveSet.cpp index 41733fb498..e7ca6752c1 100644 --- a/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurveSet.cpp +++ b/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurveSet.cpp @@ -18,16 +18,20 @@ #include "RimGridCrossPlotCurveSet.h" #include "RiaApplication.h" +#include "RiaColorTables.h" #include "RiaLogging.h" #include "RigActiveCellInfo.h" #include "RigActiveCellsResultAccessor.h" #include "RigCaseCellResultCalculator.h" +#include "RigEclipseCaseData.h" #include "RigEclipseCrossPlotDataExtractor.h" #include "RigFormationNames.h" #include "RigMainGrid.h" +#include "RiuGridCrossQwtPlot.h" + #include "RimCase.h" #include "RimEclipseCase.h" #include "RimEclipseView.h" @@ -36,10 +40,14 @@ #include "RimGridCrossPlotCurve.h" #include "RimGridView.h" #include "RimProject.h" +#include "RimRegularLegendConfig.h" #include "RimTools.h" +#include "cafColorTable.h" #include "cafPdmUiComboBoxEditor.h" #include "cafPdmUiSliderEditor.h" +#include "cafPdmUiTreeOrdering.h" +#include "cvfScalarMapper.h" #include @@ -89,9 +97,6 @@ RimGridCrossPlotCurveSet::RimGridCrossPlotCurveSet() m_categoryProperty.uiCapability()->setUiHidden(true); m_categoryProperty.uiCapability()->setUiTreeChildrenHidden(true); - CAF_PDM_InitField(&m_categoryBinCount, "CategoryBinCount", 2, "Number of Data Groups", "", "", ""); - m_categoryBinCount.uiCapability()->setUiEditorTypeName(caf::PdmUiSliderEditor::uiEditorTypeName()); - CAF_PDM_InitFieldNoDefault(&m_nameConfig, "NameConfig", "Name", "", "", ""); m_nameConfig = new RimGridCrossPlotCurveSetNameConfig(this); m_nameConfig.uiCapability()->setUiTreeHidden(true); @@ -100,6 +105,12 @@ RimGridCrossPlotCurveSet::RimGridCrossPlotCurveSet() CAF_PDM_InitFieldNoDefault(&m_crossPlotCurves, "CrossPlotCurves", "Curves", "", "", ""); m_crossPlotCurves.uiCapability()->setUiTreeHidden(true); + CAF_PDM_InitFieldNoDefault(&m_legendConfig, "LegendConfig", "", "", "", ""); + m_legendConfig = new RimRegularLegendConfig(); + m_legendConfig->setColorRange(RimRegularLegendConfig::CATEGORY); + m_legendConfig->setMappingMode(RimRegularLegendConfig::CATEGORY_INTEGER); + m_legendConfig->setTickNumberFormat(RimRegularLegendConfig::AUTO); + setDefaults(); } @@ -189,23 +200,36 @@ QString RimGridCrossPlotCurveSet::createAutoName() const if (m_nameConfig->addCategorization) { - if (m_categorization() == TIME_CATEGORIZATION) - { - nameTags += QString("Grouped by Time"); - } - else if (m_categorization() == FORMATION_CATEGORIZATION) - { - nameTags += QString("Grouped by formations"); - } - else if (m_categorization() == RESULT_CATEGORIZATION && m_categoryProperty->hasResult()) - { - nameTags += QString("Grouped by %1").arg(m_categoryProperty->resultVariableUiShortName()); - } + QString catTitle = categoryTitle(); + if (!catTitle.isEmpty()) nameTags += catTitle; } return nameTags.join(", "); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimGridCrossPlotCurveSet::categoryTitle() const +{ + if (hasCategoryResult()) + { + if (m_categorization() == TIME_CATEGORIZATION) + { + return QString("Time Steps"); + } + else if (m_categorization() == FORMATION_CATEGORIZATION) + { + return QString("Formations"); + } + else if (m_categorization() == RESULT_CATEGORIZATION && m_categoryProperty->hasResult()) + { + return QString("%1").arg(m_categoryProperty->resultVariableUiShortName()); + } + } + return ""; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -228,6 +252,14 @@ void RimGridCrossPlotCurveSet::cellFilterViewUpdated() } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimRegularLegendConfig* RimGridCrossPlotCurveSet::legendConfig() +{ + return m_legendConfig; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -281,12 +313,12 @@ QString RimGridCrossPlotCurveSet::timeStepString() const //-------------------------------------------------------------------------------------------------- std::vector RimGridCrossPlotCurveSet::categoryStrings() const { - std::vector categoryNames; - for (auto categoryIndexNamePair : m_categoryNames) + std::vector catStrings; + for (auto curve : m_crossPlotCurves()) { - categoryNames.push_back(categoryIndexNamePair.second); + catStrings.push_back(m_legendConfig->categoryNameFromCategoryValue(curve->categoryIndex())); } - return categoryNames; + return catStrings; } //-------------------------------------------------------------------------------------------------- @@ -338,28 +370,87 @@ void RimGridCrossPlotCurveSet::onLoadDataAndUpdate(bool updateParentPlot) std::map timeStepCellVisibilityMap = calculateCellVisibility(eclipseCase); + updateLegend(); + RigEclipseCrossPlotResult result = RigEclipseCrossPlotDataExtractor::extract( - eclipseCase->eclipseCaseData(), m_timeStep(), xAddress, yAddress, m_categorization(), catAddress, m_categoryBinCount, timeStepCellVisibilityMap); + eclipseCase->eclipseCaseData(), m_timeStep(), xAddress, yAddress, m_categorization(), catAddress, timeStepCellVisibilityMap); - m_categoryNames = result.categoryNameMap; - for (const auto& sampleCategory : result.categorySamplesMap) - { - RimGridCrossPlotCurve* curve = new RimGridCrossPlotCurve(); - curve->setCategoryInformation(indexInPlot(), sampleCategory.first, (int)result.categorySamplesMap.size()); - curve->setUseContrastColors(m_categorization() == FORMATION_CATEGORIZATION); - curve->setSamples(sampleCategory.second.first, sampleCategory.second.second); - curve->updateCurveAppearance(); - curve->updateUiIconFromPlotSymbol(); + createCurves(result); - m_crossPlotCurves.push_back(curve); - } - if (updateParentPlot) { triggerPlotNameUpdateAndReplot(); } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimGridCrossPlotCurveSet::createCurves(const RigEclipseCrossPlotResult& result) +{ + if (m_categorization == NO_CATEGORIZATION) + { + const caf::ColorTable& colors = RiaColorTables::contrastCategoryPaletteColors(); + int colorIndex = indexInPlot(); + + RimGridCrossPlotCurve* curve = new RimGridCrossPlotCurve(); + curve->setColor(colors.cycledColor3f(colorIndex)); + curve->setCategoryInformation(indexInPlot(), 0); + curve->setSamples(result.xValues, result.yValues); + curve->updateCurveAppearance(); + curve->updateUiIconFromPlotSymbol(); + m_crossPlotCurves.push_back(curve); + } + else + { + std::map, std::vector>> categorizedResults; + + std::vector tickValues; + + if (m_categorization == TIME_CATEGORIZATION || m_categorization == FORMATION_CATEGORIZATION) + { + for (size_t i = 0; i < result.xValues.size(); ++i) + { + categorizedResults[result.catValuesDiscrete[i]].first.push_back(result.xValues[i]); + categorizedResults[result.catValuesDiscrete[i]].second.push_back(result.yValues[i]); + } + } + else + { + m_legendConfig->scalarMapper()->majorTickValues(&tickValues); + + for (size_t i = 0; i < result.xValues.size(); ++i) + { + auto upperBoundIt = std::lower_bound(tickValues.begin(), tickValues.end(), result.catValuesContinuous[i]); + int upperBoundIndex = static_cast(upperBoundIt - tickValues.begin()); + int lowerBoundIndex = std::min((int) tickValues.size() - 2, std::max(0, upperBoundIndex - 1)); + categorizedResults[lowerBoundIndex].first.push_back(result.xValues[i]); + categorizedResults[lowerBoundIndex].second.push_back(result.yValues[i]); + } + } + + for (auto it = categorizedResults.rbegin(); it != categorizedResults.rend(); ++it) + { + RimGridCrossPlotCurve* curve = new RimGridCrossPlotCurve(); + curve->setCategoryInformation(indexInPlot(), it->first); + if (m_categorization == RESULT_CATEGORIZATION) + { + curve->setColor(cvf::Color3f(m_legendConfig->scalarMapper()->mapToColor(tickValues[it->first]))); + } + else + { + curve->setColor(cvf::Color3f(m_legendConfig->scalarMapper()->mapToColor(it->first))); + } + curve->setSamples(it->second.first, it->second.second); + curve->showLegend(m_crossPlotCurves.empty()); + curve->setLegendEntryTitle(createAutoName()); + curve->updateCurveAppearance(); + curve->updateUiIconFromPlotSymbol(); + m_crossPlotCurves.push_back(curve); + } + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -414,7 +505,6 @@ void RimGridCrossPlotCurveSet::defineUiOrdering(QString uiConfigName, caf::PdmUi { caf::PdmUiGroup* categoryGroup = uiOrdering.addNewGroup("Data Grouping Property"); m_categoryProperty->uiOrdering(uiConfigName, *categoryGroup); - categoryGroup->add(&m_categoryBinCount); } caf::PdmUiGroup* xAxisGroup = uiOrdering.addNewGroup("X-Axis Property"); @@ -457,8 +547,25 @@ void RimGridCrossPlotCurveSet::fieldChangedByUi(const caf::PdmFieldHandle* chang { loadDataAndUpdate(true); } - else if (changedField == &m_categorization || changedField == &m_categoryBinCount) + else if (changedField == &m_categorization) { + if (m_categorization == TIME_CATEGORIZATION) + { + m_legendConfig->setColorRange(RimRegularLegendConfig::NORMAL); + m_legendConfig->setMappingMode(RimRegularLegendConfig::CATEGORY_INTEGER); + } + else if (m_categorization == FORMATION_CATEGORIZATION) + { + m_legendConfig->setColorRange(RimRegularLegendConfig::CATEGORY); + m_legendConfig->setMappingMode(RimRegularLegendConfig::CATEGORY_INTEGER); + } + else + { + m_legendConfig->setColorRange(RimRegularLegendConfig::NORMAL); + m_legendConfig->setMappingMode(RimRegularLegendConfig::LINEAR_DISCRETE); + + } + loadDataAndUpdate(true); } else if (changedField == &m_cellFilterView) @@ -467,6 +574,7 @@ void RimGridCrossPlotCurveSet::fieldChangedByUi(const caf::PdmFieldHandle* chang } else if (changedField == &m_isChecked) { + updateLegend(); triggerPlotNameUpdateAndReplot(); } } @@ -529,6 +637,96 @@ QList RimGridCrossPlotCurveSet::calculateValueOptions(co return options; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimGridCrossPlotCurveSet::updateLegend() +{ + m_legendConfig->setTitle(categoryTitle()); + + RimGridCrossPlot* parent; + this->firstAncestorOrThisOfTypeAsserted(parent); + if (parent->qwtPlot()) + { + if (m_categorization() != NO_CATEGORIZATION && m_case() && isChecked() && legendConfig()->showLegend()) + { + if (m_categorization() == FORMATION_CATEGORIZATION) + { + RimEclipseCase* eclipseCase = dynamic_cast(m_case()); + RigFormationNames* formationNames = eclipseCase->eclipseCaseData()->activeFormationNames(); + + const std::vector& categoryNames = formationNames->formationNames(); + m_legendConfig->setNamedCategories(categoryNames); + m_legendConfig->setAutomaticRanges(0, categoryNames.size() - 1, 0, categoryNames.size() - 1); + } + else if (m_categorization() == TIME_CATEGORIZATION) + { + QStringList timeStepNames = m_case->timeStepStrings(); + std::vector categoryNames; + for (auto name : timeStepNames) + { + categoryNames.push_back(name); + } + m_legendConfig->setNamedCategories(categoryNames); + m_legendConfig->setAutomaticRanges(0, categoryNames.size() - 1, 0, categoryNames.size() - 1); + } + else if (m_categoryProperty->eclipseResultAddress().isValid()) + { + RimEclipseCase* eclipseCase = dynamic_cast(m_case()); + RigEclipseCaseData* caseData = eclipseCase->eclipseCaseData(); + CVF_ASSERT(caseData); + if (!caseData) return; + + RigCaseCellResultsData* cellResultsData = caseData->results(RiaDefines::MATRIX_MODEL); + CVF_ASSERT(cellResultsData); + + double globalMin, globalMax; + double globalPosClosestToZero, globalNegClosestToZero; + + cellResultsData->minMaxCellScalarValues(m_categoryProperty->eclipseResultAddress(), globalMin, globalMax); + cellResultsData->posNegClosestToZero( + m_categoryProperty->eclipseResultAddress(), globalPosClosestToZero, globalNegClosestToZero); + + double localMin, localMax; + double localPosClosestToZero, localNegClosestToZero; + if (m_categoryProperty->hasDynamicResult() && m_timeStep != -1) + { + cellResultsData->minMaxCellScalarValues(m_categoryProperty->eclipseResultAddress(), m_timeStep, localMin, localMax); + cellResultsData->posNegClosestToZero( + m_categoryProperty->eclipseResultAddress(), m_timeStep, localPosClosestToZero, localNegClosestToZero); + } + else + { + localMin = globalMin; + localMax = globalMax; + + localPosClosestToZero = globalPosClosestToZero; + localNegClosestToZero = globalNegClosestToZero; + } + + CVF_ASSERT(m_legendConfig); + + m_legendConfig->setClosestToZeroValues( + globalPosClosestToZero, globalNegClosestToZero, localPosClosestToZero, localNegClosestToZero); + m_legendConfig->setAutomaticRanges(globalMin, globalMax, localMin, localMax); + } + parent->qwtPlot()->addOrUpdateCurveSetLegend(this); + } + else + { + parent->qwtPlot()->removeCurveSetLegend(this); + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimGridCrossPlotCurveSet::hasCategoryResult() const +{ + return m_categorization == FORMATION_CATEGORIZATION || m_categorization == TIME_CATEGORIZATION || m_categorization == RESULT_CATEGORIZATION; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -547,7 +745,7 @@ void RimGridCrossPlotCurveSet::triggerPlotNameUpdateAndReplot() void RimGridCrossPlotCurveSet::updateCurveNames(bool applyCaseName, bool applyAxisVariables, bool applyTimeStep, - bool applyCategories) + bool applyCategory) { for (auto curve : m_crossPlotCurves()) { @@ -568,16 +766,30 @@ void RimGridCrossPlotCurveSet::updateCurveNames(bool applyCaseName, nameTags += timeStepString(); } - if (applyCategories) + if (applyCategory && m_categorization != NO_CATEGORIZATION) { - QString categoryName = m_categoryNames[curve->categoryIndex()]; - if (!categoryName.isEmpty()) + if (m_categorization == RESULT_CATEGORIZATION) { - nameTags += categoryName; + std::vector tickValues; + m_legendConfig->scalarMapper()->majorTickValues(&tickValues); + size_t catIndex = (size_t) curve->categoryIndex(); + double lowerLimit = tickValues[catIndex]; + double upperLimit = catIndex + 1u < tickValues.size() + ? tickValues[catIndex + 1u] : std::numeric_limits::infinity(); + nameTags += QString("%1 [%2, %3]").arg(categoryTitle()).arg(lowerLimit).arg(upperLimit); + } + else + { + nameTags += m_legendConfig->categoryNameFromCategoryValue(curve->categoryIndex()); } } curve->setCustomName(nameTags.join(", ")); + if (m_categorization != NO_CATEGORIZATION) + { + curve->setLegendEntryTitle(createAutoName()); + } + curve->updateCurveNameAndUpdatePlotLegendAndTitle(); } } @@ -631,15 +843,24 @@ void RimGridCrossPlotCurveSet::defineEditorAttribute(const caf::PdmFieldHandle* QString uiConfigName, caf::PdmUiEditorAttribute* attribute) { - if (field == &m_categoryBinCount) +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimGridCrossPlotCurveSet::defineUiTreeOrdering(caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName /*= ""*/) +{ + if (m_categorization() != NO_CATEGORIZATION) { - auto myAttr = dynamic_cast(attribute); - if (myAttr) - { - myAttr->m_minimum = 2; - myAttr->m_maximum = 50; - } + uiTreeOrdering.add(m_legendConfig()); } + + for (auto curve : m_crossPlotCurves()) + { + uiTreeOrdering.add(curve); + } + + uiTreeOrdering.skipRemainingChildren(true); } CAF_PDM_SOURCE_INIT(RimGridCrossPlotCurveSetNameConfig, "RimGridCrossPlotCurveSetNameConfig"); @@ -655,7 +876,7 @@ RimGridCrossPlotCurveSetNameConfig::RimGridCrossPlotCurveSetNameConfig(RimNameCo CAF_PDM_InitField(&addCaseName, "AddCaseName", false, "Add Case Name", "", "", ""); CAF_PDM_InitField(&addAxisVariables, "AddAxisVariables", true, "Add Axis Variables", "", "", ""); CAF_PDM_InitField(&addTimestep, "AddTimeStep", false, "Add Time Step", "", "", ""); - CAF_PDM_InitField(&addCategorization, "AddCategorization", false, "Add Data Categorization", "", "", ""); + CAF_PDM_InitField(&addCategorization, "AddCategorization", true, "Add Data Categorization", "", "", ""); setCustomName(""); } diff --git a/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurveSet.h b/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurveSet.h index 2b0a120fb0..05f6ef3251 100644 --- a/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurveSet.h +++ b/ApplicationCode/ProjectDataModel/GridCrossPlots/RimGridCrossPlotCurveSet.h @@ -34,11 +34,13 @@ #include #include +struct RigEclipseCrossPlotResult; class RimCase; class RimGridCrossPlotCurve; class RimGridView; class RimEclipseCase; class RimEclipseResultDefinition; +class RimRegularLegendConfig; class QwtPlot; class QwtPlotCurve; class QString; @@ -83,8 +85,11 @@ public: int indexInPlot() const; QString createAutoName() const override; + QString categoryTitle() const; void detachAllCurves(); void cellFilterViewUpdated(); + + RimRegularLegendConfig* legendConfig(); std::vector< RimGridCrossPlotCurve*> curves() const; @@ -93,12 +98,16 @@ public: QString timeStepString() const; std::vector categoryStrings() const; - void updateCurveNames(bool applyCaseName, bool applyAxisVariables, bool applyTimeStep, bool applyCategories); - + void updateCurveNames(bool applyCaseName, bool applyAxisVariables, bool applyTimeStep, bool applyCategory); + void updateLegend(); + bool hasCategoryResult() const; + protected: void initAfterRead() override; void onLoadDataAndUpdate(bool updateParentPlot); + void createCurves(const RigEclipseCrossPlotResult& result); + std::map calculateCellVisibility(RimEclipseCase* eclipseCase) const; void defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) override; @@ -111,6 +120,8 @@ protected: void setDefaults(); void defineEditorAttribute(const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute) override; + void defineUiTreeOrdering(caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "") override; + private: caf::PdmPtrField m_case; caf::PdmField m_timeStep; @@ -119,11 +130,9 @@ private: caf::PdmChildField m_xAxisProperty; caf::PdmChildField m_yAxisProperty; caf::PdmChildField m_categoryProperty; - caf::PdmField m_categoryBinCount; caf::PdmChildField m_nameConfig; caf::PdmChildArrayField m_crossPlotCurves; - - std::map m_categoryNames; + caf::PdmChildField m_legendConfig; }; diff --git a/ApplicationCode/ProjectDataModel/RimPlotCurve.cpp b/ApplicationCode/ProjectDataModel/RimPlotCurve.cpp index 600a5d3a4a..ea4d1695a6 100644 --- a/ApplicationCode/ProjectDataModel/RimPlotCurve.cpp +++ b/ApplicationCode/ProjectDataModel/RimPlotCurve.cpp @@ -96,6 +96,9 @@ RimPlotCurve::RimPlotCurve() CAF_PDM_InitFieldNoDefault(&m_customCurveName, "CurveDescription", "Custom Name", "", "", ""); m_customCurveName.uiCapability()->setUiHidden(true); + CAF_PDM_InitFieldNoDefault(&m_legendEntryTitle, "LegendDescription", "Legend Name", "", "", ""); + m_legendEntryTitle.uiCapability()->setUiHidden(true); + CAF_PDM_InitField(&m_isUsingAutoName, "AutoName", true, "Auto Name", "", "", ""); CAF_PDM_InitField(&m_curveColor, "Color", cvf::Color3f(cvf::Color3::BLACK), "Color", "", "", ""); @@ -213,6 +216,14 @@ void RimPlotCurve::setCustomName(const QString& customName) m_customCurveName = customName; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimPlotCurve::setLegendEntryTitle(const QString& legendEntryTitle) +{ + m_legendEntryTitle = legendEntryTitle; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -354,9 +365,9 @@ void RimPlotCurve::setCurveVisiblity(bool visible) } //-------------------------------------------------------------------------------------------------- -/// +/// //-------------------------------------------------------------------------------------------------- -void RimPlotCurve::updateCurveNameAndUpdatePlotLegendAndTitle() +void RimPlotCurve::updateCurveName() { if (m_isUsingAutoName) { @@ -367,7 +378,23 @@ void RimPlotCurve::updateCurveNameAndUpdatePlotLegendAndTitle() m_curveName = m_customCurveName; } - m_qwtPlotCurve->setTitle(m_curveName); + if (!m_legendEntryTitle().isEmpty()) + { + m_qwtPlotCurve->setTitle(m_legendEntryTitle); + } + else + { + m_qwtPlotCurve->setTitle(m_curveName); + } + +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimPlotCurve::updateCurveNameAndUpdatePlotLegendAndTitle() +{ + updateCurveName(); updateLegendEntryVisibilityAndPlotLegend(); } @@ -375,17 +402,8 @@ void RimPlotCurve::updateCurveNameAndUpdatePlotLegendAndTitle() /// //-------------------------------------------------------------------------------------------------- void RimPlotCurve::updateCurveNameNoLegendUpdate() -{ - if (m_isUsingAutoName) - { - m_curveName = this->createCurveAutoName(); - } - else - { - m_curveName = m_customCurveName; - } - - m_qwtPlotCurve->setTitle(m_curveName); +{ + updateCurveName(); updateLegendEntryVisibilityNoPlotUpdate(); } diff --git a/ApplicationCode/ProjectDataModel/RimPlotCurve.h b/ApplicationCode/ProjectDataModel/RimPlotCurve.h index ec38defa86..a11320d6be 100644 --- a/ApplicationCode/ProjectDataModel/RimPlotCurve.h +++ b/ApplicationCode/ProjectDataModel/RimPlotCurve.h @@ -71,12 +71,15 @@ public: bool isCurveVisible() const; void setCurveVisiblity(bool visible); + void updateCurveName(); void updateCurveNameAndUpdatePlotLegendAndTitle(); void updateCurveNameNoLegendUpdate(); QString curveName() const { return m_curveName; } virtual QString curveExportDescription(const RifEclipseSummaryAddress& address = RifEclipseSummaryAddress()) const { return m_curveName; } void setCustomName(const QString& customName); + void setLegendEntryTitle(const QString& legendEntryTitle); + void updateCurveVisibility(bool updateParentPlot); void updateLegendEntryVisibilityAndPlotLegend(); void updateLegendEntryVisibilityNoPlotUpdate(); @@ -124,7 +127,8 @@ protected: caf::PdmField m_showLegend; QString m_symbolLabel; caf::PdmField m_symbolSize; - + caf::PdmField m_legendEntryTitle; + caf::PdmField m_isUsingAutoName; caf::PdmField m_curveColor; caf::PdmField m_curveThickness; diff --git a/ApplicationCode/ProjectDataModel/RimRegularLegendConfig.cpp b/ApplicationCode/ProjectDataModel/RimRegularLegendConfig.cpp index 0f28a3e1c1..08896dab1f 100644 --- a/ApplicationCode/ProjectDataModel/RimRegularLegendConfig.cpp +++ b/ApplicationCode/ProjectDataModel/RimRegularLegendConfig.cpp @@ -31,6 +31,7 @@ #include "RimEnsembleCurveSetColorManager.h" #include "RimEclipseView.h" #include "RimGeoMechResultDefinition.h" +#include "RimGridCrossPlotCurveSet.h" #include "RimIntersectionCollection.h" #include "RimStimPlanColors.h" #include "RimViewLinker.h" @@ -256,6 +257,13 @@ void RimRegularLegendConfig::fieldChangedByUi(const caf::PdmFieldHandle* changed { ensembleCurveSet->onLegendDefinitionChanged(); } + + RimGridCrossPlotCurveSet* crossPlotCurveSet; + firstAncestorOrThisOfType(crossPlotCurveSet); + if (crossPlotCurveSet) + { + crossPlotCurveSet->loadDataAndUpdate(true); + } } //-------------------------------------------------------------------------------------------------- @@ -439,6 +447,14 @@ void RimRegularLegendConfig::updateLegend() } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimRegularLegendConfig::setTickNumberFormat(NumberFormatType numberFormat) +{ + m_tickNumberFormat = numberFormat; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -861,6 +877,9 @@ QList RimRegularLegendConfig::calculateValueOptions(cons this->firstAncestorOrThisOfType(ensembleCurveSet); if (ensembleCurveSet) hasEnsembleCurveSetParent = true; + RimGridCrossPlotCurveSet* crossPlotCurveSet = nullptr; + this->firstAncestorOrThisOfType(crossPlotCurveSet); + bool isCategoryResult = false; { RimEclipseCellColors* eclCellColors = nullptr; @@ -873,7 +892,8 @@ QList RimRegularLegendConfig::calculateValueOptions(cons if ( ( eclCellColors && eclCellColors->hasCategoryResult()) || ( gmCellColors && gmCellColors->hasCategoryResult()) || ( eclCellEdgColors && eclCellEdgColors->hasCategoryResult()) - || ( ensembleCurveSet && ensembleCurveSet->currentEnsembleParameterType() == EnsembleParameter::TYPE_TEXT) ) + || ( ensembleCurveSet && ensembleCurveSet->currentEnsembleParameterType() == EnsembleParameter::TYPE_TEXT) + || ( crossPlotCurveSet && crossPlotCurveSet->hasCategoryResult())) { isCategoryResult = true; } @@ -886,8 +906,12 @@ QList RimRegularLegendConfig::calculateValueOptions(cons // This is an app enum field, see cafInternalPdmFieldTypeSpecializations.h for the default specialization of this type std::vector mappingTypes; mappingTypes.push_back(LINEAR_DISCRETE); - mappingTypes.push_back(LINEAR_CONTINUOUS); - mappingTypes.push_back(LOG10_CONTINUOUS); + + if (!crossPlotCurveSet) + { + mappingTypes.push_back(LINEAR_CONTINUOUS); + mappingTypes.push_back(LOG10_CONTINUOUS); + } mappingTypes.push_back(LOG10_DISCRETE); if (isCategoryResult) diff --git a/ApplicationCode/ProjectDataModel/RimRegularLegendConfig.h b/ApplicationCode/ProjectDataModel/RimRegularLegendConfig.h index a1ba2857cd..9d9fe4e3aa 100644 --- a/ApplicationCode/ProjectDataModel/RimRegularLegendConfig.h +++ b/ApplicationCode/ProjectDataModel/RimRegularLegendConfig.h @@ -103,6 +103,8 @@ public: ColorRangesType colorRange() { return m_colorRangeMode();} void setMappingMode(MappingType mappingType); MappingType mappingMode() { return m_mappingMode();} + void setTickNumberFormat(NumberFormatType numberFormat); + void disableAllTimeStepsRange(bool doDisable); void setAutomaticRanges(double globalMin, double globalMax, double localMin, double localMax); diff --git a/ApplicationCode/ReservoirDataModel/RigEclipseCrossPlotDataExtractor.cpp b/ApplicationCode/ReservoirDataModel/RigEclipseCrossPlotDataExtractor.cpp index 0c3d0c0743..67291f94d8 100644 --- a/ApplicationCode/ReservoirDataModel/RigEclipseCrossPlotDataExtractor.cpp +++ b/ApplicationCode/ReservoirDataModel/RigEclipseCrossPlotDataExtractor.cpp @@ -40,12 +40,9 @@ RigEclipseCrossPlotResult RigEclipseCrossPlotDataExtractor::extract(RigEclipseCa const RigEclipseResultAddress& yAddress, RigGridCrossPlotCurveCategorization categorizationType, const RigEclipseResultAddress& catAddress, - int categoryBinCount, std::map timeStepCellVisibilityMap) { RigEclipseCrossPlotResult result; - RigEclipseCrossPlotResult::CategorySamplesMap& categorySamplesMap = result.categorySamplesMap; - RigEclipseCrossPlotResult::CategoryNameMap& categoryNameMap = result.categoryNameMap; RigCaseCellResultsData* resultData = caseData->results(RiaDefines::MATRIX_MODEL); RigFormationNames* activeFormationNames = resultData->activeFormationNames(); @@ -68,7 +65,6 @@ RigEclipseCrossPlotResult RigEclipseCrossPlotDataExtractor::extract(RigEclipseCa { resultData->ensureKnownResultLoaded(catAddress); catValuesForAllSteps = &resultData->cellScalarResults(catAddress); - catBinSorter.reset(new RigEclipseResultBinSorter(*catValuesForAllSteps, categoryBinCount)); } std::set timeStepsToInclude; @@ -119,61 +115,38 @@ RigEclipseCrossPlotResult RigEclipseCrossPlotDataExtractor::extract(RigEclipseCa double xValue = xAccessor.cellScalarGlobIdx(globalCellIdx); double yValue = yAccessor.cellScalarGlobIdx(globalCellIdx); - int category = 0; + if (xValue == HUGE_VAL || yValue == HUGE_VAL) continue; + + result.xValues.push_back(xValue); + result.yValues.push_back(yValue); + if (categorizationType == TIME_CATEGORIZATION) { - category = timeStep; + result.catValuesDiscrete.push_back(timeStep); } - else if (categorizationType == FORMATION_CATEGORIZATION && activeFormationNames) + else if (categorizationType == FORMATION_CATEGORIZATION) { + CVF_ASSERT(activeFormationNames); + int category = 0; size_t i(cvf::UNDEFINED_SIZE_T), j(cvf::UNDEFINED_SIZE_T), k(cvf::UNDEFINED_SIZE_T); if (mainGrid->ijkFromCellIndex(globalCellIdx, &i, &j, &k)) { category = activeFormationNames->formationIndexFromKLayerIdx(k); } + result.catValuesDiscrete.push_back(category); } - else if (catAccessor && catBinSorter) + else if (categorizationType == RESULT_CATEGORIZATION) { - double catValue = catAccessor->cellScalarGlobIdx(globalCellIdx); - category = catBinSorter->binNumber(catValue); - } - if (xValue != HUGE_VAL && yValue != HUGE_VAL) - { - categorySamplesMap[category].first.push_back(xValue); - categorySamplesMap[category].second.push_back(yValue); + double catValue = HUGE_VAL; + if (catAccessor) + { + catValue = catAccessor->cellScalarGlobIdx(globalCellIdx); + } + result.catValuesContinuous.push_back(catValue); } } } } - std::vector timeStepDates = resultData->timeStepDates(); - QString timeFormatString = RiaQDateTimeTools::createTimeFormatStringFromDates(timeStepDates); - - for (const auto& sampleCategory : categorySamplesMap) - { - QString categoryName; - if (categorizationType == TIME_CATEGORIZATION && categorySamplesMap.size() > 1u) - { - if (sampleCategory.first < static_cast(timeStepDates.size())) - { - categoryName = RiaQDateTimeTools::toStringUsingApplicationLocale(timeStepDates[sampleCategory.first], timeFormatString); - } - } - else if (categorizationType == FORMATION_CATEGORIZATION && activeFormationNames) - { - categoryName = activeFormationNames->formationNameFromKLayerIdx(sampleCategory.first); - } - else if (catBinSorter) - { - std::pair binRange = catBinSorter->binRange(sampleCategory.first); - - categoryName = QString("%1 [%2, %3]") - .arg(catAddress.m_resultName) - .arg(binRange.first) - .arg(binRange.second); - } - categoryNameMap.insert(std::make_pair(sampleCategory.first, categoryName)); - } - return result; } diff --git a/ApplicationCode/ReservoirDataModel/RigEclipseCrossPlotDataExtractor.h b/ApplicationCode/ReservoirDataModel/RigEclipseCrossPlotDataExtractor.h index f2c55fe7b9..cbf209a85c 100644 --- a/ApplicationCode/ReservoirDataModel/RigEclipseCrossPlotDataExtractor.h +++ b/ApplicationCode/ReservoirDataModel/RigEclipseCrossPlotDataExtractor.h @@ -33,13 +33,10 @@ class QString; struct RigEclipseCrossPlotResult { - typedef std::pair, std::vector> ResultXYValues; - - typedef std::map CategorySamplesMap; - typedef std::map CategoryNameMap; - - CategorySamplesMap categorySamplesMap; - CategoryNameMap categoryNameMap; + std::vector xValues; + std::vector yValues; + std::vector catValuesContinuous; + std::vector catValuesDiscrete; }; class RigEclipseCrossPlotDataExtractor @@ -51,6 +48,5 @@ public: const RigEclipseResultAddress& yAddress, RigGridCrossPlotCurveCategorization categorizationType, const RigEclipseResultAddress& categoryAddress, - int categoryBinCount, std::map timeStepCellVisibilityMap); }; diff --git a/ApplicationCode/UserInterface/CMakeLists_files.cmake b/ApplicationCode/UserInterface/CMakeLists_files.cmake index 98d9a92806..03ac8f0c88 100644 --- a/ApplicationCode/UserInterface/CMakeLists_files.cmake +++ b/ApplicationCode/UserInterface/CMakeLists_files.cmake @@ -37,6 +37,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RiuSelectionChangedHandler.h ${CMAKE_CURRENT_LIST_DIR}/Riu3dSelectionManager.h ${CMAKE_CURRENT_LIST_DIR}/RiuSimpleHistogramWidget.h ${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlot.h +${CMAKE_CURRENT_LIST_DIR}/RiuGridCrossQwtPlot.h ${CMAKE_CURRENT_LIST_DIR}/RiuSummaryQwtPlot.h ${CMAKE_CURRENT_LIST_DIR}/RiuTextDialog.h ${CMAKE_CURRENT_LIST_DIR}/RiuTimeStepChangedHandler.h @@ -118,6 +119,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RiuSelectionChangedHandler.cpp ${CMAKE_CURRENT_LIST_DIR}/Riu3dSelectionManager.cpp ${CMAKE_CURRENT_LIST_DIR}/RiuSimpleHistogramWidget.cpp ${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlot.cpp +${CMAKE_CURRENT_LIST_DIR}/RiuGridCrossQwtPlot.cpp ${CMAKE_CURRENT_LIST_DIR}/RiuSummaryQwtPlot.cpp ${CMAKE_CURRENT_LIST_DIR}/RiuTextDialog.cpp ${CMAKE_CURRENT_LIST_DIR}/RiuTimeStepChangedHandler.cpp @@ -185,6 +187,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RiuWellLogPlot.h ${CMAKE_CURRENT_LIST_DIR}/RiuWellLogTrack.h ${CMAKE_CURRENT_LIST_DIR}/RiuRecentFileActionProvider.h ${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlot.h +${CMAKE_CURRENT_LIST_DIR}/RiuGridCrossQwtPlot.h ${CMAKE_CURRENT_LIST_DIR}/RiuSummaryQwtPlot.h ${CMAKE_CURRENT_LIST_DIR}/RiuTofAccumulatedPhaseFractionsPlot.h ${CMAKE_CURRENT_LIST_DIR}/RiuQwtScalePicker.h diff --git a/ApplicationCode/UserInterface/RiuGridCrossQwtPlot.cpp b/ApplicationCode/UserInterface/RiuGridCrossQwtPlot.cpp new file mode 100644 index 0000000000..8319a65ec0 --- /dev/null +++ b/ApplicationCode/UserInterface/RiuGridCrossQwtPlot.cpp @@ -0,0 +1,140 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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 "RiuGridCrossQwtPlot.h" + +#include "RiuCvfOverlayItemWidget.h" +#include "RiuWidgetDragger.h" + +#include "RimGridCrossPlot.h" +#include "RimGridCrossPlotCurveSet.h" +#include "RimRegularLegendConfig.h" + +#include "cafTitledOverlayFrame.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RiuGridCrossQwtPlot::RiuGridCrossQwtPlot(RimViewWindow* ownerViewWindow, QWidget* parent /*= nullptr*/) + : RiuQwtPlot(ownerViewWindow, parent) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuGridCrossQwtPlot::addOrUpdateCurveSetLegend(RimGridCrossPlotCurveSet* curveSetToShowLegendFor) +{ + RiuCvfOverlayItemWidget* overlayWidget = nullptr; + + auto it = m_legendWidgets.find(curveSetToShowLegendFor); + if (it == m_legendWidgets.end() || it->second == nullptr) + { + overlayWidget = new RiuCvfOverlayItemWidget(this); + + new RiuWidgetDragger(overlayWidget); + + m_legendWidgets[curveSetToShowLegendFor] = overlayWidget; + } + else + { + overlayWidget = it->second; + } + + if (overlayWidget) + { + caf::TitledOverlayFrame* overlayItem = curveSetToShowLegendFor->legendConfig()->titledOverlayFrame(); + overlayItem->setRenderSize(overlayItem->preferredSize()); + + overlayWidget->updateFromOverlayItem(curveSetToShowLegendFor->legendConfig()->titledOverlayFrame()); + } + + this->updateLegendLayout(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuGridCrossQwtPlot::removeCurveSetLegend(RimGridCrossPlotCurveSet* curveSetToShowLegendFor) +{ + auto it = m_legendWidgets.find(curveSetToShowLegendFor); + if (it != m_legendWidgets.end()) + { + if (it->second != nullptr) it->second->deleteLater(); + + m_legendWidgets.erase(it); + } + + this->updateLegendLayout(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuGridCrossQwtPlot::updateLayout() +{ + QwtPlot::updateLayout(); + updateLegendLayout(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuGridCrossQwtPlot::updateLegendLayout() +{ + const int spacing = 5; + int startMarginX = this->canvas()->pos().x() + spacing; + int startMarginY = this->canvas()->pos().y() + spacing; + + int xpos = startMarginX; + int ypos = startMarginY; + int maxColumnWidth = 0; + + RimGridCrossPlot* crossPlot = dynamic_cast(ownerPlotDefinition()); + + if (!crossPlot) return; + + std::set legendTypes; + + for (RimGridCrossPlotCurveSet* curveSet : crossPlot->curveSets()) + { + if (!curveSet->isChecked() || !curveSet->legendConfig()->showLegend()) continue; + + auto pairIt = m_legendWidgets.find(curveSet); + if (pairIt != m_legendWidgets.end()) + { + RiuCvfOverlayItemWidget* overlayWidget = pairIt->second; + // Show only one copy of each legend type + if (!legendTypes.count(curveSet->categoryTitle())) + { + if (ypos + overlayWidget->height() + spacing > this->canvas()->height()) + { + xpos += spacing + maxColumnWidth; + ypos = startMarginY; + maxColumnWidth = 0; + } + + overlayWidget->show(); + overlayWidget->move(xpos, ypos); + + ypos += pairIt->second->height() + spacing; + maxColumnWidth = std::max(maxColumnWidth, pairIt->second->width()); + legendTypes.insert(curveSet->categoryTitle()); + } + } + } +} diff --git a/ApplicationCode/UserInterface/RiuGridCrossQwtPlot.h b/ApplicationCode/UserInterface/RiuGridCrossQwtPlot.h new file mode 100644 index 0000000000..eb9926d37a --- /dev/null +++ b/ApplicationCode/UserInterface/RiuGridCrossQwtPlot.h @@ -0,0 +1,52 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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. +// +///////////////////////////////////////////////////////////////////////////////// +#pragma once + + +#include "RiuInterfaceToViewWindow.h" +#include "RiuQwtPlot.h" + +#include "cafPdmPointer.h" + +#include + +class RimGridCrossPlotCurveSet; +class RiuCvfOverlayItemWidget; + +//================================================================================================== +// +// +// +//================================================================================================== +class RiuGridCrossQwtPlot : public RiuQwtPlot +{ + Q_OBJECT; + +public: + RiuGridCrossQwtPlot(RimViewWindow* ownerViewWindow, QWidget* parent = nullptr); + + void addOrUpdateCurveSetLegend(RimGridCrossPlotCurveSet* curveSetToShowLegendFor); + void removeCurveSetLegend(RimGridCrossPlotCurveSet* curveSetToShowLegendFor); + +protected: + void updateLayout() override; + void updateLegendLayout(); + +private: + std::map, QPointer> m_legendWidgets; +}; diff --git a/ApplicationCode/UserInterface/RiuSummaryQwtPlot.cpp b/ApplicationCode/UserInterface/RiuSummaryQwtPlot.cpp index 25dc3ce2e8..8ed53aaff7 100644 --- a/ApplicationCode/UserInterface/RiuSummaryQwtPlot.cpp +++ b/ApplicationCode/UserInterface/RiuSummaryQwtPlot.cpp @@ -143,7 +143,7 @@ void RiuSummaryQwtPlot::addOrUpdateEnsembleCurveSetLegend(RimEnsembleCurveSet* c overlayWidget->show(); } - this->updateEnsembleLegendLayout(); + this->updateLegendLayout(); } //-------------------------------------------------------------------------------------------------- @@ -159,7 +159,7 @@ void RiuSummaryQwtPlot::removeEnsembleCurveSetLegend(RimEnsembleCurveSet* curveS m_ensembleLegendWidgets.erase(it); } - this->updateEnsembleLegendLayout(); + this->updateLegendLayout(); } //-------------------------------------------------------------------------------------------------- @@ -215,13 +215,13 @@ void RiuSummaryQwtPlot::setDefaults() void RiuSummaryQwtPlot::updateLayout() { QwtPlot::updateLayout(); - updateEnsembleLegendLayout(); + updateLegendLayout(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RiuSummaryQwtPlot::updateEnsembleLegendLayout() +void RiuSummaryQwtPlot::updateLegendLayout() { const int spacing = 5; int startMarginX = this->canvas()->pos().x() + spacing; diff --git a/ApplicationCode/UserInterface/RiuSummaryQwtPlot.h b/ApplicationCode/UserInterface/RiuSummaryQwtPlot.h index ca939f0b69..a70d9c068f 100644 --- a/ApplicationCode/UserInterface/RiuSummaryQwtPlot.h +++ b/ApplicationCode/UserInterface/RiuSummaryQwtPlot.h @@ -23,15 +23,8 @@ #include "cafPdmPointer.h" -#include "qwt_plot.h" - #include -class QwtInterval; -class QwtPlotZoomer; - -class RimGridCrossPlot; -class RimSummaryPlot; class RimEnsembleCurveSet; class RiuCvfOverlayItemWidget; @@ -59,7 +52,7 @@ protected: void setDefaults(); void updateLayout() override; private: - void updateEnsembleLegendLayout(); + void updateLegendLayout(); std::map, QPointer> m_ensembleLegendWidgets; };