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