34 inline double mag(
double input )
43 inline bool nodataValue(
double x,
double y )
45 return ( std::isnan( x ) || std::isnan( y ) );
49 const QVector<double> &datasetValuesX,
50 const QVector<double> &datasetValuesY,
51 const QVector<double> &datasetValuesMag,
52 double datasetMagMinimumValue,
53 double datasetMagMaximumValue,
54 bool dataIsOnVertices,
57 : mTriangularMesh( m )
58 , mDatasetValuesX( datasetValuesX )
59 , mDatasetValuesY( datasetValuesY )
60 , mDatasetValuesMag( datasetValuesMag )
61 , mMinMag( datasetMagMinimumValue )
62 , mMaxMag( datasetMagMaximumValue )
65 , mDataOnVertices( dataIsOnVertices )
67 , mBufferedExtent( context.extent() )
70 Q_ASSERT( !mDatasetValuesMag.empty() );
71 Q_ASSERT( !std::isnan( mMinMag ) );
72 Q_ASSERT( !std::isnan( mMaxMag ) );
78 mBufferedExtent.setXMinimum( mBufferedExtent.xMinimum() - extension );
79 mBufferedExtent.setXMaximum( mBufferedExtent.xMaximum() + extension );
80 mBufferedExtent.setYMinimum( mBufferedExtent.yMinimum() - extension );
81 mBufferedExtent.setYMaximum( mBufferedExtent.yMaximum() + extension );
84 QgsMeshVectorRenderer::~QgsMeshVectorRenderer() =
default;
86 void QgsMeshVectorRenderer::draw()
89 QPainter *painter = mContext.painter();
92 painter->setRenderHint( QPainter::Antialiasing,
true );
94 QPen pen = painter->pen();
95 pen.setCapStyle( Qt::FlatCap );
96 pen.setJoinStyle( Qt::MiterJoin );
98 double penWidth = mContext.convertToPainterUnits( mCfg.lineWidth(),
99 QgsUnitTypes::RenderUnit::RenderMillimeters );
100 pen.setWidthF( penWidth );
101 pen.setColor( mCfg.color() );
102 painter->setPen( pen );
104 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
106 if ( mCfg.isOnUserDefinedGrid() )
107 drawVectorDataOnGrid( trianglesInExtent );
108 else if ( mDataOnVertices )
109 drawVectorDataOnVertices( trianglesInExtent );
111 drawVectorDataOnFaces( trianglesInExtent );
116 bool QgsMeshVectorRenderer::calcVectorLineEnd(
118 double &vectorLength,
129 if ( xVal == 0.0 && yVal == 0.0 )
133 if ( mCfg.filterMin() >= 0 && magnitude < mCfg.filterMin() )
135 if ( mCfg.filterMax() >= 0 && magnitude > mCfg.filterMax() )
140 double vectorAngle = -1.0 * atan( ( -1.0 * yVal ) / xVal );
141 cosAlpha = cos( vectorAngle ) * mag( xVal );
142 sinAlpha = sin( vectorAngle ) * mag( xVal );
147 switch ( mCfg.shaftLengthMethod() )
149 case QgsMeshRendererVectorSettings::ArrowScalingMethod::MinMax:
151 double minShaftLength = mContext.convertToPainterUnits( mCfg.minShaftLength(),
152 QgsUnitTypes::RenderUnit::RenderMillimeters );
153 double maxShaftLength = mContext.convertToPainterUnits( mCfg.maxShaftLength(),
154 QgsUnitTypes::RenderUnit::RenderMillimeters );
155 double minVal = mMinMag;
156 double maxVal = mMaxMag;
157 double k = ( magnitude - minVal ) / ( maxVal - minVal );
158 double L = minShaftLength + k * ( maxShaftLength - minShaftLength );
159 xDist = cosAlpha * L;
160 yDist = sinAlpha * L;
163 case QgsMeshRendererVectorSettings::ArrowScalingMethod::Scaled:
165 double scaleFactor = mCfg.scaleFactor();
166 xDist = scaleFactor * xVal;
167 yDist = scaleFactor * yVal;
170 case QgsMeshRendererVectorSettings::ArrowScalingMethod::Fixed:
173 double fixedShaftLength = mContext.convertToPainterUnits( mCfg.fixedShaftLength(),
174 QgsUnitTypes::RenderUnit::RenderMillimeters );
175 xDist = cosAlpha * fixedShaftLength;
176 yDist = sinAlpha * fixedShaftLength;
184 if ( std::abs( xDist ) < 1 && std::abs( yDist ) < 1 )
189 lineStart.
y() + yDist );
191 vectorLength = sqrt( xDist * xDist + yDist * yDist );
194 if ( ( lineStart.
x() < 0 || lineStart.
x() > mOutputSize.width() ||
195 lineStart.
y() < 0 || lineStart.
y() > mOutputSize.height() ) &&
196 ( lineEnd.
x() < 0 || lineEnd.
x() > mOutputSize.width() ||
197 lineEnd.
y() < 0 || lineEnd.
y() > mOutputSize.height() ) )
203 double QgsMeshVectorRenderer::calcExtentBufferSize()
const 206 switch ( mCfg.shaftLengthMethod() )
208 case QgsMeshRendererVectorSettings::ArrowScalingMethod::MinMax:
210 buffer = mContext.convertToPainterUnits( mCfg.maxShaftLength(),
211 QgsUnitTypes::RenderUnit::RenderMillimeters );
214 case QgsMeshRendererVectorSettings::ArrowScalingMethod::Scaled:
216 buffer = mCfg.scaleFactor() * mMaxMag;
219 case QgsMeshRendererVectorSettings::ArrowScalingMethod::Fixed:
221 buffer = mContext.convertToPainterUnits( mCfg.fixedShaftLength(),
222 QgsUnitTypes::RenderUnit::RenderMillimeters );
227 if ( mCfg.filterMax() >= 0 && buffer > mCfg.filterMax() )
228 buffer = mCfg.filterMax();
237 void QgsMeshVectorRenderer::drawVectorDataOnVertices(
const QList<int> &trianglesInExtent )
239 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
240 const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
241 QSet<int> drawnVertices;
244 Q_ASSERT( mDatasetValuesMag.count() == vertices.count() );
246 for (
int triangleIndex : trianglesInExtent )
248 if ( mContext.renderingStopped() )
251 const QgsMeshFace triangle = triangles[triangleIndex];
252 for (
int i : triangle )
254 if ( drawnVertices.contains( i ) )
256 drawnVertices.insert( i );
259 if ( !mBufferedExtent.contains( vertex ) )
262 double xVal = mDatasetValuesX[i];
263 double yVal = mDatasetValuesY[i];
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 QgsMeshVectorRenderer::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 ) )
289 double xVal = mDatasetValuesX[i];
290 double yVal = mDatasetValuesY[i];
291 if ( nodataValue( xVal, yVal ) )
294 double V = mDatasetValuesMag[i];
295 QgsPointXY lineStart = mContext.mapToPixel().transform( center.
x(), center.
y() );
297 drawVectorArrow( lineStart, xVal, yVal, V );
301 void QgsMeshVectorRenderer::drawVectorDataOnGrid(
const QList<int> &trianglesInExtent )
303 int cellx = mCfg.userGridCellWidth();
304 int celly = mCfg.userGridCellHeight();
306 const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
307 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
309 for (
const int i : trianglesInExtent )
311 if ( mContext.renderingStopped() )
316 const int v1 = face[0], v2 = face[1], v3 = face[2];
317 const QgsPoint p1 = vertices[v1], p2 = vertices[v2], p3 = vertices[v3];
319 const int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces()[i];
322 QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( p1, p2, p3 );
323 int left, right, top, bottom;
324 QgsMeshLayerUtils::boundingBoxToScreenRectangle( mContext.mapToPixel(), mOutputSize, bbox, left, right, top, bottom );
327 if ( left % cellx != 0 )
328 left += cellx - ( left % cellx );
329 if ( right % cellx != 0 )
330 right -= ( right % cellx );
331 if ( top % celly != 0 )
332 top += celly - ( top % celly );
333 if ( bottom % celly != 0 )
334 bottom -= ( bottom % celly );
336 for (
int y = top; y <= bottom; y += celly )
338 for (
int x = left; x <= right; x += cellx )
341 const QgsPointXY p = mContext.mapToPixel().toMapCoordinates( x, y );
343 if ( mDataOnVertices )
346 QgsMeshLayerUtils::interpolateFromVerticesData(
354 QgsMeshLayerUtils::interpolateFromVerticesData(
365 QgsMeshLayerUtils::interpolateFromFacesData(
367 mDatasetValuesX[nativeFaceIndex],
372 QgsMeshLayerUtils::interpolateFromFacesData(
374 mDatasetValuesY[nativeFaceIndex],
380 if ( nodataValue( val.
x(), val.
y() ) )
384 drawVectorArrow( lineStart, val.
x(), val.
y(), val.
scalar() );
390 void QgsMeshVectorRenderer::drawVectorArrow(
const QgsPointXY &lineStart,
double xVal,
double yVal,
double magnitude )
394 double cosAlpha, sinAlpha;
395 if ( calcVectorLineEnd( lineEnd, vectorLength, cosAlpha, sinAlpha,
396 lineStart, xVal, yVal, magnitude ) )
402 QVector<QPointF> finalVectorHeadPoints( 3 );
404 double vectorHeadWidthRatio = mCfg.arrowHeadWidthRatio();
405 double vectorHeadLengthRatio = mCfg.arrowHeadLengthRatio();
408 vectorHeadPoints[0].
setX( -1.0 * vectorHeadLengthRatio );
409 vectorHeadPoints[0].
setY( vectorHeadWidthRatio * 0.5 );
412 vectorHeadPoints[1].
setX( 0.0 );
413 vectorHeadPoints[1].
setY( 0.0 );
416 vectorHeadPoints[2].
setX( -1.0 * vectorHeadLengthRatio );
417 vectorHeadPoints[2].
setY( -1.0 * vectorHeadWidthRatio * 0.5 );
420 for (
int j = 0; j < 3; j++ )
422 finalVectorHeadPoints[j].setX( lineEnd.
x()
423 + ( vectorHeadPoints[j].
x() * cosAlpha * vectorLength )
424 - ( vectorHeadPoints[j].y() * sinAlpha * vectorLength )
427 finalVectorHeadPoints[j].setY( lineEnd.
y()
428 - ( vectorHeadPoints[j].
x() * sinAlpha * vectorLength )
429 - ( vectorHeadPoints[j].y() * cosAlpha * vectorLength )
435 mContext.painter()->drawPolygon( finalVectorHeadPoints );
QPointF toQPointF() const
Converts a point to a QPointF.
A rectangle specified with double values.
Triangular/Derived Mesh is mesh with vertices in map coordinates.
Use antialiasing while drawing.
A class to represent a 2D point.
double convertToMapUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to map units.
void setY(double y)
Sets Y value.
Represents a mesh renderer settings for vector datasets.
void setY(double y)
Sets the y value of the point.
double y() const
Returns y value.
Point geometry type, with support for z-dimension and m-values.
void setX(double x)
Sets the x value of the point.
double scalar() const
Returns magnitude of vector for vector data or scalar value for scalar data.
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.
double x() const
Returns x value.
void setX(double x)
Sets X value.