diff --git a/python/core/auto_generated/mesh/qgsmeshdataprovider.sip.in b/python/core/auto_generated/mesh/qgsmeshdataprovider.sip.in index e8333b4a73d..763842bc325 100644 --- a/python/core/auto_generated/mesh/qgsmeshdataprovider.sip.in +++ b/python/core/auto_generated/mesh/qgsmeshdataprovider.sip.in @@ -53,6 +53,28 @@ typedef QgsPoint QgsMeshVertex; typedef QVector QgsMeshFace; +struct QgsMesh +{ + int vertexCount() const; +%Docstring +Returns number of vertices +%End + int faceCount() const; +%Docstring +Returns number of faces +%End + + QgsMeshVertex vertex( int index ) const; +%Docstring +Returns a vertex at the index +%End + QgsMeshFace face( int index ) const; +%Docstring +Returns a face at the index +%End + +}; + class QgsMeshDatasetValue { %Docstring @@ -122,6 +144,76 @@ Returns y value bool operator==( const QgsMeshDatasetValue &other ) const; +}; + +class QgsMeshDataBlock +{ +%Docstring + +QgsMeshDataBlock is a block that can be used to retrieve a block of +active flag (e.g. face's active integer flag) +scalars (e.g. scalar dataset double values) +vectors (e.g. vector dataset doubles x,y values) + +data are implicitly shared, so the class can be quickly copied +std.numeric_limits.quiet_NaN() represents NODATA value + +Data can be accessed all at once with buffer() (faster) or +value by value (slower) with active() or value() + +.. versionadded:: 3.6 +%End + +%TypeHeaderCode +#include "qgsmeshdataprovider.h" +%End + public: + enum DataType + { + ActiveFlagInteger, + ScalarDouble, + Vector2DDouble, + }; + + QgsMeshDataBlock(); +%Docstring +Constructs an invalid block +%End + + QgsMeshDataBlock( DataType type, int count ); +%Docstring +Constructs a new block +%End + + DataType type() const; +%Docstring +Type of data stored in the block +%End + + int count() const; +%Docstring +Number of items stored in the block +%End + + bool isValid() const; +%Docstring +Whether the block is valid +%End + + QgsMeshDatasetValue value( int index ) const; +%Docstring +Returns a value represented by the index +For active flag the behavior is undefined +%End + + bool active( int index ) const; +%Docstring +Returns a value for active flag by the index +For scalar and vector 2d the behavior is undefined +%End + + + }; class QgsMeshDatasetGroupMetadata @@ -157,6 +249,8 @@ Constructs an empty metadata object QgsMeshDatasetGroupMetadata( const QString &name, bool isScalar, bool isOnVertices, + double minimum, + double maximum, const QMap &extraOptions ); %Docstring Constructs a valid metadata object @@ -164,6 +258,8 @@ Constructs a valid metadata object :param name: name of the dataset group :param isScalar: dataset contains scalar data, specifically the y-value of QgsMeshDatasetValue is NaN :param isOnVertices: dataset values are defined on mesh's vertices. If false, values are defined on faces. +:param minimum: minimum value (magnitude for vectors) present among all group's dataset values +:param maximum: maximum value (magnitude for vectors) present among all group's dataset values :param extraOptions: dataset's extra options stored by the provider. Usually contains the name, time value, time units, data file vendor, ... %End @@ -190,6 +286,16 @@ Returns whether dataset group has scalar data DataType dataType() const; %Docstring Returns whether dataset group data is defined on vertices or faces +%End + + double minimum() const; +%Docstring +Returns minimum scalar value/vector magnitude present for whole dataset group +%End + + double maximum() const; +%Docstring +Returns maximum scalar value/vector magnitude present for whole dataset group %End }; @@ -218,7 +324,10 @@ Constructs an empty metadata object %End QgsMeshDatasetMetadata( double time, - bool isValid ); + bool isValid, + double minimum, + double maximum + ); %Docstring Constructs a valid metadata object @@ -234,6 +343,16 @@ Returns the time value for this dataset bool isValid() const; %Docstring Returns whether dataset is valid +%End + + double minimum() const; +%Docstring +Returns minimum scalar value/vector magnitude present for the dataset +%End + + double maximum() const; +%Docstring +Returns maximum scalar value/vector magnitude present for the dataset %End }; @@ -278,14 +397,11 @@ Returns number of faces in the native mesh :return: Number of faces in the mesh %End - virtual QgsMeshVertex vertex( int index ) const = 0; + virtual void populateMesh( QgsMesh *mesh ) const = 0; %Docstring -Returns the mesh vertex at index -%End +Populates the mesh vertices and faces - virtual QgsMeshFace face( int index ) const = 0; -%Docstring -Returns the mesh face at index +.. versionadded:: 3.6 %End }; @@ -359,8 +475,19 @@ Returns dataset metadata virtual QgsMeshDatasetValue datasetValue( QgsMeshDatasetIndex index, int valueIndex ) const = 0; %Docstring Returns vector/scalar value associated with the index from the dataset +To read multiple continuous values, use :py:func:`QgsMeshDatasetSourceInterface.datasetValues()` + +See QgsMeshDatasetMetadata.isVector() or :py:func:`QgsMeshDataBlock.type()` +to check if the returned value is vector or scalar +%End + + virtual QgsMeshDataBlock datasetValues( QgsMeshDatasetIndex index, int valueIndex, int count ) const = 0; +%Docstring +Returns N vector/scalar values from the index from the dataset See QgsMeshDatasetMetadata.isVector() to check if the returned value is vector or scalar + +.. versionadded:: 3.6 %End virtual bool isFaceActive( QgsMeshDatasetIndex index, int faceIndex ) const = 0; @@ -373,6 +500,13 @@ set active flag for F2 to false. V1 ---- V2 ---- V5-----V7 | F1 | F2 | F3 | V3 ---- V4 ---- V6-----V8 +%End + + virtual QgsMeshDataBlock areFacesActive( QgsMeshDatasetIndex index, int faceIndex, int count ) const = 0; +%Docstring +Returns whether the faces are active for particular dataset + +.. versionadded:: 3.6 %End }; @@ -398,14 +532,6 @@ Responsible for reading native mesh data QgsMeshDataProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options ); %Docstring Ctor -%End - - virtual QgsRectangle extent() const; - -%Docstring -Returns the extent of the layer - -:return: QgsRectangle containing the extent of the layer %End signals: diff --git a/src/app/mesh/qgsmeshrendererscalarsettingswidget.cpp b/src/app/mesh/qgsmeshrendererscalarsettingswidget.cpp index 6c35a0b60d8..1f2c9a595a4 100644 --- a/src/app/mesh/qgsmeshrendererscalarsettingswidget.cpp +++ b/src/app/mesh/qgsmeshrendererscalarsettingswidget.cpp @@ -93,8 +93,9 @@ void QgsMeshRendererScalarSettingsWidget::minMaxEdited() void QgsMeshRendererScalarSettingsWidget::recalculateMinMaxButtonClicked() { - double min, max; - QgsMeshLayerUtils::calculateMinMaxForDatasetGroup( min, max, mMeshLayer->dataProvider(), mActiveDatasetGroup ); + const QgsMeshDatasetGroupMetadata metadata = mMeshLayer->dataProvider()->datasetGroupMetadata( mActiveDatasetGroup ); + double min = metadata.minimum(); + double max = metadata.maximum(); whileBlocking( mScalarMinLineEdit )->setText( QString::number( min ) ); whileBlocking( mScalarMaxLineEdit )->setText( QString::number( max ) ); mScalarColorRampShaderWidget->setMinimumMaximumAndClassify( min, max ); diff --git a/src/core/mesh/qgsmeshdataprovider.cpp b/src/core/mesh/qgsmeshdataprovider.cpp index 42d06893010..424a4908756 100644 --- a/src/core/mesh/qgsmeshdataprovider.cpp +++ b/src/core/mesh/qgsmeshdataprovider.cpp @@ -58,23 +58,6 @@ QgsMeshDataProvider::QgsMeshDataProvider( const QString &uri, const QgsDataProvi { } - -QgsRectangle QgsMeshDataProvider::extent() const -{ - QgsRectangle rec; - rec.setMinimal(); - for ( int i = 0; i < vertexCount(); ++i ) - { - QgsMeshVertex v = vertex( i ); - rec.setXMinimum( std::min( rec.xMinimum(), v.x() ) ); - rec.setYMinimum( std::min( rec.yMinimum(), v.y() ) ); - rec.setXMaximum( std::max( rec.xMaximum(), v.x() ) ); - rec.setYMaximum( std::max( rec.yMaximum(), v.y() ) ); - } - return rec; - -} - QgsMeshDatasetValue::QgsMeshDatasetValue( double x, double y ) : mX( x ), mY( y ) {} @@ -144,14 +127,17 @@ bool QgsMeshDatasetValue::operator==( const QgsMeshDatasetValue &other ) const return equal; } -QgsMeshDatasetGroupMetadata::QgsMeshDatasetGroupMetadata( - const QString &name, - bool isScalar, - bool isOnVertices, - const QMap &extraOptions ) +QgsMeshDatasetGroupMetadata::QgsMeshDatasetGroupMetadata( const QString &name, + bool isScalar, + bool isOnVertices, + double minimum, + double maximum, + const QMap &extraOptions ) : mName( name ) - , mIsScalar( isScalar ) + , mIsScalar( isScalar ) , mIsOnVertices( isOnVertices ) + , mMinimumValue( minimum ) + , mMaximumValue( maximum ) , mExtraOptions( extraOptions ) { } @@ -171,8 +157,6 @@ bool QgsMeshDatasetGroupMetadata::isScalar() const return mIsScalar; } - - QString QgsMeshDatasetGroupMetadata::name() const { return mName; @@ -183,6 +167,16 @@ QgsMeshDatasetGroupMetadata::DataType QgsMeshDatasetGroupMetadata::dataType() co return ( mIsOnVertices ) ? DataType::DataOnVertices : DataType::DataOnFaces; } +double QgsMeshDatasetGroupMetadata::minimum() const +{ + return mMinimumValue; +} + +double QgsMeshDatasetGroupMetadata::maximum() const +{ + return mMaximumValue; +} + int QgsMeshDatasetSourceInterface::datasetCount( QgsMeshDatasetIndex index ) const { return datasetCount( index.group() ); @@ -194,9 +188,13 @@ QgsMeshDatasetGroupMetadata QgsMeshDatasetSourceInterface::datasetGroupMetadata( } QgsMeshDatasetMetadata::QgsMeshDatasetMetadata( double time, - bool isValid ) + bool isValid, + double minimum, + double maximum ) : mTime( time ) , mIsValid( isValid ) + , mMinimumValue( minimum ) + , mMaximumValue( maximum ) { } @@ -209,3 +207,130 @@ bool QgsMeshDatasetMetadata::isValid() const { return mIsValid; } + +double QgsMeshDatasetMetadata::minimum() const +{ + return mMinimumValue; +} + +double QgsMeshDatasetMetadata::maximum() const +{ + return mMaximumValue; +} + +QgsMeshDataBlock::QgsMeshDataBlock() + : mType( ActiveFlagInteger ) +{ +} + +QgsMeshDataBlock::QgsMeshDataBlock( QgsMeshDataBlock::DataType type, int count ) + : mType( type ) +{ + switch ( type ) + { + case ActiveFlagInteger: + mIntegerBuffer.resize( count ); + break; + case ScalarDouble: + mDoubleBuffer.resize( count ); + break; + case Vector2DDouble: + mDoubleBuffer.resize( 2 * count ); + break; + } +} + +QgsMeshDataBlock::DataType QgsMeshDataBlock::type() const +{ + return mType; +} + +int QgsMeshDataBlock::count() const +{ + switch ( mType ) + { + case ActiveFlagInteger: + return mIntegerBuffer.size(); + case ScalarDouble: + return mDoubleBuffer.size(); + case Vector2DDouble: + return static_cast( mDoubleBuffer.size() / 2.0 ); + } +} + +bool QgsMeshDataBlock::isValid() const +{ + return count() > 0; +} + +QgsMeshDatasetValue QgsMeshDataBlock::value( int index ) const +{ + switch ( mType ) + { + case ActiveFlagInteger: + return QgsMeshDatasetValue(); + case ScalarDouble: + return QgsMeshDatasetValue( mDoubleBuffer[index] ); + case Vector2DDouble: + return QgsMeshDatasetValue( + mDoubleBuffer[2 * index], + mDoubleBuffer[2 * index + 1] + ); + } +} + +bool QgsMeshDataBlock::active( int index ) const +{ + if ( ActiveFlagInteger == mType ) + return bool( mIntegerBuffer[index] ); + else + return false; +} + +void *QgsMeshDataBlock::buffer() +{ + if ( ActiveFlagInteger == mType ) + { + return mIntegerBuffer.data(); + } + else + { + return mDoubleBuffer.data(); + } +} + +const void *QgsMeshDataBlock::constBuffer() const +{ + if ( ActiveFlagInteger == mType ) + { + return mIntegerBuffer.constData(); + } + else + { + return mDoubleBuffer.constData(); + } +} + +QgsMeshVertex QgsMesh::vertex( int index ) const +{ + if ( index < vertices.size() && index >= 0 ) + return vertices[index]; + return QgsMeshVertex(); +} + +QgsMeshFace QgsMesh::face( int index ) const +{ + if ( index < faces.size() && index >= 0 ) + return faces[index]; + return QgsMeshFace(); +} + +int QgsMesh::vertexCount() const +{ + return vertices.size(); +} + +int QgsMesh::faceCount() const +{ + return faces.size(); +} diff --git a/src/core/mesh/qgsmeshdataprovider.h b/src/core/mesh/qgsmeshdataprovider.h index 3811541fa03..b96e900c082 100644 --- a/src/core/mesh/qgsmeshdataprovider.h +++ b/src/core/mesh/qgsmeshdataprovider.h @@ -65,6 +65,31 @@ typedef QgsPoint QgsMeshVertex; //! List of vertex indexes typedef QVector QgsMeshFace; +/** + * \ingroup core + * + * Mesh - vertices and faces + * + * \since QGIS 3.6 + */ +struct CORE_EXPORT QgsMesh +{ + //! Returns number of vertices + int vertexCount() const; + //! Returns number of faces + int faceCount() const; + + //! Returns a vertex at the index + QgsMeshVertex vertex( int index ) const; + //! Returns a face at the index + QgsMeshFace face( int index ) const; + + //! vertices + QVector vertices SIP_SKIP; + //! faces + QVector faces SIP_SKIP; +}; + /** * \ingroup core * @@ -117,6 +142,90 @@ class CORE_EXPORT QgsMeshDatasetValue double mY = std::numeric_limits::quiet_NaN(); }; +/** + * \ingroup core + * + * QgsMeshDataBlock is a block that can be used to retrieve a block of + * active flag (e.g. face's active integer flag) + * scalars (e.g. scalar dataset double values) + * vectors (e.g. vector dataset doubles x,y values) + * + * data are implicitly shared, so the class can be quickly copied + * std::numeric_limits::quiet_NaN() represents NODATA value + * + * Data can be accessed all at once with buffer() (faster) or + * value by value (slower) with active() or value() + * + * \since QGIS 3.6 + */ +class CORE_EXPORT QgsMeshDataBlock +{ + public: + //! Type of data stored in the block + enum DataType + { + ActiveFlagInteger, //!< Integer boolean flag whether face is active + ScalarDouble, //!< Scalar double values + Vector2DDouble, //!< Vector double pairs (x1, y1, x2, y2, ... ) + }; + + //! Constructs an invalid block + QgsMeshDataBlock(); + + //! Constructs a new block + QgsMeshDataBlock( DataType type, int count ); + + //! Type of data stored in the block + DataType type() const; + + //! Number of items stored in the block + int count() const; + + //! Whether the block is valid + bool isValid() const; + + /** + * Returns a value represented by the index + * For active flag the behavior is undefined + */ + QgsMeshDatasetValue value( int index ) const; + + /** + * Returns a value for active flag by the index + * For scalar and vector 2d the behavior is undefined + */ + bool active( int index ) const; + + /** + * Returns internal buffer to the array + * + * The buffer is already allocated with size: + * count() * sizeof(int) for ActiveFlagInteger + * count() * sizeof(double) for ScalarDouble + * count() * 2 * sizeof(double) for Vector2DDouble + * + * Primary usage of the function is to write/populate + * data to the block by data provider. + */ + void *buffer() SIP_SKIP; + + /** + * Returns internal buffer to the array for fast + * values reading + * + * The buffer is allocated with size: + * count() * sizeof(int) for ActiveFlagInteger + * count() * sizeof(double) for ScalarDouble + * count() * 2 * sizeof(double) for Vector2DDouble + */ + const void *constBuffer() const SIP_SKIP; + + private: + QVector mDoubleBuffer; + QVector mIntegerBuffer; + DataType mType; +}; + /** * \ingroup core * @@ -147,11 +256,15 @@ class CORE_EXPORT QgsMeshDatasetGroupMetadata * \param name name of the dataset group * \param isScalar dataset contains scalar data, specifically the y-value of QgsMeshDatasetValue is NaN * \param isOnVertices dataset values are defined on mesh's vertices. If false, values are defined on faces. + * \param minimum minimum value (magnitude for vectors) present among all group's dataset values + * \param maximum maximum value (magnitude for vectors) present among all group's dataset values * \param extraOptions dataset's extra options stored by the provider. Usually contains the name, time value, time units, data file vendor, ... */ QgsMeshDatasetGroupMetadata( const QString &name, bool isScalar, bool isOnVertices, + double minimum, + double maximum, const QMap &extraOptions ); /** @@ -179,10 +292,22 @@ class CORE_EXPORT QgsMeshDatasetGroupMetadata */ DataType dataType() const; + /** + * \brief Returns minimum scalar value/vector magnitude present for whole dataset group + */ + double minimum() const; + + /** + * \brief Returns maximum scalar value/vector magnitude present for whole dataset group + */ + double maximum() const; + private: QString mName; bool mIsScalar = false; bool mIsOnVertices = false; + double mMinimumValue = std::numeric_limits::quiet_NaN(); + double mMaximumValue = std::numeric_limits::quiet_NaN(); QMap mExtraOptions; }; @@ -209,7 +334,10 @@ class CORE_EXPORT QgsMeshDatasetMetadata * \param isValid dataset is loadad and valid for fetching the data */ QgsMeshDatasetMetadata( double time, - bool isValid ); + bool isValid, + double minimum, + double maximum + ); /** * \brief Returns the time value for this dataset @@ -221,9 +349,21 @@ class CORE_EXPORT QgsMeshDatasetMetadata */ bool isValid() const; + /** + * \brief Returns minimum scalar value/vector magnitude present for the dataset + */ + double minimum() const; + + /** + * \brief Returns maximum scalar value/vector magnitude present for the dataset + */ + double maximum() const; + private: double mTime = std::numeric_limits::quiet_NaN(); bool mIsValid = false; + double mMinimumValue = std::numeric_limits::quiet_NaN(); + double mMaximumValue = std::numeric_limits::quiet_NaN(); }; /** @@ -261,14 +401,10 @@ class CORE_EXPORT QgsMeshDataSourceInterface SIP_ABSTRACT virtual int faceCount() const = 0; /** - * Returns the mesh vertex at index + * Populates the mesh vertices and faces + * \since QGIS 3.6 */ - virtual QgsMeshVertex vertex( int index ) const = 0; - - /** - * Returns the mesh face at index - */ - virtual QgsMeshFace face( int index ) const = 0; + virtual void populateMesh( QgsMesh *mesh ) const = 0; }; /** @@ -336,11 +472,22 @@ class CORE_EXPORT QgsMeshDatasetSourceInterface SIP_ABSTRACT /** * \brief Returns vector/scalar value associated with the index from the dataset + * To read multiple continuous values, use QgsMeshDatasetSourceInterface::datasetValues() * - * See QgsMeshDatasetMetadata::isVector() to check if the returned value is vector or scalar + * See QgsMeshDatasetMetadata::isVector() or QgsMeshDataBlock::type() + * to check if the returned value is vector or scalar */ virtual QgsMeshDatasetValue datasetValue( QgsMeshDatasetIndex index, int valueIndex ) const = 0; + /** + * \brief Returns N vector/scalar values from the index from the dataset + * + * See QgsMeshDatasetMetadata::isVector() to check if the returned value is vector or scalar + * + * \since QGIS 3.6 + */ + virtual QgsMeshDataBlock datasetValues( QgsMeshDatasetIndex index, int valueIndex, int count ) const = 0; + /** * \brief Returns whether the face is active for particular dataset * @@ -352,6 +499,13 @@ class CORE_EXPORT QgsMeshDatasetSourceInterface SIP_ABSTRACT * V3 ---- V4 ---- V6-----V8 */ virtual bool isFaceActive( QgsMeshDatasetIndex index, int faceIndex ) const = 0; + + /** + * \brief Returns whether the faces are active for particular dataset + * + * \since QGIS 3.6 + */ + virtual QgsMeshDataBlock areFacesActive( QgsMeshDatasetIndex index, int faceIndex, int count ) const = 0; }; @@ -373,12 +527,6 @@ class CORE_EXPORT QgsMeshDataProvider: public QgsDataProvider, public QgsMeshDat //! Ctor QgsMeshDataProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options ); - /** - * Returns the extent of the layer - * \returns QgsRectangle containing the extent of the layer - */ - QgsRectangle extent() const override; - signals: //! Emitted when some new dataset groups have been added void datasetGroupsAdded( int count ); diff --git a/src/core/mesh/qgsmeshlayer.cpp b/src/core/mesh/qgsmeshlayer.cpp index 26c59180a76..24c22542003 100644 --- a/src/core/mesh/qgsmeshlayer.cpp +++ b/src/core/mesh/qgsmeshlayer.cpp @@ -177,17 +177,7 @@ void QgsMeshLayer::fillNativeMesh() if ( !( dataProvider() && dataProvider()->isValid() ) ) return; - mNativeMesh->vertices.resize( dataProvider()->vertexCount() ); - for ( int i = 0; i < dataProvider()->vertexCount(); ++i ) - { - mNativeMesh->vertices[i] = dataProvider()->vertex( i ); - } - - mNativeMesh->faces.resize( dataProvider()->faceCount() ); - for ( int i = 0; i < dataProvider()->faceCount(); ++i ) - { - mNativeMesh->faces[i] = dataProvider()->face( i ); - } + dataProvider()->populateMesh( mNativeMesh.get() ); } void QgsMeshLayer::onDatasetGroupsAdded( int count ) @@ -224,8 +214,9 @@ static QgsColorRamp *_createDefaultColorRamp() void QgsMeshLayer::assignDefaultStyleToDatasetGroup( int groupIndex ) { - double groupMin, groupMax; - QgsMeshLayerUtils::calculateMinMaxForDatasetGroup( groupMin, groupMax, mDataProvider, groupIndex ); + const QgsMeshDatasetGroupMetadata metadata = mDataProvider->datasetGroupMetadata( groupIndex ); + double groupMin = metadata.minimum(); + double groupMax = metadata.maximum(); QgsColorRampShader fcn( groupMin, groupMax, _createDefaultColorRamp() ); fcn.classifyColorRamp( 5, -1, QgsRectangle(), nullptr ); diff --git a/src/core/mesh/qgsmeshlayerinterpolator.cpp b/src/core/mesh/qgsmeshlayerinterpolator.cpp index 820285b636b..79a584f0085 100644 --- a/src/core/mesh/qgsmeshlayerinterpolator.cpp +++ b/src/core/mesh/qgsmeshlayerinterpolator.cpp @@ -29,7 +29,7 @@ #include "qgsmeshlayerutils.h" QgsMeshLayerInterpolator::QgsMeshLayerInterpolator( const QgsTriangularMesh &m, - const QVector &datasetValues, const QVector &activeFaceFlagValues, + const QVector &datasetValues, const QgsMeshDataBlock &activeFaceFlagValues, bool dataIsOnVertices, const QgsRenderContext &context, const QSize &size ) @@ -87,7 +87,7 @@ QgsRasterBlock *QgsMeshLayerInterpolator::block( int, const QgsRectangle &extent const QgsPoint p1 = vertices[v1], p2 = vertices[v2], p3 = vertices[v3]; const int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces()[i]; - const bool isActive = mActiveFaceFlagValues[nativeFaceIndex]; + const bool isActive = mActiveFaceFlagValues.active( nativeFaceIndex ); if ( !isActive ) continue; diff --git a/src/core/mesh/qgsmeshlayerinterpolator.h b/src/core/mesh/qgsmeshlayerinterpolator.h index 9dceb6c338e..770bb946688 100644 --- a/src/core/mesh/qgsmeshlayerinterpolator.h +++ b/src/core/mesh/qgsmeshlayerinterpolator.h @@ -49,7 +49,7 @@ class QgsMeshLayerInterpolator : public QgsRasterInterface //! Ctor QgsMeshLayerInterpolator( const QgsTriangularMesh &m, const QVector &datasetValues, - const QVector &activeFaceFlagValues, + const QgsMeshDataBlock &activeFaceFlagValues, bool dataIsOnVertices, const QgsRenderContext &context, const QSize &size ); @@ -63,7 +63,7 @@ class QgsMeshLayerInterpolator : public QgsRasterInterface private: const QgsTriangularMesh &mTriangularMesh; const QVector &mDatasetValues; - const QVector &mActiveFaceFlagValues; + const QgsMeshDataBlock &mActiveFaceFlagValues; const QgsRenderContext &mContext; bool mDataOnVertices = true; QSize mOutputSize; diff --git a/src/core/mesh/qgsmeshlayerrenderer.cpp b/src/core/mesh/qgsmeshlayerrenderer.cpp index 156b3092329..b2c5e601d84 100644 --- a/src/core/mesh/qgsmeshlayerrenderer.cpp +++ b/src/core/mesh/qgsmeshlayerrenderer.cpp @@ -97,28 +97,25 @@ void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer ) { const QgsMeshDatasetGroupMetadata metadata = layer->dataProvider()->datasetGroupMetadata( datasetIndex ); mScalarDataOnVertices = metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices; - int count; - if ( mScalarDataOnVertices ) - count = mNativeMesh.vertices.count(); - else - count = mNativeMesh.faces.count(); - mScalarDatasetValues.resize( count ); - for ( int i = 0; i < count; ++i ) - { - double v = layer->dataProvider()->datasetValue( datasetIndex, i ).scalar(); - mScalarDatasetValues[i] = v; - } + // populate scalar values + QgsMeshDataBlock vals = layer->dataProvider()->datasetValues( + datasetIndex, + 0, + mScalarDataOnVertices ? mNativeMesh.vertices.count() : mNativeMesh.faces.count() ); + + // vals could be scalar or vectors, for contour rendering we want always magnitude + mScalarDatasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals ); // populate face active flag, always defined on faces - mScalarActiveFaceFlagValues.resize( mNativeMesh.faces.count() ); - for ( int i = 0; i < mNativeMesh.faces.count(); ++i ) - { - bool active = layer->dataProvider()->isFaceActive( datasetIndex, i ); - mScalarActiveFaceFlagValues[i] = active; - } + mScalarActiveFaceFlagValues = layer->dataProvider()->areFacesActive( + datasetIndex, + 0, + mNativeMesh.faces.count() ); - QgsMeshLayerUtils::calculateMinimumMaximum( mScalarDatasetMinimum, mScalarDatasetMaximum, mScalarDatasetValues ); + const QgsMeshDatasetMetadata datasetMetadata = layer->dataProvider()->datasetMetadata( datasetIndex ); + mScalarDatasetMinimum = datasetMetadata.minimum(); + mScalarDatasetMaximum = datasetMetadata.maximum(); } // update cache @@ -141,11 +138,12 @@ void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer ) if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) && ( cache->mActiveVectorDatasetIndex == datasetIndex ) ) { - mVectorDatasetValuesX = cache->mVectorDatasetValuesX; - mVectorDatasetValuesY = cache->mVectorDatasetValuesY; + mVectorDatasetValues = cache->mVectorDatasetValues; mVectorDatasetValuesMag = cache->mVectorDatasetValuesMag; mVectorDatasetMagMinimum = cache->mVectorDatasetMagMinimum; mVectorDatasetMagMaximum = cache->mVectorDatasetMagMaximum; + mVectorDatasetGroupMagMinimum = cache->mVectorDatasetMagMinimum; + mVectorDatasetGroupMagMaximum = cache->mVectorDatasetMagMaximum; mVectorDataOnVertices = cache->mVectorDataOnVertices; return; } @@ -164,39 +162,38 @@ void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer ) else { mVectorDataOnVertices = metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices; + mVectorDatasetGroupMagMinimum = metadata.minimum(); + mVectorDatasetGroupMagMaximum = metadata.maximum(); + int count; if ( mVectorDataOnVertices ) count = mNativeMesh.vertices.count(); else count = mNativeMesh.faces.count(); - mVectorDatasetValuesX.resize( count ); - mVectorDatasetValuesY.resize( count ); - mVectorDatasetValuesMag.resize( count ); - for ( int i = 0; i < count; ++i ) - { - double x = layer->dataProvider()->datasetValue( datasetIndex, i ).x(); - mVectorDatasetValuesX[i] = x; - double y = layer->dataProvider()->datasetValue( datasetIndex, i ).y(); - mVectorDatasetValuesY[i] = y; + mVectorDatasetValues = layer->dataProvider()->datasetValues( + datasetIndex, + 0, + count ); - double mag = layer->dataProvider()->datasetValue( datasetIndex, i ).scalar(); - mVectorDatasetValuesMag[i] = mag; - } + mVectorDatasetValuesMag = QgsMeshLayerUtils::calculateMagnitudes( mVectorDatasetValues ); + + const QgsMeshDatasetMetadata datasetMetadata = layer->dataProvider()->datasetMetadata( datasetIndex ); + mVectorDatasetMagMinimum = datasetMetadata.minimum(); + mVectorDatasetMagMaximum = datasetMetadata.maximum(); } - - QgsMeshLayerUtils::calculateMinimumMaximum( mVectorDatasetMagMinimum, mVectorDatasetMagMaximum, mVectorDatasetValuesMag ); } // update cache cache->mDatasetGroupsCount = datasetGroupCount; cache->mActiveVectorDatasetIndex = datasetIndex; - cache->mVectorDatasetValuesX = mVectorDatasetValuesX; - cache->mVectorDatasetValuesY = mVectorDatasetValuesY; + cache->mVectorDatasetValues = mVectorDatasetValues; cache->mVectorDatasetValuesMag = mVectorDatasetValuesMag; cache->mVectorDatasetMagMinimum = mVectorDatasetMagMinimum; cache->mVectorDatasetMagMaximum = mVectorDatasetMagMaximum; + cache->mVectorDatasetGroupMagMinimum = mVectorDatasetMagMinimum; + cache->mVectorDatasetGroupMagMaximum = mVectorDatasetMagMaximum; cache->mVectorDataOnVertices = mVectorDataOnVertices; } @@ -304,8 +301,12 @@ void QgsMeshLayerRenderer::renderScalarDataset() QgsColorRampShader *fcn = new QgsColorRampShader( scalarSettings.colorRampShader() ); QgsRasterShader *sh = new QgsRasterShader(); sh->setRasterShaderFunction( fcn ); // takes ownership of fcn - QgsMeshLayerInterpolator interpolator( mTriangularMesh, mScalarDatasetValues, mScalarActiveFaceFlagValues, - mScalarDataOnVertices, mContext, mOutputSize ); + QgsMeshLayerInterpolator interpolator( mTriangularMesh, + mScalarDatasetValues, + mScalarActiveFaceFlagValues, + mScalarDataOnVertices, + mContext, + mOutputSize ); QgsSingleBandPseudoColorRenderer renderer( &interpolator, 0, sh ); // takes ownership of sh renderer.setClassificationMin( scalarSettings.classificationMinimum() ); renderer.setClassificationMax( scalarSettings.classificationMaximum() ); @@ -323,19 +324,21 @@ void QgsMeshLayerRenderer::renderVectorDataset() if ( !index.isValid() ) return; - if ( mVectorDatasetValuesX.isEmpty() ) - return; + if ( !mVectorDatasetValues.isValid() ) + return; // no data at all if ( std::isnan( mVectorDatasetMagMinimum ) || std::isnan( mVectorDatasetMagMaximum ) ) return; // only NODATA values - if ( mVectorDatasetValuesX.size() != mVectorDatasetValuesY.size() ) - return; - QgsMeshVectorRenderer renderer( mTriangularMesh, - mVectorDatasetValuesX, mVectorDatasetValuesY, mVectorDatasetValuesMag, - mVectorDatasetMagMinimum, mVectorDatasetMagMaximum, - mVectorDataOnVertices, mRendererSettings.vectorSettings( index.group() ), mContext, mOutputSize ); + mVectorDatasetValues, + mVectorDatasetValuesMag, + mVectorDatasetMagMinimum, + mVectorDatasetMagMaximum, + mVectorDataOnVertices, + mRendererSettings.vectorSettings( index.group() ), + mContext, + mOutputSize ); renderer.draw(); } diff --git a/src/core/mesh/qgsmeshlayerrenderer.h b/src/core/mesh/qgsmeshlayerrenderer.h index a8b715d6fbd..2e1977cf9ef 100644 --- a/src/core/mesh/qgsmeshlayerrenderer.h +++ b/src/core/mesh/qgsmeshlayerrenderer.h @@ -56,18 +56,19 @@ struct CORE_NO_EXPORT QgsMeshLayerRendererCache // scalar dataset QgsMeshDatasetIndex mActiveScalarDatasetIndex; QVector mScalarDatasetValues; - QVector mScalarActiveFaceFlagValues; + QgsMeshDataBlock mScalarActiveFaceFlagValues; bool mScalarDataOnVertices = true; double mScalarDatasetMinimum = std::numeric_limits::quiet_NaN(); double mScalarDatasetMaximum = std::numeric_limits::quiet_NaN(); // vector dataset QgsMeshDatasetIndex mActiveVectorDatasetIndex; - QVector mVectorDatasetValuesX; - QVector mVectorDatasetValuesY; + QgsMeshDataBlock mVectorDatasetValues; QVector mVectorDatasetValuesMag; double mVectorDatasetMagMinimum = std::numeric_limits::quiet_NaN(); double mVectorDatasetMagMaximum = std::numeric_limits::quiet_NaN(); + double mVectorDatasetGroupMagMinimum = std::numeric_limits::quiet_NaN(); + double mVectorDatasetGroupMagMaximum = std::numeric_limits::quiet_NaN(); bool mVectorDataOnVertices = true; }; @@ -110,17 +111,18 @@ class QgsMeshLayerRenderer : public QgsMapLayerRenderer // copy of the scalar dataset QVector mScalarDatasetValues; - QVector mScalarActiveFaceFlagValues; + QgsMeshDataBlock mScalarActiveFaceFlagValues; bool mScalarDataOnVertices = true; double mScalarDatasetMinimum = std::numeric_limits::quiet_NaN(); double mScalarDatasetMaximum = std::numeric_limits::quiet_NaN(); // copy of the vector dataset - QVector mVectorDatasetValuesX; - QVector mVectorDatasetValuesY; + QgsMeshDataBlock mVectorDatasetValues; QVector mVectorDatasetValuesMag; double mVectorDatasetMagMinimum = std::numeric_limits::quiet_NaN(); double mVectorDatasetMagMaximum = std::numeric_limits::quiet_NaN(); + double mVectorDatasetGroupMagMinimum = std::numeric_limits::quiet_NaN(); + double mVectorDatasetGroupMagMaximum = std::numeric_limits::quiet_NaN(); bool mVectorDataOnVertices = true; // rendering context diff --git a/src/core/mesh/qgsmeshlayerutils.cpp b/src/core/mesh/qgsmeshlayerutils.cpp index ad890375c60..e50fd9f47e5 100644 --- a/src/core/mesh/qgsmeshlayerutils.cpp +++ b/src/core/mesh/qgsmeshlayerutils.cpp @@ -17,103 +17,22 @@ #include "qgsmeshlayerutils.h" -#include "qgsmeshdataprovider.h" - #include ///@cond PRIVATE -void QgsMeshLayerUtils::calculateMinimumMaximum( double &min, double &max, const QVector &arr ) +QVector QgsMeshLayerUtils::calculateMagnitudes( const QgsMeshDataBlock &block ) { - bool found = false; + Q_ASSERT( QgsMeshDataBlock::ActiveFlagInteger != block.type() ); + int count = block.count(); + QVector ret( count ); - min = std::numeric_limits::max(); - max = std::numeric_limits::min(); - - for ( const double val : arr ) - { - if ( !std::isnan( val ) ) - { - found = true; - if ( val < min ) - min = val; - if ( val > max ) - max = val; - } - } - - if ( !found ) - { - min = std::numeric_limits::quiet_NaN(); - max = std::numeric_limits::quiet_NaN(); - } -} - -void QgsMeshLayerUtils::calculateMinMaxForDatasetGroup( double &min, double &max, QgsMeshDataProvider *provider, int groupIndex ) -{ - if ( groupIndex < 0 || !provider || groupIndex >= provider->datasetGroupCount() ) - { - min = std::numeric_limits::quiet_NaN(); - max = std::numeric_limits::quiet_NaN(); - return; - } - - min = std::numeric_limits::max(); - max = std::numeric_limits::min(); - - int count = provider->datasetCount( groupIndex ); for ( int i = 0; i < count; ++i ) { - double dMin, dMax; - calculateMinMaxForDataset( dMin, dMax, provider, QgsMeshDatasetIndex( groupIndex, i ) ); - min = std::min( min, dMin ); - max = std::max( max, dMax ); + double mag = block.value( i ).scalar(); + ret[i] = mag; } -} - -void QgsMeshLayerUtils::calculateMinMaxForDataset( double &min, double &max, QgsMeshDataProvider *provider, QgsMeshDatasetIndex index ) -{ - if ( !index.isValid() || !provider ) - { - min = std::numeric_limits::quiet_NaN(); - max = std::numeric_limits::quiet_NaN(); - return; - } - - const QgsMeshDatasetGroupMetadata metadata = provider->datasetGroupMetadata( index ); - bool onVertices = metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices; - int count; - if ( onVertices ) - count = provider->vertexCount(); - else - count = provider->faceCount(); - - bool firstIteration = true; - for ( int i = 0; i < count; ++i ) - { - double v = provider->datasetValue( index, i ).scalar(); - - if ( std::isnan( v ) ) - continue; - if ( firstIteration ) - { - firstIteration = false; - min = v; - max = v; - } - else - { - if ( v < min ) - { - min = v; - } - if ( v > max ) - { - max = v; - } - } - } - + return ret; } void QgsMeshLayerUtils::boundingBoxToScreenRectangle( const QgsMapToPixel &mtp, diff --git a/src/core/mesh/qgsmeshlayerutils.h b/src/core/mesh/qgsmeshlayerutils.h index d1f252399e1..250d086af4d 100644 --- a/src/core/mesh/qgsmeshlayerutils.h +++ b/src/core/mesh/qgsmeshlayerutils.h @@ -23,9 +23,7 @@ #include "qgis_core.h" #include "qgsrectangle.h" #include "qgsmaptopixel.h" - -class QgsMeshDataProvider; -class QgsMeshDatasetIndex; +#include "qgsmeshdataprovider.h" #include #include @@ -44,22 +42,11 @@ class CORE_EXPORT QgsMeshLayerUtils public: /** - * Calculates min/max values from the given vector of values. - * Ignores any NaN values in the input. Returns NaN for min/max on error. + * Calculates magnitude values from the given QgsMeshDataBlock. + * + * \since QGIS 3.6 */ - static void calculateMinimumMaximum( double &min, double &max, const QVector &arr ); - - /** - * Calculates min/max values for the whole dataset group (considering all datasets within it). - * Ignores any NaN values in the input. Returns NaN for min/max on error. - */ - static void calculateMinMaxForDatasetGroup( double &min, double &max, QgsMeshDataProvider *provider, int groupIndex ); - - /** - * Calculates min/max values for one dataset. - * Ignores any NaN values in the input. Returns NaN for min/max on error. - */ - static void calculateMinMaxForDataset( double &min, double &max, QgsMeshDataProvider *provider, QgsMeshDatasetIndex index ); + static QVector calculateMagnitudes( const QgsMeshDataBlock &block ); /** * Transformes the bounding box to rectangle in screen coordinates (in pixels) diff --git a/src/core/mesh/qgsmeshmemorydataprovider.cpp b/src/core/mesh/qgsmeshmemorydataprovider.cpp index 428a92d9de8..5ebc9b9abde 100644 --- a/src/core/mesh/qgsmeshmemorydataprovider.cpp +++ b/src/core/mesh/qgsmeshmemorydataprovider.cpp @@ -17,6 +17,9 @@ ///@cond PRIVATE #include "qgsmeshmemorydataprovider.h" +#include "qgsmeshlayerutils.h" +#include "qgstriangularmesh.h" +#include static const QString TEXT_PROVIDER_KEY = QStringLiteral( "mesh_memory" ); static const QString TEXT_PROVIDER_DESCRIPTION = QStringLiteral( "Mesh memory provider" ); @@ -299,16 +302,18 @@ int QgsMeshMemoryDataProvider::faceCount() const return mFaces.size(); } -QgsMeshVertex QgsMeshMemoryDataProvider::vertex( int index ) const +void QgsMeshMemoryDataProvider::populateMesh( QgsMesh *mesh ) const { - Q_ASSERT( vertexCount() > index ); - return mVertices[index]; + if ( mesh ) + { + mesh->faces = mFaces; + mesh->vertices = mVertices; + } } -QgsMeshFace QgsMeshMemoryDataProvider::face( int index ) const +QgsRectangle QgsMeshMemoryDataProvider::extent() const { - Q_ASSERT( faceCount() > index ); - return mFaces[index]; + return calculateExtent( ); } bool QgsMeshMemoryDataProvider::addDataset( const QString &uri ) @@ -326,6 +331,7 @@ bool QgsMeshMemoryDataProvider::addDataset( const QString &uri ) QStringLiteral( "Mesh Memory Provider" ) ) ); } + calculateMinMaxForDatasetGroup( group ); mDatasetGroups.push_back( group ); if ( valid ) @@ -364,6 +370,8 @@ QgsMeshDatasetGroupMetadata QgsMeshMemoryDataProvider::datasetGroupMetadata( int mDatasetGroups[groupIndex].name, mDatasetGroups[groupIndex].isScalar, mDatasetGroups[groupIndex].isOnVertices, + mDatasetGroups[groupIndex].minimum, + mDatasetGroups[groupIndex].maximum, mDatasetGroups[groupIndex].metadata ); return metadata; @@ -383,7 +391,9 @@ QgsMeshDatasetMetadata QgsMeshMemoryDataProvider::datasetMetadata( QgsMeshDatase const QgsMeshMemoryDatasetGroup &grp = mDatasetGroups.at( index.group() ); QgsMeshDatasetMetadata metadata( grp.datasets[index.dataset()].time, - grp.datasets[index.dataset()].valid + grp.datasets[index.dataset()].valid, + grp.datasets[index.dataset()].minimum, + grp.datasets[index.dataset()].maximum ); return metadata; } @@ -407,6 +417,26 @@ QgsMeshDatasetValue QgsMeshMemoryDataProvider::datasetValue( QgsMeshDatasetIndex } } +QgsMeshDataBlock QgsMeshMemoryDataProvider::datasetValues( QgsMeshDatasetIndex index, int valueIndex, int count ) const +{ + bool isScalar = mDatasetGroups[index.group()].isScalar; + QgsMeshDataBlock ret( isScalar ? QgsMeshDataBlock::ScalarDouble : QgsMeshDataBlock::Vector2DDouble, count ); + double *buf = static_cast( ret.buffer() ); + + for ( int i = 0; i < count; ++i ) + { + QgsMeshDatasetValue val = datasetValue( index, valueIndex + i ); + if ( isScalar ) + buf[i] = val.x(); + else + { + buf[2 * i] = val.x(); + buf[2 * i + 1] = val.y(); + } + } + return ret; +} + bool QgsMeshMemoryDataProvider::isFaceActive( QgsMeshDatasetIndex index, int faceIndex ) const { Q_UNUSED( index ); @@ -414,5 +444,83 @@ bool QgsMeshMemoryDataProvider::isFaceActive( QgsMeshDatasetIndex index, int fac return true; } +QgsMeshDataBlock QgsMeshMemoryDataProvider::areFacesActive( QgsMeshDatasetIndex index, int faceIndex, int count ) const +{ + Q_UNUSED( index ); + Q_UNUSED( faceIndex ); + QgsMeshDataBlock ret( QgsMeshDataBlock::ActiveFlagInteger, count ); + memset( ret.buffer(), 1, static_cast( count ) * sizeof( int ) ); + return ret; +} +void QgsMeshMemoryDataProvider::calculateMinMaxForDatasetGroup( QgsMeshMemoryDatasetGroup &grp ) const +{ + double min = std::numeric_limits::max(); + double max = std::numeric_limits::min(); + + int count = grp.datasets.size(); + for ( int i = 0; i < count; ++i ) + { + calculateMinMaxForDataset( grp.datasets[i] ); + min = std::min( min, grp.datasets[i].minimum ); + max = std::max( max, grp.datasets[i].maximum ); + } + + grp.minimum = min; + grp.maximum = max; +} + +void QgsMeshMemoryDataProvider::calculateMinMaxForDataset( QgsMeshMemoryDataset &dataset ) const +{ + double min = std::numeric_limits::max(); + double max = std::numeric_limits::min(); + + if ( !dataset.valid ) + { + return; + } + + bool firstIteration = true; + for ( int i = 0; i < dataset.values.size(); ++i ) + { + double v = dataset.values[i].scalar(); + + if ( std::isnan( v ) ) + continue; + if ( firstIteration ) + { + firstIteration = false; + min = v; + max = v; + } + else + { + if ( v < min ) + { + min = v; + } + if ( v > max ) + { + max = v; + } + } + } + + dataset.minimum = min; + dataset.maximum = max; +} + +QgsRectangle QgsMeshMemoryDataProvider::calculateExtent() const +{ + QgsRectangle rec; + rec.setMinimal(); + for ( const QgsMeshVertex &v : mVertices ) + { + rec.setXMinimum( std::min( rec.xMinimum(), v.x() ) ); + rec.setYMinimum( std::min( rec.yMinimum(), v.y() ) ); + rec.setXMaximum( std::max( rec.xMaximum(), v.x() ) ); + rec.setYMaximum( std::max( rec.yMaximum(), v.y() ) ); + } + return rec; +} ///@endcond diff --git a/src/core/mesh/qgsmeshmemorydataprovider.h b/src/core/mesh/qgsmeshmemorydataprovider.h index dc273e47ecf..83bf6d952ab 100644 --- a/src/core/mesh/qgsmeshmemorydataprovider.h +++ b/src/core/mesh/qgsmeshmemorydataprovider.h @@ -34,6 +34,8 @@ struct QgsMeshMemoryDataset QVector values; double time = -1; bool valid = false; + double minimum = std::numeric_limits::quiet_NaN(); + double maximum = std::numeric_limits::quiet_NaN(); }; struct QgsMeshMemoryDatasetGroup @@ -43,6 +45,8 @@ struct QgsMeshMemoryDatasetGroup QString name; bool isScalar = true; bool isOnVertices = true; + double minimum = std::numeric_limits::quiet_NaN(); + double maximum = std::numeric_limits::quiet_NaN(); }; /** @@ -87,9 +91,8 @@ class QgsMeshMemoryDataProvider: public QgsMeshDataProvider int vertexCount() const override; int faceCount() const override; - QgsMeshVertex vertex( int index ) const override; - QgsMeshFace face( int index ) const override; - + void populateMesh( QgsMesh *mesh ) const override; + QgsRectangle extent() const override; /** * Adds dataset to a mesh in-memory data provider from data string @@ -128,7 +131,9 @@ class QgsMeshMemoryDataProvider: public QgsMeshDataProvider QgsMeshDatasetGroupMetadata datasetGroupMetadata( int groupIndex ) const override; QgsMeshDatasetMetadata datasetMetadata( QgsMeshDatasetIndex index ) const override; QgsMeshDatasetValue datasetValue( QgsMeshDatasetIndex index, int valueIndex ) const override; + QgsMeshDataBlock datasetValues( QgsMeshDatasetIndex index, int valueIndex, int count ) const override; bool isFaceActive( QgsMeshDatasetIndex index, int faceIndex ) const override; + QgsMeshDataBlock areFacesActive( QgsMeshDatasetIndex index, int faceIndex, int count ) const override; //! Returns the memory provider key static QString providerKey(); @@ -136,7 +141,12 @@ class QgsMeshMemoryDataProvider: public QgsMeshDataProvider static QString providerDescription(); //! Provider factory static QgsMeshMemoryDataProvider *createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options ); + private: + void calculateMinMaxForDatasetGroup( QgsMeshMemoryDatasetGroup &grp ) const; + void calculateMinMaxForDataset( QgsMeshMemoryDataset &dataset ) const; + QgsRectangle calculateExtent( ) const; + bool splitMeshSections( const QString &uri ); bool addMeshVertices( const QString &def ); bool addMeshFaces( const QString &def ); diff --git a/src/core/mesh/qgsmeshvectorrenderer.cpp b/src/core/mesh/qgsmeshvectorrenderer.cpp index 0542ddd37b7..ce47d90a998 100644 --- a/src/core/mesh/qgsmeshvectorrenderer.cpp +++ b/src/core/mesh/qgsmeshvectorrenderer.cpp @@ -46,8 +46,7 @@ inline bool nodataValue( double x, double y ) } QgsMeshVectorRenderer::QgsMeshVectorRenderer( const QgsTriangularMesh &m, - const QVector &datasetValuesX, - const QVector &datasetValuesY, + const QgsMeshDataBlock &datasetValues, const QVector &datasetValuesMag, double datasetMagMinimumValue, double datasetMagMaximumValue, @@ -55,8 +54,7 @@ QgsMeshVectorRenderer::QgsMeshVectorRenderer( const QgsTriangularMesh &m, const QgsMeshRendererVectorSettings &settings, QgsRenderContext &context, QSize size ) : mTriangularMesh( m ) - , mDatasetValuesX( datasetValuesX ) - , mDatasetValuesY( datasetValuesY ) + , mDatasetValues( datasetValues ) , mDatasetValuesMag( datasetValuesMag ) , mMinMag( datasetMagMinimumValue ) , mMaxMag( datasetMagMaximumValue ) @@ -70,6 +68,8 @@ QgsMeshVectorRenderer::QgsMeshVectorRenderer( const QgsTriangularMesh &m, Q_ASSERT( !mDatasetValuesMag.empty() ); Q_ASSERT( !std::isnan( mMinMag ) ); Q_ASSERT( !std::isnan( mMaxMag ) ); + Q_ASSERT( mDatasetValues.isValid() ); + Q_ASSERT( QgsMeshDataBlock::Vector2DDouble == mDatasetValues.type() ); // we need to expand out the extent so that it includes // arrows which start or end up outside of the @@ -259,8 +259,9 @@ void QgsMeshVectorRenderer::drawVectorDataOnVertices( const QList &triangle if ( !mBufferedExtent.contains( vertex ) ) continue; - double xVal = mDatasetValuesX[i]; - double yVal = mDatasetValuesY[i]; + const QgsMeshDatasetValue val = mDatasetValues.value( i ); + double xVal = val.x(); + double yVal = val.y(); if ( nodataValue( xVal, yVal ) ) continue; @@ -286,8 +287,9 @@ void QgsMeshVectorRenderer::drawVectorDataOnFaces( const QList &trianglesIn if ( !mBufferedExtent.contains( center ) ) continue; - double xVal = mDatasetValuesX[i]; - double yVal = mDatasetValuesY[i]; + const QgsMeshDatasetValue val = mDatasetValues.value( i ); + double xVal = val.x(); + double yVal = val.y(); if ( nodataValue( xVal, yVal ) ) continue; @@ -342,36 +344,40 @@ void QgsMeshVectorRenderer::drawVectorDataOnGrid( const QList &trianglesInE if ( mDataOnVertices ) { + const auto val1 = mDatasetValues.value( v1 ); + const auto val2 = mDatasetValues.value( v2 ); + const auto val3 = mDatasetValues.value( v3 ); val.setX( QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, - mDatasetValuesX[v1], - mDatasetValuesX[v2], - mDatasetValuesX[v3], + val1.x(), + val2.x(), + val3.x(), p ) ); val.setY( QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, - mDatasetValuesY[v1], - mDatasetValuesY[v2], - mDatasetValuesY[v3], + val1.y(), + val2.y(), + val3.y(), p ) ); } else { + const auto val1 = mDatasetValues.value( nativeFaceIndex ); val.setX( QgsMeshLayerUtils::interpolateFromFacesData( p1, p2, p3, - mDatasetValuesX[nativeFaceIndex], + val1.x(), p ) ); val.setY( QgsMeshLayerUtils::interpolateFromFacesData( p1, p2, p3, - mDatasetValuesY[nativeFaceIndex], + val1.y(), p ) ); diff --git a/src/core/mesh/qgsmeshvectorrenderer.h b/src/core/mesh/qgsmeshvectorrenderer.h index 81043b34d29..ddaf3a6d035 100644 --- a/src/core/mesh/qgsmeshvectorrenderer.h +++ b/src/core/mesh/qgsmeshvectorrenderer.h @@ -46,8 +46,7 @@ class QgsMeshVectorRenderer public: //! Ctor QgsMeshVectorRenderer( const QgsTriangularMesh &m, - const QVector &datasetValuesX, - const QVector &datasetValuesY, + const QgsMeshDataBlock &datasetValues, const QVector &datasetValuesMag, double datasetMagMaximumValue, double datasetMagMinimumValue, @@ -92,8 +91,7 @@ class QgsMeshVectorRenderer double calcExtentBufferSize() const; const QgsTriangularMesh &mTriangularMesh; - const QVector &mDatasetValuesX; - const QVector &mDatasetValuesY; + const QgsMeshDataBlock &mDatasetValues; const QVector &mDatasetValuesMag; //magnitudes double mMinMag = 0.0; double mMaxMag = 0.0; diff --git a/src/core/mesh/qgstriangularmesh.h b/src/core/mesh/qgstriangularmesh.h index f32f8a6ef05..4a3b6830b6d 100644 --- a/src/core/mesh/qgstriangularmesh.h +++ b/src/core/mesh/qgstriangularmesh.h @@ -33,15 +33,6 @@ class QgsRenderContext; class QgsCoordinateTransform; class QgsRectangle; -//! Mesh - vertices and faces -struct CORE_EXPORT QgsMesh -{ - //! vertices - QVector vertices; - //! faces - QVector faces; -}; - ///@cond PRIVATE /** diff --git a/src/providers/mdal/qgsmdalprovider.cpp b/src/providers/mdal/qgsmdalprovider.cpp index e35021ba640..f14f6287c79 100644 --- a/src/providers/mdal/qgsmdalprovider.cpp +++ b/src/providers/mdal/qgsmdalprovider.cpp @@ -18,6 +18,7 @@ #include #include "qgsmdalprovider.h" +#include "qgstriangularmesh.h" #ifdef HAVE_GUI #include "qgssourceselectprovider.h" @@ -82,26 +83,91 @@ int QgsMdalProvider::faceCount() const return 0; } -QgsMeshVertex QgsMdalProvider::vertex( int index ) const +void QgsMdalProvider::populateMesh( QgsMesh *mesh ) const { - Q_ASSERT( index < vertexCount() ); - double x = MDAL_M_vertexXCoordinatesAt( mMeshH, index ); - double y = MDAL_M_vertexYCoordinatesAt( mMeshH, index ); - QgsMeshVertex vertex( x, y ); - return vertex; + if ( mesh ) + { + mesh->faces = faces(); + mesh->vertices = vertices(); + } } -QgsMeshFace QgsMdalProvider::face( int index ) const +QVector QgsMdalProvider::vertices( ) const { - Q_ASSERT( index < faceCount() ); - QgsMeshFace face; - int n_face_vertices = MDAL_M_faceVerticesCountAt( mMeshH, index ); - for ( int j = 0; j < n_face_vertices; ++j ) + const int bufferSize = std::min( vertexCount(), 1000 ); + QVector ret( vertexCount() ); + QVector buffer( bufferSize * 3 ); + MeshVertexIteratorH it = MDAL_M_vertexIterator( mMeshH ); + int vertexIndex = 0; + while ( vertexIndex < vertexCount() ) { - int vertex_index = MDAL_M_faceVerticesIndexAt( mMeshH, index, j ); - face.push_back( vertex_index ); + int verticesRead = MDAL_VI_next( it, bufferSize, buffer.data() ); + if ( verticesRead == 0 ) + break; + for ( int i = 0; i < verticesRead; i++ ) + { + QgsMeshVertex vertex( + buffer[3 * i], + buffer[3 * i + 1], + buffer[3 * i + 2] + ); + ret[vertexIndex + i] = vertex; + } + vertexIndex += verticesRead; } - return face; + MDAL_VI_close( it ); + return ret; +} + +QVector QgsMdalProvider::faces( ) const +{ + const int faceOffsetsBufferLen = std::min( faceCount(), 1000 ); + const int vertexIndicesBufferLen = faceOffsetsBufferLen * 4; // most usually we have quads + int facesCount = faceCount(); + + QVector ret( facesCount ); + QVector faceOffsetsBuffer( faceOffsetsBufferLen ); + QVector vertexIndicesBuffer( vertexIndicesBufferLen ); + + MeshFaceIteratorH it = MDAL_M_faceIterator( mMeshH ); + int faceIndex = 0; + while ( faceIndex < facesCount ) + { + int facesRead = MDAL_FI_next( it, + faceOffsetsBufferLen, + faceOffsetsBuffer.data(), + vertexIndicesBufferLen, + vertexIndicesBuffer.data() ); + if ( facesRead == 0 ) + break; + + for ( int i = 0; i < facesRead; i++ ) + { + QgsMeshFace face; + int startIndex = 0; + if ( i > 0 ) + startIndex = faceOffsetsBuffer[ i - 1 ]; + int endIndex = faceOffsetsBuffer[ i ]; + + for ( int j = startIndex; j < endIndex; ++j ) + { + int vertexIndex = vertexIndicesBuffer[j]; + face.push_back( vertexIndex ); + } + ret[faceIndex + i] = face; + } + faceIndex += facesRead; + } + MDAL_FI_close( it ); + return ret; +} + +QgsRectangle QgsMdalProvider::extent() const +{ + double xMin, yMin, xMax, yMax; + MDAL_M_extent( mMeshH, &xMin, &xMax, &yMin, &yMax ); + QgsRectangle ret( xMin, yMin, xMax, yMax ); + return ret; } /*----------------------------------------------------------------------------------------------*/ @@ -155,6 +221,8 @@ QgsMeshDatasetGroupMetadata QgsMdalProvider::datasetGroupMetadata( int groupInde bool isScalar = MDAL_G_hasScalarData( group ); bool isOnVertices = MDAL_G_isOnVertices( group ); QString name = MDAL_G_name( group ); + double min, max; + MDAL_G_minimumMaximum( group, &min, &max ); QMap metadata; int n = MDAL_G_metadataCount( group ); @@ -169,6 +237,8 @@ QgsMeshDatasetGroupMetadata QgsMdalProvider::datasetGroupMetadata( int groupInde name, isScalar, isOnVertices, + min, + max, metadata ); @@ -187,10 +257,14 @@ QgsMeshDatasetMetadata QgsMdalProvider::datasetMetadata( QgsMeshDatasetIndex ind bool isValid = MDAL_D_isValid( dataset ); double time = MDAL_D_time( dataset ); + double min, max; + MDAL_D_minimumMaximum( dataset, &min, &max ); QgsMeshDatasetMetadata meta( time, - isValid + isValid, + min, + max ); return meta; @@ -198,41 +272,58 @@ QgsMeshDatasetMetadata QgsMdalProvider::datasetMetadata( QgsMeshDatasetIndex ind } QgsMeshDatasetValue QgsMdalProvider::datasetValue( QgsMeshDatasetIndex index, int valueIndex ) const +{ + QgsMeshDataBlock vals = datasetValues( index, valueIndex, 1 ); + return vals.value( 0 ); +} + +QgsMeshDataBlock QgsMdalProvider::datasetValues( QgsMeshDatasetIndex index, int valueIndex, int count ) const { DatasetGroupH group = MDAL_M_datasetGroup( mMeshH, index.group() ); if ( !group ) - return QgsMeshDatasetValue(); + return QgsMeshDataBlock(); DatasetH dataset = MDAL_G_dataset( group, index.dataset() ); if ( !dataset ) - return QgsMeshDatasetValue(); + return QgsMeshDataBlock(); - QgsMeshDatasetValue val; + bool isScalar = MDAL_G_hasScalarData( group ); - if ( MDAL_G_hasScalarData( group ) ) - { - val.setX( MDAL_D_value( dataset, valueIndex ) ); - } - else - { - val.setX( MDAL_D_valueX( dataset, valueIndex ) ); - val.setY( MDAL_D_valueY( dataset, valueIndex ) ); - } + QgsMeshDataBlock ret( isScalar ? QgsMeshDataBlock::ScalarDouble : QgsMeshDataBlock::Vector2DDouble, count ); + int valRead = MDAL_D_data( dataset, + valueIndex, + count, + isScalar ? MDAL_DataType::SCALAR_DOUBLE : MDAL_DataType::VECTOR_2D_DOUBLE, + ret.buffer() ); + if ( valRead != count ) + return QgsMeshDataBlock(); - return val; + return ret; } bool QgsMdalProvider::isFaceActive( QgsMeshDatasetIndex index, int faceIndex ) const +{ + QgsMeshDataBlock vals = areFacesActive( index, faceIndex, 1 ); + return vals.active( 0 ); +} + +QgsMeshDataBlock QgsMdalProvider::areFacesActive( QgsMeshDatasetIndex index, int faceIndex, int count ) const { DatasetGroupH group = MDAL_M_datasetGroup( mMeshH, index.group() ); if ( !group ) - return false; + return QgsMeshDataBlock(); DatasetH dataset = MDAL_G_dataset( group, index.dataset() ); if ( !dataset ) - return false; + return QgsMeshDataBlock(); - return MDAL_D_active( dataset, faceIndex ); + QgsMeshDataBlock ret( QgsMeshDataBlock::ActiveFlagInteger, count ); + + int valRead = MDAL_D_data( dataset, faceIndex, count, MDAL_DataType::ACTIVE_INTEGER, ret.buffer() ); + if ( valRead != count ) + return ret; + + return ret; } /*----------------------------------------------------------------------------------------------*/ diff --git a/src/providers/mdal/qgsmdalprovider.h b/src/providers/mdal/qgsmdalprovider.h index 2b73c5383ad..e3189f38f0e 100644 --- a/src/providers/mdal/qgsmdalprovider.h +++ b/src/providers/mdal/qgsmdalprovider.h @@ -53,8 +53,7 @@ class QgsMdalProvider : public QgsMeshDataProvider int vertexCount() const override; int faceCount() const override; - QgsMeshVertex vertex( int index ) const override; - QgsMeshFace face( int index ) const override; + void populateMesh( QgsMesh *mesh ) const override; bool addDataset( const QString &uri ) override; QStringList extraDatasets() const override; @@ -65,9 +64,14 @@ class QgsMdalProvider : public QgsMeshDataProvider QgsMeshDatasetGroupMetadata datasetGroupMetadata( int groupIndex ) const override; QgsMeshDatasetMetadata datasetMetadata( QgsMeshDatasetIndex index ) const override; QgsMeshDatasetValue datasetValue( QgsMeshDatasetIndex index, int valueIndex ) const override; + QgsMeshDataBlock datasetValues( QgsMeshDatasetIndex index, int valueIndex, int count ) const override; bool isFaceActive( QgsMeshDatasetIndex index, int faceIndex ) const override; + QgsMeshDataBlock areFacesActive( QgsMeshDatasetIndex index, int faceIndex, int count ) const override; + QgsRectangle extent() const override; private: + QVector vertices( ) const; + QVector faces( ) const; MeshH mMeshH; QStringList mExtraDatasetUris; QgsCoordinateReferenceSystem mCrs; diff --git a/tests/src/core/testqgsmeshlayer.cpp b/tests/src/core/testqgsmeshlayer.cpp index 1814e7826a0..f4cb54a64d0 100644 --- a/tests/src/core/testqgsmeshlayer.cpp +++ b/tests/src/core/testqgsmeshlayer.cpp @@ -25,6 +25,7 @@ #include "qgsapplication.h" #include "qgsproviderregistry.h" #include "qgsproject.h" +#include "qgstriangularmesh.h" /** * \ingroup UnitTests @@ -126,21 +127,31 @@ void TestQgsMeshLayer::test_read_mesh() QVERIFY( dp != nullptr ); QVERIFY( dp->isValid() ); + QgsMesh mesh; + dp->populateMesh( &mesh ); + QCOMPARE( 5, dp->vertexCount() ); - QCOMPARE( QgsMeshVertex( 1000.0, 2000.0 ), dp->vertex( 0 ) ); - QCOMPARE( QgsMeshVertex( 2000.0, 2000.0 ), dp->vertex( 1 ) ); - QCOMPARE( QgsMeshVertex( 3000.0, 2000.0 ), dp->vertex( 2 ) ); - QCOMPARE( QgsMeshVertex( 2000.0, 3000.0 ), dp->vertex( 3 ) ); - QCOMPARE( QgsMeshVertex( 1000.0, 3000.0 ), dp->vertex( 4 ) ); + const QVector vertices = mesh.vertices; + QCOMPARE( 1000.0, vertices.at( 0 ).x() ); + QCOMPARE( 2000.0, vertices.at( 1 ).x() ); + QCOMPARE( 3000.0, vertices.at( 2 ).x() ); + QCOMPARE( 2000.0, vertices.at( 3 ).x() ); + QCOMPARE( 1000.0, vertices.at( 4 ).x() ); + QCOMPARE( 2000.0, vertices.at( 0 ).y() ); + QCOMPARE( 2000.0, vertices.at( 1 ).y() ); + QCOMPARE( 2000.0, vertices.at( 2 ).y() ); + QCOMPARE( 3000.0, vertices.at( 3 ).y() ); + QCOMPARE( 3000.0, vertices.at( 4 ).y() ); QCOMPARE( 2, dp->faceCount() ); + const QVector faces = mesh.faces; QgsMeshFace f1; f1 << 0 << 1 << 3 << 4; - QCOMPARE( f1, dp->face( 0 ) ); + QCOMPARE( f1, faces.at( 0 ) ); QgsMeshFace f2; f2 << 1 << 2 << 3; - QCOMPARE( f2, dp->face( 1 ) ); + QCOMPARE( f2, faces.at( 1 ) ); } }