QGIS API Documentation 4.3.0-Master (a9c95da348f)
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 QgsMeshLayerRendererFeedback *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;
64 renderer
65 = new QgsMeshVectorStreamlineRenderer( m, datasetVectorValues, scalarActiveFaceFlagValues, datasetValuesMag, dataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVertices, settings, context, layerExtent, feedBack, datasetMagMaximumValue );
66 break;
68 renderer
69 = new QgsMeshVectorTraceRenderer( m, datasetVectorValues, scalarActiveFaceFlagValues, dataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVertices, settings, context, layerExtent, datasetMagMaximumValue );
70 break;
71 }
72
73 return renderer;
74}
75
76QgsMeshVectorGlyphRenderer::QgsMeshVectorGlyphRenderer(
77 const QgsTriangularMesh &m,
78 const QgsMeshDataBlock &datasetValues,
79 const QVector<double> &datasetValuesMag,
80 double datasetMagMaximumValue,
81 double datasetMagMinimumValue,
83 const QgsVectorFieldSettings &settings,
84 QgsRenderContext &context,
85 QSize size
86)
87 : mTriangularMesh( m )
88 , mDatasetValues( datasetValues )
89 , mDatasetValuesMag( datasetValuesMag )
90 , mMinMag( datasetMagMinimumValue )
91 , mMaxMag( datasetMagMaximumValue )
92 , mDataType( dataType )
93 , mBufferedExtent( context.mapExtent() )
94 , mContext( context )
95 , mCfg( settings )
96 , mOutputSize( size )
97 , mEngine( datasetMagMaximumValue, datasetMagMinimumValue, settings, context, size )
98{
99 // should be checked in caller
100 Q_ASSERT( !mDatasetValuesMag.empty() );
101 Q_ASSERT( !std::isnan( mMinMag ) );
102 Q_ASSERT( !std::isnan( mMaxMag ) );
103 Q_ASSERT( mDatasetValues.isValid() );
104 Q_ASSERT( QgsMeshDataBlock::Vector2DDouble == mDatasetValues.type() );
105
106 // we need to expand out the extent so that it includes
107 // arrows which start or end up outside of the
108 // actual visible extent
109 const double extension = context.convertToMapUnits( calcExtentBufferSize(), Qgis::RenderUnit::Pixels );
110 mBufferedExtent.setXMinimum( mBufferedExtent.xMinimum() - extension );
111 mBufferedExtent.setXMaximum( mBufferedExtent.xMaximum() + extension );
112 mBufferedExtent.setYMinimum( mBufferedExtent.yMinimum() - extension );
113 mBufferedExtent.setYMaximum( mBufferedExtent.yMaximum() + extension );
114}
115
116QgsMeshVectorGlyphRenderer::~QgsMeshVectorGlyphRenderer() = default;
117
118void QgsMeshVectorGlyphRenderer::draw()
119{
120 if ( mCfg.isOnUserDefinedGrid() )
121 {
122 drawVectorDataOnGrid();
123 }
125 {
126 drawVectorDataOnVertices();
127 }
129 {
130 drawVectorDataOnFaces();
131 }
133 {
134 drawVectorDataOnEdges();
135 }
136}
137
138double QgsMeshVectorGlyphRenderer::calcExtentBufferSize() const
139{
140 double buffer = 0;
141 switch ( mCfg.arrowSettings().shaftLengthMethod() )
142 {
144 {
145 buffer = mContext.convertToPainterUnits( mCfg.arrowSettings().maxShaftLength(), Qgis::RenderUnit::Millimeters );
146 break;
147 }
149 {
150 buffer = mCfg.arrowSettings().scaleFactor() * mMaxMag;
151 break;
152 }
154 {
155 buffer = mContext.convertToPainterUnits( mCfg.arrowSettings().fixedShaftLength(), Qgis::RenderUnit::Millimeters );
156 break;
157 }
158 }
159
160 if ( mCfg.filterMax() >= 0 && buffer > mCfg.filterMax() )
161 buffer = mCfg.filterMax();
162
163 if ( buffer < 0.0 )
164 buffer = 0.0;
165
166 return buffer;
167}
168
169void QgsMeshVectorGlyphRenderer::drawVectorDataOnVertices()
170{
171 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
172 QSet<int> verticesToDraw;
173
174 // currently expecting that triangulation does not add any new extra vertices on the way
175 Q_ASSERT( mDatasetValuesMag.count() == vertices.count() );
176
177 // find all vertices from faces to render
178 {
179 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
180 const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
181 verticesToDraw.unite( QgsMeshUtils::nativeVerticesFromTriangles( trianglesInExtent, triangles ) );
182 }
183
184 // find all vertices from edges to render
185 {
186 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( mBufferedExtent );
187 const QVector<QgsMeshEdge> &edges = mTriangularMesh.edges();
188 verticesToDraw.unite( QgsMeshUtils::nativeVerticesFromEdges( edgesInExtent, edges ) );
189 }
190
191 // render
192 drawVectorDataOnPoints( verticesToDraw, vertices );
193}
194
195void QgsMeshVectorGlyphRenderer::drawVectorDataOnPoints( const QSet<int> indexesToRender, const QVector<QgsMeshVertex> &points )
196{
197 for ( const int i : indexesToRender )
198 {
199 if ( mContext.renderingStopped() )
200 break;
201
202 const QgsPointXY center = points.at( i );
203 if ( !mBufferedExtent.contains( center ) )
204 continue;
205
206 const QgsMeshDatasetValue val = mDatasetValues.value( i );
207 const double xVal = val.x();
208 const double yVal = val.y();
209 if ( nodataValue( xVal, yVal ) )
210 continue;
211
212 const double V = mDatasetValuesMag[i]; // pre-calculated magnitude
213 const QgsPointXY lineStart = mContext.mapToPixel().transform( center.x(), center.y() );
214
215 mEngine.drawGlyph( lineStart, xVal, yVal, V );
216 }
217}
218
219void QgsMeshVectorGlyphRenderer::drawVectorDataOnFaces()
220{
221 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
222 const QVector<QgsMeshVertex> &centroids = mTriangularMesh.faceCentroids();
223 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent, mTriangularMesh.trianglesToNativeFaces() );
224 drawVectorDataOnPoints( nativeFacesInExtent, centroids );
225}
226
227void QgsMeshVectorGlyphRenderer::drawVectorDataOnEdges()
228{
229 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( mBufferedExtent );
230 const QVector<QgsMeshVertex> &centroids = mTriangularMesh.edgeCentroids();
231 const QSet<int> nativeEdgesInExtent = QgsMeshUtils::nativeEdgesFromEdges( edgesInExtent, mTriangularMesh.edgesToNativeEdges() );
232 drawVectorDataOnPoints( nativeEdgesInExtent, centroids );
233}
234
235void QgsMeshVectorGlyphRenderer::drawVectorDataOnGrid()
236{
238 return;
239
240 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mBufferedExtent );
241 const int cellx = mCfg.userGridCellWidth();
242 const int celly = mCfg.userGridCellHeight();
243
244 const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
245 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
246
247 for ( const int i : trianglesInExtent )
248 {
249 if ( mContext.renderingStopped() )
250 break;
251
252 const QgsMeshFace &face = triangles[i];
253
254 const int v1 = face[0], v2 = face[1], v3 = face[2];
255 const QgsPoint p1 = vertices[v1], p2 = vertices[v2], p3 = vertices[v3];
256
257 const int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces()[i];
258
259 // Get the BBox of the element in pixels
260 const QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( p1, p2, p3 );
261 int left, right, top, bottom;
262 QgsMeshLayerUtils::boundingBoxToScreenRectangle( mContext.mapToPixel(), mOutputSize, bbox, left, right, top, bottom );
263
264 // Align rect to the grid (e.g. interval <13, 36> with grid cell 10 will be trimmed to <20,30>
265 if ( left % cellx != 0 )
266 left += cellx - ( left % cellx );
267 if ( right % cellx != 0 )
268 right -= ( right % cellx );
269 if ( top % celly != 0 )
270 top += celly - ( top % celly );
271 if ( bottom % celly != 0 )
272 bottom -= ( bottom % celly );
273
274 for ( int y = top; y <= bottom; y += celly )
275 {
276 for ( int x = left; x <= right; x += cellx )
277 {
279 const QgsPointXY p = mContext.mapToPixel().toMapCoordinates( x, y );
280
282 {
283 const auto val1 = mDatasetValues.value( v1 );
284 const auto val2 = mDatasetValues.value( v2 );
285 const auto val3 = mDatasetValues.value( v3 );
286 val.setX( QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.x(), val2.x(), val3.x(), p ) );
287 val.setY( QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), p ) );
288 }
290 {
291 const auto val1 = mDatasetValues.value( nativeFaceIndex );
292 val.setX( QgsMeshLayerUtils::interpolateFromFacesData( p1, p2, p3, val1.x(), p ) );
293 val.setY( QgsMeshLayerUtils::interpolateFromFacesData( p1, p2, p3, val1.y(), p ) );
294 }
295 if ( nodataValue( val.x(), val.y() ) )
296 continue;
297
298 const QgsPointXY lineStart( x, y );
299 mEngine.drawGlyph( lineStart, val.x(), val.y(), val.scalar() );
300 }
301 }
302 }
303}
304
@ WindBarbs
Displaying vector dataset with wind barbs.
Definition qgis.h:7150
@ Arrows
Displaying vector dataset with arrows.
Definition qgis.h:7147
@ Traces
Displaying vector dataset with particle traces.
Definition qgis.h:7149
@ Streamlines
Displaying vector dataset with streamlines.
Definition qgis.h:7148
@ Millimeters
Millimeters.
Definition qgis.h:5717
@ Pixels
Pixels.
Definition qgis.h:5719
@ Fixed
Use fixed length fixedShaftLength() regardless of vector's magnitude.
Definition qgis.h:7134
@ Scaled
Scale vector magnitude by factor scaleFactor().
Definition qgis.h:7133
@ MinMax
Scale vector magnitude linearly to fit in range of vectorFilterMin() and vectorFilterMax().
Definition qgis.h:7132
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
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.