QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgstriangularmesh.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgstriangularmesh.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 <memory>
19 #include <QList>
20 #include "qgspolygon.h"
21 #include "qgslinestring.h"
22 #include "qgstriangularmesh.h"
23 #include "qgsrendercontext.h"
24 #include "qgscoordinatetransform.h"
25 #include "qgsfeatureid.h"
26 #include "qgsgeometry.h"
27 #include "qgsrectangle.h"
28 #include "qgsfeatureiterator.h"
29 #include "qgslogger.h"
30 
32 
33 QgsMeshFeatureIterator::QgsMeshFeatureIterator( QgsMesh *mesh )
35  , mMesh( mesh )
36 {}
37 
38 QgsMeshFeatureIterator::~QgsMeshFeatureIterator() = default;
39 
40 bool QgsMeshFeatureIterator::rewind()
41 {
42  it = 0;
43  return true;
44 }
45 bool QgsMeshFeatureIterator::close()
46 {
47  mMesh = nullptr;
48  return true;
49 }
50 
51 bool QgsMeshFeatureIterator::fetchFeature( QgsFeature &f )
52 {
53  if ( !mMesh || mMesh->faces.size() <= it )
54  return false;
55 
56  const QgsMeshFace &face = mMesh->faces.at( it ) ;
57  QgsGeometry geom = QgsMeshUtils::toGeometry( face, mMesh->vertices );
58  f.setGeometry( geom );
59  f.setId( it );
60  ++it;
61  return true;
62 }
63 
64 
66 
67 static void ENP_centroid_step( const QPolygonF &pX, double &cx, double &cy, double &signedArea, int i, int i1 )
68 {
69  double x0 = 0.0; // Current vertex X
70  double y0 = 0.0; // Current vertex Y
71  double x1 = 0.0; // Next vertex X
72  double y1 = 0.0; // Next vertex Y
73  double a = 0.0; // Partial signed area
74 
75  x0 = pX[i].x();
76  y0 = pX[i].y();
77  x1 = pX[i1].x();
78  y1 = pX[i1].y();
79  a = x0 * y1 - x1 * y0;
80  signedArea += a;
81  cx += ( x0 + x1 ) * a;
82  cy += ( y0 + y1 ) * a;
83 }
84 
85 static void ENP_centroid( const QPolygonF &pX, double &cx, double &cy )
86 {
87  // http://stackoverflow.com/questions/2792443/finding-the-centroid-of-a-polygon/2792459#2792459
88  cx = 0;
89  cy = 0;
90 
91  if ( pX.isEmpty() )
92  return;
93 
94  double signedArea = 0.0;
95 
96  // For all vertices except last
97  int i = 0;
98  for ( ; i < pX.size() - 1; ++i )
99  {
100  ENP_centroid_step( pX, cx, cy, signedArea, i, i + 1 );
101  }
102  // Do last vertex separately to avoid performing an expensive
103  // modulus operation in each iteration.
104  ENP_centroid_step( pX, cx, cy, signedArea, i, 0 );
105 
106  signedArea *= 0.5;
107  cx /= ( 6.0 * signedArea );
108  cy /= ( 6.0 * signedArea );
109 }
110 
112 {
113  Q_ASSERT( nativeMesh );
114  Q_ASSERT( context );
115 
116  // FIND OUT IF UPDATE IS NEEDED
117  if ( mTriangularMesh.vertices.size() >= nativeMesh->vertices.size() &&
118  mTriangularMesh.faces.size() >= nativeMesh->faces.size() &&
119  mCoordinateTransform.sourceCrs() == context->coordinateTransform().sourceCrs() &&
120  mCoordinateTransform.destinationCrs() == context->coordinateTransform().destinationCrs() )
121  return;
122 
123  // CLEAN-UP
124  mTriangularMesh.vertices.clear();
125  mTriangularMesh.faces.clear();
126  mTrianglesToNativeFaces.clear();
127  mNativeMeshFaceCentroids.clear();
128 
129  // TRANSFORM VERTICES
130  mCoordinateTransform = context->coordinateTransform();
131  mTriangularMesh.vertices.resize( nativeMesh->vertices.size() );
132  for ( int i = 0; i < nativeMesh->vertices.size(); ++i )
133  {
134  const QgsMeshVertex &vertex = nativeMesh->vertices.at( i );
135  if ( mCoordinateTransform.isValid() )
136  {
137  try
138  {
139  QgsPointXY mapPoint = mCoordinateTransform.transform( QgsPointXY( vertex.x(), vertex.y() ) );
140  QgsMeshVertex mapVertex( mapPoint );
141  mapVertex.setZ( vertex.z() );
142  mapVertex.setM( vertex.m() );
143  mTriangularMesh.vertices[i] = mapVertex;
144  }
145  catch ( QgsCsException &cse )
146  {
147  Q_UNUSED( cse );
148  QgsDebugMsg( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
149  mTriangularMesh.vertices[i] = vertex;
150  }
151  }
152  else
153  {
154  mTriangularMesh.vertices[i] = vertex;
155  }
156  }
157 
158  // CREATE TRIANGULAR MESH
159  for ( int i = 0; i < nativeMesh->faces.size(); ++i )
160  {
161  const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
162  if ( face.size() == 3 )
163  {
164  // triangle
165  mTriangularMesh.faces.push_back( face );
166  mTrianglesToNativeFaces.push_back( i );
167  }
168  else if ( face.size() == 4 )
169  {
170  // quad
171  QgsMeshFace face1;
172  face1.push_back( face[0] );
173  face1.push_back( face[1] );
174  face1.push_back( face[2] );
175 
176  mTriangularMesh.faces.push_back( face1 );
177  mTrianglesToNativeFaces.push_back( i );
178 
179  QgsMeshFace face2;
180  face2.push_back( face[0] );
181  face2.push_back( face[2] );
182  face2.push_back( face[3] );
183 
184  mTriangularMesh.faces.push_back( face2 );
185  mTrianglesToNativeFaces.push_back( i );
186  }
187  }
188 
189  // CALCULATE CENTROIDS
190  mNativeMeshFaceCentroids.resize( nativeMesh->faces.size() );
191  for ( int i = 0; i < nativeMesh->faces.size(); ++i )
192  {
193  const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
194  QVector<QPointF> points;
195  points.reserve( face.size() );
196  for ( int j = 0; j < face.size(); ++j )
197  {
198  int index = face.at( j );
199  const QgsMeshVertex &vertex = mTriangularMesh.vertices[index]; // we need projected vertices
200  points.push_back( vertex.toQPointF() );
201  }
202  QPolygonF poly( points );
203  double cx, cy;
204  ENP_centroid( poly, cx, cy );
205  mNativeMeshFaceCentroids[i] = QgsMeshVertex( cx, cy );
206  }
207 
208  // CALCULATE SPATIAL INDEX
209  mSpatialIndex = QgsSpatialIndex( new QgsMeshFeatureIterator( &mTriangularMesh ) );
210 }
211 
212 const QVector<QgsMeshVertex> &QgsTriangularMesh::vertices() const
213 {
214  return mTriangularMesh.vertices;
215 }
216 
217 const QVector<QgsMeshFace> &QgsTriangularMesh::triangles() const
218 {
219  return mTriangularMesh.faces;
220 }
221 
222 const QVector<QgsMeshVertex> &QgsTriangularMesh::centroids() const
223 {
224  return mNativeMeshFaceCentroids;
225 }
226 
227 const QVector<int> &QgsTriangularMesh::trianglesToNativeFaces() const
228 {
229  return mTrianglesToNativeFaces;
230 }
231 
233 {
234  const QList<QgsFeatureId> faceIndexes = mSpatialIndex.intersects( QgsRectangle( point, point ) );
235  for ( const QgsFeatureId fid : faceIndexes )
236  {
237  int faceIndex = static_cast<int>( fid );
238  const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
239  const QgsGeometry geom = QgsMeshUtils::toGeometry( face, mTriangularMesh.vertices );
240  if ( geom.contains( &point ) )
241  return faceIndex;
242  }
243  return -1;
244 }
245 
246 QList<int> QgsTriangularMesh::faceIndexesForRectangle( const QgsRectangle &rectangle ) const
247 {
248  const QList<QgsFeatureId> faceIndexes = mSpatialIndex.intersects( rectangle );
249  QList<int> indexes;
250  for ( const QgsFeatureId fid : faceIndexes )
251  {
252  int faceIndex = static_cast<int>( fid );
253  indexes.append( faceIndex );
254  }
255  return indexes;
256 }
257 
258 QgsGeometry QgsMeshUtils::toGeometry( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
259 {
260  QVector<QgsPoint> ring;
261  for ( int j = 0; j < face.size(); ++j )
262  {
263  int vertexId = face[j];
264  Q_ASSERT( vertexId < vertices.size() );
265  const QgsPoint &vertex = vertices[vertexId];
266  ring.append( vertex );
267  }
268  std::unique_ptr< QgsPolygon > polygon = qgis::make_unique< QgsPolygon >();
269  polygon->setExteriorRing( new QgsLineString( ring ) );
270  return QgsGeometry( std::move( polygon ) );
271 }
272 
273 QList<int> QgsMeshUtils::nativeFacesFromTriangles( const QList<int> &triangleIndexes, const QVector<int> &trianglesToNativeFaces )
274 {
275  QSet<int> nativeFaces;
276  for ( const int triangleIndex : triangleIndexes )
277  {
278  const int nativeIndex = trianglesToNativeFaces[triangleIndex];
279  nativeFaces.insert( nativeIndex );
280  }
281  return nativeFaces.toList();
282 }
int faceIndexForPoint(const QgsPointXY &point) const
Finds index of triangle at given point It uses spatial indexing.
QgsGeometry toGeometry(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns face as polygon geometry.
A rectangle specified with double values.
Definition: qgsrectangle.h:40
double y
Definition: qgspoint.h:42
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
void setZ(double z)
Sets the point&#39;s z-coordinate.
Definition: qgspoint.h:237
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsPointXY transform(const QgsPointXY &point, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source coordinate reference system, which the transform will transform coordinates from...
A class to represent a 2D point.
Definition: qgspointxy.h:43
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:106
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
bool contains(const QgsPointXY *p) const
Tests for containment of a point (uses GEOS)
QVector< QgsMeshVertex > vertices
vertices
void setM(double m)
Sets the point&#39;s m-value.
Definition: qgspoint.h:252
Internal feature iterator to be implemented within data providers.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void setId(QgsFeatureId id)
Sets the feature ID for this feature.
Definition: qgsfeature.cpp:112
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QList< int > nativeFacesFromTriangles(const QList< int > &triangleIndexes, const QVector< int > &trianglesToNativeFaces)
Returns unique native faces indexes from list of triangle indexes.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
QString what() const
Definition: qgsexception.h:48
void update(QgsMesh *nativeMesh, QgsRenderContext *context)
Constructs triangular mesh from layer&#39;s native mesh and context.
Contains information about the context of a rendering operation.
Mesh - vertices and faces.
A spatial index for QgsFeature objects.
const QVector< QgsMeshVertex > & centroids() const
Returns centroids of the native faces in map CRS.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
QVector< int > QgsMeshFace
List of vertex indexes.
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
double z
Definition: qgspoint.h:43
QVector< QgsMeshFace > faces
faces
const QVector< int > & trianglesToNativeFaces() const
Returns mapping between triangles and original faces.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
QList< int > faceIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of triangles intersecting given bounding box It uses spatial indexing.
double m
Definition: qgspoint.h:44
QgsPoint QgsMeshVertex
xyz coords of vertex
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QPointF toQPointF() const
Returns the point as a QPointF.
Definition: qgspoint.h:264
double x
Definition: qgspoint.h:41