36#define M_DEG2RAD 0.0174532925 
   39inline double mag( 
double input )
 
   48inline bool nodataValue( 
double x, 
double y )
 
   50  return ( std::isnan( x ) || std::isnan( y ) );
 
   53QgsMeshVectorArrowRenderer::QgsMeshVectorArrowRenderer(
 
   56  const QVector<double> &datasetValuesMag,
 
   57  double datasetMagMaximumValue, 
double datasetMagMinimumValue,
 
   63  , mDatasetValues( datasetValues )
 
   64  , mDatasetValuesMag( datasetValuesMag )
 
   65  , mMinMag( datasetMagMinimumValue )
 
   66  , mMaxMag( datasetMagMaximumValue )
 
   69  , mDataType( dataType )
 
   71  , mBufferedExtent( context.mapExtent() )
 
   74  Q_ASSERT( !mDatasetValuesMag.empty() );
 
   75  Q_ASSERT( !std::isnan( mMinMag ) );
 
   76  Q_ASSERT( !std::isnan( mMaxMag ) );
 
   77  Q_ASSERT( mDatasetValues.isValid() );
 
   84  mBufferedExtent.setXMinimum( mBufferedExtent.xMinimum() - extension );
 
   85  mBufferedExtent.setXMaximum( mBufferedExtent.xMaximum() + extension );
 
   86  mBufferedExtent.setYMinimum( mBufferedExtent.yMinimum() - extension );
 
   87  mBufferedExtent.setYMaximum( mBufferedExtent.yMaximum() + extension );
 
   92QgsMeshVectorArrowRenderer::~QgsMeshVectorArrowRenderer() = 
default;
 
   94void QgsMeshVectorArrowRenderer::draw()
 
   97  QPainter *painter = mContext.painter();
 
  100  mContext.setPainterFlagsUsingContext( painter );
 
  102  QPen pen = painter->pen();
 
  103  pen.setCapStyle( Qt::FlatCap );
 
  104  pen.setJoinStyle( Qt::MiterJoin );
 
  106  const double penWidth = mContext.convertToPainterUnits( mCfg.lineWidth(),
 
  107                          QgsUnitTypes::RenderUnit::RenderMillimeters );
 
  108  pen.setWidthF( penWidth );
 
  109  painter->setPen( pen );
 
  111  if ( mCfg.isOnUserDefinedGrid() )
 
  113    drawVectorDataOnGrid( );
 
  115  else if ( mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVertices )
 
  117    drawVectorDataOnVertices( );
 
  119  else if ( mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnFaces )
 
  121    drawVectorDataOnFaces( );
 
  123  else if ( mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnEdges )
 
  125    drawVectorDataOnEdges( );
 
  129bool QgsMeshVectorArrowRenderer::calcVectorLineEnd(
 
  131  double &vectorLength,
 
  142  if ( xVal == 0.0 && yVal == 0.0 )
 
  146  if ( mCfg.filterMin() >= 0 && magnitude < mCfg.filterMin() )
 
  148  if ( mCfg.filterMax() >= 0 && magnitude > mCfg.filterMax() )
 
  153  const double vectorAngle = -1.0 * atan( ( -1.0 * yVal ) / xVal ) - mContext.mapToPixel().mapRotation() * 
M_DEG2RAD;
 
  155  cosAlpha = cos( vectorAngle ) * mag( xVal );
 
  156  sinAlpha = sin( vectorAngle ) * mag( xVal );
 
  161  switch ( mCfg.arrowSettings().shaftLengthMethod() )
 
  163    case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::MinMax:
 
  165      const double minShaftLength = mContext.convertToPainterUnits( mCfg.arrowSettings().minShaftLength(),
 
  166                                    QgsUnitTypes::RenderUnit::RenderMillimeters );
 
  167      const double maxShaftLength = mContext.convertToPainterUnits( mCfg.arrowSettings().maxShaftLength(),
 
  168                                    QgsUnitTypes::RenderUnit::RenderMillimeters );
 
  169      const double minVal = mMinMag;
 
  170      const double maxVal = mMaxMag;
 
  171      const double k = ( magnitude - minVal ) / ( maxVal - minVal );
 
  172      const double L = minShaftLength + k * ( maxShaftLength - minShaftLength );
 
  173      xDist = cosAlpha * L;
 
  174      yDist = sinAlpha * L;
 
  177    case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::Scaled:
 
  179      const double scaleFactor = mCfg.arrowSettings().scaleFactor();
 
  180      xDist = scaleFactor * xVal;
 
  181      yDist = scaleFactor * yVal;
 
  184    case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::Fixed:
 
  187      const double fixedShaftLength = mContext.convertToPainterUnits( mCfg.arrowSettings().fixedShaftLength(),
 
  188                                      QgsUnitTypes::RenderUnit::RenderMillimeters );
 
  189      xDist = cosAlpha * fixedShaftLength;
 
  190      yDist = sinAlpha * fixedShaftLength;
 
  198  if ( std::abs( xDist ) < 1 && std::abs( yDist ) < 1 )
 
  203                        lineStart.
y() + yDist );
 
  205  vectorLength = sqrt( xDist * xDist + yDist * yDist );
 
  208  if ( ( lineStart.
x() < 0 || lineStart.
x() > mOutputSize.width() ||
 
  209         lineStart.
y() < 0 || lineStart.
y() > mOutputSize.height() ) &&
 
  210       ( lineEnd.
x() < 0   || lineEnd.
x() > mOutputSize.width() ||
 
  211         lineEnd.
y() < 0   || lineEnd.
y() > mOutputSize.height() ) )
 
  217double QgsMeshVectorArrowRenderer::calcExtentBufferSize()
 const 
  220  switch ( mCfg.arrowSettings().shaftLengthMethod() )
 
  222    case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::MinMax:
 
  224      buffer = mContext.convertToPainterUnits( mCfg.arrowSettings().maxShaftLength(),
 
  225               QgsUnitTypes::RenderUnit::RenderMillimeters );
 
  228    case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::Scaled:
 
  230      buffer = mCfg.arrowSettings().scaleFactor() * mMaxMag;
 
  233    case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::Fixed:
 
  235      buffer = mContext.convertToPainterUnits( mCfg.arrowSettings().fixedShaftLength(),
 
  236               QgsUnitTypes::RenderUnit::RenderMillimeters );
 
  241  if ( mCfg.filterMax() >= 0 && buffer > mCfg.filterMax() )
 
  242    buffer = mCfg.filterMax();
 
  251void QgsMeshVectorArrowRenderer::drawVectorDataOnVertices()
 
  253  const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
 
  254  QSet<int> verticesToDraw;
 
  257  Q_ASSERT( mDatasetValuesMag.count() == vertices.count() );
 
  261    const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
 
  262    const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
 
  268    const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( mBufferedExtent );
 
  269    const QVector<QgsMeshEdge> &edges = mTriangularMesh.edges();
 
  274  drawVectorDataOnPoints( verticesToDraw, vertices );
 
  277void QgsMeshVectorArrowRenderer::drawVectorDataOnPoints( 
const QSet<int> indexesToRender, 
const QVector<QgsMeshVertex> &points )
 
  279  for ( 
const int i : indexesToRender )
 
  281    if ( mContext.renderingStopped() )
 
  285    if ( !mBufferedExtent.contains( center ) )
 
  289    const double xVal = val.
x();
 
  290    const double yVal = val.
y();
 
  291    if ( nodataValue( xVal, yVal ) )
 
  294    const double V = mDatasetValuesMag[i];  
 
  295    const QgsPointXY lineStart = mContext.mapToPixel().transform( center.
x(), center.
y() );
 
  297    drawVectorArrow( lineStart, xVal, yVal, V );
 
  301void QgsMeshVectorArrowRenderer::drawVectorDataOnFaces( )
 
  303  const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
 
  304  const QVector<QgsMeshVertex> ¢roids = mTriangularMesh.faceCentroids();
 
  306                                        mTriangularMesh.trianglesToNativeFaces() );
 
  307  drawVectorDataOnPoints( nativeFacesInExtent, centroids );
 
  310void QgsMeshVectorArrowRenderer::drawVectorDataOnEdges()
 
  312  const QList<int> egdesInExtent = mTriangularMesh.edgeIndexesForRectangle( mBufferedExtent );
 
  313  const QVector<QgsMeshVertex> ¢roids = mTriangularMesh.edgeCentroids();
 
  315                                        mTriangularMesh.edgesToNativeEdges() );
 
  316  drawVectorDataOnPoints( nativeEdgesInExtent, centroids );
 
  319void QgsMeshVectorArrowRenderer::drawVectorDataOnGrid( )
 
  321  if ( mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnEdges ||
 
  322       mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVolumes )
 
  325  const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
 
  326  const int cellx = mCfg.userGridCellWidth();
 
  327  const int celly = mCfg.userGridCellHeight();
 
  329  const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
 
  330  const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
 
  332  for ( 
const int i : trianglesInExtent )
 
  334    if ( mContext.renderingStopped() )
 
  339    const int v1 = face[0], v2 = face[1], v3 = face[2];
 
  340    const QgsPoint p1 = vertices[v1], p2 = vertices[v2], p3 = vertices[v3];
 
  342    const int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces()[i];
 
  345    const QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( p1, p2, p3 );
 
  346    int left, right, top, bottom;
 
  347    QgsMeshLayerUtils::boundingBoxToScreenRectangle( mContext.mapToPixel(), mOutputSize, bbox, left, right, top, bottom );
 
  350    if ( left % cellx != 0 )
 
  351      left += cellx - ( left % cellx );
 
  352    if ( right % cellx != 0 )
 
  353      right -= ( right % cellx );
 
  354    if ( top % celly != 0 )
 
  355      top += celly - ( top % celly );
 
  356    if ( bottom % celly != 0 )
 
  357      bottom -= ( bottom % celly );
 
  359    for ( 
int y = top; y <= bottom; y += celly )
 
  361      for ( 
int x = left; x <= right; x += cellx )
 
  364        const QgsPointXY p = mContext.mapToPixel().toMapCoordinates( x, y );
 
  366        if ( mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVertices )
 
  368          const auto val1 = mDatasetValues.value( v1 );
 
  369          const auto val2 = mDatasetValues.value( v2 );
 
  370          const auto val3 = mDatasetValues.value( v3 );
 
  372            QgsMeshLayerUtils::interpolateFromVerticesData(
 
  380            QgsMeshLayerUtils::interpolateFromVerticesData(
 
  388        else if ( mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnFaces )
 
  390          const auto val1 = mDatasetValues.value( nativeFaceIndex );
 
  392            QgsMeshLayerUtils::interpolateFromFacesData(
 
  399            QgsMeshLayerUtils::interpolateFromFacesData(
 
  406        if ( nodataValue( val.
x(), val.
y() ) )
 
  410        drawVectorArrow( lineStart, val.
x(), val.
y(), val.
scalar() );
 
  416void QgsMeshVectorArrowRenderer::drawVectorArrow( 
const QgsPointXY &lineStart, 
double xVal, 
double yVal, 
double magnitude )
 
  420  double cosAlpha, sinAlpha;
 
  421  if ( calcVectorLineEnd( lineEnd, vectorLength, cosAlpha, sinAlpha,
 
  422                          lineStart, xVal, yVal, magnitude ) )
 
  428  QVector<QPointF> finalVectorHeadPoints( 3 );
 
  430  const double vectorHeadWidthRatio  = mCfg.arrowSettings().arrowHeadWidthRatio();
 
  431  const double vectorHeadLengthRatio = mCfg.arrowSettings().arrowHeadLengthRatio();
 
  434  vectorHeadPoints[0].
setX( -1.0 * vectorHeadLengthRatio );
 
  435  vectorHeadPoints[0].
setY( vectorHeadWidthRatio * 0.5 );
 
  438  vectorHeadPoints[1].
setX( 0.0 );
 
  439  vectorHeadPoints[1].
setY( 0.0 );
 
  442  vectorHeadPoints[2].
setX( -1.0 * vectorHeadLengthRatio );
 
  443  vectorHeadPoints[2].
setY( -1.0 * vectorHeadWidthRatio * 0.5 );
 
  446  for ( 
int j = 0; j < 3; j++ )
 
  448    finalVectorHeadPoints[j].setX( lineEnd.
x()
 
  449                                   + ( vectorHeadPoints[j].x() * cosAlpha * vectorLength )
 
  450                                   - ( vectorHeadPoints[j].y() * sinAlpha * vectorLength )
 
  453    finalVectorHeadPoints[j].setY( lineEnd.
y()
 
  454                                   - ( vectorHeadPoints[j].x() * sinAlpha * vectorLength )
 
  455                                   - ( vectorHeadPoints[j].y() * cosAlpha * vectorLength )
 
  460  QPen pen( mContext.painter()->pen() );
 
  461  pen.setColor( mVectorColoring.color( magnitude ) );
 
  462  mContext.painter()->setPen( pen );
 
  464  mContext.painter()->drawPolygon( finalVectorHeadPoints );
 
  467QgsMeshVectorRenderer::~QgsMeshVectorRenderer() = 
default;
 
  469QgsMeshVectorRenderer *QgsMeshVectorRenderer::makeVectorRenderer(
 
  473  const QVector<double> &datasetValuesMag,
 
  474  double datasetMagMaximumValue,
 
  475  double datasetMagMinimumValue,
 
  482  QgsMeshVectorRenderer *renderer = 
nullptr;
 
  487      renderer = 
new QgsMeshVectorArrowRenderer(
 
  491        datasetMagMaximumValue,
 
  492        datasetMagMinimumValue,
 
  499      renderer = 
new QgsMeshVectorStreamlineRenderer(
 
  502        scalarActiveFaceFlagValues,
 
  503        dataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVertices,
 
  507        datasetMagMaximumValue );
 
  510      renderer = 
new QgsMeshVectorTraceRenderer(
 
  513        scalarActiveFaceFlagValues,
 
  514        dataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVertices,
 
  518        datasetMagMaximumValue );
 
QgsMeshDataBlock is a block of integers/doubles that can be used to retrieve: active flags (e....
 
@ Vector2DDouble
Vector double pairs (x1, y1, x2, y2, ... )
 
QgsMeshDatasetValue represents single dataset value.
 
void setY(double y)
Sets Y value.
 
double y() const
Returns y value.
 
double scalar() const
Returns magnitude of vector for vector data or scalar value for scalar data.
 
double x() const
Returns x value.
 
void setX(double x)
Sets X value.
 
Represents a renderer settings for vector datasets.
 
@ Traces
Displaying vector dataset with particle traces.
 
@ Arrows
Displaying vector dataset with arrows.
 
@ Streamlines
Displaying vector dataset with streamlines.
 
Symbology symbology() const
Returns the displaying method used to render vector datasets.
 
QgsInterpolatedLineColor vectorStrokeColoring() const
Returns the stroke coloring used to render vector datasets.
 
A class to represent a 2D point.
 
void setX(double x) SIP_HOLDGIL
Sets the x value of the point.
 
void setY(double y) SIP_HOLDGIL
Sets the y value of the point.
 
QPointF toQPointF() const
Converts a point to a QPointF.
 
Point geometry type, with support for z-dimension and m-values.
 
A rectangle specified with double values.
 
Contains information about the context of a rendering operation.
 
double convertToMapUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to map units.
 
Scoped object for saving and restoring a QPainter object's state.
 
Triangular/Derived Mesh is mesh with vertices in map coordinates.
 
CORE_EXPORT QSet< int > nativeEdgesFromEdges(const QList< int > &edgesIndexes, const QVector< int > &edgesToNativeEdges)
Returns unique native faces indexes from list of triangle indexes.
 
CORE_EXPORT QSet< int > nativeVerticesFromEdges(const QList< int > &edgesIndexes, const QVector< QgsMeshEdge > &edges)
Returns unique native faces indexes from list of vertices of triangles.
 
CORE_EXPORT QSet< int > nativeVerticesFromTriangles(const QList< int > &triangleIndexes, const QVector< QgsMeshFace > &triangles)
Returns unique native vertex indexes from list of vertices of triangles.
 
CORE_EXPORT QSet< int > nativeFacesFromTriangles(const QList< int > &triangleIndexes, const QVector< int > &trianglesToNativeFaces)
Returns unique native faces indexes from list of triangle indexes.
 
QVector< int > QgsMeshFace
List of vertex indexes.