34#define M_DEG2RAD 0.0174532925 
   37inline bool nodataValue( 
double x, 
double y )
 
   39  return ( std::isnan( x ) || std::isnan( y ) );
 
   42QgsMeshVectorArrowRenderer::QgsMeshVectorArrowRenderer(
 
   45  const QVector<double> &datasetValuesMag,
 
   46  double datasetMagMaximumValue, 
double datasetMagMinimumValue,
 
   52  , mDatasetValues( datasetValues )
 
   53  , mDatasetValuesMag( datasetValuesMag )
 
   54  , mMinMag( datasetMagMinimumValue )
 
   55  , mMaxMag( datasetMagMaximumValue )
 
   56  , mDataType( dataType )
 
   57  , mBufferedExtent( context.mapExtent() )
 
   63  Q_ASSERT( !mDatasetValuesMag.empty() );
 
   64  Q_ASSERT( !std::isnan( mMinMag ) );
 
   65  Q_ASSERT( !std::isnan( mMaxMag ) );
 
   66  Q_ASSERT( mDatasetValues.isValid() );
 
   73  mBufferedExtent.setXMinimum( mBufferedExtent.xMinimum() - extension );
 
   74  mBufferedExtent.setXMaximum( mBufferedExtent.xMaximum() + extension );
 
   75  mBufferedExtent.setYMinimum( mBufferedExtent.yMinimum() - extension );
 
   76  mBufferedExtent.setYMaximum( mBufferedExtent.yMaximum() + extension );
 
   81QgsMeshVectorArrowRenderer::~QgsMeshVectorArrowRenderer() = 
default;
 
   83void QgsMeshVectorArrowRenderer::draw()
 
   86  QPainter *painter = mContext.painter();
 
   89  mContext.setPainterFlagsUsingContext( painter );
 
   91  QPen pen = painter->pen();
 
   92  pen.setCapStyle( Qt::FlatCap );
 
   93  pen.setJoinStyle( Qt::MiterJoin );
 
   95  const double penWidth = mContext.convertToPainterUnits( mCfg.lineWidth(),
 
   97  pen.setWidthF( penWidth );
 
   98  painter->setPen( pen );
 
  100  if ( mCfg.isOnUserDefinedGrid() )
 
  102    drawVectorDataOnGrid( );
 
  106    drawVectorDataOnVertices( );
 
  110    drawVectorDataOnFaces( );
 
  114    drawVectorDataOnEdges( );
 
  118bool QgsMeshVectorArrowRenderer::calcVectorLineEnd(
 
  120  double &vectorLength,
 
  131  if ( xVal == 0.0 && yVal == 0.0 )
 
  135  if ( mCfg.filterMin() >= 0 && magnitude < mCfg.filterMin() )
 
  137  if ( mCfg.filterMax() >= 0 && magnitude > mCfg.filterMax() )
 
  142  const double vectorAngle = std::atan2( yVal, xVal ) - mContext.mapToPixel().mapRotation() * 
M_DEG2RAD;
 
  144  cosAlpha = cos( vectorAngle );
 
  145  sinAlpha = sin( vectorAngle );
 
  150  switch ( mCfg.arrowSettings().shaftLengthMethod() )
 
  154      const double minShaftLength = mContext.convertToPainterUnits( mCfg.arrowSettings().minShaftLength(),
 
  156      const double maxShaftLength = mContext.convertToPainterUnits( mCfg.arrowSettings().maxShaftLength(),
 
  158      const double minVal = mMinMag;
 
  159      const double maxVal = mMaxMag;
 
  160      const double k = ( magnitude - minVal ) / ( maxVal - minVal );
 
  161      const double L = minShaftLength + k * ( maxShaftLength - minShaftLength );
 
  162      xDist = cosAlpha * L;
 
  163      yDist = sinAlpha * L;
 
  168      const double scaleFactor = mCfg.arrowSettings().scaleFactor();
 
  169      xDist = scaleFactor * xVal;
 
  170      yDist = scaleFactor * yVal;
 
  176      const double fixedShaftLength = mContext.convertToPainterUnits( mCfg.arrowSettings().fixedShaftLength(),
 
  178      xDist = cosAlpha * fixedShaftLength;
 
  179      yDist = sinAlpha * fixedShaftLength;
 
  187  if ( std::abs( xDist ) < 1 && std::abs( yDist ) < 1 )
 
  192                        lineStart.
y() + yDist );
 
  194  vectorLength = sqrt( xDist * xDist + yDist * yDist );
 
  197  if ( !
QgsRectangle( lineStart, lineEnd ).intersects( 
QgsRectangle( 0, 0, mOutputSize.width(), mOutputSize.height() ) ) )
 
  203double QgsMeshVectorArrowRenderer::calcExtentBufferSize()
 const 
  206  switch ( mCfg.arrowSettings().shaftLengthMethod() )
 
  210      buffer = mContext.convertToPainterUnits( mCfg.arrowSettings().maxShaftLength(),
 
  216      buffer = mCfg.arrowSettings().scaleFactor() * mMaxMag;
 
  221      buffer = mContext.convertToPainterUnits( mCfg.arrowSettings().fixedShaftLength(),
 
  227  if ( mCfg.filterMax() >= 0 && buffer > mCfg.filterMax() )
 
  228    buffer = mCfg.filterMax();
 
  237void QgsMeshVectorArrowRenderer::drawVectorDataOnVertices()
 
  239  const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
 
  240  QSet<int> verticesToDraw;
 
  243  Q_ASSERT( mDatasetValuesMag.count() == vertices.count() );
 
  247    const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
 
  248    const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
 
  249    verticesToDraw.unite( QgsMeshUtils::nativeVerticesFromTriangles( trianglesInExtent, triangles ) );
 
  254    const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( mBufferedExtent );
 
  255    const QVector<QgsMeshEdge> &edges = mTriangularMesh.edges();
 
  256    verticesToDraw.unite( QgsMeshUtils::nativeVerticesFromEdges( edgesInExtent, edges ) );
 
  260  drawVectorDataOnPoints( verticesToDraw, vertices );
 
  263void QgsMeshVectorArrowRenderer::drawVectorDataOnPoints( 
const QSet<int> indexesToRender, 
const QVector<QgsMeshVertex> &points )
 
  265  for ( 
const int i : indexesToRender )
 
  267    if ( mContext.renderingStopped() )
 
  271    if ( !mBufferedExtent.contains( center ) )
 
  275    const double xVal = val.
x();
 
  276    const double yVal = val.
y();
 
  277    if ( nodataValue( xVal, yVal ) )
 
  280    const double V = mDatasetValuesMag[i];  
 
  281    const QgsPointXY lineStart = mContext.mapToPixel().transform( center.
x(), center.
y() );
 
  283    drawVector( lineStart, xVal, yVal, V );
 
  287void QgsMeshVectorArrowRenderer::drawVectorDataOnFaces( )
 
  289  const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
 
  290  const QVector<QgsMeshVertex> ¢roids = mTriangularMesh.faceCentroids();
 
  291  const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
 
  292                                        mTriangularMesh.trianglesToNativeFaces() );
 
  293  drawVectorDataOnPoints( nativeFacesInExtent, centroids );
 
  296void QgsMeshVectorArrowRenderer::drawVectorDataOnEdges()
 
  298  const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( mBufferedExtent );
 
  299  const QVector<QgsMeshVertex> ¢roids = mTriangularMesh.edgeCentroids();
 
  300  const QSet<int> nativeEdgesInExtent = QgsMeshUtils::nativeEdgesFromEdges( edgesInExtent,
 
  301                                        mTriangularMesh.edgesToNativeEdges() );
 
  302  drawVectorDataOnPoints( nativeEdgesInExtent, centroids );
 
  305void QgsMeshVectorArrowRenderer::drawVectorDataOnGrid( )
 
  311  const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
 
  312  const int cellx = mCfg.userGridCellWidth();
 
  313  const int celly = mCfg.userGridCellHeight();
 
  315  const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
 
  316  const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
 
  318  for ( 
const int i : trianglesInExtent )
 
  320    if ( mContext.renderingStopped() )
 
  325    const int v1 = face[0], v2 = face[1], v3 = face[2];
 
  326    const QgsPoint p1 = vertices[v1], p2 = vertices[v2], p3 = vertices[v3];
 
  328    const int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces()[i];
 
  331    const QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( p1, p2, p3 );
 
  332    int left, right, top, bottom;
 
  333    QgsMeshLayerUtils::boundingBoxToScreenRectangle( mContext.mapToPixel(), mOutputSize, bbox, left, right, top, bottom );
 
  336    if ( left % cellx != 0 )
 
  337      left += cellx - ( left % cellx );
 
  338    if ( right % cellx != 0 )
 
  339      right -= ( right % cellx );
 
  340    if ( top % celly != 0 )
 
  341      top += celly - ( top % celly );
 
  342    if ( bottom % celly != 0 )
 
  343      bottom -= ( bottom % celly );
 
  345    for ( 
int y = top; y <= bottom; y += celly )
 
  347      for ( 
int x = left; x <= right; x += cellx )
 
  350        const QgsPointXY p = mContext.mapToPixel().toMapCoordinates( x, y );
 
  354          const auto val1 = mDatasetValues.value( v1 );
 
  355          const auto val2 = mDatasetValues.value( v2 );
 
  356          const auto val3 = mDatasetValues.value( v3 );
 
  358            QgsMeshLayerUtils::interpolateFromVerticesData(
 
  366            QgsMeshLayerUtils::interpolateFromVerticesData(
 
  376          const auto val1 = mDatasetValues.value( nativeFaceIndex );
 
  378            QgsMeshLayerUtils::interpolateFromFacesData(
 
  385            QgsMeshLayerUtils::interpolateFromFacesData(
 
  392        if ( nodataValue( val.
x(), val.
y() ) )
 
  396        drawVector( lineStart, val.
x(), val.
y(), val.
scalar() );
 
  402void QgsMeshVectorArrowRenderer::drawVector( 
const QgsPointXY &lineStart, 
double xVal, 
double yVal, 
double magnitude )
 
  406  double cosAlpha, sinAlpha;
 
  407  if ( calcVectorLineEnd( lineEnd, vectorLength, cosAlpha, sinAlpha,
 
  408                          lineStart, xVal, yVal, magnitude ) )
 
  414  QVector<QPointF> finalVectorHeadPoints( 3 );
 
  416  const double vectorHeadWidthRatio  = mCfg.arrowSettings().arrowHeadWidthRatio();
 
  417  const double vectorHeadLengthRatio = mCfg.arrowSettings().arrowHeadLengthRatio();
 
  420  vectorHeadPoints[0].
setX( -1.0 * vectorHeadLengthRatio );
 
  421  vectorHeadPoints[0].
setY( vectorHeadWidthRatio * 0.5 );
 
  424  vectorHeadPoints[1].
setX( 0.0 );
 
  425  vectorHeadPoints[1].
setY( 0.0 );
 
  428  vectorHeadPoints[2].
setX( -1.0 * vectorHeadLengthRatio );
 
  429  vectorHeadPoints[2].
setY( -1.0 * vectorHeadWidthRatio * 0.5 );
 
  432  for ( 
int j = 0; j < 3; j++ )
 
  434    finalVectorHeadPoints[j].setX( lineEnd.
x()
 
  435                                   + ( vectorHeadPoints[j].x() * cosAlpha * vectorLength )
 
  436                                   - ( vectorHeadPoints[j].y() * sinAlpha * vectorLength )
 
  439    finalVectorHeadPoints[j].setY( lineEnd.
y()
 
  440                                   - ( vectorHeadPoints[j].x() * sinAlpha * vectorLength )
 
  441                                   - ( vectorHeadPoints[j].y() * cosAlpha * vectorLength )
 
  446  QPen pen( mContext.painter()->pen() );
 
  447  pen.setColor( mVectorColoring.color( magnitude ) );
 
  448  mContext.painter()->setPen( pen );
 
  450  mContext.painter()->drawPolygon( finalVectorHeadPoints );
 
  453QgsMeshVectorRenderer::~QgsMeshVectorRenderer() = 
default;
 
  455QgsMeshVectorRenderer *QgsMeshVectorRenderer::makeVectorRenderer(
 
  459  const QVector<double> &datasetValuesMag,
 
  460  double datasetMagMaximumValue,
 
  461  double datasetMagMinimumValue,
 
  466  QgsMeshLayerRendererFeedback *feedBack,
 
  469  QgsMeshVectorRenderer *renderer = 
nullptr;
 
  474      renderer = 
new QgsMeshVectorArrowRenderer(
 
  478        datasetMagMaximumValue,
 
  479        datasetMagMinimumValue,
 
  486      renderer = 
new QgsMeshVectorStreamlineRenderer(
 
  489        scalarActiveFaceFlagValues,
 
  496        datasetMagMaximumValue );
 
  499      renderer = 
new QgsMeshVectorTraceRenderer(
 
  502        scalarActiveFaceFlagValues,
 
  507        datasetMagMaximumValue );
 
  510      renderer = 
new QgsMeshVectorWindBarbRenderer(
 
  514        datasetMagMaximumValue,
 
  515        datasetMagMinimumValue,
 
  527QgsMeshVectorWindBarbRenderer::QgsMeshVectorWindBarbRenderer(
 
  530  const QVector<double> &datasetValuesMag,
 
  531  double datasetMagMaximumValue, 
double datasetMagMinimumValue,
 
  535  QSize size )  : QgsMeshVectorArrowRenderer( m,
 
  538        datasetMagMinimumValue,
 
  539        datasetMagMaximumValue,
 
  549QgsMeshVectorWindBarbRenderer::~QgsMeshVectorWindBarbRenderer() = 
default;
 
  551void QgsMeshVectorWindBarbRenderer::drawVector( 
const QgsPointXY &lineStart, 
double xVal, 
double yVal, 
double magnitude )
 
  554  if ( mCfg.filterMin() >= 0 && magnitude < mCfg.filterMin() )
 
  556  if ( mCfg.filterMax() >= 0 && magnitude > mCfg.filterMax() )
 
  559  QPen pen( mContext.painter()->pen() );
 
  560  pen.setColor( mVectorColoring.color( magnitude ) );
 
  561  mContext.painter()->setPen( pen );
 
  564  QBrush brush( pen.color() );
 
  565  mContext.painter()->setBrush( brush );
 
  567  const double shaftLength = mContext.convertToPainterUnits( mCfg.windBarbSettings().shaftLength(),
 
  568                             mCfg.windBarbSettings().shaftLengthUnits() );
 
  569  if ( shaftLength < 1 )
 
  573  const QgsPointXY mapPoint = mContext.mapToPixel().toMapCoordinates( lineStart.
x(), lineStart.
y() );
 
  574  bool isNorthHemisphere = 
true;
 
  577    const QgsPointXY geoPoint = mGeographicTransform.transform( mapPoint );
 
  578    isNorthHemisphere = geoPoint.
y() >= 0;
 
  582    QgsDebugError( QStringLiteral( 
"Could not transform wind barb coordinates to geographic ones" ) );
 
  585  const double d = shaftLength / 25; 
 
  586  const double centerRadius = d;
 
  587  const double zeroCircleRadius = 2 * d;
 
  588  const double barbLength = 8 * d + pen.widthF();
 
  589  const double barbAngle = 135;
 
  590  const double barbOffset = 2 * d + pen.widthF();
 
  591  const int sign = isNorthHemisphere ? 1 : -1;
 
  595  const double vectorAngle = std::atan2( yVal, xVal ) - mContext.mapToPixel().mapRotation() * 
M_DEG2RAD;
 
  599  const double xDist = cos( vectorAngle ) * shaftLength;
 
  600  const double yDist = - sin( vectorAngle ) * shaftLength;
 
  604                                         lineStart.
y() - yDist );
 
  607  if ( !
QgsRectangle( lineStart, lineEnd ).intersects( 
QgsRectangle( 0, 0, mOutputSize.width(), mOutputSize.height() ) ) )
 
  611  double knots = magnitude * mCfg.windBarbSettings().magnitudeMultiplier() ;
 
  617    mContext.painter()->setBrush( Qt::NoBrush );
 
  618    mContext.painter()->drawEllipse( lineStart.
toQPointF(), zeroCircleRadius, zeroCircleRadius );
 
  619    mContext.painter()->setBrush( brush );
 
  623  const double azimuth = lineEnd.
azimuth( lineStart );
 
  626  if ( knots < 47.5 && knots > 7.5 )
 
  629    const QVector< QPointF > pts{ lineStart.
toQPointF(),
 
  631                                  nextLineOrigin.
project( barbLength, azimuth + barbAngle * sign ).
toQPointF() };
 
  632    mContext.painter()->drawPolyline( pts );
 
  633    nextLineOrigin = nextLineOrigin.
project( barbOffset, azimuth );
 
  643  mContext.painter()->drawEllipse( lineStart.
toQPointF(), centerRadius, centerRadius );
 
  646  while ( knots > 47.5 )
 
  648    const QVector< QPointF > pts{ nextLineOrigin.
toQPointF(),
 
  649                                  nextLineOrigin.
project( barbLength / 1.414, azimuth + 90 * sign ).
toQPointF(),
 
  651    mContext.painter()->drawPolygon( pts );
 
  656      nextLineOrigin = nextLineOrigin.
project( barbLength / 1.414, azimuth );
 
  658      nextLineOrigin = nextLineOrigin.
project( barbLength / 1.414 + barbOffset, azimuth );
 
  662  while ( knots > 7.5 )
 
  664    mContext.painter()->drawLine( nextLineOrigin.
toQPointF(), nextLineOrigin.
project( barbLength, azimuth + barbAngle * sign ).
toQPointF() );
 
  665    nextLineOrigin = nextLineOrigin.
project( barbOffset, azimuth );
 
  673    if ( nextLineOrigin == lineEnd )
 
  674      nextLineOrigin = nextLineOrigin.
project( barbLength / 2, azimuth );
 
  676    mContext.painter()->drawLine( nextLineOrigin.
toQPointF(), nextLineOrigin.
project( barbLength / 2, azimuth + barbAngle * sign ).
toQPointF() );
 
@ Millimeters
Millimeters.
 
This class represents a coordinate reference system (CRS).
 
QgsCoordinateReferenceSystem toGeographicCrs() const
Returns the geographic CRS associated with this CRS object.
 
Custom exception class for Coordinate Reference System related exceptions.
 
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.
 
@ Scaled
Scale vector magnitude by factor scaleFactor()
 
@ MinMax
Scale vector magnitude linearly to fit in range of vectorFilterMin() and vectorFilterMax()
 
@ Fixed
Use fixed length fixedShaftLength() regardless of vector's magnitude.
 
Represents a renderer settings for vector datasets.
 
@ Traces
Displaying vector dataset with particle traces.
 
@ Arrows
Displaying vector dataset with arrows.
 
@ WindBarbs
Displaying vector dataset with wind barbs.
 
@ 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.
 
QgsPointXY project(double distance, double bearing) const
Returns a new point which corresponds to this point projected by a specified distance in a specified ...
 
void setY(double y)
Sets the y value of the point.
 
double azimuth(const QgsPointXY &other) const
Calculates azimuth between this point and other one (clockwise in degree, starting from north)
 
void setX(double x)
Sets the x 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, Qgis::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.
 
#define QgsDebugError(str)
 
QVector< int > QgsMeshFace
List of vertex indexes.