34#define M_DEG2RAD 0.0174532925
37inline double mag(
double input )
46inline bool nodataValue(
double x,
double y )
48 return ( std::isnan( x ) || std::isnan( y ) );
51QgsMeshVectorArrowRenderer::QgsMeshVectorArrowRenderer(
54 const QVector<double> &datasetValuesMag,
55 double datasetMagMaximumValue,
double datasetMagMinimumValue,
61 , mDatasetValues( datasetValues )
62 , mDatasetValuesMag( datasetValuesMag )
63 , mMinMag( datasetMagMinimumValue )
64 , mMaxMag( datasetMagMaximumValue )
67 , mDataType( dataType )
69 , mBufferedExtent( context.mapExtent() )
72 Q_ASSERT( !mDatasetValuesMag.empty() );
73 Q_ASSERT( !std::isnan( mMinMag ) );
74 Q_ASSERT( !std::isnan( mMaxMag ) );
75 Q_ASSERT( mDatasetValues.isValid() );
81 const double extension = context.
convertToMapUnits( calcExtentBufferSize(), Qgis::RenderUnit::Pixels );
82 mBufferedExtent.setXMinimum( mBufferedExtent.xMinimum() - extension );
83 mBufferedExtent.setXMaximum( mBufferedExtent.xMaximum() + extension );
84 mBufferedExtent.setYMinimum( mBufferedExtent.yMinimum() - extension );
85 mBufferedExtent.setYMaximum( mBufferedExtent.yMaximum() + extension );
90QgsMeshVectorArrowRenderer::~QgsMeshVectorArrowRenderer() =
default;
92void QgsMeshVectorArrowRenderer::draw()
95 QPainter *painter = mContext.painter();
98 mContext.setPainterFlagsUsingContext( painter );
100 QPen pen = painter->pen();
101 pen.setCapStyle( Qt::FlatCap );
102 pen.setJoinStyle( Qt::MiterJoin );
104 const double penWidth = mContext.convertToPainterUnits( mCfg.lineWidth(),
105 Qgis::RenderUnit::Millimeters );
106 pen.setWidthF( penWidth );
107 painter->setPen( pen );
109 if ( mCfg.isOnUserDefinedGrid() )
111 drawVectorDataOnGrid( );
113 else if ( mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVertices )
115 drawVectorDataOnVertices( );
117 else if ( mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnFaces )
119 drawVectorDataOnFaces( );
121 else if ( mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnEdges )
123 drawVectorDataOnEdges( );
127bool QgsMeshVectorArrowRenderer::calcVectorLineEnd(
129 double &vectorLength,
140 if ( xVal == 0.0 && yVal == 0.0 )
144 if ( mCfg.filterMin() >= 0 && magnitude < mCfg.filterMin() )
146 if ( mCfg.filterMax() >= 0 && magnitude > mCfg.filterMax() )
151 const double vectorAngle = -1.0 * atan( ( -1.0 * yVal ) / xVal ) - mContext.mapToPixel().mapRotation() *
M_DEG2RAD;
153 cosAlpha = cos( vectorAngle ) * mag( xVal );
154 sinAlpha = sin( vectorAngle ) * mag( xVal );
159 switch ( mCfg.arrowSettings().shaftLengthMethod() )
161 case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::MinMax:
163 const double minShaftLength = mContext.convertToPainterUnits( mCfg.arrowSettings().minShaftLength(),
164 Qgis::RenderUnit::Millimeters );
165 const double maxShaftLength = mContext.convertToPainterUnits( mCfg.arrowSettings().maxShaftLength(),
166 Qgis::RenderUnit::Millimeters );
167 const double minVal = mMinMag;
168 const double maxVal = mMaxMag;
169 const double k = ( magnitude - minVal ) / ( maxVal - minVal );
170 const double L = minShaftLength + k * ( maxShaftLength - minShaftLength );
171 xDist = cosAlpha * L;
172 yDist = sinAlpha * L;
175 case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::Scaled:
177 const double scaleFactor = mCfg.arrowSettings().scaleFactor();
178 xDist = scaleFactor * xVal;
179 yDist = scaleFactor * yVal;
182 case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::Fixed:
185 const double fixedShaftLength = mContext.convertToPainterUnits( mCfg.arrowSettings().fixedShaftLength(),
186 Qgis::RenderUnit::Millimeters );
187 xDist = cosAlpha * fixedShaftLength;
188 yDist = sinAlpha * fixedShaftLength;
196 if ( std::abs( xDist ) < 1 && std::abs( yDist ) < 1 )
201 lineStart.
y() + yDist );
203 vectorLength = sqrt( xDist * xDist + yDist * yDist );
206 if ( ( lineStart.
x() < 0 || lineStart.
x() > mOutputSize.width() ||
207 lineStart.
y() < 0 || lineStart.
y() > mOutputSize.height() ) &&
208 ( lineEnd.
x() < 0 || lineEnd.
x() > mOutputSize.width() ||
209 lineEnd.
y() < 0 || lineEnd.
y() > mOutputSize.height() ) )
215double QgsMeshVectorArrowRenderer::calcExtentBufferSize()
const
218 switch ( mCfg.arrowSettings().shaftLengthMethod() )
220 case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::MinMax:
222 buffer = mContext.convertToPainterUnits( mCfg.arrowSettings().maxShaftLength(),
223 Qgis::RenderUnit::Millimeters );
226 case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::Scaled:
228 buffer = mCfg.arrowSettings().scaleFactor() * mMaxMag;
231 case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::Fixed:
233 buffer = mContext.convertToPainterUnits( mCfg.arrowSettings().fixedShaftLength(),
234 Qgis::RenderUnit::Millimeters );
239 if ( mCfg.filterMax() >= 0 && buffer > mCfg.filterMax() )
240 buffer = mCfg.filterMax();
249void QgsMeshVectorArrowRenderer::drawVectorDataOnVertices()
251 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
252 QSet<int> verticesToDraw;
255 Q_ASSERT( mDatasetValuesMag.count() == vertices.count() );
259 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
260 const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
266 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( mBufferedExtent );
267 const QVector<QgsMeshEdge> &edges = mTriangularMesh.edges();
272 drawVectorDataOnPoints( verticesToDraw, vertices );
275void QgsMeshVectorArrowRenderer::drawVectorDataOnPoints(
const QSet<int> indexesToRender,
const QVector<QgsMeshVertex> &points )
277 for (
const int i : indexesToRender )
279 if ( mContext.renderingStopped() )
283 if ( !mBufferedExtent.contains( center ) )
287 const double xVal = val.
x();
288 const double yVal = val.
y();
289 if ( nodataValue( xVal, yVal ) )
292 const double V = mDatasetValuesMag[i];
293 const QgsPointXY lineStart = mContext.mapToPixel().transform( center.
x(), center.
y() );
295 drawVectorArrow( lineStart, xVal, yVal, V );
299void QgsMeshVectorArrowRenderer::drawVectorDataOnFaces( )
301 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
302 const QVector<QgsMeshVertex> ¢roids = mTriangularMesh.faceCentroids();
304 mTriangularMesh.trianglesToNativeFaces() );
305 drawVectorDataOnPoints( nativeFacesInExtent, centroids );
308void QgsMeshVectorArrowRenderer::drawVectorDataOnEdges()
310 const QList<int> egdesInExtent = mTriangularMesh.edgeIndexesForRectangle( mBufferedExtent );
311 const QVector<QgsMeshVertex> ¢roids = mTriangularMesh.edgeCentroids();
313 mTriangularMesh.edgesToNativeEdges() );
314 drawVectorDataOnPoints( nativeEdgesInExtent, centroids );
317void QgsMeshVectorArrowRenderer::drawVectorDataOnGrid( )
319 if ( mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnEdges ||
320 mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVolumes )
323 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
324 const int cellx = mCfg.userGridCellWidth();
325 const int celly = mCfg.userGridCellHeight();
327 const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
328 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
330 for (
const int i : trianglesInExtent )
332 if ( mContext.renderingStopped() )
337 const int v1 = face[0], v2 = face[1], v3 = face[2];
338 const QgsPoint p1 = vertices[v1], p2 = vertices[v2], p3 = vertices[v3];
340 const int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces()[i];
343 const QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( p1, p2, p3 );
344 int left, right, top, bottom;
345 QgsMeshLayerUtils::boundingBoxToScreenRectangle( mContext.mapToPixel(), mOutputSize, bbox, left, right, top, bottom );
348 if ( left % cellx != 0 )
349 left += cellx - ( left % cellx );
350 if ( right % cellx != 0 )
351 right -= ( right % cellx );
352 if ( top % celly != 0 )
353 top += celly - ( top % celly );
354 if ( bottom % celly != 0 )
355 bottom -= ( bottom % celly );
357 for (
int y = top; y <= bottom; y += celly )
359 for (
int x = left; x <= right; x += cellx )
362 const QgsPointXY p = mContext.mapToPixel().toMapCoordinates( x, y );
364 if ( mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVertices )
366 const auto val1 = mDatasetValues.value( v1 );
367 const auto val2 = mDatasetValues.value( v2 );
368 const auto val3 = mDatasetValues.value( v3 );
370 QgsMeshLayerUtils::interpolateFromVerticesData(
378 QgsMeshLayerUtils::interpolateFromVerticesData(
386 else if ( mDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnFaces )
388 const auto val1 = mDatasetValues.value( nativeFaceIndex );
390 QgsMeshLayerUtils::interpolateFromFacesData(
397 QgsMeshLayerUtils::interpolateFromFacesData(
404 if ( nodataValue( val.
x(), val.
y() ) )
408 drawVectorArrow( lineStart, val.
x(), val.
y(), val.
scalar() );
414void QgsMeshVectorArrowRenderer::drawVectorArrow(
const QgsPointXY &lineStart,
double xVal,
double yVal,
double magnitude )
418 double cosAlpha, sinAlpha;
419 if ( calcVectorLineEnd( lineEnd, vectorLength, cosAlpha, sinAlpha,
420 lineStart, xVal, yVal, magnitude ) )
426 QVector<QPointF> finalVectorHeadPoints( 3 );
428 const double vectorHeadWidthRatio = mCfg.arrowSettings().arrowHeadWidthRatio();
429 const double vectorHeadLengthRatio = mCfg.arrowSettings().arrowHeadLengthRatio();
432 vectorHeadPoints[0].
setX( -1.0 * vectorHeadLengthRatio );
433 vectorHeadPoints[0].
setY( vectorHeadWidthRatio * 0.5 );
436 vectorHeadPoints[1].
setX( 0.0 );
437 vectorHeadPoints[1].
setY( 0.0 );
440 vectorHeadPoints[2].
setX( -1.0 * vectorHeadLengthRatio );
441 vectorHeadPoints[2].
setY( -1.0 * vectorHeadWidthRatio * 0.5 );
444 for (
int j = 0; j < 3; j++ )
446 finalVectorHeadPoints[j].setX( lineEnd.
x()
447 + ( vectorHeadPoints[j].x() * cosAlpha * vectorLength )
448 - ( vectorHeadPoints[j].y() * sinAlpha * vectorLength )
451 finalVectorHeadPoints[j].setY( lineEnd.
y()
452 - ( vectorHeadPoints[j].x() * sinAlpha * vectorLength )
453 - ( vectorHeadPoints[j].y() * cosAlpha * vectorLength )
458 QPen pen( mContext.painter()->pen() );
459 pen.setColor( mVectorColoring.color( magnitude ) );
460 mContext.painter()->setPen( pen );
462 mContext.painter()->drawPolygon( finalVectorHeadPoints );
465QgsMeshVectorRenderer::~QgsMeshVectorRenderer() =
default;
467QgsMeshVectorRenderer *QgsMeshVectorRenderer::makeVectorRenderer(
471 const QVector<double> &datasetValuesMag,
472 double datasetMagMaximumValue,
473 double datasetMagMinimumValue,
478 QgsMeshLayerRendererFeedback *feedBack,
481 QgsMeshVectorRenderer *renderer =
nullptr;
486 renderer =
new QgsMeshVectorArrowRenderer(
490 datasetMagMaximumValue,
491 datasetMagMinimumValue,
498 renderer =
new QgsMeshVectorStreamlineRenderer(
501 scalarActiveFaceFlagValues,
503 dataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVertices,
508 datasetMagMaximumValue );
511 renderer =
new QgsMeshVectorTraceRenderer(
514 scalarActiveFaceFlagValues,
515 dataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVertices,
519 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, 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.
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.