Contour Map: Add optional value filtering

* #12071 Contour Map: Add optional value filtering 
* Move management of min/max values to common base class
This commit is contained in:
Magne Sjaastad 2025-01-16 13:52:03 +01:00 committed by GitHub
parent 15428304be
commit d37bc104e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 200 additions and 74 deletions

View File

@ -422,11 +422,10 @@ void RimGeoMechContourMapProjection::defineEditorAttribute( const caf::PdmFieldH
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<double, double> RimGeoMechContourMapProjection::minmaxValuesAllTimeSteps()
std::pair<double, double> RimGeoMechContourMapProjection::computeMinMaxValuesAllTimeSteps()
{
if ( !resultRangeIsValid() )
{
clearTimeStepRange();
double minimum = std::numeric_limits<double>::infinity();
double maximum = -std::numeric_limits<double>::infinity();
if ( geoMechCase()->geoMechData()->femPartResults() )
{
@ -434,10 +433,9 @@ std::pair<double, double> RimGeoMechContourMapProjection::minmaxValuesAllTimeSte
for ( int stepIdx = 0; stepIdx < steps; stepIdx++ )
{
std::vector<double> aggregatedResults = generateResults( stepIdx );
m_minResultAllTimeSteps = std::min( m_minResultAllTimeSteps, RigContourMapProjection::minValue( aggregatedResults ) );
m_maxResultAllTimeSteps = std::max( m_maxResultAllTimeSteps, RigContourMapProjection::maxValue( aggregatedResults ) );
minimum = std::min( minimum, RigContourMapProjection::minValue( aggregatedResults ) );
maximum = std::max( maximum, RigContourMapProjection::maxValue( aggregatedResults ) );
}
}
}
return std::make_pair( m_minResultAllTimeSteps, m_maxResultAllTimeSteps );
return std::make_pair( minimum, maximum );
}

View File

@ -69,7 +69,7 @@ protected:
RimGeoMechCase* geoMechCase() const;
RimGeoMechContourMapView* view() const;
std::pair<double, double> minmaxValuesAllTimeSteps() override;
std::pair<double, double> computeMinMaxValuesAllTimeSteps() override;
void updateAfterResultGeneration( int timeStep ) override;

View File

@ -25,7 +25,7 @@ namespace caf
template <>
void caf::AppEnum<RimIntersectionFilterEnum>::setUp()
{
addItem( RimIntersectionFilterEnum::INTERSECT_FILTER_NONE, "INTERSECT_SHOW_ALL", "None" );
addItem( RimIntersectionFilterEnum::INTERSECT_FILTER_NONE, "INTERSECT_SHOW_ALL", "Disabled" );
addItem( RimIntersectionFilterEnum::INTERSECT_FILTER_ABOVE, "INTERSECT_SHOW_ABOVE", "Above" );
addItem( RimIntersectionFilterEnum::INTERSECT_FILTER_BELOW, "INTERSECT_SHOW_BELOW", "Below" );
addItem( RimIntersectionFilterEnum::INTERSECT_FILTER_BETWEEN, "INTERSECT_SHOW_BELOW", "Between" );

View File

@ -84,6 +84,13 @@ RimContourMapProjection::RimContourMapProjection()
CAF_PDM_InitField( &m_showContourLabels, "ContourLabels", true, "Show Contour Labels" );
CAF_PDM_InitField( &m_smoothContourLines, "SmoothContourLines", true, "Smooth Contour Lines" );
auto defaultValue = caf::AppEnum<RimIntersectionFilterEnum>( RimIntersectionFilterEnum::INTERSECT_FILTER_NONE );
CAF_PDM_InitField( &m_valueFilterType, "ValueFilterType", defaultValue, "Value Filter" );
CAF_PDM_InitField( &m_upperThreshold, "UpperThreshold", 0.0, "Upper Threshold" );
m_upperThreshold.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() );
CAF_PDM_InitField( &m_lowerThreshold, "LowerThreshold", 0.0, "Lower Threshold" );
m_lowerThreshold.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() );
setName( "Map Projection" );
nameField()->uiCapability()->setUiReadOnly( true );
}
@ -109,7 +116,7 @@ void RimContourMapProjection::generateResultsIfNecessary( int timeStep )
if ( gridMappingNeedsUpdating() || mapCellVisibilityNeedsUpdating( timeStep ) || resultVariableChanged() )
{
clearResults();
clearTimeStepRange();
clearMinMaxValueRange();
if ( gridMappingNeedsUpdating() )
{
@ -146,6 +153,8 @@ void RimContourMapProjection::generateGeometryIfNecessary()
if ( geometryNeedsUpdating() )
{
m_contourMapProjection->setValueFilter( valueFilterMinMax() );
std::vector<double> contourLevels;
bool discrete = false;
@ -286,6 +295,31 @@ const RigContourMapGrid* RimContourMapProjection::mapGrid() const
return m_contourMapGrid.get();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::optional<std::pair<double, double>> RimContourMapProjection::valueFilterMinMax() const
{
if ( m_valueFilterType() == RimIntersectionFilterEnum::INTERSECT_FILTER_NONE ) return std::nullopt;
double minimum = m_minResultAllTimeSteps;
double maximum = m_maxResultAllTimeSteps;
if ( m_valueFilterType() == RimIntersectionFilterEnum::INTERSECT_FILTER_BELOW ||
m_valueFilterType() == RimIntersectionFilterEnum::INTERSECT_FILTER_BETWEEN )
{
maximum = m_upperThreshold;
}
if ( m_valueFilterType() == RimIntersectionFilterEnum::INTERSECT_FILTER_ABOVE ||
m_valueFilterType() == RimIntersectionFilterEnum::INTERSECT_FILTER_BETWEEN )
{
minimum = m_lowerThreshold;
}
return std::pair<double, double>( minimum, maximum );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -366,7 +400,7 @@ bool RimContourMapProjection::resultsNeedsUpdating( int timeStep ) const
if ( !m_contourMapProjection ) return true;
return ( m_contourMapProjection->aggregatedResults().size() != m_contourMapProjection->numberOfCells() ||
m_contourMapProjection->aggregatedVertexResults().size() != m_contourMapProjection->numberOfVertices() ||
m_contourMapProjection->aggregatedVertexResultsFiltered().size() != m_contourMapProjection->numberOfVertices() ||
timeStep != m_currentResultTimestep );
}
@ -384,7 +418,7 @@ bool RimContourMapProjection::geometryNeedsUpdating() const
void RimContourMapProjection::clearGridMapping()
{
clearResults();
clearTimeStepRange();
clearMinMaxValueRange();
m_contourMapProjection.reset();
m_contourMapGrid.reset();
@ -404,7 +438,7 @@ void RimContourMapProjection::clearResults()
clearResultVariable();
clearTimeStepRange();
clearMinMaxValueRange();
}
//--------------------------------------------------------------------------------------------------
@ -415,6 +449,24 @@ cvf::ref<cvf::UByteArray> RimContourMapProjection::getCellVisibility() const
return baseView()->currentTotalCellVisibility();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<double, double> RimContourMapProjection::minmaxValuesAllTimeSteps()
{
if ( !resultRangeIsValid() )
{
const auto [minVal, maxVal] = computeMinMaxValuesAllTimeSteps();
m_minResultAllTimeSteps = minVal;
m_maxResultAllTimeSteps = maxVal;
m_lowerThreshold = minVal;
m_upperThreshold = maxVal;
}
return std::pair<double, double>( m_minResultAllTimeSteps, m_maxResultAllTimeSteps );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -474,7 +526,7 @@ void RimContourMapProjection::fieldChangedByUi( const caf::PdmFieldHandle* chang
{
clearResults();
}
clearTimeStepRange();
clearMinMaxValueRange();
}
else if ( changedField == &m_smoothContourLines )
{
@ -484,7 +536,7 @@ void RimContourMapProjection::fieldChangedByUi( const caf::PdmFieldHandle* chang
{
clearGridMapping();
clearResults();
clearTimeStepRange();
clearMinMaxValueRange();
}
baseView()->updateConnectedEditors();
@ -500,8 +552,7 @@ void RimContourMapProjection::defineEditorAttribute( const caf::PdmFieldHandle*
{
if ( &m_relativeSampleSpacing == field )
{
caf::PdmUiDoubleSliderEditorAttribute* myAttr = dynamic_cast<caf::PdmUiDoubleSliderEditorAttribute*>( attribute );
if ( myAttr )
if ( auto myAttr = dynamic_cast<caf::PdmUiDoubleSliderEditorAttribute*>( attribute ) )
{
myAttr->m_minimum = 0.2;
myAttr->m_maximum = 20.0;
@ -509,6 +560,16 @@ void RimContourMapProjection::defineEditorAttribute( const caf::PdmFieldHandle*
myAttr->m_delaySliderUpdateUntilRelease = true;
}
}
if ( &m_lowerThreshold == field || &m_upperThreshold == field )
{
if ( auto myAttr = dynamic_cast<caf::PdmUiDoubleSliderEditorAttribute*>( attribute ) )
{
myAttr->m_minimum = m_minResultAllTimeSteps;
myAttr->m_maximum = m_maxResultAllTimeSteps;
myAttr->m_sliderTickCount = 20;
}
}
}
//--------------------------------------------------------------------------------------------------
@ -525,6 +586,9 @@ void RimContourMapProjection::defineUiOrdering( QString uiConfigName, caf::PdmUi
m_showContourLabels.uiCapability()->setUiReadOnly( !m_showContourLines() );
mainGroup->add( &m_smoothContourLines );
m_smoothContourLines.uiCapability()->setUiReadOnly( !m_showContourLines() );
appendValueFilterGroup( uiOrdering );
uiOrdering.skipRemainingFields( true );
}
@ -539,8 +603,34 @@ void RimContourMapProjection::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTr
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimContourMapProjection::initAfterRead()
void RimContourMapProjection::appendValueFilterGroup( caf::PdmUiOrdering& uiOrdering )
{
auto valueFilterGroup = uiOrdering.addNewGroup( "Value Filter" );
valueFilterGroup->add( &m_valueFilterType );
switch ( m_valueFilterType() )
{
case RimIntersectionFilterEnum::INTERSECT_FILTER_BELOW:
m_upperThreshold.uiCapability()->setUiName( "Threshold" );
valueFilterGroup->add( &m_upperThreshold );
break;
case RimIntersectionFilterEnum::INTERSECT_FILTER_BETWEEN:
m_lowerThreshold.uiCapability()->setUiName( "Lower Threshold" );
valueFilterGroup->add( &m_lowerThreshold );
m_upperThreshold.uiCapability()->setUiName( "Upper Threshold" );
valueFilterGroup->add( &m_upperThreshold );
break;
case RimIntersectionFilterEnum::INTERSECT_FILTER_ABOVE:
m_lowerThreshold.uiCapability()->setUiName( "Threshold" );
valueFilterGroup->add( &m_lowerThreshold );
break;
case RimIntersectionFilterEnum::INTERSECT_FILTER_NONE:
default:
break;
}
}
//--------------------------------------------------------------------------------------------------
@ -555,7 +645,7 @@ bool RimContourMapProjection::resultRangeIsValid() const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimContourMapProjection::clearTimeStepRange()
void RimContourMapProjection::clearMinMaxValueRange()
{
m_minResultAllTimeSteps = std::numeric_limits<double>::infinity();
m_maxResultAllTimeSteps = -std::numeric_limits<double>::infinity();

View File

@ -19,6 +19,7 @@
#pragma once
#include "RimCheckableNamedObject.h"
#include "RimIntersectionEnums.h"
#include "RigContourMapCalculator.h"
#include "RigContourPolygonsTools.h"
@ -104,16 +105,10 @@ protected:
double calculateValueInMapCell( uint i, uint j, const std::vector<double>& gridCellValues ) const;
// Keep track of whether cached data needs updating
bool gridMappingNeedsUpdating() const;
bool resultsNeedsUpdating( int timeStep ) const;
bool geometryNeedsUpdating() const;
bool resultRangeIsValid() const;
void clearTimeStepRange();
void clearGridMapping();
void clearResults();
virtual std::pair<double, double> minmaxValuesAllTimeSteps() = 0;
virtual std::pair<double, double> computeMinMaxValuesAllTimeSteps() = 0;
std::pair<double, double> minmaxValuesAllTimeSteps();
bool mapCellVisibilityNeedsUpdating( int timeStep );
@ -123,12 +118,21 @@ protected:
virtual void updateAfterResultGeneration( int timeStep ) = 0;
protected:
// Framework overrides
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override;
void initAfterRead() override;
void appendValueFilterGroup( caf::PdmUiOrdering& uiOrdering );
private:
bool resultsNeedsUpdating( int timeStep ) const;
bool gridMappingNeedsUpdating() const;
bool geometryNeedsUpdating() const;
void clearResults();
void clearMinMaxValueRange();
bool resultRangeIsValid() const;
std::optional<std::pair<double, double>> valueFilterMinMax() const;
protected:
caf::PdmField<double> m_relativeSampleSpacing;
@ -145,9 +149,14 @@ protected:
int m_currentResultTimestep;
std::vector<bool> m_mapCellVisibility;
double m_minResultAllTimeSteps;
double m_maxResultAllTimeSteps;
caf::PdmField<caf::AppEnum<RimIntersectionFilterEnum>> m_valueFilterType;
caf::PdmField<double> m_upperThreshold;
caf::PdmField<double> m_lowerThreshold;
std::unique_ptr<RigContourMapGrid> m_contourMapGrid;
std::unique_ptr<RigContourMapProjection> m_contourMapProjection;
private:
double m_minResultAllTimeSteps;
double m_maxResultAllTimeSteps;
};

View File

@ -114,11 +114,11 @@ void RimEclipseContourMapProjection::updateLegend()
{
RimEclipseCellColors* cellColors = view()->cellResult();
auto [minValAllTimeSteps, maxValAllTimeSteps] = minmaxValuesAllTimeSteps();
double minVal = m_contourMapProjection ? m_contourMapProjection->minValue() : std::numeric_limits<double>::infinity();
double maxVal = m_contourMapProjection ? m_contourMapProjection->maxValue() : -std::numeric_limits<double>::infinity();
auto [minValAllTimeSteps, maxValAllTimeSteps] = minmaxValuesAllTimeSteps();
legendConfig()->setAutomaticRanges( minValAllTimeSteps, maxValAllTimeSteps, minVal, maxVal );
if ( isColumnResult() )
@ -361,19 +361,17 @@ void RimEclipseContourMapProjection::initAfterRead()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<double, double> RimEclipseContourMapProjection::minmaxValuesAllTimeSteps()
std::pair<double, double> RimEclipseContourMapProjection::computeMinMaxValuesAllTimeSteps()
{
if ( !resultRangeIsValid() )
{
clearTimeStepRange();
double minimum = std::numeric_limits<double>::infinity();
double maximum = -std::numeric_limits<double>::infinity();
int timeStepCount = std::max( static_cast<int>( eclipseCase()->timeStepStrings().size() ), 1 );
for ( int i = 0; i < (int)timeStepCount; ++i )
{
std::vector<double> aggregatedResults = generateResults( i );
m_minResultAllTimeSteps = std::min( m_minResultAllTimeSteps, RigContourMapProjection::minValue( aggregatedResults ) );
m_maxResultAllTimeSteps = std::max( m_maxResultAllTimeSteps, RigContourMapProjection::maxValue( aggregatedResults ) );
minimum = std::min( minimum, RigContourMapProjection::minValue( aggregatedResults ) );
maximum = std::max( maximum, RigContourMapProjection::maxValue( aggregatedResults ) );
}
}
return std::make_pair( m_minResultAllTimeSteps, m_maxResultAllTimeSteps );
return std::make_pair( minimum, maximum );
}

View File

@ -67,7 +67,7 @@ protected:
RimEclipseCase* eclipseCase() const;
RimEclipseContourMapView* view() const;
std::pair<double, double> minmaxValuesAllTimeSteps() override;
std::pair<double, double> computeMinMaxValuesAllTimeSteps() override;
void updateAfterResultGeneration( int timeStep ) override;

View File

@ -112,11 +112,11 @@ RimRegularLegendConfig* RimStatisticsContourMapProjection::legendConfig() const
//--------------------------------------------------------------------------------------------------
void RimStatisticsContourMapProjection::updateLegend()
{
auto [minValAllTimeSteps, maxValAllTimeSteps] = minmaxValuesAllTimeSteps();
double minVal = m_contourMapProjection ? m_contourMapProjection->minValue() : std::numeric_limits<double>::infinity();
double maxVal = m_contourMapProjection ? m_contourMapProjection->maxValue() : -std::numeric_limits<double>::infinity();
auto [minValAllTimeSteps, maxValAllTimeSteps] = minmaxValuesAllTimeSteps();
legendConfig()->setAutomaticRanges( minValAllTimeSteps, maxValAllTimeSteps, minVal, maxVal );
if ( statisticsContourMap()->isColumnResult() )
@ -267,24 +267,22 @@ void RimStatisticsContourMapProjection::updateAfterResultGeneration( int timeSte
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<double, double> RimStatisticsContourMapProjection::minmaxValuesAllTimeSteps()
std::pair<double, double> RimStatisticsContourMapProjection::computeMinMaxValuesAllTimeSteps()
{
if ( !resultRangeIsValid() )
{
clearTimeStepRange();
double minimum = std::numeric_limits<double>::infinity();
double maximum = -std::numeric_limits<double>::infinity();
if ( auto map = statisticsContourMap() )
{
for ( size_t ts = 0; ts < map->selectedTimeSteps().size(); ts++ )
{
std::vector<double> aggregatedResults = statisticsContourMap()->result( ts, m_statisticsType() );
m_minResultAllTimeSteps = std::min( m_minResultAllTimeSteps, RigContourMapProjection::minValue( aggregatedResults ) );
m_maxResultAllTimeSteps = std::max( m_maxResultAllTimeSteps, RigContourMapProjection::maxValue( aggregatedResults ) );
}
minimum = std::min( minimum, RigContourMapProjection::minValue( aggregatedResults ) );
maximum = std::max( maximum, RigContourMapProjection::maxValue( aggregatedResults ) );
}
}
return std::make_pair( m_minResultAllTimeSteps, m_maxResultAllTimeSteps );
return std::make_pair( minimum, maximum );
}
//--------------------------------------------------------------------------------------------------

View File

@ -71,7 +71,7 @@ protected:
RimStatisticsContourMap* statisticsContourMap() const;
RimStatisticsContourMapView* view() const;
std::pair<double, double> minmaxValuesAllTimeSteps() override;
std::pair<double, double> computeMinMaxValuesAllTimeSteps() override;
void updateAfterResultGeneration( int timeStep ) override;

View File

@ -259,6 +259,22 @@ void RigContourMapProjection::generateVertexResults()
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigContourMapProjection::setValueFilter( std::optional<std::pair<double, double>> valueFilter )
{
m_valueFilter = valueFilter;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::optional<std::pair<double, double>> RigContourMapProjection::valueFilter() const
{
return m_valueFilter;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -422,9 +438,21 @@ const std::vector<double>& RigContourMapProjection::aggregatedResults() const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<double>& RigContourMapProjection::aggregatedVertexResults() const
std::vector<double> RigContourMapProjection::aggregatedVertexResultsFiltered() const
{
return m_aggregatedVertexResults;
std::vector<double> filteredResults = m_aggregatedVertexResults;
if ( m_valueFilter )
{
std::transform( filteredResults.begin(),
filteredResults.end(),
filteredResults.begin(),
[this]( double value ) {
return ( value < m_valueFilter->first || value > m_valueFilter->second ) ? std::numeric_limits<double>::infinity()
: value;
} );
}
return filteredResults;
}
//--------------------------------------------------------------------------------------------------

View File

@ -25,6 +25,8 @@
#include "cvfBoundingBox.h"
#include "cvfVector2.h"
#include <optional>
class RigContourMapGrid;
class RimGridView;
class RimRegularLegendConfig;
@ -44,6 +46,8 @@ public:
void clearGridMapping();
void generateVertexResults();
void setValueFilter( std::optional<std::pair<double, double>> valueFilter );
std::optional<std::pair<double, double>> valueFilter() const;
double maxValue() const;
double minValue() const;
@ -86,7 +90,7 @@ public:
double interpolateValue( const cvf::Vec2d& gridPosition2d ) const;
const std::vector<double>& aggregatedResults() const;
const std::vector<double>& aggregatedVertexResults() const;
std::vector<double> aggregatedVertexResultsFiltered() const;
const std::vector<std::vector<std::pair<size_t, double>>>& projected3dGridIndices() const;
// Cell index and position conversion
@ -115,5 +119,7 @@ protected:
int m_currentResultTimestep;
std::vector<bool> m_mapCellVisibility;
std::optional<std::pair<double, double>> m_valueFilter;
const RigContourMapGrid& m_contourMapGrid;
};

View File

@ -64,7 +64,7 @@ std::vector<cvf::Vec4d>
std::vector<std::vector<std::vector<cvf::Vec4d>>> threadTriangles( numberOfThreads );
const std::vector<double>& aggregatedVertexResults = contourMapProjection.aggregatedVertexResults();
auto aggregatedVertexResults = contourMapProjection.aggregatedVertexResultsFiltered();
#pragma omp parallel
{
@ -297,8 +297,7 @@ std::pair<std::vector<RigContourMapTrianglesGenerator::ContourPolygons>, std::ve
simplifyEpsilon *= 4.0;
}
const std::vector<double>& aggregatedVertexResults = contourMapProjection.aggregatedVertexResults();
auto aggregatedVertexResults = contourMapProjection.aggregatedVertexResultsFiltered();
if ( aggregatedVertexResults.empty() ) return {};
std::vector<caf::ContourLines::ListOfLineSegments> unorderedLineSegmentsPerLevel =