QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsmeshtriangulation.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmeshtriangulation.cpp
3 -----------------
4 begin : August 9th, 2020
5 copyright : (C) 2020 by Vincent Cloarec
6 email : vcloarec 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 <memory>
21
22#include "qgscurve.h"
23#include "qgscurvepolygon.h"
25#include "qgsfeature.h"
26#include "qgsfeatureiterator.h"
27#include "qgsfeedback.h"
28#include "qgslogger.h"
29#include "qgsmesheditor.h"
30#include "qgsmulticurve.h"
31#include "qgsmultisurface.h"
32
33#include "moc_qgsmeshtriangulation.cpp"
34
36 : QObject()
37{
38 mTriangulation = std::make_unique<QgsDualEdgeTriangulation>();
39}
40
41
43
44bool QgsMeshTriangulation::addVertices( QgsFeatureIterator &vertexFeatureIterator, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback, long featureCount )
45{
46 if ( feedback )
47 feedback->setProgress( 0 );
48
49 QgsFeature feat;
50 long i = 0;
51 while ( vertexFeatureIterator.nextFeature( feat ) )
52 {
53 if ( feedback )
54 {
55 if ( feedback->isCanceled() )
56 break;
57
58 feedback->setProgress( 100 * i / featureCount );
59 i++;
60 }
61
62 addVerticesFromFeature( feat, valueAttribute, transform, feedback );
63 }
64
65 return true;
66}
67
68bool QgsMeshTriangulation::addBreakLines( QgsFeatureIterator &lineFeatureIterator, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback, long featureCount )
69{
70 if ( feedback )
71 feedback->setProgress( 0 );
72
73 QgsFeature feat;
74 long i = 0;
75 while ( lineFeatureIterator.nextFeature( feat ) )
76 {
77 if ( feedback )
78 {
79 if ( feedback->isCanceled() )
80 break;
81
82 feedback->setProgress( 100 * i / featureCount );
83 i++;
84 }
85
86 Qgis::GeometryType geomType = feat.geometry().type();
87 switch ( geomType )
88 {
90 addVerticesFromFeature( feat, valueAttribute, transform, feedback );
91 break;
94 addBreakLinesFromFeature( feat, valueAttribute, transform, feedback );
95 break;
96 default:
97 break;
98 }
99 }
100
101 return true;
102}
103
105{
106 return mTriangulation->addPoint( vertex );
107}
108
110{
111 return mTriangulation->triangulationToMesh( feedback );
112}
113
115{
116 mCrs = crs;
117}
118
119void QgsMeshTriangulation::addVerticesFromFeature( const QgsFeature &feature, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback )
120{
121 QgsGeometry geom = feature.geometry();
122 try
123 {
124 geom.transform( transform, Qgis::TransformDirection::Forward, true );
125 }
126 catch ( QgsCsException &cse )
127 {
128 Q_UNUSED( cse )
129 QgsDebugMsgLevel( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ), 4 );
130 return;
131 }
132
133 QgsAbstractGeometry::vertex_iterator vit = geom.vertices_begin();
134
135 double value = 0;
136 if ( valueAttribute >= 0 )
137 value = feature.attribute( valueAttribute ).toDouble();
138
139 while ( vit != geom.vertices_end() )
140 {
141 if ( feedback && feedback->isCanceled() )
142 break;
143 if ( valueAttribute < 0 )
144 mTriangulation->addPoint( *vit );
145 else
146 {
147 mTriangulation->addPoint( QgsPoint( Qgis::WkbType::PointZ, ( *vit ).x(), ( *vit ).y(), value ) );
148 }
149 ++vit;
150 }
151}
152
153void QgsMeshTriangulation::addBreakLinesFromFeature( const QgsFeature &feature, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback )
154{
155 double valueOnVertex = 0;
156 if ( valueAttribute >= 0 )
157 valueOnVertex = feature.attribute( valueAttribute ).toDouble();
158
159 //from QgsTinInterpolator::insertData()
160 std::vector<const QgsCurve *> curves;
161 QgsGeometry geom = feature.geometry();
162 try
163 {
164 geom.transform( transform, Qgis::TransformDirection::Forward, true );
165 }
166 catch ( QgsCsException &cse )
167 {
168 Q_UNUSED( cse )
169 QgsDebugMsgLevel( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ), 4 );
170 return;
171 }
172
174 {
175 std::vector<const QgsCurvePolygon *> polygons;
176 if ( geom.isMultipart() )
177 {
178 const QgsMultiSurface *ms = qgsgeometry_cast<const QgsMultiSurface *>( geom.constGet() );
179 for ( int i = 0; i < ms->numGeometries(); ++i )
180 {
181 polygons.emplace_back( qgsgeometry_cast<const QgsCurvePolygon *>( ms->geometryN( i ) ) );
182 }
183 }
184 else
185 {
186 polygons.emplace_back( qgsgeometry_cast<const QgsCurvePolygon *>( geom.constGet() ) );
187 }
188
189 for ( const QgsCurvePolygon *polygon : polygons )
190 {
191 if ( feedback && feedback->isCanceled() )
192 break;
193 if ( !polygon )
194 continue;
195
196 if ( polygon->exteriorRing() )
197 curves.emplace_back( polygon->exteriorRing() );
198
199 for ( int i = 0; i < polygon->numInteriorRings(); ++i )
200 {
201 if ( feedback && feedback->isCanceled() )
202 break;
203 curves.emplace_back( polygon->interiorRing( i ) );
204 }
205 }
206 }
207 else
208 {
209 if ( geom.isMultipart() )
210 {
211 const QgsMultiCurve *mc = qgsgeometry_cast<const QgsMultiCurve *>( geom.constGet() );
212 for ( int i = 0; i < mc->numGeometries(); ++i )
213 {
214 if ( feedback && feedback->isCanceled() )
215 break;
216 curves.emplace_back( qgsgeometry_cast<const QgsCurve *>( mc->geometryN( i ) ) );
217 }
218 }
219 else
220 {
221 curves.emplace_back( qgsgeometry_cast<const QgsCurve *>( geom.constGet() ) );
222 }
223 }
224
225 for ( const QgsCurve *curve : curves )
226 {
227 if ( !curve )
228 continue;
229
230 if ( feedback && feedback->isCanceled() )
231 break;
232
233 QgsPointSequence linePoints;
234 curve->points( linePoints );
235 bool hasZ = curve->is3D();
236 if ( valueAttribute >= 0 )
237 for ( int i = 0; i < linePoints.count(); ++i )
238 {
239 if ( feedback && feedback->isCanceled() )
240 break;
241 if ( hasZ )
242 linePoints[i].setZ( valueOnVertex );
243 else
244 {
245 const QgsPoint &point = linePoints.at( i );
246 linePoints[i] = QgsPoint( point.x(), point.y(), valueOnVertex );
247 }
248 }
249
250 mTriangulation->addLine( linePoints, QgsInterpolator::SourceType::BreakLines );
251 }
252}
253
254QgsMeshZValueDatasetGroup::QgsMeshZValueDatasetGroup( const QString &datasetGroupName, const QgsMesh &mesh )
255 : QgsMeshDatasetGroup( datasetGroupName, QgsMeshDatasetGroupMetadata::DataOnVertices )
256{
257 mDataset = std::make_unique<QgsMeshZValueDataset>( mesh );
258}
259
264
266{
267 if ( datasetIndex != 0 )
268 return QgsMeshDatasetMetadata();
269
270 return mDataset->metadata();
271}
272
274
276{
277 if ( index != 0 )
278 return nullptr;
279
280 return mDataset.get();
281}
282
283QDomElement QgsMeshZValueDatasetGroup::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
284{
285 Q_UNUSED( doc );
286 Q_UNUSED( context );
287 return QDomElement();
288}
289
291 : mMesh( mesh )
292{
293 for ( const QgsMeshVertex &vertex : mesh.vertices )
294 {
295 if ( vertex.z() < mZMinimum )
296 mZMinimum = vertex.z();
297 if ( vertex.z() > mZMaximum )
298 mZMaximum = vertex.z();
299 }
300}
301
303{
304 if ( valueIndex < 0 || valueIndex >= mMesh.vertexCount() )
305 return QgsMeshDatasetValue();
306
307 return QgsMeshDatasetValue( mMesh.vertex( valueIndex ).z() );
308}
309
310QgsMeshDataBlock QgsMeshZValueDataset::datasetValues( bool isScalar, int valueIndex, int count ) const
311{
312 Q_UNUSED( isScalar )
314 QVector<double> zValues( count );
315 for ( int i = valueIndex; i < valueIndex + count; ++i )
316 zValues[i - valueIndex] = mMesh.vertex( i ).z();
317 ret.setValues( zValues );
318 return ret;
319}
320
322{
323 Q_UNUSED( faceIndex );
324 Q_UNUSED( count );
326 ret.setValid( true );
327 return ret;
328}
329
330bool QgsMeshZValueDataset::isActive( int faceIndex ) const
331{
332 return ( faceIndex > 0 && faceIndex < mMesh.faceCount() );
333}
334
336{
337 return QgsMeshDatasetMetadata( 0, true, mZMinimum, mZMaximum, 0 );
338}
339
341{
342 return mMesh.vertexCount();
343}
344
346
348{
349 return QObject::tr( "Delaunay triangulation" );
350}
351
352QgsTopologicalMesh::Changes QgsMeshEditingDelaunayTriangulation::apply( QgsMeshEditor *meshEditor )
353{
354 //use only vertices that are on boundary or free, if boundary
355 QList<int> vertexIndextoTriangulate;
356
357 QList<int> removedVerticesFromTriangulation;
358
359 for ( const int vertexIndex : std::as_const( mInputVertices ) )
360 {
361 if ( meshEditor->isVertexFree( vertexIndex ) || meshEditor->isVertexOnBoundary( vertexIndex ) )
362 vertexIndextoTriangulate.append( vertexIndex );
363 else
364 removedVerticesFromTriangulation.append( vertexIndex );
365 }
366
367 bool triangulationReady = false;
368 bool giveUp = false;
369 QgsTopologicalMesh::TopologicalFaces topologicFaces;
370
371 while ( !triangulationReady )
372 {
373 QgsMeshTriangulation triangulation;
374
375 QVector<int> triangulationVertexToMeshVertex( vertexIndextoTriangulate.count() );
376 const QgsMesh *destinationMesh = meshEditor->topologicalMesh().mesh();
377
378 for ( int i = 0; i < vertexIndextoTriangulate.count(); ++i )
379 {
380 triangulationVertexToMeshVertex[i] = vertexIndextoTriangulate.at( i );
381 triangulation.addVertex( destinationMesh->vertices.at( vertexIndextoTriangulate.at( i ) ) );
382 }
383
384 QgsMesh resultingTriangulation = triangulation.triangulatedMesh();
385
386 //Transform the new mesh triangulation to destination mesh faces
387 QVector<QgsMeshFace> rawDestinationFaces = resultingTriangulation.faces;
388
389 for ( QgsMeshFace &destinationFace : rawDestinationFaces )
390 {
391 for ( int &vertexIndex : destinationFace )
392 vertexIndex = triangulationVertexToMeshVertex[vertexIndex];
393 }
394
395 //The new triangulation may contains faces that intersect existing faces, we need to remove them
396 QVector<QgsMeshFace> destinationFaces;
397 for ( const QgsMeshFace &face : rawDestinationFaces )
398 {
399 if ( meshEditor->isFaceGeometricallyCompatible( face ) )
400 destinationFaces.append( face );
401 }
402
403 bool facesReady = false;
404 QgsMeshEditingError previousError;
405 while ( !facesReady && !giveUp )
406 {
407 QgsMeshEditingError error;
408 topologicFaces = QgsTopologicalMesh::createNewTopologicalFaces( destinationFaces, true, error );
409
410 if ( error == QgsMeshEditingError() )
411 error = meshEditor->topologicalMesh().facesCanBeAdded( topologicFaces );
412
413 switch ( error.errorType )
414 {
416 facesReady = true;
417 triangulationReady = true;
418 break;
423 if ( error.elementIndex != -1 )
424 destinationFaces.remove( error.elementIndex );
425 else
426 giveUp = true; //we don't know what happens, better to give up
427 break;
430 facesReady = true;
431 if ( error.elementIndex != -1 )
432 {
433 removedVerticesFromTriangulation.append( error.elementIndex );
434 vertexIndextoTriangulate.removeOne( error.elementIndex );
435 }
436 else
437 giveUp = true; //we don't know what happens, better to give up
438 break;
439 }
440 }
441 }
442
443 Q_ASSERT( meshEditor->topologicalMesh().checkConsistency() == QgsMeshEditingError() );
444
445 if ( !removedVerticesFromTriangulation.isEmpty() )
446 mMessage = QObject::tr( "%n vertices have not been included in the triangulation", nullptr, removedVerticesFromTriangulation.count() );
447
448 mIsFinished = true;
449
450 if ( triangulationReady && !giveUp )
451 return meshEditor->topologicalMesh().addFaces( topologicFaces );
452 else
453 return QgsTopologicalMesh::Changes();
454}
@ TooManyVerticesInFace
A face has more vertices than the maximum number supported per face.
Definition qgis.h:1662
@ InvalidFace
An error occurs due to an invalid face (for example, vertex indexes are unordered).
Definition qgis.h:1661
@ UniqueSharedVertex
A least two faces share only one vertices.
Definition qgis.h:1664
@ ManifoldFace
ManifoldFace.
Definition qgis.h:1666
@ InvalidVertex
An error occurs due to an invalid vertex (for example, vertex index is out of range the available ver...
Definition qgis.h:1665
@ FlatFace
A flat face is present.
Definition qgis.h:1663
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:358
@ Point
Points.
Definition qgis.h:359
@ Line
Lines.
Definition qgis.h:360
@ Polygon
Polygons.
Definition qgis.h:361
@ PointZ
PointZ.
Definition qgis.h:295
@ Forward
Forward transform (from source to destination).
Definition qgis.h:2672
Represents a coordinate reference system (CRS).
Handles coordinate transforms between two coordinate systems.
QString what() const
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsGeometry geometry
Definition qgsfeature.h:69
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
A geometry is the spatial representation of a feature.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Qgis::GeometryType type
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
A block of integers/doubles from a mesh dataset.
@ ScalarDouble
Scalar double values.
@ ActiveFlagInteger
Integer boolean flag whether face is active.
void setValues(const QVector< double > &vals)
Sets values.
void setValid(bool valid)
Sets block validity.
A collection of dataset group metadata such as whether the data is vector or scalar,...
QgsMeshDatasetGroup()=default
void calculateStatistic() const
Calculates the statistics (minimum and maximum).
Represents mesh dataset metadata, such as whether the data is valid or the associated time.
Represents a single mesh dataset value.
Abstract class that represents a mesh dataset.
QString text() const override
Returns a short text string describing what this advanced edit does. Default implementation return a ...
Qgis::MeshEditingErrorType errorType
Handles edit operations on a mesh layer.
bool isFaceGeometricallyCompatible(const QgsMeshFace &face) const
Returns true if the face does not intersect or contains any other elements (faces or vertices) The to...
bool isVertexOnBoundary(int vertexIndex) const
Returns whether the vertex with index vertexIndex is on a boundary.
bool isVertexFree(int vertexIndex) const
Returns whether the vertex with index vertexIndex is a free vertex.
QgsTopologicalMesh & topologicalMesh()
Returns a reference to the topological mesh.
bool addBreakLines(QgsFeatureIterator &lineFeatureIterator, int valueAttribute, const QgsCoordinateTransform &transformContext, QgsFeedback *feedback=nullptr, long featureCount=1)
Adds break lines from a vector layer, return true if successful.
~QgsMeshTriangulation() override
int addVertex(const QgsPoint &vertex)
Adds a new vertex in the triangulation and returns the index of the new vertex.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the coordinate reference system used for the triangulation.
bool addVertices(QgsFeatureIterator &vertexFeatureIterator, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback=nullptr, long featureCount=1)
Adds vertices to the triangulation from a feature iterator, return true if successful.
QgsMesh triangulatedMesh(QgsFeedback *feedback=nullptr) const
Returns the triangulated mesh.
int datasetCount() const override
Returns the count of datasets in the group.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const override
Write dataset group information in a DOM element.
void initialize() override
Initialize the dataset group.
QgsMeshDataset * dataset(int index) const override
Returns the dataset with index.
QgsMeshDatasetMetadata datasetMetadata(int datasetIndex) const override
Returns the metadata of the dataset with index datasetIndex.
QgsMeshZValueDatasetGroup(const QString &datasetGroupName, const QgsMesh &mesh)
Constructor.
QgsMeshDataBlock areFacesActive(int faceIndex, int count) const override
Returns whether faces are active.
QgsMeshDatasetMetadata metadata() const override
Returns the metadata of the dataset.
QgsMeshZValueDataset(const QgsMesh &mesh)
Constructor with the mesh.
bool isActive(int faceIndex) const override
Returns whether the face is active.
int valuesCount() const override
Returns the values count.
QgsMeshDatasetValue datasetValue(int valueIndex) const override
Returns the value with index valueIndex.
QgsMeshDataBlock datasetValues(bool isScalar, int valueIndex, int count) const override
Returns count values from valueIndex.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double x
Definition qgspoint.h:52
double y
Definition qgspoint.h:53
A container for the context for various read/write operations on objects.
Contains topological differences between two states of a topological mesh, only accessible from the Q...
QgsMeshEditingError checkConsistency() const
Checks the consistency of the topological mesh and return false if there is a consistency issue.
QgsMeshEditingError facesCanBeAdded(const TopologicalFaces &topologicalFaces) const
Returns whether the faces can be added to the mesh.
Changes addFaces(const TopologicalFaces &topologicFaces)
Adds faces topologicFaces to the topologic mesh.
QgsMesh * mesh() const
Returns a pointer to the wrapped mesh.
static TopologicalFaces createNewTopologicalFaces(const QVector< QgsMeshFace > &faces, bool uniqueSharedVertexAllowed, QgsMeshEditingError &error)
Creates new topological faces that are not yet included in the mesh.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsPoint > QgsPointSequence
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
QVector< int > QgsMeshFace
List of vertex indexes.
QgsPoint QgsMeshVertex
xyz coords of vertex
Mesh - vertices, edges and faces.
QVector< QgsMeshVertex > vertices
QVector< QgsMeshFace > faces