QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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
20#include <memory>
21
22#include "qgsgeometry.h"
23#include "qgslinestring.h"
24#include "qgsmaptopixel.h"
25#include "qgsmeshlayer.h"
27#include "qgsmeshlayerutils.h"
28#include "qgspolygon.h"
29#include "qgsrendercontext.h"
30#include "qgstriangularmesh.h"
31
33 const QgsMeshLayer &layer,
34 const QgsMeshDatasetIndex &datasetIndex,
35 const QgsCoordinateReferenceSystem &destinationCrs,
36 const QgsCoordinateTransformContext &transformContext,
37 double mapUnitsPerPixel,
38 const QgsRectangle &extent,
39 QgsRasterBlockFeedback *feedback )
40{
41 if ( !layer.dataProvider() )
42 return nullptr;
43
44 if ( !datasetIndex.isValid() )
45 return nullptr;
46
47 const int widthPixel = static_cast<int>( extent.width() / mapUnitsPerPixel );
48 const int heightPixel = static_cast<int>( extent.height() / mapUnitsPerPixel );
49
50 const QgsPointXY center = extent.center();
51 const QgsMapToPixel mapToPixel( mapUnitsPerPixel,
52 center.x(),
53 center.y(),
54 widthPixel,
55 heightPixel,
56 0 );
57 const QgsCoordinateTransform transform( layer.crs(), destinationCrs, transformContext );
58
59 QgsRenderContext renderContext;
60 renderContext.setCoordinateTransform( transform );
61 renderContext.setMapToPixel( mapToPixel );
62 renderContext.setExtent( extent );
63
64 auto nativeMesh = std::make_unique<QgsMesh>();
65 layer.dataProvider()->populateMesh( nativeMesh.get() );
66 auto triangularMesh = std::make_unique<QgsTriangularMesh>();
67 triangularMesh->update( nativeMesh.get(), transform );
68
69 const QgsMeshDatasetGroupMetadata metadata = layer.datasetGroupMetadata( datasetIndex );
70 const QgsMeshDatasetGroupMetadata::DataType scalarDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
71 const int count = QgsMeshLayerUtils::datasetValuesCount( nativeMesh.get(), scalarDataType );
72 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues(
73 &layer,
74 datasetIndex,
75 0,
76 count );
77 if ( !vals.isValid() )
78 return nullptr;
79
80 const QVector<double> datasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals );
81 const QgsMeshDataBlock activeFaceFlagValues = layer.areFacesActive(
82 datasetIndex,
83 0,
84 nativeMesh->faces.count() );
85
86 QgsMeshLayerInterpolator interpolator(
87 *( triangularMesh.get() ),
88 datasetValues,
89 activeFaceFlagValues,
90 scalarDataType,
91 renderContext,
92 QSize( widthPixel, heightPixel )
93 );
94
95 return interpolator.block( 0, extent, widthPixel, heightPixel, feedback );
96}
97
99 const QgsTriangularMesh &triangularMesh,
100 const QgsMeshDataBlock &datasetValues,
101 const QgsMeshDataBlock &activeFlags,
103 const QgsCoordinateTransform &transform,
104 double mapUnitsPerPixel,
105 const QgsRectangle &extent,
106 QgsRasterBlockFeedback *feedback )
107{
108
109 const int widthPixel = static_cast<int>( extent.width() / mapUnitsPerPixel );
110 const int heightPixel = static_cast<int>( extent.height() / mapUnitsPerPixel );
111
112 const QgsPointXY center = extent.center();
113 const QgsMapToPixel mapToPixel( mapUnitsPerPixel,
114 center.x(),
115 center.y(),
116 widthPixel,
117 heightPixel,
118 0 );
119
120 QgsRenderContext renderContext;
121 renderContext.setCoordinateTransform( transform );
122 renderContext.setMapToPixel( mapToPixel );
123 renderContext.setExtent( extent );
124
125 const QVector<double> magnitudes = QgsMeshLayerUtils::calculateMagnitudes( datasetValues );
126
127 QgsMeshLayerInterpolator interpolator(
128 triangularMesh,
129 magnitudes,
130 activeFlags,
131 dataType,
132 renderContext,
133 QSize( widthPixel, heightPixel )
134 );
135
136 return interpolator.block( 0, extent, widthPixel, heightPixel, feedback );
137}
138
139std::unique_ptr< QgsPolygon > QgsMeshUtils::toPolygon( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
140{
141 QVector<QgsPoint> ring;
142 for ( int j = 0; j < face.size(); ++j )
143 {
144 int vertexId = face[j];
145 Q_ASSERT( vertexId < vertices.size() );
146 const QgsPoint &vertex = vertices[vertexId];
147 ring.append( vertex );
148 }
149 auto polygon = std::make_unique< QgsPolygon >();
150 polygon->setExteriorRing( new QgsLineString( ring ) );
151 return polygon;
152}
153
154QgsGeometry QgsMeshUtils::toGeometry( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
155{
156 return QgsGeometry( QgsMeshUtils::toPolygon( face, vertices ) );
157}
158
159static QSet<int> nativeElementsFromElements( const QList<int> &indexes, const QVector<int> &elementToNativeElements )
160{
161 QSet<int> nativeElements;
162 for ( const int index : indexes )
163 {
164 if ( index < elementToNativeElements.count() )
165 {
166 const int nativeIndex = elementToNativeElements[index];
167 nativeElements.insert( nativeIndex );
168 }
169 }
170 return nativeElements;
171}
172
173QSet<int> QgsMeshUtils::nativeFacesFromTriangles( const QList<int> &triangleIndexes, const QVector<int> &trianglesToNativeFaces )
174{
175 return nativeElementsFromElements( triangleIndexes, trianglesToNativeFaces );
176}
177
178QSet<int> QgsMeshUtils::nativeEdgesFromEdges( const QList<int> &edgesIndexes, const QVector<int> &edgesToNativeEdges )
179{
180 return nativeElementsFromElements( edgesIndexes, edgesToNativeEdges );
181}
182
183static double isLeft2D( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p )
184{
185 return ( p2.x() - p1.x() ) * ( p.y() - p1.y() ) - ( p.x() - p1.x() ) * ( p2.y() - p1.y() );
186}
187
188static bool isInTriangle2D( const QgsPoint &p, const QVector<QgsMeshVertex> &triangle )
189{
190 return ( ( isLeft2D( triangle[2], triangle[0], p ) * isLeft2D( triangle[2], triangle[0], triangle[1] ) >= 0 )
191 && ( isLeft2D( triangle[0], triangle[1], p ) * isLeft2D( triangle[0], triangle[1], triangle[2] ) >= 0 )
192 && ( isLeft2D( triangle[2], triangle[1], p ) * isLeft2D( triangle[2], triangle[1], triangle[0] ) >= 0 ) );
193}
194
195bool QgsMeshUtils::isInTriangleFace( const QgsPointXY point, const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
196{
197 if ( face.count() != 3 )
198 return false;
199
200 QVector<QgsMeshVertex> triangle( 3 );
201 for ( int i = 0; i < 3; ++i )
202 {
203 if ( face[i] >= vertices.count() )
204 return false;
205 triangle[i] = vertices[face[i]];
206 }
207
208 const QgsPoint p( point.x(), point.y() );
209
210 return isInTriangle2D( p, triangle );
211}
212
213QSet<int> QgsMeshUtils::nativeVerticesFromTriangles( const QList<int> &triangleIndexes, const QVector<QgsMeshFace> &triangles )
214{
215 QSet<int> uniqueVertices;
216 for ( int triangleIndex : triangleIndexes )
217 {
218 const QgsMeshFace triangle = triangles[triangleIndex];
219 for ( int i : triangle )
220 {
221 uniqueVertices.insert( i );
222 }
223 }
224 return uniqueVertices;
225}
226
227QSet<int> QgsMeshUtils::nativeVerticesFromEdges( const QList<int> &edgesIndexes, const QVector<QgsMeshEdge> &edges )
228{
229 QSet<int> uniqueVertices;
230 for ( int edgeIndex : edgesIndexes )
231 {
232 const QgsMeshEdge edge = edges[edgeIndex];
233 uniqueVertices.insert( edge.first );
234 uniqueVertices.insert( edge.second );
235 }
236 return uniqueVertices;
237}
238
239static void ENP_centroid_step( const QPolygonF &pX, double &cx, double &cy, double &signedArea, int i, int i1 )
240{
241 double x0 = 0.0; // Current vertex X
242 double y0 = 0.0; // Current vertex Y
243 double x1 = 0.0; // Next vertex X
244 double y1 = 0.0; // Next vertex Y
245 double a = 0.0; // Partial signed area
246
247 x0 = pX[i].x();
248 y0 = pX[i].y();
249 x1 = pX[i1].x();
250 y1 = pX[i1].y();
251 a = x0 * y1 - x1 * y0;
252 signedArea += a;
253 cx += ( x0 + x1 ) * a;
254 cy += ( y0 + y1 ) * a;
255}
256
257static void ENP_centroid( const QPolygonF &pX, double &cx, double &cy )
258{
259 // http://stackoverflow.com/questions/2792443/finding-the-centroid-of-a-polygon/2792459#2792459
260 cx = 0;
261 cy = 0;
262
263 if ( pX.isEmpty() )
264 return;
265
266 double signedArea = 0.0;
267
268 const QPointF &pt0 = pX.first();
269 QPolygonF localPolygon( pX.count() );
270 for ( int i = 0; i < pX.count(); ++i )
271 localPolygon[i] = pX.at( i ) - pt0;
272
273 // For all vertices except last
274 int i = 0;
275 for ( ; i < localPolygon.size() - 1; ++i )
276 {
277 ENP_centroid_step( localPolygon, cx, cy, signedArea, i, i + 1 );
278 }
279 // Do last vertex separately to avoid performing an expensive
280 // modulus operation in each iteration.
281 ENP_centroid_step( localPolygon, cx, cy, signedArea, i, 0 );
282
283 signedArea *= 0.5;
284 cx /= ( 6.0 * signedArea );
285 cy /= ( 6.0 * signedArea );
286
287 cx = cx + pt0.x();
288 cy = cy + pt0.y();
289}
290
291QgsMeshVertex QgsMeshUtils::centroid( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
292{
293 QVector<QPointF> points( face.size() );
294 for ( int j = 0; j < face.size(); ++j )
295 {
296 int index = face.at( j );
297 const QgsMeshVertex &vertex = vertices.at( index ); // we need vertices in map coordinate
298 points[j] = vertex.toQPointF();
299 }
300 QPolygonF poly( points );
301 double cx, cy;
302 ENP_centroid( poly, cx, cy );
303 return QgsMeshVertex( cx, cy );
304}
305
307{
308 //To have consistent clock wise orientation of triangles which is necessary for 3D rendering
309 //Check the clock wise, and if it is not counter clock wise, swap indexes to make the oientation counter clock wise
310 double ux = v1.x() - v0.x();
311 double uy = v1.y() - v0.y();
312 double vx = v2.x() - v0.x();
313 double vy = v2.y() - v0.y();
314
315 double crossProduct = ux * vy - uy * vx;
316 if ( crossProduct < 0 ) //CW -->change the orientation
317 {
318 std::swap( triangle[1], triangle[2] );
319 }
320}
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:87
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