QGIS API Documentation 4.3.0-Master (9dce731a7a1)
Loading...
Searching...
No Matches
qgsmeshvectorrenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmeshvectorrenderer.cpp
3 -------------------------
4 begin : May 2018
5 copyright : (C) 2018 by Peter Petrik
6 email : zilolv at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include <cmath>
21
22#include "qgsmaptopixel.h"
23#include "qgsmeshlayerutils.h"
25#include "qgsrendercontext.h"
26#include "qgstriangularmesh.h"
27
28#include <QPainter>
29#include <QPen>
30
32
33inline bool nodataValue( double x, double y )
34{
35 return ( std::isnan( x ) || std::isnan( y ) );
36}
37
38QgsMeshVectorRenderer::~QgsMeshVectorRenderer() = default;
39
40QgsMeshVectorRenderer *QgsMeshVectorRenderer::makeVectorRenderer(
41 const QgsTriangularMesh &m,
42 const QgsMeshDataBlock &datasetVectorValues,
43 const QgsMeshDataBlock &scalarActiveFaceFlagValues,
44 const QVector<double> &datasetValuesMag,
45 double datasetMagMaximumValue,
46 double datasetMagMinimumValue,
48 const QgsVectorFieldSettings &settings,
49 QgsRenderContext &context,
50 const QgsRectangle &layerExtent,
51 QgsRasterBlockFeedback *feedBack,
52 const QSize &size
53)
54{
55 QgsMeshVectorRenderer *renderer = nullptr;
56
57 switch ( settings.symbology() )
58 {
61 renderer = new QgsMeshVectorGlyphRenderer( m, datasetVectorValues, datasetValuesMag, datasetMagMaximumValue, datasetMagMinimumValue, dataType, settings, context, size );
62 break;
65 renderer
66 = new QgsMeshVectorStreamlineRenderer( m, datasetVectorValues, scalarActiveFaceFlagValues, datasetValuesMag, datasetMagMaximumValue, datasetMagMinimumValue, dataType, settings, context, layerExtent, feedBack, size );
67 break;
68 }
69
70 return renderer;
71}
72
73QgsMeshVectorGlyphRenderer::QgsMeshVectorGlyphRenderer(
74 const QgsTriangularMesh &m,
75 const QgsMeshDataBlock &datasetValues,
76 const QVector<double> &datasetValuesMag,
77 double datasetMagMaximumValue,
78 double datasetMagMinimumValue,
80 const QgsVectorFieldSettings &settings,
81 QgsRenderContext &context,
82 QSize size
83)
84 : mTriangularMesh( m )
85 , mDatasetValues( datasetValues )
86 , mDatasetValuesMag( datasetValuesMag )
87 , mMinMag( datasetMagMinimumValue )
88 , mMaxMag( datasetMagMaximumValue )
89 , mDataType( dataType )
90 , mBufferedExtent( context.mapExtent() )
91 , mContext( context )
92 , mCfg( settings )
93 , mOutputSize( size )
94 , mEngine( datasetMagMaximumValue, datasetMagMinimumValue, settings, context, size )
95{
96 // should be checked in caller
97 Q_ASSERT( !mDatasetValuesMag.empty() );
98 Q_ASSERT( !std::isnan( mMinMag ) );
99 Q_ASSERT( !std::isnan( mMaxMag ) );
100 Q_ASSERT( mDatasetValues.isValid() );
101 Q_ASSERT( QgsMeshDataBlock::Vector2DDouble == mDatasetValues.type() );
102
103 // we need to expand out the extent so that it includes
104 // arrows which start or end up outside of the
105 // actual visible extent
106 const double extension = context.convertToMapUnits( mEngine.glyphExtentBuffer(), Qgis::RenderUnit::Pixels );
107 mBufferedExtent.setXMinimum( mBufferedExtent.xMinimum() - extension );
108 mBufferedExtent.setXMaximum( mBufferedExtent.xMaximum() + extension );
109 mBufferedExtent.setYMinimum( mBufferedExtent.yMinimum() - extension );
110 mBufferedExtent.setYMaximum( mBufferedExtent.yMaximum() + extension );
111}
112
113QgsMeshVectorGlyphRenderer::~QgsMeshVectorGlyphRenderer() = default;
114
115void QgsMeshVectorGlyphRenderer::draw()
116{
117 if ( mCfg.isOnUserDefinedGrid() )
118 {
119 drawVectorDataOnGrid();
120 }
122 {
123 drawVectorDataOnVertices();
124 }
126 {
127 drawVectorDataOnFaces();
128 }
130 {
131 drawVectorDataOnEdges();
132 }
133}
134
135void QgsMeshVectorGlyphRenderer::drawVectorDataOnVertices()
136{
137 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
138 QSet<int> verticesToDraw;
139
140 // currently expecting that triangulation does not add any new extra vertices on the way
141 Q_ASSERT( mDatasetValuesMag.count() == vertices.count() );
142
143 // find all vertices from faces to render
144 {
145 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
146 const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
147 verticesToDraw.unite( QgsMeshUtils::nativeVerticesFromTriangles( trianglesInExtent, triangles ) );
148 }
149
150 // find all vertices from edges to render
151 {
152 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( mBufferedExtent );
153 const QVector<QgsMeshEdge> &edges = mTriangularMesh.edges();
154 verticesToDraw.unite( QgsMeshUtils::nativeVerticesFromEdges( edgesInExtent, edges ) );
155 }
156
157 // render
158 drawVectorDataOnPoints( verticesToDraw, vertices );
159}
160
161void QgsMeshVectorGlyphRenderer::drawVectorDataOnPoints( const QSet<int> indexesToRender, const QVector<QgsMeshVertex> &points )
162{
163 for ( const int i : indexesToRender )
164 {
165 if ( mContext.renderingStopped() )
166 break;
167
168 const QgsPointXY center = points.at( i );
169 if ( !mBufferedExtent.contains( center ) )
170 continue;
171
172 const QgsMeshDatasetValue val = mDatasetValues.value( i );
173 const double xVal = val.x();
174 const double yVal = val.y();
175 if ( nodataValue( xVal, yVal ) )
176 continue;
177
178 const double V = mDatasetValuesMag[i]; // pre-calculated magnitude
179 const QgsPointXY lineStart = mContext.mapToPixel().transform( center.x(), center.y() );
180
181 mEngine.drawGlyph( lineStart, xVal, yVal, V );
182 }
183}
184
185void QgsMeshVectorGlyphRenderer::drawVectorDataOnFaces()
186{
187 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
188 const QVector<QgsMeshVertex> &centroids = mTriangularMesh.faceCentroids();
189 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent, mTriangularMesh.trianglesToNativeFaces() );
190 drawVectorDataOnPoints( nativeFacesInExtent, centroids );
191}
192
193void QgsMeshVectorGlyphRenderer::drawVectorDataOnEdges()
194{
195 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( mBufferedExtent );
196 const QVector<QgsMeshVertex> &centroids = mTriangularMesh.edgeCentroids();
197 const QSet<int> nativeEdgesInExtent = QgsMeshUtils::nativeEdgesFromEdges( edgesInExtent, mTriangularMesh.edgesToNativeEdges() );
198 drawVectorDataOnPoints( nativeEdgesInExtent, centroids );
199}
200
201void QgsMeshVectorGlyphRenderer::drawVectorDataOnGrid()
202{
204 return;
205
206 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
207 const int cellx = mCfg.userGridCellWidth();
208 const int celly = mCfg.userGridCellHeight();
209
210 const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
211 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
212
213 for ( const int i : trianglesInExtent )
214 {
215 if ( mContext.renderingStopped() )
216 break;
217
218 const QgsMeshFace &face = triangles[i];
219
220 const int v1 = face[0], v2 = face[1], v3 = face[2];
221 const QgsPoint p1 = vertices[v1], p2 = vertices[v2], p3 = vertices[v3];
222
223 const int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces()[i];
224
225 // Get the BBox of the element in pixels
226 const QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( p1, p2, p3 );
227 int left, right, top, bottom;
228 QgsMeshLayerUtils::boundingBoxToScreenRectangle( mContext.mapToPixel(), mOutputSize, bbox, left, right, top, bottom );
229
230 // Align rect to the grid (e.g. interval <13, 36> with grid cell 10 will be trimmed to <20,30>
231 if ( left % cellx != 0 )
232 left += cellx - ( left % cellx );
233 if ( right % cellx != 0 )
234 right -= ( right % cellx );
235 if ( top % celly != 0 )
236 top += celly - ( top % celly );
237 if ( bottom % celly != 0 )
238 bottom -= ( bottom % celly );
239
240 for ( int y = top; y <= bottom; y += celly )
241 {
242 for ( int x = left; x <= right; x += cellx )
243 {
245 const QgsPointXY p = mContext.mapToPixel().toMapCoordinates( x, y );
246
248 {
249 const auto val1 = mDatasetValues.value( v1 );
250 const auto val2 = mDatasetValues.value( v2 );
251 const auto val3 = mDatasetValues.value( v3 );
252 val.setX( QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.x(), val2.x(), val3.x(), p ) );
253 val.setY( QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), p ) );
254 }
256 {
257 const auto val1 = mDatasetValues.value( nativeFaceIndex );
258 val.setX( QgsMeshLayerUtils::interpolateFromFacesData( p1, p2, p3, val1.x(), p ) );
259 val.setY( QgsMeshLayerUtils::interpolateFromFacesData( p1, p2, p3, val1.y(), p ) );
260 }
261 if ( nodataValue( val.x(), val.y() ) )
262 continue;
263
264 const QgsPointXY lineStart( x, y );
265 mEngine.drawGlyph( lineStart, val.x(), val.y(), val.scalar() );
266 }
267 }
268 }
269}
270
271QgsMeshVectorStreamlineRenderer::QgsMeshVectorStreamlineRenderer(
272 const QgsTriangularMesh &m,
273 const QgsMeshDataBlock &datasetValues,
274 const QgsMeshDataBlock &scalarActiveFaceFlagValues,
275 const QVector<double> &datasetValuesMag,
276 double datasetMagMaximumValue,
277 double datasetMagMinimumValue,
279 const QgsVectorFieldSettings &settings,
280 QgsRenderContext &context,
281 const QgsRectangle &layerExtent,
282 QgsRasterBlockFeedback *feedBack,
283 QSize size
284)
285 : mCfg( settings )
286 , mFeedBack( feedBack )
287 , mSource( QgsMeshVectorFieldValueSource::create( m, datasetValues, scalarActiveFaceFlagValues, datasetValuesMag, dataType, layerExtent, datasetMagMaximumValue ) )
288 , mEngine( datasetMagMaximumValue, datasetMagMinimumValue, settings, context, size )
289{}
290
291QgsMeshVectorStreamlineRenderer::~QgsMeshVectorStreamlineRenderer() = default;
292
293void QgsMeshVectorStreamlineRenderer::draw()
294{
295 switch ( mCfg.symbology() )
296 {
298 mEngine.drawStreamlines( std::move( mSource ), mFeedBack );
299 break;
301 mEngine.drawTraces( std::move( mSource ) );
302 break;
305 // drawn glyph by glyph, see QgsMeshVectorGlyphRenderer
306 break;
307 }
308}
309
@ WindBarbs
Displaying vector dataset with wind barbs.
Definition qgis.h:7250
@ Arrows
Displaying vector dataset with arrows.
Definition qgis.h:7247
@ Traces
Displaying vector dataset with particle traces.
Definition qgis.h:7249
@ Streamlines
Displaying vector dataset with streamlines.
Definition qgis.h:7248
@ Pixels
Pixels.
Definition qgis.h:5773
A block of integers/doubles from a mesh dataset.
@ Vector2DDouble
Vector double pairs (x1, y1, x2, y2, ... ).
DataType
Location of where data is specified for datasets in the dataset group.
@ DataOnEdges
Data is defined on edges.
@ DataOnFaces
Data is defined on faces.
@ DataOnVertices
Data is defined on vertices.
@ DataOnVolumes
Data is defined on volumes.
Represents a single mesh 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.
static QSet< int > nativeEdgesFromEdges(const QList< int > &edgesIndexes, const QVector< int > &edgesToNativeEdges)
Returns unique native faces indexes from list of triangle indexes.
static QSet< int > nativeVerticesFromEdges(const QList< int > &edgesIndexes, const QVector< QgsMeshEdge > &edges)
Returns unique native faces indexes from list of vertices of triangles.
static QSet< int > nativeVerticesFromTriangles(const QList< int > &triangleIndexes, const QVector< QgsMeshFace > &triangles)
Returns unique native vertex indexes from list of vertices of triangles.
static QSet< int > nativeFacesFromTriangles(const QList< int > &triangleIndexes, const QVector< int > &trianglesToNativeFaces)
Returns unique native faces indexes from list of triangle indexes.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
Feedback object tailored for raster block reading.
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.
A triangular/derived mesh with vertices in map coordinates.
Represents a renderer settings for vector datasets.
Qgis::VectorFieldSymbology symbology() const
Returns the displaying method used to render vector datasets.
QVector< int > QgsMeshFace
List of vertex indexes.