QGIS API Documentation 3.43.0-Master (b60ef06885e)
qgsmeshutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmeshutils.cpp
3 ----------------------------
4 begin : April 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
18#include "qgsmeshutils.h"
19#include "qgsmeshlayer.h"
20#include "qgsmaptopixel.h"
21#include "qgsrendercontext.h"
22#include "qgstriangularmesh.h"
23#include "qgsmeshlayerutils.h"
25#include "qgsgeometry.h"
26#include "qgspolygon.h"
27#include "qgslinestring.h"
28
29#include <memory>
30
32 const QgsMeshLayer &layer,
33 const QgsMeshDatasetIndex &datasetIndex,
34 const QgsCoordinateReferenceSystem &destinationCrs,
35 const QgsCoordinateTransformContext &transformContext,
36 double mapUnitsPerPixel,
37 const QgsRectangle &extent,
38 QgsRasterBlockFeedback *feedback )
39{
40 if ( !layer.dataProvider() )
41 return nullptr;
42
43 if ( !datasetIndex.isValid() )
44 return nullptr;
45
46 const int widthPixel = static_cast<int>( extent.width() / mapUnitsPerPixel );
47 const int heightPixel = static_cast<int>( extent.height() / mapUnitsPerPixel );
48
49 const QgsPointXY center = extent.center();
50 const QgsMapToPixel mapToPixel( mapUnitsPerPixel,
51 center.x(),
52 center.y(),
53 widthPixel,
54 heightPixel,
55 0 );
56 const QgsCoordinateTransform transform( layer.crs(), destinationCrs, transformContext );
57
58 QgsRenderContext renderContext;
59 renderContext.setCoordinateTransform( transform );
60 renderContext.setMapToPixel( mapToPixel );
61 renderContext.setExtent( extent );
62
63 auto nativeMesh = std::make_unique<QgsMesh>();
64 layer.dataProvider()->populateMesh( nativeMesh.get() );
65 auto triangularMesh = std::make_unique<QgsTriangularMesh>();
66 triangularMesh->update( nativeMesh.get(), transform );
67
68 const QgsMeshDatasetGroupMetadata metadata = layer.datasetGroupMetadata( datasetIndex );
69 const QgsMeshDatasetGroupMetadata::DataType scalarDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
70 const int count = QgsMeshLayerUtils::datasetValuesCount( nativeMesh.get(), scalarDataType );
71 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues(
72 &layer,
73 datasetIndex,
74 0,
75 count );
76 if ( !vals.isValid() )
77 return nullptr;
78
79 const QVector<double> datasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals );
80 const QgsMeshDataBlock activeFaceFlagValues = layer.areFacesActive(
81 datasetIndex,
82 0,
83 nativeMesh->faces.count() );
84
85 QgsMeshLayerInterpolator interpolator(
86 *( triangularMesh.get() ),
87 datasetValues,
88 activeFaceFlagValues,
89 scalarDataType,
90 renderContext,
91 QSize( widthPixel, heightPixel )
92 );
93
94 return interpolator.block( 0, extent, widthPixel, heightPixel, feedback );
95}
96
98 const QgsTriangularMesh &triangularMesh,
99 const QgsMeshDataBlock &datasetValues,
100 const QgsMeshDataBlock &activeFlags,
102 const QgsCoordinateTransform &transform,
103 double mapUnitsPerPixel,
104 const QgsRectangle &extent,
105 QgsRasterBlockFeedback *feedback )
106{
107
108 const int widthPixel = static_cast<int>( extent.width() / mapUnitsPerPixel );
109 const int heightPixel = static_cast<int>( extent.height() / mapUnitsPerPixel );
110
111 const QgsPointXY center = extent.center();
112 const QgsMapToPixel mapToPixel( mapUnitsPerPixel,
113 center.x(),
114 center.y(),
115 widthPixel,
116 heightPixel,
117 0 );
118
119 QgsRenderContext renderContext;
120 renderContext.setCoordinateTransform( transform );
121 renderContext.setMapToPixel( mapToPixel );
122 renderContext.setExtent( extent );
123
124 const QVector<double> magnitudes = QgsMeshLayerUtils::calculateMagnitudes( datasetValues );
125
126 QgsMeshLayerInterpolator interpolator(
127 triangularMesh,
128 magnitudes,
129 activeFlags,
130 dataType,
131 renderContext,
132 QSize( widthPixel, heightPixel )
133 );
134
135 return interpolator.block( 0, extent, widthPixel, heightPixel, feedback );
136}
137
138std::unique_ptr< QgsPolygon > QgsMeshUtils::toPolygon( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
139{
140 QVector<QgsPoint> ring;
141 for ( int j = 0; j < face.size(); ++j )
142 {
143 int vertexId = face[j];
144 Q_ASSERT( vertexId < vertices.size() );
145 const QgsPoint &vertex = vertices[vertexId];
146 ring.append( vertex );
147 }
148 auto polygon = std::make_unique< QgsPolygon >();
149 polygon->setExteriorRing( new QgsLineString( ring ) );
150 return polygon;
151}
152
153QgsGeometry QgsMeshUtils::toGeometry( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
154{
155 return QgsGeometry( QgsMeshUtils::toPolygon( face, vertices ) );
156}
157
158static QSet<int> nativeElementsFromElements( const QList<int> &indexes, const QVector<int> &elementToNativeElements )
159{
160 QSet<int> nativeElements;
161 for ( const int index : indexes )
162 {
163 if ( index < elementToNativeElements.count() )
164 {
165 const int nativeIndex = elementToNativeElements[index];
166 nativeElements.insert( nativeIndex );
167 }
168 }
169 return nativeElements;
170}
171
172QSet<int> QgsMeshUtils::nativeFacesFromTriangles( const QList<int> &triangleIndexes, const QVector<int> &trianglesToNativeFaces )
173{
174 return nativeElementsFromElements( triangleIndexes, trianglesToNativeFaces );
175}
176
177QSet<int> QgsMeshUtils::nativeEdgesFromEdges( const QList<int> &edgesIndexes, const QVector<int> &edgesToNativeEdges )
178{
179 return nativeElementsFromElements( edgesIndexes, edgesToNativeEdges );
180}
181
182static double isLeft2D( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p )
183{
184 return ( p2.x() - p1.x() ) * ( p.y() - p1.y() ) - ( p.x() - p1.x() ) * ( p2.y() - p1.y() );
185}
186
187static bool isInTriangle2D( const QgsPoint &p, const QVector<QgsMeshVertex> &triangle )
188{
189 return ( ( isLeft2D( triangle[2], triangle[0], p ) * isLeft2D( triangle[2], triangle[0], triangle[1] ) >= 0 )
190 && ( isLeft2D( triangle[0], triangle[1], p ) * isLeft2D( triangle[0], triangle[1], triangle[2] ) >= 0 )
191 && ( isLeft2D( triangle[2], triangle[1], p ) * isLeft2D( triangle[2], triangle[1], triangle[0] ) >= 0 ) );
192}
193
194bool QgsMeshUtils::isInTriangleFace( const QgsPointXY point, const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
195{
196 if ( face.count() != 3 )
197 return false;
198
199 QVector<QgsMeshVertex> triangle( 3 );
200 for ( int i = 0; i < 3; ++i )
201 {
202 if ( face[i] >= vertices.count() )
203 return false;
204 triangle[i] = vertices[face[i]];
205 }
206
207 const QgsPoint p( point.x(), point.y() );
208
209 return isInTriangle2D( p, triangle );
210}
211
212QSet<int> QgsMeshUtils::nativeVerticesFromTriangles( const QList<int> &triangleIndexes, const QVector<QgsMeshFace> &triangles )
213{
214 QSet<int> uniqueVertices;
215 for ( int triangleIndex : triangleIndexes )
216 {
217 const QgsMeshFace triangle = triangles[triangleIndex];
218 for ( int i : triangle )
219 {
220 uniqueVertices.insert( i );
221 }
222 }
223 return uniqueVertices;
224}
225
226QSet<int> QgsMeshUtils::nativeVerticesFromEdges( const QList<int> &edgesIndexes, const QVector<QgsMeshEdge> &edges )
227{
228 QSet<int> uniqueVertices;
229 for ( int edgeIndex : edgesIndexes )
230 {
231 const QgsMeshEdge edge = edges[edgeIndex];
232 uniqueVertices.insert( edge.first );
233 uniqueVertices.insert( edge.second );
234 }
235 return uniqueVertices;
236}
237
238static void ENP_centroid_step( const QPolygonF &pX, double &cx, double &cy, double &signedArea, int i, int i1 )
239{
240 double x0 = 0.0; // Current vertex X
241 double y0 = 0.0; // Current vertex Y
242 double x1 = 0.0; // Next vertex X
243 double y1 = 0.0; // Next vertex Y
244 double a = 0.0; // Partial signed area
245
246 x0 = pX[i].x();
247 y0 = pX[i].y();
248 x1 = pX[i1].x();
249 y1 = pX[i1].y();
250 a = x0 * y1 - x1 * y0;
251 signedArea += a;
252 cx += ( x0 + x1 ) * a;
253 cy += ( y0 + y1 ) * a;
254}
255
256static void ENP_centroid( const QPolygonF &pX, double &cx, double &cy )
257{
258 // http://stackoverflow.com/questions/2792443/finding-the-centroid-of-a-polygon/2792459#2792459
259 cx = 0;
260 cy = 0;
261
262 if ( pX.isEmpty() )
263 return;
264
265 double signedArea = 0.0;
266
267 const QPointF &pt0 = pX.first();
268 QPolygonF localPolygon( pX.count() );
269 for ( int i = 0; i < pX.count(); ++i )
270 localPolygon[i] = pX.at( i ) - pt0;
271
272 // For all vertices except last
273 int i = 0;
274 for ( ; i < localPolygon.size() - 1; ++i )
275 {
276 ENP_centroid_step( localPolygon, cx, cy, signedArea, i, i + 1 );
277 }
278 // Do last vertex separately to avoid performing an expensive
279 // modulus operation in each iteration.
280 ENP_centroid_step( localPolygon, cx, cy, signedArea, i, 0 );
281
282 signedArea *= 0.5;
283 cx /= ( 6.0 * signedArea );
284 cy /= ( 6.0 * signedArea );
285
286 cx = cx + pt0.x();
287 cy = cy + pt0.y();
288}
289
290QgsMeshVertex QgsMeshUtils::centroid( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
291{
292 QVector<QPointF> points( face.size() );
293 for ( int j = 0; j < face.size(); ++j )
294 {
295 int index = face.at( j );
296 const QgsMeshVertex &vertex = vertices.at( index ); // we need vertices in map coordinate
297 points[j] = vertex.toQPointF();
298 }
299 QPolygonF poly( points );
300 double cx, cy;
301 ENP_centroid( poly, cx, cy );
302 return QgsMeshVertex( cx, cy );
303}
304
306{
307 //To have consistent clock wise orientation of triangles which is necessary for 3D rendering
308 //Check the clock wise, and if it is not counter clock wise, swap indexes to make the oientation counter clock wise
309 double ux = v1.x() - v0.x();
310 double uy = v1.y() - v0.y();
311 double vx = v2.x() - v0.x();
312 double vy = v2.y() - v0.y();
313
314 double crossProduct = ux * vy - uy * vx;
315 if ( crossProduct < 0 ) //CW -->change the orientation
316 {
317 std::swap( triangle[1], triangle[2] );
318 }
319}
Represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
A geometry is the spatial representation of a feature.
Line string geometry type, with support for z-dimension and m-values.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:84
Perform transforms between map coordinates and device coordinates.
A block of integers/doubles from a mesh dataset.
bool isValid() const
Whether the block is valid.
virtual void populateMesh(QgsMesh *mesh) const =0
Populates the mesh vertices, edges and faces.
A collection of dataset group metadata such as whether the data is vector or scalar,...
DataType dataType() const
Returns whether dataset group data is defined on vertices or faces or volumes.
DataType
Location of where data is specified for datasets in the dataset group.
An index that identifies the dataset group (e.g.
bool isValid() const
Returns whether index is valid, ie at least groups is set.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QgsMeshDataBlock areFacesActive(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns whether the faces are active for particular dataset.
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
QgsMeshDatasetGroupMetadata datasetGroupMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset groups metadata.
static bool isInTriangleFace(const QgsPointXY point, const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Tests if point p is on the face defined with vertices.
static QSet< int > nativeEdgesFromEdges(const QList< int > &edgesIndexes, const QVector< int > &edgesToNativeEdges)
Returns unique native faces indexes from list of triangle indexes.
static std::unique_ptr< QgsPolygon > toPolygon(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns face as polygon geometry, caller is responsible for delete.
static QgsGeometry toGeometry(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns face as polygon geometry.
static void setCounterClockwise(QgsMeshFace &triangle, const QgsMeshVertex &v0, const QgsMeshVertex &v1, const QgsMeshVertex &v2)
Checks if the triangle is counter clockwise, if not sets it counter clockwise.
static QSet< int > nativeVerticesFromEdges(const QList< int > &edgesIndexes, const QVector< QgsMeshEdge > &edges)
Returns unique native faces indexes from list of vertices of triangles.
static QgsMeshVertex centroid(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns the centroid of the face.
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.
static QgsRasterBlock * exportRasterBlock(const QgsMeshLayer &layer, const QgsMeshDatasetIndex &datasetIndex, const QgsCoordinateReferenceSystem &destinationCrs, const QgsCoordinateTransformContext &transformContext, double mapUnitsPerPixel, const QgsRectangle &extent, QgsRasterBlockFeedback *feedback=nullptr)
Exports mesh layer's dataset values as raster block.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
QPointF toQPointF() const
Returns the point as a QPointF.
Definition qgspoint.h:376
double x
Definition qgspoint.h:52
double y
Definition qgspoint.h:53
Feedback object tailored for raster block reading.
Raster data container.
A rectangle specified with double values.
QgsPointXY center
Contains information about the context of a rendering operation.
void setCoordinateTransform(const QgsCoordinateTransform &t)
Sets the current coordinate transform for the context.
void setExtent(const QgsRectangle &extent)
When rendering a map layer, calling this method sets the "clipping" extent for the layer (in the laye...
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
A triangular/derived mesh with vertices in map coordinates.
QVector< int > QgsMeshFace
List of vertex indexes.
QPair< int, int > QgsMeshEdge
Edge is a straight line seqment between 2 points.
QgsPoint QgsMeshVertex
xyz coords of vertex