Skip to content
Permalink
Browse files

[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 34d44c08ed3863447ec9a954d9a9c738a1f17aee
@@ -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
@@ -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
@@ -96,7 +96,8 @@ Represents a mesh renderer settings for scalar datasets
#include "qgsmeshrenderersettings.h"
%End
public:
enum DataInterpolationMethod

enum DataResamplingMethod
{

None,
@@ -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
@@ -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

@@ -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 =
@@ -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 );
@@ -58,7 +58,7 @@ QgsGeometry QgsMeshContours::exportPolygons(
const QgsMeshDatasetIndex &index,
double min_value,
double max_value,
QgsMeshRendererScalarSettings::DataInterpolationMethod method,
QgsMeshRendererScalarSettings::DataResamplingMethod method,
QgsFeedback *feedback
)
{
@@ -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
@@ -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 )
{
@@ -69,7 +69,7 @@ class ANALYSIS_EXPORT QgsMeshContours
*/
QgsGeometry exportLines( const QgsMeshDatasetIndex &index,
double value,
QgsMeshRendererScalarSettings::DataInterpolationMethod method,
QgsMeshRendererScalarSettings::DataResamplingMethod method,
QgsFeedback *feedback = nullptr );

/**
@@ -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;

@@ -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
@@ -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;
@@ -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() &&
@@ -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
@@ -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;
}


@@ -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;
@@ -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()
@@ -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 ) &&
@@ -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 );
@@ -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();
@@ -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
@@ -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 );

@@ -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;

@@ -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
);

/**
@@ -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

0 comments on commit 34d44c0

Please sign in to comment.
You can’t perform that action at this time.