[FEATURE] Rendering of vector data on mesh layers

Rudimentary support of rendering of vector data (e.g. velocity) on mesh map layers.
Rendering can be adjusted by QgsMeshRenderer*Settings. Only in Python
API, no GUI widgets for styling present.
This commit is contained in:
Peter Petrik 2018-05-15 12:39:03 +02:00
parent 9296528822
commit aea3dccea7
26 changed files with 1645 additions and 231 deletions

View File

@ -36,19 +36,64 @@ support of nodata values
public:
QgsMeshDatasetValue( double x,
double y );
%Docstring
Ctor
%End
QgsMeshDatasetValue( double scalar );
%Docstring
Ctor
%End
QgsMeshDatasetValue( );
%Docstring
Ctor
%End
~QgsMeshDatasetValue();
void setNodata( bool nodata = true );
%Docstring
Sets nodata value for this dataset value
%End
bool isNodata() const;
%Docstring
Returns whether it is nodata value
%End
bool isScalar() const;
double scalar() const; //length for vectors, value for scalars
%Docstring
Returns whether it is scalar value
%End
double scalar() const;
%Docstring
Returns scalar value. Length for vectors, value for scalars
%End
void set( double scalar );
%Docstring
Sets scalar value
%End
void setX( double x );
%Docstring
Sets X value
%End
void setY( double y );
%Docstring
Sets Y value
%End
double x() const;
%Docstring
Returns x value
%End
double y() const;
%Docstring
Returns y value
%End
bool operator==( const QgsMeshDatasetValue &other ) const;
};
@ -78,14 +123,14 @@ read on demand
virtual int vertexCount() const = 0;
%Docstring
Return number of vertices in the native mesh
Returns number of vertices in the native mesh
:return: Number of vertices in the mesh
%End
virtual int faceCount() const = 0;
%Docstring
Return number of faces in the native mesh
Returns number of faces in the native mesh
:return: Number of faces in the mesh
%End
@ -94,7 +139,7 @@ read on demand
%Docstring
Factory for mesh vertex with index
:return: new mesh vertex on index
:return: New mesh vertex on index
%End
virtual QgsMeshFace face( int index ) const = 0;
@ -133,32 +178,32 @@ read on demand
virtual int datasetCount() const = 0;
%Docstring
Return number of datasets loaded
Returns number of datasets loaded
%End
virtual bool datasetHasScalarData( int index ) const = 0;
%Docstring
Whether dataset has scalar data associated
Returns whether dataset has scalar data associated
%End
virtual bool datasetIsOnVertices( int index ) const = 0;
%Docstring
Whether dataset is on vertices
Returns whether dataset is on vertices
%End
virtual QgsMeshDatasetMetadata datasetMetadata( int index ) const = 0;
%Docstring
Return dataset metadata
Returns dataset metadata
%End
virtual QgsMeshDatasetValue datasetValue( int datasetIndex, int valueIndex ) const = 0;
%Docstring
Return value associated with the index from the dataset
Returns value associated with the index from the dataset
%End
virtual bool datasetIsValid( int index ) const = 0;
%Docstring
Return whether dataset is valid
Returns whether dataset is valid
%End
};

View File

@ -52,8 +52,6 @@ E.g. to create mesh with one quad and one triangle
);
QgsMeshLayer *scratchLayer = new QgsMeshLayer(uri, "My Scratch layer", "memory_mesh");
Add datasets by adding them through data provider, :py:func:`QgsMeshDatasetSource.addDataset`
\subsection mdal MDAL data provider (mdal)
Accesses data using the MDAL drivers (https://github.com/lutraconsulting/MDAL). The url
@ -118,32 +116,68 @@ QgsMeshLayer cannot be copied.
QString providerType() const;
%Docstring
Return the provider type for this layer
Returns the provider type for this layer
%End
QgsSymbol *nativeMeshSymbol();
QgsMeshRendererMeshSettings rendererNativeMeshSettings() const /Factory/;
%Docstring
Returns a line symbol used for rendering native mesh.
Returns rendrer settings
%End
QgsSymbol *triangularMeshSymbol();
void setRendererNativeMeshSettings( const QgsMeshRendererMeshSettings &settings );
%Docstring
Returns a line symbol used for rendering of triangular (derived) mesh.
.. seealso:: :py:func:`toggleTriangularMeshRendering`
Sets new rendering settings, triggers repaint
%End
void toggleTriangularMeshRendering( bool toggle );
QgsMeshRendererMeshSettings rendererTriangularMeshSettings() const /Factory/;
%Docstring
Toggle rendering of triangular (derived) mesh. Off by default
Returns rendrer settings
%End
void setRendererTriangularMeshSettings( const QgsMeshRendererMeshSettings &settings );
%Docstring
Sets new rendering settings, triggers repaint
%End
QgsMeshRendererScalarSettings rendererScalarSettings() const /Factory/;
%Docstring
Returns rendrer settings
%End
void setRendererScalarSettings( const QgsMeshRendererScalarSettings &settings );
%Docstring
Sets new rendering settings, triggers repaint
%End
QgsMeshRendererVectorSettings rendererVectorSettings() const /Factory/;
%Docstring
Returns rendrer settings
%End
void setRendererVectorSettings( const QgsMeshRendererVectorSettings &settings );
%Docstring
Sets new rendering settings, triggers repaint
%End
void setActiveScalarDataset( int index = -1 );
void setActiveVectorDataset( int index = -1 );
%Docstring
Sets active scalar dataset for rendering
%End
int activeScalarDataset() const;
%Docstring
Returns active scalar dataset
%End
void setActiveVectorDataset( int index = -1 );
%Docstring
Sets active vector dataset for rendering. If dataset is not vector based, do nothing
%End
int activeVectorDataset() const;
%Docstring
Returns active vector dataset
%End
private: // Private methods
QgsMeshLayer( const QgsMeshLayer &rhs );

View File

@ -0,0 +1,282 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/mesh/qgsmeshrenderersettings.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsMeshRendererMeshSettings
{
%Docstring
Represents a mesh renderer settings for mesh object
.. note::
The API is considered EXPERIMENTAL and can be changed without a notice
.. versionadded:: 3.2
%End
%TypeHeaderCode
#include "qgsmeshrenderersettings.h"
%End
public:
bool isEnabled() const;
%Docstring
Returns whether mesh structure rendering is enabled
%End
void setEnabled( bool enabled );
%Docstring
Sets whether mesh structure rendering is enabled
%End
double lineWidth() const;
%Docstring
Returns line width used for rendering (in millimeters)
%End
void setLineWidth( double lineWidth );
%Docstring
Sets line width used for rendering (in millimeters)
%End
QColor color() const;
%Docstring
Returns color used for rendering
%End
void setColor( const QColor &color );
%Docstring
Sets color used for rendering of the mesh
%End
};
class QgsMeshRendererScalarSettings
{
%Docstring
Represents a mesh renderer settings for scalar datasets
.. note::
The API is considered EXPERIMENTAL and can be changed without a notice
.. versionadded:: 3.2
%End
%TypeHeaderCode
#include "qgsmeshrenderersettings.h"
%End
public:
QColor maxColor() const;
%Docstring
Returns color representing maximum scalar value in the dataset
%End
void setMaxColor( const QColor &maxColor );
%Docstring
Sets color representing maximum scalar value in the dataset
%End
QColor minColor() const;
%Docstring
Returns color representing minimum scalar value in the dataset
%End
void setMinColor( const QColor &minColor );
%Docstring
Sets color representing maximum scalar value in the dataset
%End
double minValue() const;
%Docstring
Returns min scalar value that represents minColor()
if set to numerical_limits<double>.quiet_NaN(), value for minColor() is
taken from minimum value of active scalar dataset
%End
void setMinValue( double minValue );
%Docstring
Sets min scalar value that represents minColor()
.. seealso:: :py:func:`QgsMeshRendererScalarSettings.minValue`
%End
double maxValue() const;
%Docstring
Returns max scalar value that represents maxColor()
if set to numerical_limits<double>.quiet_NaN(), value for maxColor() is
taken from maximum value of active scalar dataset
%End
void setMaxValue( double maxValue );
%Docstring
Sets min scalar value that represents minColor()
.. seealso:: :py:func:`QgsMeshRendererScalarSettings.maxValue`
%End
};
class QgsMeshRendererVectorSettings
{
%Docstring
Represents a mesh renderer settings for vector datasets
.. note::
The API is considered EXPERIMENTAL and can be changed without a notice
.. versionadded:: 3.2
%End
%TypeHeaderCode
#include "qgsmeshrenderersettings.h"
%End
public:
enum VectorRenderingType
{
MinMax,
Scaled,
Fixed
};
double lineWidth() const;
%Docstring
Returns line width of the arrow (in millimeters)
%End
void setLineWidth( double lineWidth );
%Docstring
Sets line width of the arrow in pixels (in millimeters)
%End
QColor color() const;
%Docstring
Returns color used for drawing arrows
%End
void setColor( const QColor &color );
%Docstring
Sets color used for drawing arrows
%End
double filterMin() const;
%Docstring
Returns filter value for vector magnitudes.
If magnitude of the vector is lower than this value, the vector is not
drawn. -1 represents that filtering is not active.
%End
void setFilterMin( double filterMin );
%Docstring
Sets filter value for vector magnitudes.
.. seealso:: :py:func:`QgsMeshRendererVectorSettings.vectorFilterMin`
%End
double filterMax() const;
%Docstring
Returns filter value for vector magnitudes.
If magnitude of the vector is higher than this value, the vector is not
drawn. -1 represents that filtering is not active.
%End
void setFilterMax( double filterMax );
%Docstring
Sets filter value for vector magnitudes.
.. seealso:: :py:func:`QgsMeshRendererVectorSettings.vectorFilterMax`
%End
QgsMeshRendererVectorSettings::VectorRenderingType shaftLengthMethod() const;
%Docstring
Returns method used for drawing arrows
%End
void setShaftLengthMethod( const QgsMeshRendererVectorSettings::VectorRenderingType &shaftLengthMethod );
%Docstring
Sets method used for drawing arrows
%End
double minShaftLength() const;
%Docstring
Returns mininimum shaft length (in millimeters)
Only for QgsMeshRendererVectorSettings.ArrowType.MinMax
%End
void setMinShaftLength( double minShaftLength );
%Docstring
Sets mininimum shaft length (in millimeters)
Only for QgsMeshRendererVectorSettings.ArrowType.MinMax
%End
double maxShaftLength() const;
%Docstring
Returns maximum shaft length (in millimeters)
Only for QgsMeshRendererVectorSettings.ArrowType.MinMax
%End
void setMaxShaftLength( double maxShaftLength );
%Docstring
Sets maximum shaft length (in millimeters)
Only for QgsMeshRendererVectorSettings.ArrowType.MinMax
%End
double scaleFactor() const;
%Docstring
Returns scale factor. Only for QgsMeshRendererVectorSettings.ArrowType.Scaled
%End
void setScaleFactor( double scaleFactor );
%Docstring
Sets scale factor. Only for QgsMeshRendererVectorSettings.ArrowType.Scaled
%End
double fixedShaftLength() const;
%Docstring
Returns fixed arrow length (in millimeters). Only for QgsMeshRendererVectorSettings.ArrowType.Fixed
%End
void setFixedShaftLength( double fixedShaftLength );
%Docstring
Sets fixed length (in millimeters). Only for QgsMeshRendererVectorSettings.ArrowType.Fixed
%End
double arrowHeadWidthRatio() const;
%Docstring
Returns ratio of the head width of the arrow (range 0-1)
%End
void setArrowHeadWidthRatio( double arrowHeadWidthRatio );
%Docstring
Sets ratio of the head width of the arrow (range 0-1)
%End
double arrowHeadLengthRatio() const;
%Docstring
Returns ratio of the head length of the arrow (range 0-1)
%End
void setArrowHeadLengthRatio( double arrowHeadLengthRatio );
%Docstring
Sets ratio of the head length of the arrow (range 0-1)
%End
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/mesh/qgsmeshrenderersettings.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -1,4 +1,3 @@
// Include auto-generated SIP files
%Include auto_generated/expression/qgsexpression.sip
%Include auto_generated/expression/qgsexpressionnode.sip
%Include auto_generated/expression/qgsexpressionnodeimpl.sip
@ -381,6 +380,7 @@
%Include auto_generated/raster/qgsrasterprojector.sip
%Include auto_generated/mesh/qgsmeshdataprovider.sip
%Include auto_generated/mesh/qgsmeshlayer.sip
%Include auto_generated/mesh/qgsmeshrenderersettings.sip
%Include auto_generated/geometry/qgsabstractgeometry.sip
%Include auto_generated/geometry/qgsgeometry.sip
%Include auto_generated/geometry/qgspoint.sip

View File

@ -454,6 +454,8 @@ SET(QGIS_CORE_SRCS
mesh/qgsmeshmemorydataprovider.cpp
mesh/qgstriangularmesh.cpp
mesh/qgsmeshlayerinterpolator.cpp
mesh/qgsmeshvectorrenderer.cpp
mesh/qgsmeshrenderersettings.cpp
geometry/qgsabstractgeometry.cpp
geometry/qgsbox3d.cpp
@ -1077,6 +1079,8 @@ SET(QGIS_CORE_HDRS
mesh/qgstriangularmesh.h
mesh/qgsmeshlayerrenderer.h
mesh/qgsmeshlayerinterpolator.h
mesh/qgsmeshvectorrenderer.h
mesh/qgsmeshrenderersettings.h
scalebar/qgsdoubleboxscalebarrenderer.h
scalebar/qgsnumericscalebarrenderer.h

View File

@ -52,21 +52,44 @@ class CORE_EXPORT QgsMeshDatasetValue
Q_GADGET
public:
//! Ctor
QgsMeshDatasetValue( double x,
double y );
//! Ctor
QgsMeshDatasetValue( double scalar );
//! Ctor
QgsMeshDatasetValue( ) = default;
//! Dtor
~QgsMeshDatasetValue() = default;
//! Sets nodata value for this dataset value
void setNodata( bool nodata = true );
//! Returns whether it is nodata value
bool isNodata() const;
//! Returns whether it is scalar value
bool isScalar() const;
double scalar() const; //length for vectors, value for scalars
//! Returns scalar value. Length for vectors, value for scalars
double scalar() const;
//! Sets scalar value
void set( double scalar );
//! Sets X value
void setX( double x );
//! Sets Y value
void setY( double y ) ;
//! Returns x value
double x() const;
//! Returns y value
double y() const;
//! Equals
bool operator==( const QgsMeshDatasetValue &other ) const;
private:
@ -96,20 +119,20 @@ class CORE_EXPORT QgsMeshSource SIP_ABSTRACT
virtual ~QgsMeshSource() = default;
/**
* \brief Return number of vertices in the native mesh
* \brief Returns number of vertices in the native mesh
* \returns Number of vertices in the mesh
*/
virtual int vertexCount() const = 0;
/**
* \brief Return number of faces in the native mesh
* \brief Returns number of faces in the native mesh
* \returns Number of faces in the mesh
*/
virtual int faceCount() const = 0;
/**
* \brief Factory for mesh vertex with index
* \returns new mesh vertex on index
* \returns New mesh vertex on index
*/
virtual QgsMeshVertex vertex( int index ) const = 0;
@ -143,32 +166,32 @@ class CORE_EXPORT QgsMeshDatasetSource SIP_ABSTRACT
virtual bool addDataset( const QString &uri ) = 0;
/**
* \brief Return number of datasets loaded
* \brief Returns number of datasets loaded
*/
virtual int datasetCount() const = 0;
/**
* \brief Whether dataset has scalar data associated
* \brief Returns whether dataset has scalar data associated
*/
virtual bool datasetHasScalarData( int index ) const = 0;
/**
* \brief Whether dataset is on vertices
* \brief Returns whether dataset is on vertices
*/
virtual bool datasetIsOnVertices( int index ) const = 0;
/**
* \brief Return dataset metadata
* \brief Returns dataset metadata
*/
virtual QgsMeshDatasetMetadata datasetMetadata( int index ) const = 0;
/**
* \brief Return value associated with the index from the dataset
* \brief Returns value associated with the index from the dataset
*/
virtual QgsMeshDatasetValue datasetValue( int datasetIndex, int valueIndex ) const = 0;
/**
* \brief Return whether dataset is valid
* \brief Returns whether dataset is valid
*/
virtual bool datasetIsValid( int index ) const = 0;
};

View File

@ -19,7 +19,7 @@
#include <QUuid>
#include "qgsfillsymbollayer.h"
#include "qgslogger.h"
#include "qgsmeshdataprovider.h"
#include "qgsmeshlayer.h"
@ -37,12 +37,8 @@ QgsMeshLayer::QgsMeshLayer( const QString &meshLayerPath,
// load data
setDataProvider( providerKey );
QgsSymbolLayerList l1;
l1 << new QgsSimpleFillSymbolLayer( Qt::white, Qt::NoBrush, Qt::black, Qt::SolidLine, 1.0 );
mNativeMeshSymbol.reset( new QgsFillSymbol( l1 ) );
toggleTriangularMeshRendering( false );
// show at least the mesh by default so we render something
mRendererNativeMeshSettings.setEnabled( true );
} // QgsMeshLayer ctor
@ -99,34 +95,53 @@ QgsTriangularMesh *QgsMeshLayer::triangularMesh() SIP_SKIP
return mTriangularMesh.get();
}
QgsSymbol *QgsMeshLayer::nativeMeshSymbol()
QgsMeshRendererMeshSettings QgsMeshLayer::rendererNativeMeshSettings() const
{
return mNativeMeshSymbol.get();
return mRendererNativeMeshSettings;
}
QgsSymbol *QgsMeshLayer::triangularMeshSymbol()
void QgsMeshLayer::setRendererNativeMeshSettings( const QgsMeshRendererMeshSettings &settings )
{
return mTriangularMeshSymbol.get();
}
void QgsMeshLayer::toggleTriangularMeshRendering( bool toggle )
{
if ( toggle && mTriangularMeshSymbol )
return;
if ( toggle )
{
QgsSymbolLayerList l2;
l2 << new QgsSimpleFillSymbolLayer( Qt::white, Qt::NoBrush, Qt::red, Qt::SolidLine, 0.26 );
mTriangularMeshSymbol.reset( new QgsFillSymbol( l2 ) );
}
else
{
mTriangularMeshSymbol.reset();
}
mRendererNativeMeshSettings = settings;
triggerRepaint();
}
QgsMeshRendererMeshSettings QgsMeshLayer::rendererTriangularMeshSettings() const
{
return mRendererTriangularMeshSettings;
}
void QgsMeshLayer::setRendererTriangularMeshSettings( const QgsMeshRendererMeshSettings &settings )
{
mRendererTriangularMeshSettings = settings;
triggerRepaint();
}
QgsMeshRendererScalarSettings QgsMeshLayer::rendererScalarSettings() const
{
return mRendererScalarSettings;
}
void QgsMeshLayer::setRendererScalarSettings( const QgsMeshRendererScalarSettings &settings )
{
mRendererScalarSettings = settings;
triggerRepaint();
}
QgsMeshRendererVectorSettings QgsMeshLayer::rendererVectorSettings() const
{
return mRendererVectorSettings;
}
void QgsMeshLayer::setRendererVectorSettings( const QgsMeshRendererVectorSettings &settings )
{
mRendererVectorSettings = settings;
triggerRepaint();
}
void QgsMeshLayer::setActiveScalarDataset( int index )
{
if ( index < 0 )
@ -136,8 +151,8 @@ void QgsMeshLayer::setActiveScalarDataset( int index )
}
Q_ASSERT( dataProvider()->datasetCount() > index );
if ( dataProvider()->datasetHasScalarData( index ) )
mActiveScalarDataset = index;
// for vector datasets, we render magnitude
mActiveScalarDataset = index;
}
void QgsMeshLayer::setActiveVectorDataset( int index )

View File

@ -24,6 +24,7 @@
#include "qgsmaplayer.h"
#include "qgsrendercontext.h"
#include "qgsmeshdataprovider.h"
#include "qgsmeshrenderersettings.h"
class QgsMapLayerRenderer;
class QgsSymbol;
@ -70,8 +71,6 @@ struct QgsMesh;
* QgsMeshLayer *scratchLayer = new QgsMeshLayer(uri, "My Scratch layer", "memory_mesh");
* \endcode
*
* Add datasets by adding them through data provider, \see QgsMeshDatasetSource::addDataset()
*
* \subsection mdal MDAL data provider (mdal)
*
* Accesses data using the MDAL drivers (https://github.com/lutraconsulting/MDAL). The url
@ -123,32 +122,49 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer
bool readXml( const QDomNode &layer_node, QgsReadWriteContext &context ) override;
bool writeXml( QDomNode &layer_node, QDomDocument &doc, const QgsReadWriteContext &context ) const override;
//! Return the provider type for this layer
//! Returns the provider type for this layer
QString providerType() const;
//! return native mesh (nullprt before rendering)
//! Returns native mesh (nullprt before rendering)
QgsMesh *nativeMesh() SIP_SKIP;
//! return triangular mesh (nullprt before rendering)
//! Returns triangular mesh (nullprt before rendering)
QgsTriangularMesh *triangularMesh() SIP_SKIP;
//! Returns a line symbol used for rendering native mesh.
QgsSymbol *nativeMeshSymbol();
//! Returns rendrer settings
QgsMeshRendererMeshSettings rendererNativeMeshSettings() const SIP_FACTORY;
/**
* Returns a line symbol used for rendering of triangular (derived) mesh.
* \see toggleTriangularMeshRendering
*/
QgsSymbol *triangularMeshSymbol();
//! Sets new rendering settings, triggers repaint
void setRendererNativeMeshSettings( const QgsMeshRendererMeshSettings &settings );
//! Toggle rendering of triangular (derived) mesh. Off by default
void toggleTriangularMeshRendering( bool toggle );
//! Returns rendrer settings
QgsMeshRendererMeshSettings rendererTriangularMeshSettings() const SIP_FACTORY;
//! Sets new rendering settings, triggers repaint
void setRendererTriangularMeshSettings( const QgsMeshRendererMeshSettings &settings );
//! Returns rendrer settings
QgsMeshRendererScalarSettings rendererScalarSettings() const SIP_FACTORY;
//! Sets new rendering settings, triggers repaint
void setRendererScalarSettings( const QgsMeshRendererScalarSettings &settings );
//! Returns rendrer settings
QgsMeshRendererVectorSettings rendererVectorSettings() const SIP_FACTORY;
//! Sets new rendering settings, triggers repaint
void setRendererVectorSettings( const QgsMeshRendererVectorSettings &settings );
//! Sets active scalar dataset for rendering
void setActiveScalarDataset( int index = -1 );
void setActiveVectorDataset( int index = -1 );
//! Returns active scalar dataset
int activeScalarDataset() const { return mActiveScalarDataset; }
//! Sets active vector dataset for rendering. If dataset is not vector based, do nothing
void setActiveVectorDataset( int index = -1 );
//! Returns active vector dataset
int activeVectorDataset() const { return mActiveVectorDataset; }
private: // Private methods
/**
@ -157,7 +173,7 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer
bool isReadOnly() const override {return true;}
/**
* Bind layer to a specific data provider
* Binds layer to a specific data provider
* \param provider provider key string, must match a valid QgsMeshDataProvider key. E.g. "mesh_memory", etc.
*/
bool setDataProvider( QString const &provider );
@ -182,16 +198,15 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer
//! Pointer to derived mesh structure
std::unique_ptr<QgsTriangularMesh> mTriangularMesh;
//! rendering native mesh
std::unique_ptr<QgsSymbol> mNativeMeshSymbol;
QgsMeshRendererMeshSettings mRendererNativeMeshSettings;
QgsMeshRendererMeshSettings mRendererTriangularMeshSettings;
QgsMeshRendererScalarSettings mRendererScalarSettings;
QgsMeshRendererVectorSettings mRendererVectorSettings;
//! rendering triangular mesh
std::unique_ptr<QgsSymbol> mTriangularMeshSymbol;
//! TODO
//! index of active scalar dataset; -1 if none
int mActiveScalarDataset = -1;
//! TODO
//! index of active vector dataset; -1 if none
int mActiveVectorDataset = -1;
};

View File

@ -21,66 +21,22 @@
#include "qgsrasterinterface.h"
#include <QVector2D>
#include "qgsmaptopixel.h"
// TODO: use QgsMapToPixel
class MapToPixel
static void bbox2rect(
const QgsMapToPixel &mtp,
const QSize &outputSize,
const QgsRectangle &bbox,
int &leftLim, int &rightLim, int &topLim, int &bottomLim )
{
public:
MapToPixel( double llX, double llY, double mupp, int rows )
: mLlX( llX ), mLlY( llY ), mMupp( mupp ), mRows( rows ) {}
MapToPixel( const MapToPixel &obj )
: mLlX( obj.mLlX ), mLlY( obj.mLlY ), mMupp( obj.mMupp ), mRows( obj.mRows ) {}
QPointF realToPixel( double rx, double ry ) const
{
double px = ( rx - mLlX ) / mMupp;
double py = mRows - ( ry - mLlY ) / mMupp;
return QPointF( px, py );
}
QPointF realToPixel( const QPointF &pt ) const
{
return realToPixel( pt.x(), pt.y() );
}
QPointF pixelToReal( double px, double py ) const
{
double rx = mLlX + ( px * mMupp );
double ry = mLlY + mMupp * ( mRows - py );
return QPointF( rx, ry );
}
QPointF pixelToReal( const QPointF &pt ) const
{
return pixelToReal( pt.x(), pt.y() );
}
private:
double mLlX, mLlY;
double mMupp; // map units per pixel
double mRows; // (actually integer value)
};
void bbox2rect( const MapToPixel &mtp, const QSize &outputSize, const QgsRectangle &bbox, int &leftLim, int &rightLim, int &topLim, int &bottomLim )
{
QPoint ll = mtp.realToPixel( bbox.xMinimum(), bbox.yMinimum() ).toPoint();
QPoint ur = mtp.realToPixel( bbox.xMaximum(), bbox.yMaximum() ).toPoint();
topLim = std::max( ur.y(), 0 );
bottomLim = std::min( ll.y(), outputSize.height() - 1 );
leftLim = std::max( ll.x(), 0 );
rightLim = std::min( ur.x(), outputSize.width() - 1 );
QgsPointXY ll = mtp.transform( bbox.xMinimum(), bbox.yMinimum() );
QgsPointXY ur = mtp.transform( bbox.xMaximum(), bbox.yMaximum() );
topLim = std::max( int( ur.y() ), 0 );
bottomLim = std::min( int( ll.y() ), outputSize.height() - 1 );
leftLim = std::max( int( ll.x() ), 0 );
rightLim = std::min( int( ur.x() ), outputSize.width() - 1 );
}
struct MapView
{
MapView(): mtp( 0, 0, 0, 0 ) {}
MapToPixel mtp;
QSize outputSize;
};
static void lam_tol( double &lam )
{
const static double eps = 1e-6;
@ -90,7 +46,7 @@ static void lam_tol( double &lam )
}
}
bool E3T_physicalToBarycentric( QPointF pA, QPointF pB, QPointF pC, QPointF pP, double &lam1, double &lam2, double &lam3 )
static bool E3T_physicalToBarycentric( QPointF pA, QPointF pB, QPointF pC, QPointF pP, double &lam1, double &lam2, double &lam3 )
{
if ( pA == pB || pA == pC || pB == pC )
return false; // this is not a valid triangle!
@ -148,24 +104,14 @@ double interpolateFromFacesData( const QPointF &p1, const QPointF &p2, const QPo
QgsMeshLayerInterpolator::QgsMeshLayerInterpolator( const QgsTriangularMesh &m,
const QVector<double> &datasetValues, bool dataIsOnVertices,
const QgsRenderContext &context )
const QgsRenderContext &context,
const QSize &size )
: mTriangularMesh( m ),
mDatasetValues( datasetValues ),
mContext( context ),
mDataOnVertices( dataIsOnVertices )
mDataOnVertices( dataIsOnVertices ),
mOutputSize( size )
{
// figure out image size
QgsRectangle extent = mContext.extent(); // this is extent in layer's coordinate system - but we need it in map coordinate system
QgsMapToPixel mapToPixel = mContext.mapToPixel();
// TODO: what if OTF reprojection is used - see crayfish layer_renderer.py (_calculate_extent)
QgsPointXY topleft = mapToPixel.transform( extent.xMinimum(), extent.yMaximum() );
QgsPointXY bottomright = mapToPixel.transform( extent.xMaximum(), extent.yMinimum() );
int width = bottomright.x() - topleft.x();
int height = bottomright.y() - topleft.y();
mMapView.reset( new MapView() );
mMapView->mtp = MapToPixel( extent.xMinimum(), extent.yMinimum(), mapToPixel.mapUnitsPerPixel(), height );
mMapView->outputSize = QSize( width, height );
}
QgsMeshLayerInterpolator::~QgsMeshLayerInterpolator() = default;
@ -177,7 +123,7 @@ QgsRasterInterface *QgsMeshLayerInterpolator::clone() const
Qgis::DataType QgsMeshLayerInterpolator::dataType( int ) const
{
return Qgis::Float32;
return Qgis::Float64;
}
int QgsMeshLayerInterpolator::bandCount() const
@ -187,9 +133,9 @@ int QgsMeshLayerInterpolator::bandCount() const
QgsRasterBlock *QgsMeshLayerInterpolator::block( int, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback )
{
QgsRasterBlock *b = new QgsRasterBlock( Qgis::Float32, width, height );
QgsRasterBlock *b = new QgsRasterBlock( Qgis::Float64, width, height );
b->setIsNoData(); // assume initially that all values are unset
float *data = reinterpret_cast<float *>( b->bits() );
double *data = reinterpret_cast<double *>( b->bits() );
const QVector<QgsMeshFace> &triangels = mTriangularMesh.triangles();
const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
@ -217,16 +163,16 @@ QgsRasterBlock *QgsMeshLayerInterpolator::block( int, const QgsRectangle &extent
// Get the BBox of the element in pixels
int topLim, bottomLim, leftLim, rightLim;
bbox2rect( mMapView->mtp, mMapView->outputSize, bbox, leftLim, rightLim, topLim, bottomLim );
bbox2rect( mContext.mapToPixel(), mOutputSize, bbox, leftLim, rightLim, topLim, bottomLim );
// interpolate in the bounding box of the face
for ( int j = topLim; j <= bottomLim; j++ )
{
float *line = data + ( j * width );
double *line = data + ( j * width );
for ( int k = leftLim; k <= rightLim; k++ )
{
double val;
QPointF p = mMapView->mtp.pixelToReal( k, j );
QPointF p = mContext.mapToPixel().toMapCoordinates( k, j ).toQPointF();
if ( mDataOnVertices )
val = interpolateFromVerticesData(
QPointF( p1.x(), p1.y() ),

View File

@ -24,6 +24,7 @@ class QgsSymbol;
#define SIP_NO_FILE
#include <memory>
#include <QSize>
#include "qgis.h"
@ -37,8 +38,6 @@ class QgsSymbol;
///@cond PRIVATE
struct MapView;
/**
* \ingroup core
* Interpolate mesh scalar dataset to raster block
@ -49,10 +48,12 @@ struct MapView;
class QgsMeshLayerInterpolator : public QgsRasterInterface
{
public:
//! Ctor
QgsMeshLayerInterpolator( const QgsTriangularMesh &m,
const QVector<double> &datasetValues,
bool dataIsOnVertices,
const QgsRenderContext &context );
const QgsRenderContext &context,
const QSize &size );
~QgsMeshLayerInterpolator() override;
virtual QgsRasterInterface *clone() const override;
@ -64,8 +65,8 @@ class QgsMeshLayerInterpolator : public QgsRasterInterface
const QgsTriangularMesh &mTriangularMesh;
const QVector<double> &mDatasetValues;
const QgsRenderContext &mContext;
std::unique_ptr<MapView> mMapView;
bool mDataOnVertices;
QSize mOutputSize;
};
///@endcond

View File

@ -27,10 +27,19 @@
#include "qgssinglebandpseudocolorrenderer.h"
#include "qgsrastershader.h"
#include "qgsmeshlayerinterpolator.h"
#include "qgsmeshvectorrenderer.h"
#include "qgsfillsymbollayer.h"
QgsMeshLayerRenderer::QgsMeshLayerRenderer( QgsMeshLayer *layer, QgsRenderContext &context )
: QgsMapLayerRenderer( layer->id() )
, mFeedback( new QgsMeshLayerRendererFeedback )
, mContext( context )
, mRendererNativeMeshSettings( layer->rendererNativeMeshSettings() )
, mRendererTriangularMeshSettings( layer->rendererTriangularMeshSettings() )
, mRendererScalarSettings( layer-> rendererScalarSettings() )
, mRendererVectorSettings( layer-> rendererVectorSettings() )
{
// make copies for mesh data
Q_ASSERT( layer->nativeMesh() );
@ -38,18 +47,45 @@ QgsMeshLayerRenderer::QgsMeshLayerRenderer( QgsMeshLayer *layer, QgsRenderContex
mNativeMesh = *( layer->nativeMesh() );
mTriangularMesh = *( layer->triangularMesh() );
// make copies for symbols
if ( layer->nativeMeshSymbol() )
{
mNativeMeshSymbol.reset( layer->nativeMeshSymbol()->clone() );
}
if ( layer->triangularMeshSymbol() )
{
mTriangularMeshSymbol.reset( layer->triangularMeshSymbol()->clone() );
}
createMeshSymbol( mNativeMeshSymbol, mRendererNativeMeshSettings );
createMeshSymbol( mTriangularMeshSymbol, mRendererTriangularMeshSettings );
copyScalarDatasetValues( layer );
copyVectorDatasetValues( layer );
calculateOutputSize();
}
QgsFeedback *QgsMeshLayerRenderer::feedback() const
{
return mFeedback.get();
}
void QgsMeshLayerRenderer::calculateOutputSize()
{
// figure out image size
QgsRectangle extent = mContext.extent(); // this is extent in layer's coordinate system - but we need it in map coordinate system
QgsMapToPixel mapToPixel = mContext.mapToPixel();
QgsPointXY topleft = mapToPixel.transform( extent.xMinimum(), extent.yMaximum() );
QgsPointXY bottomright = mapToPixel.transform( extent.xMaximum(), extent.yMinimum() );
int width = int( bottomright.x() - topleft.x() );
int height = int( bottomright.y() - topleft.y() );
mOutputSize = QSize( width, height );
}
void QgsMeshLayerRenderer::createMeshSymbol( std::unique_ptr<QgsSymbol> &symbol,
const QgsMeshRendererMeshSettings &settings )
{
if ( settings.isEnabled() )
{
QgsSymbolLayerList l1;
l1 << new QgsSimpleFillSymbolLayer( Qt::white,
Qt::NoBrush,
settings.color(),
Qt::SolidLine,
settings.lineWidth() );
symbol.reset( new QgsFillSymbol( l1 ) );
}
}
@ -58,39 +94,69 @@ void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer )
int datasetIndex = layer->activeScalarDataset();
if ( datasetIndex != -1 )
{
mDataOnVertices = layer->dataProvider()->datasetIsOnVertices( datasetIndex );
if ( mDataOnVertices )
mScalarDataOnVertices = layer->dataProvider()->datasetIsOnVertices( datasetIndex );
int count;
if ( mScalarDataOnVertices )
count = mNativeMesh.vertices.count();
else
count = mNativeMesh.faces.count();
mScalarDatasetValues.resize( count );
for ( int i = 0; i < count; ++i )
{
int count = mNativeMesh.vertices.count();
mDatasetValues.resize( count );
for ( int i = 0; i < count; ++i )
{
double v = layer->dataProvider()->datasetValue( datasetIndex, i ).scalar();
mDatasetValues[i] = v;
}
double v = layer->dataProvider()->datasetValue( datasetIndex, i ).scalar();
mScalarDatasetValues[i] = v;
}
}
}
void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer )
{
int datasetIndex = layer->activeVectorDataset();
if ( datasetIndex != -1 )
{
bool isScalar = layer->dataProvider()->datasetHasScalarData( datasetIndex );
if ( isScalar )
{
QgsDebugMsg( "Dataset has no vector values" );
}
else
{
//on faces
int count = mNativeMesh.faces.count();
mDatasetValues.resize( count );
mVectorDataOnVertices = layer->dataProvider()->datasetIsOnVertices( datasetIndex );
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 v = layer->dataProvider()->datasetValue( datasetIndex, i ).scalar();
mDatasetValues[i] = v;
double x = layer->dataProvider()->datasetValue( datasetIndex, i ).x();
mVectorDatasetValuesX[i] = x;
double y = layer->dataProvider()->datasetValue( datasetIndex, i ).y();
mVectorDatasetValuesY[i] = y;
double mag = layer->dataProvider()->datasetValue( datasetIndex, i ).scalar();
mVectorDatasetValuesMag[i] = mag;
}
}
}
}
bool QgsMeshLayerRenderer::render()
{
renderScalarDataset();
renderMesh( mNativeMeshSymbol, mNativeMesh.faces ); // native mesh
renderMesh( mTriangularMeshSymbol, mTriangularMesh.triangles() ); // triangular mesh
renderVectorDataset();
return true;
}
@ -131,37 +197,30 @@ void QgsMeshLayerRenderer::renderMesh( const std::unique_ptr<QgsSymbol> &symbol,
void QgsMeshLayerRenderer::renderScalarDataset()
{
if ( mDatasetValues.isEmpty() )
if ( mScalarDatasetValues.isEmpty() )
return;
// figure out image size
QgsRectangle extent = mContext.extent(); // this is extent in layer's coordinate system - but we need it in map coordinate system
QgsMapToPixel mapToPixel = mContext.mapToPixel();
// TODO: what if OTF reprojection is used - see crayfish layer_renderer.py (_calculate_extent)
QgsPointXY topleft = mapToPixel.transform( extent.xMinimum(), extent.yMaximum() );
QgsPointXY bottomright = mapToPixel.transform( extent.xMaximum(), extent.yMinimum() );
int width = bottomright.x() - topleft.x();
int height = bottomright.y() - topleft.y();
// do not render if magnitude is outside of the filtered range (if filtering is enabled)
double vMin = mRendererScalarSettings.minValue();
if ( std::isnan( vMin ) )
vMin = *std::min_element( mScalarDatasetValues.constBegin(), mScalarDatasetValues.constEnd() );
double vMin = mDatasetValues[0], vMax = mDatasetValues[0];
for ( int i = 1; i < mDatasetValues.count(); ++i )
{
double v = mDatasetValues[i];
if ( v < vMin ) vMin = v;
if ( v > vMax ) vMax = v;
}
double vMax = mRendererScalarSettings.maxValue();
if ( std::isnan( vMax ) )
vMax = *std::max_element( mScalarDatasetValues.constBegin(), mScalarDatasetValues.constEnd() );
QList<QgsColorRampShader::ColorRampItem> lst;
lst << QgsColorRampShader::ColorRampItem( vMin, Qt::red, QString::number( vMin ) );
lst << QgsColorRampShader::ColorRampItem( vMax, Qt::blue, QString::number( vMax ) );
lst << QgsColorRampShader::ColorRampItem( vMin, mRendererScalarSettings.minColor(), QString::number( vMin ) );
lst << QgsColorRampShader::ColorRampItem( vMax, mRendererScalarSettings.maxColor(), QString::number( vMax ) );
QgsColorRampShader *fcn = new QgsColorRampShader( vMin, vMax );
fcn->setColorRampItemList( lst );
QgsRasterShader *sh = new QgsRasterShader( 0, 1000 );
QgsRasterShader *sh = new QgsRasterShader( vMin, vMax );
sh->setRasterShaderFunction( fcn ); // takes ownership of fcn
QgsMeshLayerInterpolator interpolator( mTriangularMesh, mDatasetValues, mDataOnVertices, mContext );
QgsMeshLayerInterpolator interpolator( mTriangularMesh, mScalarDatasetValues, mScalarDataOnVertices, mContext, mOutputSize );
QgsSingleBandPseudoColorRenderer r( &interpolator, 0, sh ); // takes ownership of sh
QgsRasterBlock *bl = r.block( 0, extent, width, height ); // TODO: feedback
QgsRasterBlock *bl = r.block( 0, mContext.extent(), mOutputSize.width(), mOutputSize.height(), mFeedback.get() );
Q_ASSERT( bl );
QImage img = bl->image();
@ -169,3 +228,18 @@ void QgsMeshLayerRenderer::renderScalarDataset()
mContext.painter()->drawImage( 0, 0, img );
delete bl;
}
void QgsMeshLayerRenderer::renderVectorDataset()
{
if ( mVectorDatasetValuesX.isEmpty() )
return;
if ( mVectorDatasetValuesX.size() != mVectorDatasetValuesY.size() )
return;
QgsMeshVectorRenderer renderer( mTriangularMesh,
mVectorDatasetValuesX, mVectorDatasetValuesY, mVectorDatasetValuesMag,
mVectorDataOnVertices, mRendererVectorSettings, mContext, mOutputSize );
renderer.draw();
}

View File

@ -28,8 +28,24 @@ class QgsSymbol;
#include "qgis.h"
#include "qgsmaplayerrenderer.h"
#include "qgsrasterinterface.h"
#include "qgsrendercontext.h"
#include "qgstriangularmesh.h"
#include "qgsmeshlayer.h"
#include "qgssymbol.h"
///@cond PRIVATE
/**
* Feedback for mesh layer rendering - right now derived from raster block feedback so that we
* can pass it to block reading in the raster interface.
*/
class QgsMeshLayerRendererFeedback : public QgsRasterBlockFeedback
{
};
///@endcond
/**
* \ingroup core
@ -43,16 +59,23 @@ class QgsMeshLayerRenderer : public QgsMapLayerRenderer
public:
//! Ctor
QgsMeshLayerRenderer( QgsMeshLayer *layer, QgsRenderContext &context );
~QgsMeshLayerRenderer() override = default;
QgsFeedback *feedback() const override;
bool render() override;
private:
void renderMesh( const std::unique_ptr<QgsSymbol> &symbol, const QVector<QgsMeshFace> &faces );
void renderScalarDataset();
void renderVectorDataset();
void copyScalarDatasetValues( QgsMeshLayer *layer );
void copyVectorDatasetValues( QgsMeshLayer *layer );
void createMeshSymbol( std::unique_ptr<QgsSymbol> &symbol, const QgsMeshRendererMeshSettings &settings );
void calculateOutputSize();
protected:
//! feedback class for cancelation
std::unique_ptr<QgsMeshLayerRendererFeedback> mFeedback;
// copy from mesh layer
QgsMesh mNativeMesh;
@ -60,8 +83,14 @@ class QgsMeshLayerRenderer : public QgsMapLayerRenderer
QgsTriangularMesh mTriangularMesh;
// copy of the scalar dataset
QVector<double> mDatasetValues;
bool mDataOnVertices;
QVector<double> mScalarDatasetValues;
bool mScalarDataOnVertices;
// copy of the vector dataset
QVector<double> mVectorDatasetValuesX;
QVector<double> mVectorDatasetValuesY;
QVector<double> mVectorDatasetValuesMag;
bool mVectorDataOnVertices;
// copy from mesh layer
std::unique_ptr<QgsSymbol> mNativeMeshSymbol = nullptr;
@ -71,6 +100,15 @@ class QgsMeshLayerRenderer : public QgsMapLayerRenderer
// rendering context
QgsRenderContext &mContext;
// copy of rendering settings
QgsMeshRendererMeshSettings mRendererNativeMeshSettings;
QgsMeshRendererMeshSettings mRendererTriangularMeshSettings;
QgsMeshRendererScalarSettings mRendererScalarSettings;
QgsMeshRendererVectorSettings mRendererVectorSettings;
// output screen size
QSize mOutputSize;
};

View File

@ -87,7 +87,7 @@ class QgsMeshMemoryDataProvider: public QgsMeshDataProvider
/**
* Add dataset to a mesh in-memory data provider from data string
* Adds dataset to a mesh in-memory data provider from data string
*
* Data string constains simple definition of datasets
* Each entry is separated by "\n" sign and section deliminer "---"

View File

@ -0,0 +1,198 @@
/***************************************************************************
qgsmeshrenderersettings.cpp
---------------------------
begin : May 2018
copyright : (C) 2018 by Peter Petrik
email : zilolv at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsmeshrenderersettings.h"
bool QgsMeshRendererMeshSettings::isEnabled() const
{
return mEnabled;
}
void QgsMeshRendererMeshSettings::setEnabled( bool on )
{
mEnabled = on;
}
double QgsMeshRendererMeshSettings::lineWidth() const
{
return mLineWidth;
}
void QgsMeshRendererMeshSettings::setLineWidth( double lineWidth )
{
mLineWidth = lineWidth;
}
QColor QgsMeshRendererMeshSettings::color() const
{
return mColor;
}
void QgsMeshRendererMeshSettings::setColor( const QColor &color )
{
mColor = color;
}
QColor QgsMeshRendererScalarSettings::maxColor() const
{
return mMaxColor;
}
void QgsMeshRendererScalarSettings::setMaxColor( const QColor &maxColor )
{
mMaxColor = maxColor;
}
QColor QgsMeshRendererScalarSettings::minColor() const
{
return mMinColor;
}
void QgsMeshRendererScalarSettings::setMinColor( const QColor &minColor )
{
mMinColor = minColor;
}
double QgsMeshRendererScalarSettings::minValue() const
{
return mMinValue;
}
void QgsMeshRendererScalarSettings::setMinValue( double minValue )
{
mMinValue = minValue;
}
double QgsMeshRendererScalarSettings::maxValue() const
{
return mMaxValue;
}
void QgsMeshRendererScalarSettings::setMaxValue( double maxValue )
{
mMaxValue = maxValue;
}
double QgsMeshRendererVectorSettings::lineWidth() const
{
return mLineWidth;
}
void QgsMeshRendererVectorSettings::setLineWidth( double lineWidth )
{
mLineWidth = lineWidth;
}
QColor QgsMeshRendererVectorSettings::color() const
{
return mColor;
}
void QgsMeshRendererVectorSettings::setColor( const QColor &vectorColor )
{
mColor = vectorColor;
}
double QgsMeshRendererVectorSettings::filterMin() const
{
return mFilterMin;
}
void QgsMeshRendererVectorSettings::setFilterMin( double vectorFilterMin )
{
mFilterMin = vectorFilterMin;
}
double QgsMeshRendererVectorSettings::filterMax() const
{
return mFilterMax;
}
void QgsMeshRendererVectorSettings::setFilterMax( double vectorFilterMax )
{
mFilterMax = vectorFilterMax;
}
QgsMeshRendererVectorSettings::VectorRenderingType QgsMeshRendererVectorSettings::shaftLengthMethod() const
{
return mShaftLengthMethod;
}
void QgsMeshRendererVectorSettings::setShaftLengthMethod( const QgsMeshRendererVectorSettings::VectorRenderingType &shaftLengthMethod )
{
mShaftLengthMethod = shaftLengthMethod;
}
double QgsMeshRendererVectorSettings::minShaftLength() const
{
return mMinShaftLength;
}
void QgsMeshRendererVectorSettings::setMinShaftLength( double minShaftLength )
{
mMinShaftLength = minShaftLength;
}
double QgsMeshRendererVectorSettings::maxShaftLength() const
{
return mMaxShaftLength;
}
void QgsMeshRendererVectorSettings::setMaxShaftLength( double maxShaftLength )
{
mMaxShaftLength = maxShaftLength;
}
double QgsMeshRendererVectorSettings::scaleFactor() const
{
return mScaleFactor;
}
void QgsMeshRendererVectorSettings::setScaleFactor( double scaleFactor )
{
mScaleFactor = scaleFactor;
}
double QgsMeshRendererVectorSettings::fixedShaftLength() const
{
return mFixedShaftLength;
}
void QgsMeshRendererVectorSettings::setFixedShaftLength( double fixedShaftLength )
{
mFixedShaftLength = fixedShaftLength;
}
double QgsMeshRendererVectorSettings::arrowHeadWidthRatio() const
{
return mArrowHeadWidthRatio;
}
void QgsMeshRendererVectorSettings::setArrowHeadWidthRatio( double vectorHeadWidthRatio )
{
mArrowHeadWidthRatio = vectorHeadWidthRatio;
}
double QgsMeshRendererVectorSettings::arrowHeadLengthRatio() const
{
return mArrowHeadLengthRatio;
}
void QgsMeshRendererVectorSettings::setArrowHeadLengthRatio( double vectorHeadLengthRatio )
{
mArrowHeadLengthRatio = vectorHeadLengthRatio;
}

View File

@ -0,0 +1,254 @@
/***************************************************************************
qgsmeshrenderersettings.h
-------------------------
begin : May 2018
copyright : (C) 2018 by Peter Petrik
email : zilolv at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSMESHRENDERERSETTINGS_H
#define QGSMESHRENDERERSETTINGS_H
#include <QColor>
#include <limits>
#include "qgis_core.h"
#include "qgis.h"
/**
* \ingroup core
*
* Represents a mesh renderer settings for mesh object
*
* \note The API is considered EXPERIMENTAL and can be changed without a notice
*
* \since QGIS 3.2
*/
class CORE_EXPORT QgsMeshRendererMeshSettings
{
public:
//! Returns whether mesh structure rendering is enabled
bool isEnabled() const;
//! Sets whether mesh structure rendering is enabled
void setEnabled( bool enabled );
//! Returns line width used for rendering (in millimeters)
double lineWidth() const;
//! Sets line width used for rendering (in millimeters)
void setLineWidth( double lineWidth );
//! Returns color used for rendering
QColor color() const;
//! Sets color used for rendering of the mesh
void setColor( const QColor &color );
private:
bool mEnabled = false;
double mLineWidth = DEFAULT_LINE_WIDTH;
QColor mColor = Qt::black;
};
/**
* \ingroup core
*
* Represents a mesh renderer settings for scalar datasets
*
* \note The API is considered EXPERIMENTAL and can be changed without a notice
*
* \since QGIS 3.2
*/
class CORE_EXPORT QgsMeshRendererScalarSettings
{
public:
//! Returns color representing maximum scalar value in the dataset
QColor maxColor() const;
//! Sets color representing maximum scalar value in the dataset
void setMaxColor( const QColor &maxColor );
//! Returns color representing minimum scalar value in the dataset
QColor minColor() const;
//! Sets color representing maximum scalar value in the dataset
void setMinColor( const QColor &minColor );
/**
* Returns min scalar value that represents minColor()
*
* if set to numerical_limits<double>::quiet_NaN(), value for minColor() is
* taken from minimum value of active scalar dataset
*/
double minValue() const;
/**
* Sets min scalar value that represents minColor()
* \see QgsMeshRendererScalarSettings::minValue()
*/
void setMinValue( double minValue );
/**
* Returns max scalar value that represents maxColor()
*
* if set to numerical_limits<double>::quiet_NaN(), value for maxColor() is
* taken from maximum value of active scalar dataset
*/
double maxValue() const;
/**
* Sets min scalar value that represents minColor()
* \see QgsMeshRendererScalarSettings::maxValue()
*/
void setMaxValue( double maxValue );
private:
QColor mMaxColor = QColor::fromRgb( 255, 0, 0 );
QColor mMinColor = QColor::fromRgb( 0, 0, 255 );
double mMaxValue = std::numeric_limits<double>::quiet_NaN(); //disabled
double mMinValue = std::numeric_limits<double>::quiet_NaN(); //disabled
};
/**
* \ingroup core
*
* Represents a mesh renderer settings for vector datasets
*
* \note The API is considered EXPERIMENTAL and can be changed without a notice
*
* \since QGIS 3.2
*/
class CORE_EXPORT QgsMeshRendererVectorSettings
{
public:
//! Algorithm how to transform vector magnitude to length of arrow on the device in pixels
enum VectorRenderingType
{
/**
* Scale vector magnitude linearly to fit in range of vectorFilterMin() and vectorFilterMax()
*/
MinMax = 0,
/**
* Scale vector magnitude by factor scaleFactor()
*/
Scaled,
/**
* Use fixed length fixedShaftLength() regardless of vector's magnitude
*/
Fixed
};
//! Returns line width of the arrow (in millimeters)
double lineWidth() const;
//! Sets line width of the arrow in pixels (in millimeters)
void setLineWidth( double lineWidth );
//! Returns color used for drawing arrows
QColor color() const;
//! Sets color used for drawing arrows
void setColor( const QColor &color );
/**
* Returns filter value for vector magnitudes.
*
* If magnitude of the vector is lower than this value, the vector is not
* drawn. -1 represents that filtering is not active.
*/
double filterMin() const;
/**
* Sets filter value for vector magnitudes.
* \see QgsMeshRendererVectorSettings::vectorFilterMin()
*/
void setFilterMin( double filterMin );
/**
* Returns filter value for vector magnitudes.
*
* If magnitude of the vector is higher than this value, the vector is not
* drawn. -1 represents that filtering is not active.
*/
double filterMax() const;
/**
* Sets filter value for vector magnitudes.
* \see QgsMeshRendererVectorSettings::vectorFilterMax()
*/
void setFilterMax( double filterMax );
//! Returns method used for drawing arrows
QgsMeshRendererVectorSettings::VectorRenderingType shaftLengthMethod() const;
//! Sets method used for drawing arrows
void setShaftLengthMethod( const QgsMeshRendererVectorSettings::VectorRenderingType &shaftLengthMethod );
/**
* Returns mininimum shaft length (in millimeters)
*
* Only for QgsMeshRendererVectorSettings::ArrowType::MinMax
*/
double minShaftLength() const;
/**
* Sets mininimum shaft length (in millimeters)
*
* Only for QgsMeshRendererVectorSettings::ArrowType::MinMax
*/
void setMinShaftLength( double minShaftLength );
/**
* Returns maximum shaft length (in millimeters)
*
* Only for QgsMeshRendererVectorSettings::ArrowType::MinMax
*/
double maxShaftLength() const;
/**
* Sets maximum shaft length (in millimeters)
*
* Only for QgsMeshRendererVectorSettings::ArrowType::MinMax
*/
void setMaxShaftLength( double maxShaftLength );
//! Returns scale factor. Only for QgsMeshRendererVectorSettings::ArrowType::Scaled
double scaleFactor() const;
//! Sets scale factor. Only for QgsMeshRendererVectorSettings::ArrowType::Scaled
void setScaleFactor( double scaleFactor );
//! Returns fixed arrow length (in millimeters). Only for QgsMeshRendererVectorSettings::ArrowType::Fixed
double fixedShaftLength() const;
//! Sets fixed length (in millimeters). Only for QgsMeshRendererVectorSettings::ArrowType::Fixed
void setFixedShaftLength( double fixedShaftLength );
//! Returns ratio of the head width of the arrow (range 0-1)
double arrowHeadWidthRatio() const;
//! Sets ratio of the head width of the arrow (range 0-1)
void setArrowHeadWidthRatio( double arrowHeadWidthRatio );
//! Returns ratio of the head length of the arrow (range 0-1)
double arrowHeadLengthRatio() const;
//! Sets ratio of the head length of the arrow (range 0-1)
void setArrowHeadLengthRatio( double arrowHeadLengthRatio );
private:
double mLineWidth = DEFAULT_LINE_WIDTH; //in milimeters
QColor mColor = Qt::black;
double mFilterMin = -1; //disabled
double mFilterMax = -1; //disabled
QgsMeshRendererVectorSettings::VectorRenderingType mShaftLengthMethod = QgsMeshRendererVectorSettings::VectorRenderingType::MinMax;
double mMinShaftLength = 0.8; //in milimeters
double mMaxShaftLength = 10; //in milimeters
double mScaleFactor = 10;
double mFixedShaftLength = 20; //in milimeters
double mArrowHeadWidthRatio = 0.15;
double mArrowHeadLengthRatio = 0.40;
};
#endif //QGSMESHRENDERERSETTINGS_H

View File

@ -0,0 +1,264 @@
/***************************************************************************
qgsmeshvectorrenderer.cpp
-------------------------
begin : May 2018
copyright : (C) 2018 by Peter Petrik
email : zilolv at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsmeshvectorrenderer.h"
#include "qgsrendercontext.h"
#include "qgscoordinatetransform.h"
#include "qgsmaptopixel.h"
#include "qgsunittypes.h"
#include <stdlib.h>
#include <time.h>
#include <algorithm>
#include <QPen>
#include <QPainter>
#include <math.h>
inline double mag( double input )
{
if ( input < 0.0 )
{
return -1.0;
}
return 1.0;
}
QgsMeshVectorRenderer::QgsMeshVectorRenderer( const QgsTriangularMesh &m,
const QVector<double> &datasetValuesX,
const QVector<double> &datasetValuesY,
const QVector<double> &datasetValuesMag,
bool dataIsOnVertices,
const QgsMeshRendererVectorSettings &settings,
QgsRenderContext &context, const QSize &size )
: mTriangularMesh( m )
, mDatasetValuesX( datasetValuesX )
, mDatasetValuesY( datasetValuesY )
, mDatasetValuesMag( datasetValuesMag )
, mContext( context )
, mCfg( settings )
, mDataOnVertices( dataIsOnVertices )
, mOutputSize( size )
{
mMinX = *std::min_element( datasetValuesX.constBegin(), datasetValuesX.constEnd() );
mMaxX = *std::max_element( datasetValuesX.constBegin(), datasetValuesX.constEnd() );
mMinY = *std::min_element( datasetValuesY.constBegin(), datasetValuesY.constEnd() );
mMaxY = *std::max_element( datasetValuesY.constBegin(), datasetValuesY.constEnd() );
mMinMag = *std::min_element( mDatasetValuesMag.constBegin(), mDatasetValuesMag.constEnd() );
mMaxMag = *std::max_element( mDatasetValuesMag.constBegin(), mDatasetValuesMag.constEnd() );
}
QgsMeshVectorRenderer::~QgsMeshVectorRenderer() = default;
void QgsMeshVectorRenderer::draw()
{
// Set up the render configuration options
QPainter *p = mContext.painter();
p->setRenderHint( QPainter::Antialiasing );
QPen pen = p->pen();
pen.setCapStyle( Qt::FlatCap );
pen.setJoinStyle( Qt::MiterJoin );
double penWidth = mContext.convertToPainterUnits( mCfg.lineWidth(),
QgsUnitTypes::RenderUnit::RenderMillimeters );
pen.setWidthF( penWidth );
pen.setColor( mCfg.color() );
p->setPen( pen );
if ( mDataOnVertices )
drawVectorDataOnVertices();
else
drawVectorDataOnFaces();
}
bool QgsMeshVectorRenderer::calcVectorLineEnd(
QgsPointXY &lineEnd,
double &vectorLength,
double &cosAlpha,
double &sinAlpha, //out
const QgsPointXY &lineStart,
double xVal,
double yVal,
double magnitude //in
)
{
// return true on error
if ( xVal == 0.0 && yVal == 0.0 )
return true;
// do not render if magnitude is outside of the filtered range (if filtering is enabled)
if ( mCfg.filterMin() >= 0 && magnitude < mCfg.filterMin() )
return true;
if ( mCfg.filterMax() >= 0 && magnitude > mCfg.filterMax() )
return true;
// Determine the angle of the vector, counter-clockwise, from east
// (and associated trigs)
double vectorAngle = -1.0 * atan( ( -1.0 * yVal ) / xVal );
cosAlpha = cos( vectorAngle ) * mag( xVal );
sinAlpha = sin( vectorAngle ) * mag( xVal );
// Now determine the X and Y distances of the end of the line from the start
double xDist = 0.0;
double yDist = 0.0;
switch ( mCfg.shaftLengthMethod() )
{
case QgsMeshRendererVectorSettings::VectorRenderingType::MinMax:
{
double minShaftLength = mContext.convertToPainterUnits( mCfg.minShaftLength(),
QgsUnitTypes::RenderUnit::RenderMillimeters );
double maxShaftLength = mContext.convertToPainterUnits( mCfg.maxShaftLength(),
QgsUnitTypes::RenderUnit::RenderMillimeters );
double minVal = mMinMag;
double maxVal = mMaxMag;
double k = ( magnitude - minVal ) / ( maxVal - minVal );
double L = minShaftLength + k * ( maxShaftLength - minShaftLength );
xDist = cosAlpha * L;
yDist = sinAlpha * L;
break;
}
case QgsMeshRendererVectorSettings::VectorRenderingType::Scaled:
{
double scaleFactor = mCfg.scaleFactor();
xDist = scaleFactor * xVal;
yDist = scaleFactor * yVal;
break;
}
case QgsMeshRendererVectorSettings::VectorRenderingType::Fixed:
{
// We must be using a fixed length
double fixedShaftLength = mContext.convertToPainterUnits( mCfg.fixedShaftLength(),
QgsUnitTypes::RenderUnit::RenderMillimeters );
xDist = cosAlpha * fixedShaftLength;
yDist = sinAlpha * fixedShaftLength;
break;
}
}
// Flip the Y axis (pixel vs real-world axis)
yDist *= -1.0;
if ( qAbs( xDist ) < 1 && qAbs( yDist ) < 1 )
return true;
// Determine the line coords
lineEnd = QgsPointXY( lineStart.x() + xDist,
lineStart.y() + yDist );
vectorLength = sqrt( xDist * xDist + yDist * yDist );
// Check to see if any of the coords are outside the QImage area, if so, skip the whole vector
if ( lineStart.x() < 0 || lineStart.x() > mOutputSize.width() ||
lineStart.y() < 0 || lineStart.y() > mOutputSize.height() ||
lineEnd.x() < 0 || lineEnd.x() > mOutputSize.width() ||
lineEnd.y() < 0 || lineEnd.y() > mOutputSize.height() )
return true;
return false; //success
}
void QgsMeshVectorRenderer::drawVectorDataOnVertices()
{
const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
// currently expecting that triangulation does not add any new extra vertices on the way
Q_ASSERT( mDatasetValuesMag.count() == vertices.count() );
for ( int i = 0; i < vertices.size(); ++i )
{
const QgsMeshVertex &vertex = vertices.at( i );
//if (!nodeInsideView(nodeIndex))
// continue;
double xVal = mDatasetValuesX[i];
double yVal = mDatasetValuesY[i];
double V = mDatasetValuesMag[i]; // pre-calculated magnitude
QgsPointXY lineStart = mContext.mapToPixel().transform( vertex.x(), vertex.y() );
drawVectorArrow( lineStart, xVal, yVal, V );
}
}
void QgsMeshVectorRenderer::drawVectorDataOnFaces()
{
const QVector<QgsMeshVertex> &centroids = mTriangularMesh.centroids();
for ( int i = 0; i < centroids.count(); i++ )
{
//if (elemOutsideView(elemIndex))
// continue;
QgsPointXY center = centroids.at( i );
double xVal = mDatasetValuesX[i];
double yVal = mDatasetValuesY[i];
double V = mDatasetValuesMag[i]; // pre-calculated magnitude
QgsPointXY lineStart = mContext.mapToPixel().transform( center.x(), center.y() );
drawVectorArrow( lineStart, xVal, yVal, V );
}
}
void QgsMeshVectorRenderer::drawVectorArrow( const QgsPointXY &lineStart, double xVal, double yVal, double magnitude )
{
QgsPointXY lineEnd;
double vectorLength;
double cosAlpha, sinAlpha;
if ( calcVectorLineEnd( lineEnd, vectorLength, cosAlpha, sinAlpha,
lineStart, xVal, yVal, magnitude ) )
return;
// Make a set of vector head coordinates that we will place at the end of each vector,
// scale, translate and rotate.
QgsPointXY vectorHeadPoints[3];
QVector<QPointF> finalVectorHeadPoints( 3 );
double vectorHeadWidthRatio = mCfg.arrowHeadWidthRatio();
double vectorHeadLengthRatio = mCfg.arrowHeadLengthRatio();
// First head point: top of ->
vectorHeadPoints[0].setX( -1.0 * vectorHeadLengthRatio );
vectorHeadPoints[0].setY( vectorHeadWidthRatio * 0.5 );
// Second head point: right of ->
vectorHeadPoints[1].setX( 0.0 );
vectorHeadPoints[1].setY( 0.0 );
// Third head point: bottom of ->
vectorHeadPoints[2].setX( -1.0 * vectorHeadLengthRatio );
vectorHeadPoints[2].setY( -1.0 * vectorHeadWidthRatio * 0.5 );
// Determine the arrow head coords
for ( int j = 0; j < 3; j++ )
{
finalVectorHeadPoints[j].setX( lineEnd.x()
+ ( vectorHeadPoints[j].x() * cosAlpha * vectorLength )
- ( vectorHeadPoints[j].y() * sinAlpha * vectorLength )
);
finalVectorHeadPoints[j].setY( lineEnd.y()
- ( vectorHeadPoints[j].x() * sinAlpha * vectorLength )
- ( vectorHeadPoints[j].y() * cosAlpha * vectorLength )
);
}
// Now actually draw the vector
mContext.painter()->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
mContext.painter()->drawPolygon( finalVectorHeadPoints );
}

View File

@ -0,0 +1,103 @@
/***************************************************************************
qgstriangularmesh.h
-------------------
begin : April 2018
copyright : (C) 2018 by Peter Petrik
email : zilolv at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSMESHVECTORRENDERER_H
#define QGSMESHVECTORRENDERER_H
#define SIP_NO_FILE
#include <QVector>
#include <QSize>
#include "qgis_core.h"
#include "qgsmeshdataprovider.h"
#include "qgsrendercontext.h"
#include "qgstriangularmesh.h"
#include "qgsmeshlayer.h"
#include "qgspointxy.h"
///@cond PRIVATE
/**
* \ingroup core
*
* Helper private class for rendering vector datasets (e.g. velocity)
*
* \since QGIS 3.2
* \note not available in Python bindings
*/
class QgsMeshVectorRenderer
{
public:
//! Ctor
QgsMeshVectorRenderer( const QgsTriangularMesh &m,
const QVector<double> &datasetValuesX,
const QVector<double> &datasetValuesY,
const QVector<double> &datasetValuesMag,
bool dataIsOnVertices,
const QgsMeshRendererVectorSettings &settings,
QgsRenderContext &context,
const QSize &size );
//! Dtor
~QgsMeshVectorRenderer();
/**
* Draws vector arrows in the context's painter base on settings
*/
void draw();
private:
//! Draws for data defined on vertices
void drawVectorDataOnVertices();
//! Draws for data defined on face centers
void drawVectorDataOnFaces();
//! Draws arrow from start point and vector data
void drawVectorArrow( const QgsPointXY &lineStart, double xVal, double yVal, double magnitude );
//! Calculates the end point of the arrow based on start point and vector data
bool calcVectorLineEnd( QgsPointXY &lineEnd,
double &vectorLength,
double &cosAlpha,
double &sinAlpha, //out
const QgsPointXY &lineStart,
double xVal,
double yVal,
double magnitude //in
);
const QgsTriangularMesh &mTriangularMesh;
const QVector<double> &mDatasetValuesX;
const QVector<double> &mDatasetValuesY;
const QVector<double> &mDatasetValuesMag; //magnitudes
double mMinX;
double mMaxX;
double mMinY;
double mMaxY;
double mMinMag;
double mMaxMag;
QgsRenderContext &mContext;
const QgsMeshRendererVectorSettings &mCfg;
bool mDataOnVertices;
QSize mOutputSize;
};
///@endcond
#endif // QGSMESHVECTORRENDERER_H

View File

@ -19,6 +19,51 @@
#include "qgsrendercontext.h"
#include "qgscoordinatetransform.h"
static void ENP_centroid_step( const QPolygonF &pX, double &cx, double &cy, double &signedArea, int i, int i1 )
{
double x0 = 0.0; // Current vertex X
double y0 = 0.0; // Current vertex Y
double x1 = 0.0; // Next vertex X
double y1 = 0.0; // Next vertex Y
double a = 0.0; // Partial signed area
x0 = pX[i].x();
y0 = pX[i].y();
x1 = pX[i1].x();
y1 = pX[i1].y();
a = x0 * y1 - x1 * y0;
signedArea += a;
cx += ( x0 + x1 ) * a;
cy += ( y0 + y1 ) * a;
}
static void ENP_centroid( const QPolygonF &pX, double &cx, double &cy )
{
// http://stackoverflow.com/questions/2792443/finding-the-centroid-of-a-polygon/2792459#2792459
cx = 0;
cy = 0;
if ( pX.isEmpty() )
return;
double signedArea = 0.0;
// For all vertices except last
int i = 0;
for ( ; i < pX.size() - 1; ++i )
{
ENP_centroid_step( pX, cx, cy, signedArea, i, i + 1 );
}
// Do last vertex separately to avoid performing an expensive
// modulus operation in each iteration.
ENP_centroid_step( pX, cx, cy, signedArea, i, 0 );
signedArea *= 0.5;
cx /= ( 6.0 * signedArea );
cy /= ( 6.0 * signedArea );
}
void QgsTriangularMesh::update( QgsMesh *nativeMesh, QgsRenderContext *context )
{
Q_ASSERT( nativeMesh );
@ -27,6 +72,7 @@ void QgsTriangularMesh::update( QgsMesh *nativeMesh, QgsRenderContext *context )
mTriangularMesh.vertices.clear();
mTriangularMesh.faces.clear();
mTrianglesToNativeFaces.clear();
mNativeMeshFaceCentroids.clear();
// TRANSFORM VERTICES
QgsCoordinateTransform transform = context->coordinateTransform();
@ -79,6 +125,23 @@ void QgsTriangularMesh::update( QgsMesh *nativeMesh, QgsRenderContext *context )
}
}
// CALCULATE CENTROIDS
mNativeMeshFaceCentroids.resize( nativeMesh->faces.size() );
for ( int i = 0; i < nativeMesh->faces.size(); ++i )
{
const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
QVector<QPointF> points;
for ( int j = 0; j < face.size(); ++j )
{
int index = face.at( j );
const QgsMeshVertex &vertex = mTriangularMesh.vertices[index]; // we need projected vertices
points.push_back( vertex.toQPointF() );
}
QPolygonF poly( points );
double cx, cy;
ENP_centroid( poly, cx, cy );
mNativeMeshFaceCentroids[i] = QgsMeshVertex( cx, cy );
}
}
const QVector<QgsMeshVertex> &QgsTriangularMesh::vertices() const
@ -91,6 +154,11 @@ const QVector<QgsMeshFace> &QgsTriangularMesh::triangles() const
return mTriangularMesh.faces;
}
const QVector<QgsMeshVertex> &QgsTriangularMesh::centroids() const
{
return mNativeMeshFaceCentroids;
}
const QVector<int> &QgsTriangularMesh::trianglesToNativeFaces() const
{
return mTrianglesToNativeFaces;

View File

@ -55,22 +55,26 @@ class CORE_EXPORT QgsTriangularMesh
~QgsTriangularMesh() = default;
/**
* Construct triangular mesh from layer's native mesh and context
* Constructs triangular mesh from layer's native mesh and context
* \param nativeMesh QgsMesh to access native vertices and faces
* \param context Rendering context to estimate number of triagles to create for an face
*/
void update( QgsMesh *nativeMesh, QgsRenderContext *context );
/**
* Return vertices in map CRS
* Returns vertices in map CRS
*
* The list of consist of vertices from native mesh (0-N) and
* extra vertices needed to create triangles (N+1 - len)
*/
const QVector<QgsMeshVertex> &vertices() const ;
//! Return triangles
//! Returns triangles
const QVector<QgsMeshFace> &triangles() const ;
//! Return mapping between triangles and original faces
//! Returns centroids of the native faces in map CRS
const QVector<QgsMeshVertex> &centroids() const ;
//! Returns mapping between triangles and original faces
const QVector<int> &trianglesToNativeFaces() const ;
private:
@ -78,6 +82,9 @@ class CORE_EXPORT QgsTriangularMesh
// faces are derived triangles
QgsMesh mTriangularMesh;
QVector<int> mTrianglesToNativeFaces; //len(mTrianglesToNativeFaces) == len(mTriangles). Mapping derived -> native
// centroids of the native faces in map CRS
QVector<QgsMeshVertex> mNativeMeshFaceCentroids;
};

View File

@ -23,8 +23,8 @@ class QgsMdalLayerItem : public QgsLayerItem
{
Q_OBJECT
public:
//! Ctor
QgsMdalLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri );
QString layerName() const override;
};

View File

@ -64,14 +64,22 @@ class TestQgsMeshRenderer : public QObject
void test_native_mesh_rendering();
void test_triangular_mesh_rendering();
void test_vertex_scalar_dataset_rendering();
void test_vertex_vector_dataset_rendering();
void test_face_scalar_dataset_rendering();
void test_face_vector_dataset_rendering();
};
void TestQgsMeshRenderer::init()
{
mMemoryLayer->toggleTriangularMeshRendering( false );
mMemoryLayer->setActiveScalarDataset();
mMemoryLayer->setActiveVectorDataset();
mMemoryLayer->setRendererNativeMeshSettings( QgsMeshRendererMeshSettings() );
mMemoryLayer->setRendererTriangularMeshSettings( QgsMeshRendererMeshSettings() );
mMemoryLayer->setRendererScalarSettings( QgsMeshRendererScalarSettings() );
mMemoryLayer->setRendererVectorSettings( QgsMeshRendererVectorSettings() );
}
void TestQgsMeshRenderer::initTestCase()
@ -128,15 +136,20 @@ bool TestQgsMeshRenderer::imageCheck( const QString &testType )
void TestQgsMeshRenderer::test_native_mesh_rendering()
{
mMemoryLayer->toggleTriangularMeshRendering( false );
QVERIFY( mMemoryLayer->triangularMeshSymbol() == nullptr );
QgsMeshRendererMeshSettings settings = mMemoryLayer->rendererNativeMeshSettings();
settings.setEnabled( true );
settings.setLineWidth( 1. );
mMemoryLayer->setRendererNativeMeshSettings( settings );
QVERIFY( imageCheck( "quad_and_triangle_native_mesh" ) );
}
void TestQgsMeshRenderer::test_triangular_mesh_rendering()
{
mMemoryLayer->toggleTriangularMeshRendering( true );
QVERIFY( mMemoryLayer->triangularMeshSymbol() != nullptr );
QgsMeshRendererMeshSettings settings = mMemoryLayer->rendererTriangularMeshSettings();
settings.setEnabled( true );
settings.setColor( Qt::red );
settings.setLineWidth( 0.26 );
mMemoryLayer->setRendererTriangularMeshSettings( settings );
QVERIFY( imageCheck( "quad_and_triangle_triangular_mesh" ) );
}
@ -146,9 +159,39 @@ void TestQgsMeshRenderer::test_vertex_scalar_dataset_rendering()
mMemoryLayer->setActiveScalarDataset( ds );
QgsMeshDatasetMetadata metadata = mMemoryLayer->dataProvider()->datasetMetadata( ds );
QVERIFY( metadata["name"] == "VertexScalarDataset" );
QVERIFY( imageCheck( "quad_and_triangle_vertex_scalar_dataset" ) );
}
// TODO!
// QVERIFY( imageCheck( "quad_and_triangle_vertex_scalar_dataset" ) );
void TestQgsMeshRenderer::test_vertex_vector_dataset_rendering()
{
int ds = 1;
mMemoryLayer->setActiveVectorDataset( ds );
QgsMeshDatasetMetadata metadata = mMemoryLayer->dataProvider()->datasetMetadata( ds );
QVERIFY( metadata["name"] == "VertexVectorDataset" );
QgsMeshRendererVectorSettings settings = mMemoryLayer->rendererVectorSettings();
settings.setMinShaftLength( 15 );
mMemoryLayer->setRendererVectorSettings( settings );
QVERIFY( imageCheck( "quad_and_triangle_vertex_vector_dataset" ) );
}
void TestQgsMeshRenderer::test_face_scalar_dataset_rendering()
{
int ds = 2;
mMemoryLayer->setActiveScalarDataset( ds );
QgsMeshDatasetMetadata metadata = mMemoryLayer->dataProvider()->datasetMetadata( ds );
QVERIFY( metadata["name"] == "FaceScalarDataset" );
QVERIFY( imageCheck( "quad_and_triangle_face_scalar_dataset" ) );
}
void TestQgsMeshRenderer::test_face_vector_dataset_rendering()
{
int ds = 3;
mMemoryLayer->setActiveVectorDataset( ds );
QgsMeshDatasetMetadata metadata = mMemoryLayer->dataProvider()->datasetMetadata( ds );
QVERIFY( metadata["name"] == "FaceVectorDataset" );
QVERIFY( imageCheck( "quad_and_triangle_face_vector_dataset" ) );
}
QGSTEST_MAIN( TestQgsMeshRenderer )

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 78 KiB