Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[MESH] Resampling from vertex values to face values (#35264)
[FEATURE] Implements resampling from values on vertices to values on faces with the neighbor average method. (note that resampling method for datasets defined on faces, e.g., the value on vertices is calculated from value on faces was added in the previous QGIS release)

The default method is set to "none" for resampling from vertices to faces and to "neighbor average" for resampling from faces to vertices. Then the default rendering is now always smooth.
  • Loading branch information
vcloarec committed Mar 24, 2020
1 parent 20a7ed4 commit 34d44c0
Show file tree
Hide file tree
Showing 15 changed files with 164 additions and 63 deletions.
4 changes: 2 additions & 2 deletions python/analysis/auto_generated/mesh/qgsmeshcontours.sip.in
Expand Up @@ -36,7 +36,7 @@ Caches the native and triangular mesh from data provider

QgsGeometry exportLines( const QgsMeshDatasetIndex &index,
double value,
QgsMeshRendererScalarSettings::DataInterpolationMethod method,
QgsMeshRendererScalarSettings::DataResamplingMethod method,
QgsFeedback *feedback = 0 );
%Docstring
Exports multi line string containing the contour line for particular dataset and value
Expand All @@ -52,7 +52,7 @@ Exports multi line string containing the contour line for particular dataset and
QgsGeometry exportPolygons( const QgsMeshDatasetIndex &index,
double min_value,
double max_value,
QgsMeshRendererScalarSettings::DataInterpolationMethod method,
QgsMeshRendererScalarSettings::DataResamplingMethod method,
QgsFeedback *feedback = 0 );
%Docstring
Exports multi polygons representing the areas with values in range for particular dataset
Expand Down
Expand Up @@ -96,7 +96,8 @@ Represents a mesh renderer settings for scalar datasets
#include "qgsmeshrenderersettings.h"
%End
public:
enum DataInterpolationMethod

enum DataResamplingMethod
{

None,
Expand Down Expand Up @@ -135,7 +136,7 @@ Returns opacity
Sets opacity
%End

DataInterpolationMethod dataInterpolationMethod() const;
DataResamplingMethod dataResamplingMethod() const;
%Docstring
Returns the type of interpolation to use to
convert face defined datasets to
Expand All @@ -144,7 +145,7 @@ values on vertices
.. versionadded:: 3.12
%End

void setDataInterpolationMethod( const DataInterpolationMethod &dataInterpolationMethod );
void setDataResamplingMethod( const DataResamplingMethod &dataResamplingMethod );
%Docstring
Sets data interpolation method

Expand Down
4 changes: 2 additions & 2 deletions src/analysis/mesh/qgsmeshcalcutils.cpp
Expand Up @@ -107,7 +107,7 @@ std::shared_ptr<QgsMeshMemoryDatasetGroup> QgsMeshCalcUtils::create( const QStri
mMeshLayer->nativeMesh(),
mMeshLayer->triangularMesh(),
nullptr,
mMeshLayer->rendererSettings().scalarSettings( groupIndex ).dataInterpolationMethod()
mMeshLayer->rendererSettings().scalarSettings( groupIndex ).dataResamplingMethod()
);
Q_ASSERT( dataX.size() == resultCount );
QVector<double> dataY =
Expand All @@ -116,7 +116,7 @@ std::shared_ptr<QgsMeshMemoryDatasetGroup> QgsMeshCalcUtils::create( const QStri
mMeshLayer->nativeMesh(),
mMeshLayer->triangularMesh(),
nullptr,
mMeshLayer->rendererSettings().scalarSettings( groupIndex ).dataInterpolationMethod()
mMeshLayer->rendererSettings().scalarSettings( groupIndex ).dataResamplingMethod()
);

Q_ASSERT( dataY.size() == resultCount );
Expand Down
6 changes: 3 additions & 3 deletions src/analysis/mesh/qgsmeshcontours.cpp
Expand Up @@ -58,7 +58,7 @@ QgsGeometry QgsMeshContours::exportPolygons(
const QgsMeshDatasetIndex &index,
double min_value,
double max_value,
QgsMeshRendererScalarSettings::DataInterpolationMethod method,
QgsMeshRendererScalarSettings::DataResamplingMethod method,
QgsFeedback *feedback
)
{
Expand Down Expand Up @@ -254,7 +254,7 @@ QgsGeometry QgsMeshContours::exportPolygons(

QgsGeometry QgsMeshContours::exportLines( const QgsMeshDatasetIndex &index,
double value,
QgsMeshRendererScalarSettings::DataInterpolationMethod method,
QgsMeshRendererScalarSettings::DataResamplingMethod method,
QgsFeedback *feedback )
{
// Check if the layer/mesh is valid
Expand Down Expand Up @@ -380,7 +380,7 @@ QgsGeometry QgsMeshContours::exportLines( const QgsMeshDatasetIndex &index,
}
}

void QgsMeshContours::populateCache( const QgsMeshDatasetIndex &index, QgsMeshRendererScalarSettings::DataInterpolationMethod method )
void QgsMeshContours::populateCache( const QgsMeshDatasetIndex &index, QgsMeshRendererScalarSettings::DataResamplingMethod method )
{
if ( mCachedIndex != index )
{
Expand Down
6 changes: 3 additions & 3 deletions src/analysis/mesh/qgsmeshcontours.h
Expand Up @@ -69,7 +69,7 @@ class ANALYSIS_EXPORT QgsMeshContours
*/
QgsGeometry exportLines( const QgsMeshDatasetIndex &index,
double value,
QgsMeshRendererScalarSettings::DataInterpolationMethod method,
QgsMeshRendererScalarSettings::DataResamplingMethod method,
QgsFeedback *feedback = nullptr );

/**
Expand All @@ -84,13 +84,13 @@ class ANALYSIS_EXPORT QgsMeshContours
QgsGeometry exportPolygons( const QgsMeshDatasetIndex &index,
double min_value,
double max_value,
QgsMeshRendererScalarSettings::DataInterpolationMethod method,
QgsMeshRendererScalarSettings::DataResamplingMethod method,
QgsFeedback *feedback = nullptr );

private:
void populateCache(
const QgsMeshDatasetIndex &index,
QgsMeshRendererScalarSettings::DataInterpolationMethod method );
QgsMeshRendererScalarSettings::DataResamplingMethod method );

QgsMeshLayer *mMeshLayer = nullptr;

Expand Down
36 changes: 21 additions & 15 deletions src/app/mesh/qgsmeshrendererscalarsettingswidget.cpp
Expand Up @@ -55,13 +55,13 @@ QgsMeshRendererScalarSettingsWidget::QgsMeshRendererScalarSettingsWidget( QWidge
void QgsMeshRendererScalarSettingsWidget::setLayer( QgsMeshLayer *layer )
{
mMeshLayer = layer;
mScalarInterpolationTypeComboBox->setEnabled( dataIsDefinedOnFaces() );
mScalarInterpolationTypeComboBox->setEnabled( !dataIsDefinedOnEdges() );
}

void QgsMeshRendererScalarSettingsWidget::setActiveDatasetGroup( int groupIndex )
{
mActiveDatasetGroup = groupIndex;
mScalarInterpolationTypeComboBox->setEnabled( dataIsDefinedOnFaces() );
mScalarInterpolationTypeComboBox->setEnabled( !dataIsDefinedOnEdges() );
}

QgsMeshRendererScalarSettings QgsMeshRendererScalarSettingsWidget::settings() const
Expand All @@ -70,7 +70,7 @@ QgsMeshRendererScalarSettings QgsMeshRendererScalarSettingsWidget::settings() co
settings.setColorRampShader( mScalarColorRampShaderWidget->shader() );
settings.setClassificationMinimumMaximum( lineEditValue( mScalarMinLineEdit ), lineEditValue( mScalarMaxLineEdit ) );
settings.setOpacity( mOpacityWidget->opacity() );
settings.setDataInterpolationMethod( dataIntepolationMethod() );
settings.setDataResamplingMethod( dataIntepolationMethod() );
settings.setEdgeWidth( mScalarEdgeWidthSpinBox->value() );
settings.setEdgeWidthUnit( mScalarEdgeWidthUnitSelectionWidget->unit() );
return settings;
Expand Down Expand Up @@ -98,7 +98,7 @@ void QgsMeshRendererScalarSettingsWidget::syncToLayer( )
whileBlocking( mScalarColorRampShaderWidget )->setFromShader( shader );
whileBlocking( mScalarColorRampShaderWidget )->setMinimumMaximum( min, max );
whileBlocking( mOpacityWidget )->setOpacity( settings.opacity() );
int index = mScalarInterpolationTypeComboBox->findData( settings.dataInterpolationMethod() );
int index = mScalarInterpolationTypeComboBox->findData( settings.dataResamplingMethod() );
whileBlocking( mScalarInterpolationTypeComboBox )->setCurrentIndex( index );

bool hasEdges = ( mMeshLayer->dataProvider() &&
Expand Down Expand Up @@ -140,18 +140,11 @@ void QgsMeshRendererScalarSettingsWidget::recalculateMinMaxButtonClicked()
mScalarColorRampShaderWidget->setMinimumMaximumAndClassify( min, max );
}

QgsMeshRendererScalarSettings::DataInterpolationMethod QgsMeshRendererScalarSettingsWidget::dataIntepolationMethod() const
QgsMeshRendererScalarSettings::DataResamplingMethod QgsMeshRendererScalarSettingsWidget::dataIntepolationMethod() const
{
if ( dataIsDefinedOnFaces() )
{
const int data = mScalarInterpolationTypeComboBox->currentData().toInt();
const QgsMeshRendererScalarSettings::DataInterpolationMethod method = static_cast<QgsMeshRendererScalarSettings::DataInterpolationMethod>( data );
return method;
}
else
{
return QgsMeshRendererScalarSettings::None;
}
const int data = mScalarInterpolationTypeComboBox->currentData().toInt();
const QgsMeshRendererScalarSettings::DataResamplingMethod method = static_cast<QgsMeshRendererScalarSettings::DataResamplingMethod>( data );
return method;
}

bool QgsMeshRendererScalarSettingsWidget::dataIsDefinedOnFaces() const
Expand All @@ -167,4 +160,17 @@ bool QgsMeshRendererScalarSettingsWidget::dataIsDefinedOnFaces() const
return onFaces;
}

bool QgsMeshRendererScalarSettingsWidget::dataIsDefinedOnEdges() const
{
if ( !mMeshLayer || !mMeshLayer->dataProvider() || !mMeshLayer->dataProvider()->isValid() )
return false;

if ( mActiveDatasetGroup < 0 )
return false;

QgsMeshDatasetGroupMetadata meta = mMeshLayer->dataProvider()->datasetGroupMetadata( mActiveDatasetGroup );
const bool onEdges = ( meta.dataType() == QgsMeshDatasetGroupMetadata::DataOnEdges );
return onEdges;
}


3 changes: 2 additions & 1 deletion src/app/mesh/qgsmeshrendererscalarsettingswidget.h
Expand Up @@ -65,9 +65,10 @@ class APP_EXPORT QgsMeshRendererScalarSettingsWidget : public QWidget, private U

private:
double lineEditValue( const QLineEdit *lineEdit ) const;
QgsMeshRendererScalarSettings::DataInterpolationMethod dataIntepolationMethod() const;
QgsMeshRendererScalarSettings::DataResamplingMethod dataIntepolationMethod() const;

bool dataIsDefinedOnFaces() const;
bool dataIsDefinedOnEdges() const;

QgsMeshLayer *mMeshLayer = nullptr; // not owned
int mActiveDatasetGroup = -1;
Expand Down
26 changes: 26 additions & 0 deletions src/core/mesh/qgsmeshlayer.cpp
Expand Up @@ -70,6 +70,32 @@ void QgsMeshLayer::setDefaultRendererSettings()
meshSettings.setEnabled( true );
mRendererSettings.setNativeMeshSettings( meshSettings );
}

// Sets default resample method for scalar dataset
if ( !mDataProvider )
return;
for ( int i = 0; i < mDataProvider->datasetGroupCount(); ++i )
{
QgsMeshDatasetGroupMetadata meta = mDataProvider->datasetGroupMetadata( i );
if ( meta.isScalar() )
{
QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( i );
switch ( meta.dataType() )
{
case QgsMeshDatasetGroupMetadata::DataOnFaces:
case QgsMeshDatasetGroupMetadata::DataOnVolumes: // data on volumes are averaged to 2D data on faces
scalarSettings.setDataResamplingMethod( QgsMeshRendererScalarSettings::NeighbourAverage );
break;
case QgsMeshDatasetGroupMetadata::DataOnVertices:
scalarSettings.setDataResamplingMethod( QgsMeshRendererScalarSettings::None );
break;
case QgsMeshDatasetGroupMetadata::DataOnEdges:
break;
}
mRendererSettings.setScalarSettings( i, scalarSettings );
}
}

}

void QgsMeshLayer::createSimplifiedMeshes()
Expand Down
36 changes: 26 additions & 10 deletions src/core/mesh/qgsmeshlayerrenderer.cpp
Expand Up @@ -106,7 +106,7 @@ void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer )

// Find out if we can use cache up to date. If yes, use it and return
const int datasetGroupCount = layer->dataProvider()->datasetGroupCount();
const QgsMeshRendererScalarSettings::DataInterpolationMethod method = mRendererSettings.scalarSettings( datasetIndex.group() ).dataInterpolationMethod();
const QgsMeshRendererScalarSettings::DataResamplingMethod method = mRendererSettings.scalarSettings( datasetIndex.group() ).dataResamplingMethod();
QgsMeshLayerRendererCache *cache = layer->rendererCache();
if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
( cache->mActiveScalarDatasetIndex == datasetIndex ) &&
Expand Down Expand Up @@ -153,16 +153,31 @@ void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer )
mNativeMesh.faces.count() );

// for data on faces, there could be request to interpolate the data to vertices
if ( ( mScalarDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnFaces ) && ( method != QgsMeshRendererScalarSettings::None ) )
if ( method != QgsMeshRendererScalarSettings::None )
{
mScalarDataType = QgsMeshDatasetGroupMetadata::DataType::DataOnVertices;
mScalarDatasetValues = QgsMeshLayerUtils::interpolateFromFacesData(
mScalarDatasetValues,
&mNativeMesh,
&mTriangularMesh,
&mScalarActiveFaceFlagValues,
method
);
if ( mScalarDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnFaces )
{
mScalarDataType = QgsMeshDatasetGroupMetadata::DataType::DataOnVertices;
mScalarDatasetValues = QgsMeshLayerUtils::interpolateFromFacesData(
mScalarDatasetValues,
&mNativeMesh,
&mTriangularMesh,
&mScalarActiveFaceFlagValues,
method
);
}
else if ( mScalarDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVertices )
{
mScalarDataType = QgsMeshDatasetGroupMetadata::DataType::DataOnFaces;
mScalarDatasetValues = QgsMeshLayerUtils::resampleFromVerticesToFaces(
mScalarDatasetValues,
&mNativeMesh,
&mTriangularMesh,
&mScalarActiveFaceFlagValues,
method
);
}

}

const QgsMeshDatasetMetadata datasetMetadata = layer->dataProvider()->datasetMetadata( datasetIndex );
Expand All @@ -182,6 +197,7 @@ void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer )
cache->mScalarAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
}


void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer )
{
const QgsMeshDatasetIndex datasetIndex = mRendererSettings.activeVectorDataset();
Expand Down
2 changes: 1 addition & 1 deletion src/core/mesh/qgsmeshlayerrenderer.h
Expand Up @@ -63,7 +63,7 @@ struct CORE_NO_EXPORT QgsMeshLayerRendererCache
QgsMeshDatasetGroupMetadata::DataType mScalarDataType = QgsMeshDatasetGroupMetadata::DataType::DataOnVertices;
double mScalarDatasetMinimum = std::numeric_limits<double>::quiet_NaN();
double mScalarDatasetMaximum = std::numeric_limits<double>::quiet_NaN();
QgsMeshRendererScalarSettings::DataInterpolationMethod mDataInterpolationMethod = QgsMeshRendererScalarSettings::None;
QgsMeshRendererScalarSettings::DataResamplingMethod mDataInterpolationMethod = QgsMeshRendererScalarSettings::None;
std::unique_ptr<QgsMesh3dAveragingMethod> mScalarAveragingMethod;

// vector dataset
Expand Down
39 changes: 34 additions & 5 deletions src/core/mesh/qgsmeshlayerutils.cpp
Expand Up @@ -305,11 +305,8 @@ QVector<double> QgsMeshLayerUtils::interpolateFromFacesData(
const QgsMesh *nativeMesh,
const QgsTriangularMesh *triangularMesh,
QgsMeshDataBlock *active,
QgsMeshRendererScalarSettings::DataInterpolationMethod method )
QgsMeshRendererScalarSettings::DataResamplingMethod method )
{
Q_UNUSED( triangularMesh )
Q_UNUSED( method )

assert( nativeMesh );
assert( method == QgsMeshRendererScalarSettings::NeighbourAverage );

Expand Down Expand Up @@ -355,10 +352,42 @@ QVector<double> QgsMeshLayerUtils::interpolateFromFacesData(
return res;
}

QVector<double> QgsMeshLayerUtils::resampleFromVerticesToFaces(
const QVector<double> valuesOnVertices,
const QgsMesh *nativeMesh,
const QgsTriangularMesh *triangularMesh,
const QgsMeshDataBlock *active,
QgsMeshRendererScalarSettings::DataResamplingMethod method )
{
assert( nativeMesh );
assert( method == QgsMeshRendererScalarSettings::NeighbourAverage );

// assuming that native vertex count = triangular vertex count
assert( nativeMesh->vertices.size() == triangularMesh->vertices().size() );

QVector<double> ret( nativeMesh->faceCount(), std::numeric_limits<double>::quiet_NaN() );

for ( int i = 0; i < nativeMesh->faces.size(); ++i )
{
const QgsMeshFace face = nativeMesh->face( i );
if ( active->active( i ) && face.count() > 2 )
{
double value = 0;
for ( int j = 0; j < face.count(); ++j )
{
value += valuesOnVertices.at( face.at( j ) );
}
ret[i] = value / face.count();
}
}

return ret;
}

QVector<double> QgsMeshLayerUtils::calculateMagnitudeOnVertices( const QgsMeshLayer *meshLayer,
const QgsMeshDatasetIndex index,
QgsMeshDataBlock *activeFaceFlagValues,
const QgsMeshRendererScalarSettings::DataInterpolationMethod method )
const QgsMeshRendererScalarSettings::DataResamplingMethod method )
{
QVector<double> ret;

Expand Down
17 changes: 15 additions & 2 deletions src/core/mesh/qgsmeshlayerutils.h
Expand Up @@ -209,7 +209,20 @@ class CORE_EXPORT QgsMeshLayerUtils
const QgsMesh *nativeMesh,
const QgsTriangularMesh *triangularMesh,
QgsMeshDataBlock *active,
QgsMeshRendererScalarSettings::DataInterpolationMethod method
QgsMeshRendererScalarSettings::DataResamplingMethod method
);

/**
* Resamples values on vertices to values on faces
*
* \since QGIS 3.14
*/
static QVector<double> resampleFromVerticesToFaces(
const QVector<double> valuesOnVertices,
const QgsMesh *nativeMesh,
const QgsTriangularMesh *triangularMesh,
const QgsMeshDataBlock *active,
QgsMeshRendererScalarSettings::DataResamplingMethod method
);

/**
Expand All @@ -226,7 +239,7 @@ class CORE_EXPORT QgsMeshLayerUtils
const QgsMeshLayer *meshLayer,
const QgsMeshDatasetIndex index,
QgsMeshDataBlock *activeFaceFlagValues,
const QgsMeshRendererScalarSettings::DataInterpolationMethod method = QgsMeshRendererScalarSettings::NeighbourAverage );
const QgsMeshRendererScalarSettings::DataResamplingMethod method = QgsMeshRendererScalarSettings::NeighbourAverage );

/**
* Calculates the bounding box of the triangle
Expand Down

0 comments on commit 34d44c0

Please sign in to comment.