35 inline double mag(
double input )
44 inline bool nodataValue(
double x,
double y )
46 return ( std::isnan( x ) || std::isnan( y ) );
49 QgsMeshVectorArrowRenderer::QgsMeshVectorArrowRenderer(
const QgsTriangularMesh &m,
51 const QVector<double> &datasetValuesMag,
52 double datasetMagMaximumValue,
double datasetMagMinimumValue,
53 bool dataIsOnVertices,
56 , mDatasetValues( datasetValues )
57 , mDatasetValuesMag( datasetValuesMag )
58 , mMinMag( datasetMagMinimumValue )
59 , mMaxMag( datasetMagMaximumValue )
62 , mDataOnVertices( dataIsOnVertices )
64 , mBufferedExtent( context.mapExtent() )
67 Q_ASSERT( !mDatasetValuesMag.empty() );
68 Q_ASSERT( !std::isnan( mMinMag ) );
69 Q_ASSERT( !std::isnan( mMaxMag ) );
70 Q_ASSERT( mDatasetValues.isValid() );
77 mBufferedExtent.setXMinimum( mBufferedExtent.xMinimum() - extension );
78 mBufferedExtent.setXMaximum( mBufferedExtent.xMaximum() + extension );
79 mBufferedExtent.setYMinimum( mBufferedExtent.yMinimum() - extension );
80 mBufferedExtent.setYMaximum( mBufferedExtent.yMaximum() + extension );
83 QgsMeshVectorArrowRenderer::~QgsMeshVectorArrowRenderer() =
default;
85 void QgsMeshVectorArrowRenderer::draw()
88 QPainter *painter = mContext.painter();
91 painter->setRenderHint( QPainter::Antialiasing,
true );
93 QPen pen = painter->pen();
94 pen.setCapStyle( Qt::FlatCap );
95 pen.setJoinStyle( Qt::MiterJoin );
97 double penWidth = mContext.convertToPainterUnits( mCfg.lineWidth(),
98 QgsUnitTypes::RenderUnit::RenderMillimeters );
99 pen.setWidthF( penWidth );
100 pen.setColor( mCfg.color() );
101 painter->setPen( pen );
103 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
105 if ( mCfg.isOnUserDefinedGrid() )
106 drawVectorDataOnGrid( trianglesInExtent );
107 else if ( mDataOnVertices )
108 drawVectorDataOnVertices( trianglesInExtent );
110 drawVectorDataOnFaces( trianglesInExtent );
115 bool QgsMeshVectorArrowRenderer::calcVectorLineEnd(
117 double &vectorLength,
128 if ( xVal == 0.0 && yVal == 0.0 )
132 if ( mCfg.filterMin() >= 0 && magnitude < mCfg.filterMin() )
134 if ( mCfg.filterMax() >= 0 && magnitude > mCfg.filterMax() )
139 double vectorAngle = -1.0 * atan( ( -1.0 * yVal ) / xVal );
140 cosAlpha = cos( vectorAngle ) * mag( xVal );
141 sinAlpha = sin( vectorAngle ) * mag( xVal );
146 switch ( mCfg.arrowSettings().shaftLengthMethod() )
148 case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::MinMax:
150 double minShaftLength = mContext.convertToPainterUnits( mCfg.arrowSettings().minShaftLength(),
151 QgsUnitTypes::RenderUnit::RenderMillimeters );
152 double maxShaftLength = mContext.convertToPainterUnits( mCfg.arrowSettings().maxShaftLength(),
153 QgsUnitTypes::RenderUnit::RenderMillimeters );
154 double minVal = mMinMag;
155 double maxVal = mMaxMag;
156 double k = ( magnitude - minVal ) / ( maxVal - minVal );
157 double L = minShaftLength + k * ( maxShaftLength - minShaftLength );
158 xDist = cosAlpha * L;
159 yDist = sinAlpha * L;
162 case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::Scaled:
164 double scaleFactor = mCfg.arrowSettings().scaleFactor();
165 xDist = scaleFactor * xVal;
166 yDist = scaleFactor * yVal;
169 case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::Fixed:
172 double fixedShaftLength = mContext.convertToPainterUnits( mCfg.arrowSettings().fixedShaftLength(),
173 QgsUnitTypes::RenderUnit::RenderMillimeters );
174 xDist = cosAlpha * fixedShaftLength;
175 yDist = sinAlpha * fixedShaftLength;
183 if ( std::abs( xDist ) < 1 && std::abs( yDist ) < 1 )
188 lineStart.
y() + yDist );
190 vectorLength = sqrt( xDist * xDist + yDist * yDist );
193 if ( ( lineStart.
x() < 0 || lineStart.
x() > mOutputSize.width() ||
194 lineStart.
y() < 0 || lineStart.
y() > mOutputSize.height() ) &&
195 ( lineEnd.
x() < 0 || lineEnd.
x() > mOutputSize.width() ||
196 lineEnd.
y() < 0 || lineEnd.
y() > mOutputSize.height() ) )
202 double QgsMeshVectorArrowRenderer::calcExtentBufferSize()
const 205 switch ( mCfg.arrowSettings().shaftLengthMethod() )
207 case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::MinMax:
209 buffer = mContext.convertToPainterUnits( mCfg.arrowSettings().maxShaftLength(),
210 QgsUnitTypes::RenderUnit::RenderMillimeters );
213 case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::Scaled:
215 buffer = mCfg.arrowSettings().scaleFactor() * mMaxMag;
218 case QgsMeshRendererVectorArrowSettings::ArrowScalingMethod::Fixed:
220 buffer = mContext.convertToPainterUnits( mCfg.arrowSettings().fixedShaftLength(),
221 QgsUnitTypes::RenderUnit::RenderMillimeters );
226 if ( mCfg.filterMax() >= 0 && buffer > mCfg.filterMax() )
227 buffer = mCfg.filterMax();
236 void QgsMeshVectorArrowRenderer::drawVectorDataOnVertices(
const QList<int> &trianglesInExtent )
238 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
239 const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
240 QSet<int> drawnVertices;
243 Q_ASSERT( mDatasetValuesMag.count() == vertices.count() );
245 for (
int triangleIndex : trianglesInExtent )
247 if ( mContext.renderingStopped() )
250 const QgsMeshFace triangle = triangles[triangleIndex];
251 for (
int i : triangle )
253 if ( drawnVertices.contains( i ) )
255 drawnVertices.insert( i );
258 if ( !mBufferedExtent.contains( vertex ) )
262 double xVal = val.
x();
263 double yVal = val.
y();
264 if ( nodataValue( xVal, yVal ) )
267 double V = mDatasetValuesMag[i];
268 QgsPointXY lineStart = mContext.mapToPixel().transform( vertex.
x(), vertex.
y() );
270 drawVectorArrow( lineStart, xVal, yVal, V );
275 void QgsMeshVectorArrowRenderer::drawVectorDataOnFaces(
const QList<int> &trianglesInExtent )
277 const QVector<QgsMeshVertex> ¢roids = mTriangularMesh.centroids();
279 mTriangularMesh.trianglesToNativeFaces() );
280 for (
int i : nativeFacesInExtent )
282 if ( mContext.renderingStopped() )
286 if ( !mBufferedExtent.contains( center ) )
290 double xVal = val.
x();
291 double yVal = val.
y();
292 if ( nodataValue( xVal, yVal ) )
295 double V = mDatasetValuesMag[i];
296 QgsPointXY lineStart = mContext.mapToPixel().transform( center.
x(), center.
y() );
298 drawVectorArrow( lineStart, xVal, yVal, V );
302 void QgsMeshVectorArrowRenderer::drawVectorDataOnGrid(
const QList<int> &trianglesInExtent )
304 int cellx = mCfg.userGridCellWidth();
305 int celly = mCfg.userGridCellHeight();
307 const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
308 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
310 for (
const int i : trianglesInExtent )
312 if ( mContext.renderingStopped() )
317 const int v1 = face[0], v2 = face[1], v3 = face[2];
318 const QgsPoint p1 = vertices[v1], p2 = vertices[v2], p3 = vertices[v3];
320 const int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces()[i];
323 QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( p1, p2, p3 );
324 int left, right, top, bottom;
325 QgsMeshLayerUtils::boundingBoxToScreenRectangle( mContext.mapToPixel(), mOutputSize, bbox, left, right, top, bottom );
328 if ( left % cellx != 0 )
329 left += cellx - ( left % cellx );
330 if ( right % cellx != 0 )
331 right -= ( right % cellx );
332 if ( top % celly != 0 )
333 top += celly - ( top % celly );
334 if ( bottom % celly != 0 )
335 bottom -= ( bottom % celly );
337 for (
int y = top; y <= bottom; y += celly )
339 for (
int x = left; x <= right; x += cellx )
342 const QgsPointXY p = mContext.mapToPixel().toMapCoordinates( x, y );
344 if ( mDataOnVertices )
346 const auto val1 = mDatasetValues.value( v1 );
347 const auto val2 = mDatasetValues.value( v2 );
348 const auto val3 = mDatasetValues.value( v3 );
350 QgsMeshLayerUtils::interpolateFromVerticesData(
358 QgsMeshLayerUtils::interpolateFromVerticesData(
368 const auto val1 = mDatasetValues.value( nativeFaceIndex );
370 QgsMeshLayerUtils::interpolateFromFacesData(
377 QgsMeshLayerUtils::interpolateFromFacesData(
385 if ( nodataValue( val.
x(), val.
y() ) )
389 drawVectorArrow( lineStart, val.
x(), val.
y(), val.
scalar() );
395 void QgsMeshVectorArrowRenderer::drawVectorArrow(
const QgsPointXY &lineStart,
double xVal,
double yVal,
double magnitude )
399 double cosAlpha, sinAlpha;
400 if ( calcVectorLineEnd( lineEnd, vectorLength, cosAlpha, sinAlpha,
401 lineStart, xVal, yVal, magnitude ) )
407 QVector<QPointF> finalVectorHeadPoints( 3 );
409 double vectorHeadWidthRatio = mCfg.arrowSettings().arrowHeadWidthRatio();
410 double vectorHeadLengthRatio = mCfg.arrowSettings().arrowHeadLengthRatio();
413 vectorHeadPoints[0].
setX( -1.0 * vectorHeadLengthRatio );
414 vectorHeadPoints[0].
setY( vectorHeadWidthRatio * 0.5 );
417 vectorHeadPoints[1].
setX( 0.0 );
418 vectorHeadPoints[1].
setY( 0.0 );
421 vectorHeadPoints[2].
setX( -1.0 * vectorHeadLengthRatio );
422 vectorHeadPoints[2].
setY( -1.0 * vectorHeadWidthRatio * 0.5 );
425 for (
int j = 0; j < 3; j++ )
427 finalVectorHeadPoints[j].setX( lineEnd.
x()
428 + ( vectorHeadPoints[j].
x() * cosAlpha * vectorLength )
429 - ( vectorHeadPoints[j].y() * sinAlpha * vectorLength )
432 finalVectorHeadPoints[j].setY( lineEnd.
y()
433 - ( vectorHeadPoints[j].
x() * sinAlpha * vectorLength )
434 - ( vectorHeadPoints[j].y() * cosAlpha * vectorLength )
440 mContext.painter()->drawPolygon( finalVectorHeadPoints );
443 QgsMeshVectorRenderer::~QgsMeshVectorRenderer() =
default;
445 QgsMeshVectorRenderer *QgsMeshVectorRenderer::makeVectorRenderer(
const QgsTriangularMesh &m,
448 const QVector<double> &datasetValuesMag,
449 double datasetMagMaximumValue,
450 double datasetMagMinimumValue,
451 bool dataIsOnVertices,
457 QgsMeshVectorRenderer *renderer =
nullptr;
462 renderer =
new QgsMeshVectorArrowRenderer(
466 datasetMagMaximumValue,
467 datasetMagMinimumValue,
473 renderer =
new QgsMeshVectorStreamlineRenderer(
476 scalarActiveFaceFlagValues,
481 datasetMagMaximumValue );
484 renderer =
new QgsMeshVectorTraceRenderer(
487 scalarActiveFaceFlagValues,
492 datasetMagMaximumValue );
double convertToMapUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to map units.
A rectangle specified with double values.
Triangular/Derived Mesh is mesh with vertices in map coordinates.
Displaying vector dataset with streamlines.
Use antialiasing while drawing.
Vector double pairs (x1, y1, x2, y2, ... )
A class to represent a 2D point.
QgsMeshDataBlock is a block of integers/doubles that can be used to retrieve: active flags (e...
void setY(double y)
Sets Y value.
double y() const
Returns y value.
QPointF toQPointF() const
Converts a point to a QPointF.
Represents a streamline renderer settings for vector datasets.
void setY(double y)
Sets the y value of the point.
Displaying vector dataset with streamlines.
double scalar() const
Returns magnitude of vector for vector data or scalar value for scalar data.
Point geometry type, with support for z-dimension and m-values.
Displaying vector dataset with arrows.
void setX(double x)
Sets the x value of the point.
CORE_EXPORT QList< int > nativeFacesFromTriangles(const QList< int > &triangleIndexes, const QVector< int > &trianglesToNativeFaces)
Returns unique native faces indexes from list of triangle indexes.
Contains information about the context of a rendering operation.
QVector< int > QgsMeshFace
List of vertex indexes.
QgsMeshDatasetValue represents single dataset value.
Symbology symbology() const
Returns the displaying method used to render vector datasets.
void setX(double x)
Sets X value.
double x() const
Returns x value.