QGIS API Documentation 3.41.0-Master (af5edcb665c)
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#include "moc_qgsmeshtriangulation.cpp"
21#include "qgscurve.h"
22#include "qgscurvepolygon.h"
23#include "qgsmultisurface.h"
24#include "qgsmulticurve.h"
25#include "qgsfeedback.h"
26#include "qgslogger.h"
27#include "qgsmesheditor.h"
28#include "qgsfeature.h"
29#include "qgsfeatureiterator.h"
30
32 : QObject()
33{
34 mTriangulation.reset( new QgsDualEdgeTriangulation() );
35}
36
37
39
40bool QgsMeshTriangulation::addVertices( QgsFeatureIterator &vertexFeatureIterator, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback, long featureCount )
41{
42 if ( feedback )
43 feedback->setProgress( 0 );
44
45 QgsFeature feat;
46 long i = 0;
47 while ( vertexFeatureIterator.nextFeature( feat ) )
48 {
49 if ( feedback )
50 {
51 if ( feedback->isCanceled() )
52 break;
53
54 feedback->setProgress( 100 * i / featureCount );
55 i++;
56 }
57
58 addVerticesFromFeature( feat, valueAttribute, transform, feedback );
59 }
60
61 return true;
62}
63
64bool QgsMeshTriangulation::addBreakLines( QgsFeatureIterator &lineFeatureIterator, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback, long featureCount )
65{
66 if ( feedback )
67 feedback->setProgress( 0 );
68
69 QgsFeature feat;
70 long i = 0;
71 while ( lineFeatureIterator.nextFeature( feat ) )
72 {
73 if ( feedback )
74 {
75 if ( feedback->isCanceled() )
76 break;
77
78 feedback->setProgress( 100 * i / featureCount );
79 i++;
80 }
81
82 Qgis::GeometryType geomType = feat.geometry().type();
83 switch ( geomType )
84 {
86 addVerticesFromFeature( feat, valueAttribute, transform, feedback );
87 break;
90 addBreakLinesFromFeature( feat, valueAttribute, transform, feedback );
91 break;
92 default:
93 break;
94 }
95 }
96
97 return true;
98}
99
101{
102 return mTriangulation->addPoint( vertex );
103}
104
106{
107 return mTriangulation->triangulationToMesh( feedback );
108}
109
114
115void QgsMeshTriangulation::addVerticesFromFeature( const QgsFeature &feature, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback )
116{
117 QgsGeometry geom = feature.geometry();
118 try
119 {
120 geom.transform( transform, Qgis::TransformDirection::Forward, true );
121 }
122 catch ( QgsCsException &cse )
123 {
124 Q_UNUSED( cse )
125 QgsDebugMsgLevel( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ), 4 );
126 return;
127 }
128
130
131 double value = 0;
132 if ( valueAttribute >= 0 )
133 value = feature.attribute( valueAttribute ).toDouble();
134
135 while ( vit != geom.vertices_end() )
136 {
137 if ( feedback && feedback->isCanceled() )
138 break;
139 if ( valueAttribute < 0 )
140 mTriangulation->addPoint( *vit );
141 else
142 {
143 mTriangulation->addPoint( QgsPoint( Qgis::WkbType::PointZ, ( *vit ).x(), ( *vit ).y(), value ) );
144 }
145 ++vit;
146 }
147}
148
149void QgsMeshTriangulation::addBreakLinesFromFeature( const QgsFeature &feature, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback )
150{
151 double valueOnVertex = 0;
152 if ( valueAttribute >= 0 )
153 valueOnVertex = feature.attribute( valueAttribute ).toDouble();
154
155 //from QgsTinInterpolator::insertData()
156 std::vector<const QgsCurve *> curves;
157 QgsGeometry geom = feature.geometry();
158 try
159 {
160 geom.transform( transform, Qgis::TransformDirection::Forward, true );
161 }
162 catch ( QgsCsException &cse )
163 {
164 Q_UNUSED( cse )
165 QgsDebugMsgLevel( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ), 4 );
166 return;
167 }
168
170 {
171 std::vector<const QgsCurvePolygon *> polygons;
172 if ( geom.isMultipart() )
173 {
174 const QgsMultiSurface *ms = qgsgeometry_cast<const QgsMultiSurface *>( geom.constGet() );
175 for ( int i = 0; i < ms->numGeometries(); ++i )
176 {
177 polygons.emplace_back( qgsgeometry_cast<const QgsCurvePolygon *>( ms->geometryN( i ) ) );
178 }
179 }
180 else
181 {
182 polygons.emplace_back( qgsgeometry_cast<const QgsCurvePolygon *>( geom.constGet() ) );
183 }
184
185 for ( const QgsCurvePolygon *polygon : polygons )
186 {
187 if ( feedback && feedback->isCanceled() )
188 break;
189 if ( !polygon )
190 continue;
191
192 if ( polygon->exteriorRing() )
193 curves.emplace_back( polygon->exteriorRing() );
194
195 for ( int i = 0; i < polygon->numInteriorRings(); ++i )
196 {
197 if ( feedback && feedback->isCanceled() )
198 break;
199 curves.emplace_back( polygon->interiorRing( i ) );
200 }
201 }
202 }
203 else
204 {
205 if ( geom.isMultipart() )
206 {
207 const QgsMultiCurve *mc = qgsgeometry_cast<const QgsMultiCurve *>( geom.constGet() );
208 for ( int i = 0; i < mc->numGeometries(); ++i )
209 {
210 if ( feedback && feedback->isCanceled() )
211 break;
212 curves.emplace_back( qgsgeometry_cast<const QgsCurve *>( mc->geometryN( i ) ) );
213 }
214 }
215 else
216 {
217 curves.emplace_back( qgsgeometry_cast<const QgsCurve *>( geom.constGet() ) );
218 }
219 }
220
221 for ( const QgsCurve *curve : curves )
222 {
223 if ( !curve )
224 continue;
225
226 if ( feedback && feedback->isCanceled() )
227 break;
228
229 QgsPointSequence linePoints;
230 curve->points( linePoints );
231 bool hasZ = curve->is3D();
232 if ( valueAttribute >= 0 )
233 for ( int i = 0; i < linePoints.count(); ++i )
234 {
235 if ( feedback && feedback->isCanceled() )
236 break;
237 if ( hasZ )
238 linePoints[i].setZ( valueOnVertex );
239 else
240 {
241 const QgsPoint &point = linePoints.at( i );
242 linePoints[i] = QgsPoint( point.x(), point.y(), valueOnVertex );
243 }
244 }
245
246 mTriangulation->addLine( linePoints, QgsInterpolator::SourceBreakLines );
247 }
248}
249
250QgsMeshZValueDatasetGroup::QgsMeshZValueDatasetGroup( const QString &datasetGroupName, const QgsMesh &mesh )
251 : QgsMeshDatasetGroup( datasetGroupName, QgsMeshDatasetGroupMetadata::DataOnVertices )
252{
253 mDataset = std::make_unique<QgsMeshZValueDataset>( mesh );
254}
255
260
262{
263 if ( datasetIndex != 0 )
264 return QgsMeshDatasetMetadata();
265
266 return mDataset->metadata();
267}
268
270
272{
273 if ( index != 0 )
274 return nullptr;
275
276 return mDataset.get();
277}
278
279QDomElement QgsMeshZValueDatasetGroup::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
280{
281 Q_UNUSED( doc );
282 Q_UNUSED( context );
283 return QDomElement();
284}
285
287 : mMesh( mesh )
288{
289 for ( const QgsMeshVertex &vertex : mesh.vertices )
290 {
291 if ( vertex.z() < mZMinimum )
292 mZMinimum = vertex.z();
293 if ( vertex.z() > mZMaximum )
294 mZMaximum = vertex.z();
295 }
296}
297
299{
300 if ( valueIndex < 0 || valueIndex >= mMesh.vertexCount() )
301 return QgsMeshDatasetValue();
302
303 return QgsMeshDatasetValue( mMesh.vertex( valueIndex ).z() );
304}
305
306QgsMeshDataBlock QgsMeshZValueDataset::datasetValues( bool isScalar, int valueIndex, int count ) const
307{
308 Q_UNUSED( isScalar )
310 QVector<double> zValues( count );
311 for ( int i = valueIndex; i < valueIndex + count; ++i )
312 zValues[i - valueIndex] = mMesh.vertex( i ).z();
313 ret.setValues( zValues );
314 return ret;
315}
316
318{
319 Q_UNUSED( faceIndex );
320 Q_UNUSED( count );
322 ret.setValid( true );
323 return ret;
324}
325
326bool QgsMeshZValueDataset::isActive( int faceIndex ) const
327{
328 return ( faceIndex > 0 && faceIndex < mMesh.faceCount() );
329}
330
332{
333 return QgsMeshDatasetMetadata( 0, true, mZMinimum, mZMaximum, 0 );
334}
335
337{
338 return mMesh.vertexCount();
339}
340
342
344{
345 return QObject::tr( "Delaunay triangulation" );
346}
347
348QgsTopologicalMesh::Changes QgsMeshEditingDelaunayTriangulation::apply( QgsMeshEditor *meshEditor )
349{
350 //use only vertices that are on boundary or free, if boundary
351 QList<int> vertexIndextoTriangulate;
352
353 QList<int> removedVerticesFromTriangulation;
354
355 for ( const int vertexIndex : std::as_const( mInputVertices ) )
356 {
357 if ( meshEditor->isVertexFree( vertexIndex ) || meshEditor->isVertexOnBoundary( vertexIndex ) )
358 vertexIndextoTriangulate.append( vertexIndex );
359 else
360 removedVerticesFromTriangulation.append( vertexIndex );
361 }
362
363 bool triangulationReady = false;
364 bool giveUp = false;
366
367 while ( !triangulationReady )
368 {
369 QgsMeshTriangulation triangulation;
370
371 QVector<int> triangulationVertexToMeshVertex( vertexIndextoTriangulate.count() );
372 const QgsMesh *destinationMesh = meshEditor->topologicalMesh().mesh();
373
374 for ( int i = 0; i < vertexIndextoTriangulate.count(); ++i )
375 {
376 triangulationVertexToMeshVertex[i] = vertexIndextoTriangulate.at( i );
377 triangulation.addVertex( destinationMesh->vertices.at( vertexIndextoTriangulate.at( i ) ) );
378 }
379
380 QgsMesh resultingTriangulation = triangulation.triangulatedMesh();
381
382 //Transform the new mesh triangulation to destination mesh faces
383 QVector<QgsMeshFace> rawDestinationFaces = resultingTriangulation.faces;
384
385 for ( QgsMeshFace &destinationFace : rawDestinationFaces )
386 {
387 for ( int &vertexIndex : destinationFace )
388 vertexIndex = triangulationVertexToMeshVertex[vertexIndex];
389 }
390
391 //The new triangulation may contains faces that intersect existing faces, we need to remove them
392 QVector<QgsMeshFace> destinationFaces;
393 for ( const QgsMeshFace &face : rawDestinationFaces )
394 {
395 if ( meshEditor->isFaceGeometricallyCompatible( face ) )
396 destinationFaces.append( face );
397 }
398
399 bool facesReady = false;
400 QgsMeshEditingError previousError;
401 while ( !facesReady && !giveUp )
402 {
404 topologicFaces = QgsTopologicalMesh::createNewTopologicalFaces( destinationFaces, true, error );
405
406 if ( error == QgsMeshEditingError() )
407 error = meshEditor->topologicalMesh().facesCanBeAdded( topologicFaces );
408
409 switch ( error.errorType )
410 {
412 facesReady = true;
413 triangulationReady = true;
414 break;
419 if ( error.elementIndex != -1 )
420 destinationFaces.remove( error.elementIndex );
421 else
422 giveUp = true; //we don't know what happens, better to give up
423 break;
426 facesReady = true;
427 if ( error.elementIndex != -1 )
428 {
429 removedVerticesFromTriangulation.append( error.elementIndex );
430 vertexIndextoTriangulate.removeOne( error.elementIndex );
431 }
432 else
433 giveUp = true; //we don't know what happens, better to give up
434 break;
435 }
436 }
437 }
438
439 Q_ASSERT( meshEditor->topologicalMesh().checkConsistency() == QgsMeshEditingError() );
440
441 if ( !removedVerticesFromTriangulation.isEmpty() )
442 mMessage = QObject::tr( "%n vertices have not been included in the triangulation", nullptr, removedVerticesFromTriangulation.count() );
443
444 mIsFinished = true;
445
446 if ( triangulationReady && !giveUp )
447 return meshEditor->topologicalMesh().addFaces( topologicFaces );
448 else
450}
@ TooManyVerticesInFace
A face has more vertices than the maximum number supported per face.
@ InvalidFace
An error occurs due to an invalid face (for example, vertex indexes are unordered)
@ UniqueSharedVertex
A least two faces share only one vertices.
@ ManifoldFace
ManifoldFace.
@ InvalidVertex
An error occurs due to an invalid vertex (for example, vertex index is out of range the available ver...
@ FlatFace
A flat face is present.
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:337
@ Polygon
Polygons.
@ PointZ
PointZ.
@ Forward
Forward transform (from source to destination)
The vertex_iterator class provides STL-style iterator for vertices.
This class represents a coordinate reference system (CRS).
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
Abstract base class for curved geometry type.
Definition qgscurve.h:35
DualEdgeTriangulation is an implementation of a triangulation class based on the dual edge data struc...
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.
@ SourceBreakLines
Break lines.
QgsMeshDataBlock is a block of integers/doubles that can be used to retrieve: active flags (e....
@ 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.
QgsMeshDatasetGroupMetadata is a collection of dataset group metadata such as whether the data is vec...
Abstract class that represents a dataset group.
void calculateStatistic() const
Calculates the statistics (minimum and maximum)
QgsMeshDatasetMetadata is a collection of mesh dataset metadata such as whether the data is valid or ...
QgsMeshDatasetValue represents single dataset value.
Abstract class that represents a dataset.
QString text() const override
Returns a short text string describing what this advanced edit does. Default implementation return a ...
Class that represents an error during mesh editing.
Qgis::MeshEditingErrorType errorType
Class that makes edit operation on a mesh.
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.
Class that handles mesh creation with Delaunay constrained triangulation.
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.
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.
Multi curve geometry collection.
Multi surface geometry collection.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
double y
Definition qgspoint.h:53
The class is used as a container of context for various read/write operations on other objects.
Class that contains topological differences between two states of a topological mesh,...
Class that contains independent faces an topological information about this faces.
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...
QVector< QgsPoint > QgsPointSequence
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
QVector< int > QgsMeshFace
List of vertex indexes.
const QgsCoordinateReferenceSystem & crs
Mesh - vertices, edges and faces.
int vertexCount() const
Returns number of vertices.
QVector< QgsMeshVertex > vertices
QVector< QgsMeshFace > faces
int faceCount() const
Returns number of faces.
QgsMeshVertex vertex(int index) const
Returns a vertex at the index.