diff --git a/ApplicationCode/FileInterface/RifCsvUserData.cpp b/ApplicationCode/FileInterface/RifCsvUserData.cpp index a07ddbe757..21b85d301f 100644 --- a/ApplicationCode/FileInterface/RifCsvUserData.cpp +++ b/ApplicationCode/FileInterface/RifCsvUserData.cpp @@ -159,6 +159,7 @@ void RifCsvUserData::buildTimeStepsAndMappings() RifEclipseSummaryAddress sumAddress = ci.summaryAddress; m_allResultAddresses.push_back(sumAddress); + if (sumAddress.isErrorResult()) m_allErrorAddresses.insert(sumAddress); m_mapFromAddressToTimeStepIndex[sumAddress] = m_timeSteps.size() - 1; m_mapFromAddressToResultIndex[sumAddress] = columnIndex; diff --git a/ApplicationCode/FileInterface/RifEclipseSummaryAddress.cpp b/ApplicationCode/FileInterface/RifEclipseSummaryAddress.cpp index b9292ea83c..a22f7eb6ad 100644 --- a/ApplicationCode/FileInterface/RifEclipseSummaryAddress.cpp +++ b/ApplicationCode/FileInterface/RifEclipseSummaryAddress.cpp @@ -143,6 +143,9 @@ RifEclipseSummaryAddress RifEclipseSummaryAddress::importedAddress(const std::st std::string RifEclipseSummaryAddress::uiText() const { std::string text; + + if (m_isErrorResult) text += "ERR:"; + text += m_quantityName; switch(this->category()) { @@ -227,7 +230,7 @@ std::string RifEclipseSummaryAddress::uiText(RifEclipseSummaryAddress::SummaryId case RifEclipseSummaryAddress::INPUT_LGR_NAME: return lgrName(); case RifEclipseSummaryAddress::INPUT_SEGMENT_NUMBER: return std::to_string(wellSegmentNumber()); case RifEclipseSummaryAddress::INPUT_AQUIFER_NUMBER: return std::to_string(aquiferNumber()); - case RifEclipseSummaryAddress::INPUT_VECTOR_NAME: return quantityName() + (m_isErrorResult ? " (err)" : ""); + case RifEclipseSummaryAddress::INPUT_VECTOR_NAME: return quantityName(); } return ""; } diff --git a/ApplicationCode/FileInterface/RifSummaryReaderInterface.cpp b/ApplicationCode/FileInterface/RifSummaryReaderInterface.cpp index afd8ea8af2..0124ec1614 100644 --- a/ApplicationCode/FileInterface/RifSummaryReaderInterface.cpp +++ b/ApplicationCode/FileInterface/RifSummaryReaderInterface.cpp @@ -31,6 +31,17 @@ const std::vector& RifSummaryReaderInterface::allResul return m_allResultAddresses; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifEclipseSummaryAddress RifSummaryReaderInterface::errorAddress(const RifEclipseSummaryAddress& resultAddress) const +{ + RifEclipseSummaryAddress errAddr = resultAddress; + errAddr.setAsErrorResult(); + + return m_allErrorAddresses.find(errAddr) != m_allErrorAddresses.end() ? errAddr : RifEclipseSummaryAddress(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/FileInterface/RifSummaryReaderInterface.h b/ApplicationCode/FileInterface/RifSummaryReaderInterface.h index e96b6eb4a2..357269cd2d 100644 --- a/ApplicationCode/FileInterface/RifSummaryReaderInterface.h +++ b/ApplicationCode/FileInterface/RifSummaryReaderInterface.h @@ -38,6 +38,7 @@ class RifSummaryReaderInterface : public cvf::Object public: bool hasAddress(const RifEclipseSummaryAddress& resultAddress) const; const std::vector& allResultAddresses() const; + RifEclipseSummaryAddress errorAddress(const RifEclipseSummaryAddress& resultAddress) const; virtual const std::vector& timeSteps(const RifEclipseSummaryAddress& resultAddress) const = 0; @@ -50,5 +51,6 @@ public: static std::vector fromTimeT(const std::vector& timeSteps); protected: - std::vector m_allResultAddresses; + std::vector m_allResultAddresses; // Result and error addresses + std::set m_allErrorAddresses; // Error addresses }; diff --git a/ApplicationCode/ProjectDataModel/RimPlotCurve.cpp b/ApplicationCode/ProjectDataModel/RimPlotCurve.cpp index b1a8f070ee..52a592d104 100644 --- a/ApplicationCode/ProjectDataModel/RimPlotCurve.cpp +++ b/ApplicationCode/ProjectDataModel/RimPlotCurve.cpp @@ -20,6 +20,7 @@ #include "RimEnsembleCurveSet.h" #include "RimEnsembleCurveSetCollection.h" +#include "RimSummaryCrossPlot.h" #include "RimSummaryCurve.h" #include "RimSummaryCurveCollection.h" #include "RimSummaryCurveFilter.h" @@ -110,6 +111,8 @@ RimPlotCurve::RimPlotCurve() CAF_PDM_InitField(&m_showLegend, "ShowLegend", true, "Contribute To Legend", "", "", ""); + CAF_PDM_InitField(&m_showErrorBars, "ShowErrorBars", true, "Show Error Bars", "", "", ""); + m_qwtPlotCurve = new RiuRimQwtPlotCurve(this); m_parentQwtPlot = nullptr; @@ -170,7 +173,11 @@ void RimPlotCurve::fieldChangedByUi(const caf::PdmFieldHandle* changedField, con { updateLegendEntryVisibilityAndPlotLegend(); } - + else if (changedField == &m_showErrorBars) + { + m_qwtPlotCurve->showErrorBars(m_showErrorBars); + updateCurveAppearance(); + } if (m_parentQwtPlot) m_parentQwtPlot->replot(); } @@ -362,6 +369,9 @@ void RimPlotCurve::appearanceUiOrdering(caf::PdmUiOrdering& uiOrdering) uiOrdering.add(&m_curveThickness); uiOrdering.add(&m_lineStyle); uiOrdering.add(&m_curveInterpolation); + + if(isCrossPlotCurve()) m_showErrorBars = false; + else uiOrdering.add(&m_showErrorBars); } //-------------------------------------------------------------------------------------------------- @@ -464,6 +474,20 @@ void RimPlotCurve::updateCurveAppearance() m_qwtPlotCurve->setStyle(curveStyle); m_qwtPlotCurve->setSymbol(symbol); m_qwtPlotCurve->setSymbolSkipPixelDistance(m_symbolSkipPixelDistance()); + + m_qwtPlotCurve->setErrorBarsColor(curveColor); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimPlotCurve::isCrossPlotCurve() const +{ + RimSummaryCrossPlot* crossPlot = nullptr; + this->firstAncestorOrThisOfType(crossPlot); + if (crossPlot) return true; + + return false; } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/ProjectDataModel/RimPlotCurve.h b/ApplicationCode/ProjectDataModel/RimPlotCurve.h index 9ece407b89..9876ffc3fd 100644 --- a/ApplicationCode/ProjectDataModel/RimPlotCurve.h +++ b/ApplicationCode/ProjectDataModel/RimPlotCurve.h @@ -101,6 +101,7 @@ public: void setZOrder(double z); virtual void updateCurveAppearance(); + bool isCrossPlotCurve() const; protected: @@ -135,7 +136,7 @@ protected: caf::PdmField m_curveColor; caf::PdmField m_curveThickness; caf::PdmField m_symbolSkipPixelDistance; - + caf::PdmField m_showErrorBars; caf::PdmField< caf::AppEnum< PointSymbolEnum > > m_pointSymbol; caf::PdmField< caf::AppEnum< LineStyleEnum > > m_lineStyle; diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.cpp b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.cpp index fd52dbec7b..34acce1b22 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.cpp @@ -457,7 +457,18 @@ void RimSummaryCurve::onLoadDataAndUpdate(bool updateParentPlot) { if (plot->timeAxisProperties()->timeMode() == RimSummaryTimeAxisProperties::DATE) { - m_qwtPlotCurve->setSamplesFromTimeTAndYValues(curveTimeStepsY, curveValuesY, isLogCurve); + auto reader = summaryCaseY()->summaryReader(); + auto errAddress = reader->errorAddress(summaryAddressY()); + if (errAddress.isValid()) + { + std::vector errValues; + reader->values(errAddress, &errValues); + m_qwtPlotCurve->setSamplesFromTimeTAndYValues(curveTimeStepsY, curveValuesY, errValues, isLogCurve); + } + else + { + m_qwtPlotCurve->setSamplesFromTimeTAndYValues(curveTimeStepsY, curveValuesY, isLogCurve); + } } else { @@ -492,6 +503,8 @@ void RimSummaryCurve::onLoadDataAndUpdate(bool updateParentPlot) updateZoomInParentPlot(); m_parentQwtPlot->replot(); } + + m_qwtPlotCurve->showErrorBars(m_showErrorBars); } if (updateParentPlot) updateQwtPlotAxis(); @@ -594,18 +607,6 @@ void RimSummaryCurve::appendOptionItemsForSummaryAddresses(QListfirstAncestorOrThisOfType(crossPlot); - if (crossPlot) return true; - - return false; -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.h b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.h index 43a745edd8..4517e8f889 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.h +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.h @@ -97,8 +97,6 @@ private: RimSummaryCase* summaryCase, RimSummaryFilter* summaryFilter); - bool isCrossPlotCurve() const; - private: // Y values caf::PdmPtrField m_yValuesSummaryCase; diff --git a/ApplicationCode/UserInterface/RiuLineSegmentQwtPlotCurve.cpp b/ApplicationCode/UserInterface/RiuLineSegmentQwtPlotCurve.cpp index 9db96c0326..22be47e0be 100644 --- a/ApplicationCode/UserInterface/RiuLineSegmentQwtPlotCurve.cpp +++ b/ApplicationCode/UserInterface/RiuLineSegmentQwtPlotCurve.cpp @@ -24,7 +24,9 @@ #include "qwt_date.h" #include "qwt_point_mapper.h" #include "qwt_painter.h" - +#include "qwt_plot_intervalcurve.h" +#include "qwt_scale_map.h" +#include "qwt_interval_symbol.h" //-------------------------------------------------------------------------------------------------- /// @@ -39,6 +41,13 @@ RiuLineSegmentQwtPlotCurve::RiuLineSegmentQwtPlotCurve(const QString &title) this->setRenderHint(QwtPlotItem::RenderAntialiased, true); m_symbolSkipPixelDistance = 10.0f; + + m_errorBars = new QwtPlotIntervalCurve(); + m_errorBars->setStyle(QwtPlotIntervalCurve::CurveStyle::NoCurve); + m_errorBars->setSymbol(new QwtIntervalSymbol(QwtIntervalSymbol::Bar)); + + m_showErrorBars = true; + m_attachedToPlot = nullptr; } //-------------------------------------------------------------------------------------------------- @@ -46,20 +55,25 @@ RiuLineSegmentQwtPlotCurve::RiuLineSegmentQwtPlotCurve(const QString &title) //-------------------------------------------------------------------------------------------------- RiuLineSegmentQwtPlotCurve::~RiuLineSegmentQwtPlotCurve() { + delete m_errorBars; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RiuLineSegmentQwtPlotCurve::setSamplesFromXValuesAndYValues(const std::vector& xValues, const std::vector& yValues, bool keepOnlyPositiveValues) +void RiuLineSegmentQwtPlotCurve::setSamplesFromXValuesAndYValues(const std::vector& xValues, const std::vector& yValues, const std::vector& yErrorValues, bool keepOnlyPositiveValues) { CVF_ASSERT(xValues.size() == yValues.size()); + CVF_ASSERT(yErrorValues.empty() || yErrorValues.size() == xValues.size()); + bool showErrorBars = m_showErrorBars && !yErrorValues.empty(); QPolygonF points; + QVector errorIntervals; std::vector< std::pair > filteredIntervals; { std::vector filteredYValues; std::vector filteredXValues; + std::vector filteredYErrorValues; { auto intervalsOfValidValues = RigCurveDataTools::calculateIntervalsOfValidValues(yValues, keepOnlyPositiveValues); @@ -67,18 +81,36 @@ void RiuLineSegmentQwtPlotCurve::setSamplesFromXValuesAndYValues(const std::vect RigCurveDataTools::getValuesByIntervals(yValues, intervalsOfValidValues, &filteredYValues); RigCurveDataTools::getValuesByIntervals(xValues, intervalsOfValidValues, &filteredXValues); + if(showErrorBars) RigCurveDataTools::getValuesByIntervals(yErrorValues, intervalsOfValidValues, &filteredYErrorValues); + filteredIntervals = RigCurveDataTools::computePolyLineStartStopIndices(intervalsOfValidValues); } points.reserve(static_cast(filteredXValues.size())); + errorIntervals.reserve(static_cast(filteredXValues.size())); for ( size_t i = 0; i < filteredXValues.size(); i++ ) { points << QPointF(filteredXValues[i], filteredYValues[i]); + + if (showErrorBars) + { + errorIntervals << QwtIntervalSample(filteredXValues[i], filteredYValues[i] - filteredYErrorValues[i], filteredYValues[i] + filteredYErrorValues[i]); + } } } this->setSamples(points); this->setLineSegmentStartStopIndices(filteredIntervals); + + if(showErrorBars) m_errorBars->setSamples(errorIntervals); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuLineSegmentQwtPlotCurve::setSamplesFromXValuesAndYValues(const std::vector& xValues, const std::vector& yValues, bool keepOnlyPositiveValues) +{ + setSamplesFromXValuesAndYValues(xValues, yValues, std::vector(), keepOnlyPositiveValues); } //-------------------------------------------------------------------------------------------------- @@ -86,7 +118,7 @@ void RiuLineSegmentQwtPlotCurve::setSamplesFromXValuesAndYValues(const std::vect //-------------------------------------------------------------------------------------------------- void RiuLineSegmentQwtPlotCurve::setSamplesFromDatesAndYValues(const std::vector& dateTimes, const std::vector& yValues, bool keepOnlyPositiveValues) { - setSamplesFromXValuesAndYValues(RiuLineSegmentQwtPlotCurve::fromQDateTime(dateTimes), yValues, keepOnlyPositiveValues); + setSamplesFromXValuesAndYValues(RiuLineSegmentQwtPlotCurve::fromQDateTime(dateTimes), yValues, std::vector(), keepOnlyPositiveValues); } //-------------------------------------------------------------------------------------------------- @@ -94,7 +126,15 @@ void RiuLineSegmentQwtPlotCurve::setSamplesFromDatesAndYValues(const std::vector //-------------------------------------------------------------------------------------------------- void RiuLineSegmentQwtPlotCurve::setSamplesFromTimeTAndYValues(const std::vector& dateTimes, const std::vector& yValues, bool keepOnlyPositiveValues) { - setSamplesFromXValuesAndYValues(RiuLineSegmentQwtPlotCurve::fromTime_t(dateTimes), yValues, keepOnlyPositiveValues); + setSamplesFromXValuesAndYValues(RiuLineSegmentQwtPlotCurve::fromTime_t(dateTimes), yValues, std::vector(), keepOnlyPositiveValues); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuLineSegmentQwtPlotCurve::setSamplesFromTimeTAndYValues(const std::vector& dateTimes, const std::vector& yValues, const std::vector& yErrorValues, bool keepOnlyPositiveValues) +{ + setSamplesFromXValuesAndYValues(RiuLineSegmentQwtPlotCurve::fromTime_t(dateTimes), yValues, yErrorValues, keepOnlyPositiveValues); } //-------------------------------------------------------------------------------------------------- @@ -201,6 +241,46 @@ void RiuLineSegmentQwtPlotCurve::setSymbolSkipPixelDistance(float distance) m_symbolSkipPixelDistance = distance >= 0.0f ? distance: 0.0f; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuLineSegmentQwtPlotCurve::attach(QwtPlot *plot) +{ + QwtPlotItem::attach(plot); + if(m_showErrorBars) m_errorBars->attach(plot); + m_attachedToPlot = plot; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuLineSegmentQwtPlotCurve::detach() +{ + QwtPlotItem::detach(); + m_errorBars->detach(); + m_attachedToPlot = nullptr; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuLineSegmentQwtPlotCurve::showErrorBars(bool show) +{ + m_showErrorBars = show; + if (m_showErrorBars && m_attachedToPlot) m_errorBars->attach(m_attachedToPlot); + else m_errorBars->detach(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuLineSegmentQwtPlotCurve::setErrorBarsColor(QColor color) +{ + QwtIntervalSymbol* newSymbol = new QwtIntervalSymbol(QwtIntervalSymbol::Bar); + newSymbol->setPen(QPen(color)); + m_errorBars->setSymbol(newSymbol); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/UserInterface/RiuLineSegmentQwtPlotCurve.h b/ApplicationCode/UserInterface/RiuLineSegmentQwtPlotCurve.h index 31e220fcf9..1951b0676d 100644 --- a/ApplicationCode/UserInterface/RiuLineSegmentQwtPlotCurve.h +++ b/ApplicationCode/UserInterface/RiuLineSegmentQwtPlotCurve.h @@ -20,6 +20,9 @@ #pragma once #include "qwt_plot_curve.h" +#include "qwt_plot_intervalcurve.h" + +class RiuErrorBarsQwtPlotCurve; //================================================================================================== // @@ -50,6 +53,11 @@ public: void setSamplesFromXValuesAndYValues(const std::vector& xValues, const std::vector& yValues, bool keepOnlyPositiveValues); + + void setSamplesFromXValuesAndYValues(const std::vector& xValues, + const std::vector& yValues, + const std::vector& yErrorValues, + bool keepOnlyPositiveValues); void setSamplesFromDatesAndYValues(const std::vector& dateTimes, const std::vector& yValues, @@ -59,10 +67,20 @@ public: const std::vector& yValues, bool keepOnlyPositiveValues); + void setSamplesFromTimeTAndYValues(const std::vector& dateTimes, + const std::vector& yValues, + const std::vector& yErrorValues, + bool keepOnlyPositiveValues); + void setLineSegmentStartStopIndices(const std::vector< std::pair >& lineSegmentStartStopIndices); void setSymbolSkipPixelDistance(float distance); + void attach(QwtPlot *plot); + void detach(); + void showErrorBars(bool show); + void setErrorBarsColor(QColor color); + protected: virtual void drawCurve(QPainter* p, int style, const QwtScaleMap& xMap, const QwtScaleMap& yMap, @@ -83,4 +101,8 @@ private: private: std::vector< std::pair > m_polyLineStartStopIndices; float m_symbolSkipPixelDistance; + + bool m_showErrorBars; + QwtPlotIntervalCurve* m_errorBars; + QwtPlot* m_attachedToPlot; };