QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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 <QString>
34
35#include "moc_qgsmeshtriangulation.cpp"
36
37using namespace Qt::StringLiterals;
38
40 : QObject()
41{
42 mTriangulation = std::make_unique<QgsDualEdgeTriangulation>();
43}
44
45
47
48bool QgsMeshTriangulation::addVertices( QgsFeatureIterator &vertexFeatureIterator, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback, long featureCount )
49{
50 if ( feedback )
51 feedback->setProgress( 0 );
52
53 QgsFeature feat;
54 long i = 0;
55 while ( vertexFeatureIterator.nextFeature( feat ) )
56 {
57 if ( feedback )
58 {
59 if ( feedback->isCanceled() )
60 break;
61
62 feedback->setProgress( 100 * i / featureCount );
63 i++;
64 }
65
66 addVerticesFromFeature( feat, valueAttribute, transform, feedback );
67 }
68
69 return true;
70}
71
72bool QgsMeshTriangulation::addBreakLines( QgsFeatureIterator &lineFeatureIterator, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback, long featureCount )
73{
74 if ( feedback )
75 feedback->setProgress( 0 );
76
77 QgsFeature feat;
78 long i = 0;
79 while ( lineFeatureIterator.nextFeature( feat ) )
80 {
81 if ( feedback )
82 {
83 if ( feedback->isCanceled() )
84 break;
85
86 feedback->setProgress( 100 * i / featureCount );
87 i++;
88 }
89
90 Qgis::GeometryType geomType = feat.geometry().type();
91 switch ( geomType )
92 {
94 addVerticesFromFeature( feat, valueAttribute, transform, feedback );
95 break;
98 addBreakLinesFromFeature( feat, valueAttribute, transform, feedback );
99 break;
100 default:
101 break;
102 }
103 }
104
105 return true;
106}
107
109{
110 return mTriangulation->addPoint( vertex );
111}
112
114{
115 return mTriangulation->triangulationToMesh( feedback );
116}
117
119{
120 mCrs = crs;
121}
122
123void QgsMeshTriangulation::addVerticesFromFeature( const QgsFeature &feature, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback )
124{
125 QgsGeometry geom = feature.geometry();
126 try
127 {
128 geom.transform( transform, Qgis::TransformDirection::Forward, true );
129 }
130 catch ( QgsCsException &cse )
131 {
132 Q_UNUSED( cse )
133 QgsDebugMsgLevel( u"Caught CRS exception %1"_s.arg( cse.what() ), 4 );
134 return;
135 }
136
137 QgsAbstractGeometry::vertex_iterator vit = geom.vertices_begin();
138
139 double value = 0;
140 if ( valueAttribute >= 0 )
141 value = feature.attribute( valueAttribute ).toDouble();
142
143 while ( vit != geom.vertices_end() )
144 {
145 if ( feedback && feedback->isCanceled() )
146 break;
147 if ( valueAttribute < 0 )
148 mTriangulation->addPoint( *vit );
149 else
150 {
151 mTriangulation->addPoint( QgsPoint( Qgis::WkbType::PointZ, ( *vit ).x(), ( *vit ).y(), value ) );
152 }
153 ++vit;
154 }
155}
156
157void QgsMeshTriangulation::addBreakLinesFromFeature( const QgsFeature &feature, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback )
158{
159 double valueOnVertex = 0;
160 if ( valueAttribute >= 0 )
161 valueOnVertex = feature.attribute( valueAttribute ).toDouble();
162
163 //from QgsTinInterpolator::insertData()
164 std::vector<const QgsCurve *> curves;
165 QgsGeometry geom = feature.geometry();
166 try
167 {
168 geom.transform( transform, Qgis::TransformDirection::Forward, true );
169 }
170 catch ( QgsCsException &cse )
171 {
172 Q_UNUSED( cse )
173 QgsDebugMsgLevel( u"Caught CRS exception %1"_s.arg( cse.what() ), 4 );
174 return;
175 }
176
178 {
179 std::vector<const QgsCurvePolygon *> polygons;
180 if ( geom.isMultipart() )
181 {
182 const QgsMultiSurface *ms = qgsgeometry_cast<const QgsMultiSurface *>( geom.constGet() );
183 for ( int i = 0; i < ms->numGeometries(); ++i )
184 {
185 polygons.emplace_back( qgsgeometry_cast<const QgsCurvePolygon *>( ms->geometryN( i ) ) );
186 }
187 }
188 else
189 {
190 polygons.emplace_back( qgsgeometry_cast<const QgsCurvePolygon *>( geom.constGet() ) );
191 }
192
193 for ( const QgsCurvePolygon *polygon : polygons )
194 {
195 if ( feedback && feedback->isCanceled() )
196 break;
197 if ( !polygon )
198 continue;
199
200 if ( polygon->exteriorRing() )
201 curves.emplace_back( polygon->exteriorRing() );
202
203 for ( int i = 0; i < polygon->numInteriorRings(); ++i )
204 {
205 if ( feedback && feedback->isCanceled() )
206 break;
207 curves.emplace_back( polygon->interiorRing( i ) );
208 }
209 }
210 }
211 else
212 {
213 if ( geom.isMultipart() )
214 {
215 const QgsMultiCurve *mc = qgsgeometry_cast<const QgsMultiCurve *>( geom.constGet() );
216 for ( int i = 0; i < mc->numGeometries(); ++i )
217 {
218 if ( feedback && feedback->isCanceled() )
219 break;
220 curves.emplace_back( qgsgeometry_cast<const QgsCurve *>( mc->geometryN( i ) ) );
221 }
222 }
223 else
224 {
225 curves.emplace_back( qgsgeometry_cast<const QgsCurve *>( geom.constGet() ) );
226 }
227 }
228
229 for ( const QgsCurve *curve : curves )
230 {
231 if ( !curve )
232 continue;
233
234 if ( feedback && feedback->isCanceled() )
235 break;
236
237 QgsPointSequence linePoints;
238 curve->points( linePoints );
239 bool hasZ = curve->is3D();
240 if ( valueAttribute >= 0 )
241 for ( int i = 0; i < linePoints.count(); ++i )
242 {
243 if ( feedback && feedback->isCanceled() )
244 break;
245 if ( hasZ )
246 linePoints[i].setZ( valueOnVertex );
247 else
248 {
249 const QgsPoint &point = linePoints.at( i );
250 linePoints[i] = QgsPoint( point.x(), point.y(), valueOnVertex );
251 }
252 }
253
254 mTriangulation->addLine( linePoints, QgsInterpolator::SourceType::BreakLines );
255 }
256}
257
258QgsMeshZValueDatasetGroup::QgsMeshZValueDatasetGroup( const QString &datasetGroupName, const QgsMesh &mesh )
259 : QgsMeshDatasetGroup( datasetGroupName, QgsMeshDatasetGroupMetadata::DataOnVertices )
260{
261 mDataset = std::make_unique<QgsMeshZValueDataset>( mesh );
262}
263
268
270{
271 if ( datasetIndex != 0 )
272 return QgsMeshDatasetMetadata();
273
274 return mDataset->metadata();
275}
276
278{
279 return 1;
280}
281
283{
284 if ( index != 0 )
285 return nullptr;
286
287 return mDataset.get();
288}
289
290QDomElement QgsMeshZValueDatasetGroup::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
291{
292 Q_UNUSED( doc );
293 Q_UNUSED( context );
294 return QDomElement();
295}
296
298 : mMesh( mesh )
299{
300 for ( const QgsMeshVertex &vertex : mesh.vertices )
301 {
302 if ( vertex.z() < mZMinimum )
303 mZMinimum = vertex.z();
304 if ( vertex.z() > mZMaximum )
305 mZMaximum = vertex.z();
306 }
307}
308
310{
311 if ( valueIndex < 0 || valueIndex >= mMesh.vertexCount() )
312 return QgsMeshDatasetValue();
313
314 return QgsMeshDatasetValue( mMesh.vertex( valueIndex ).z() );
315}
316
317QgsMeshDataBlock QgsMeshZValueDataset::datasetValues( bool isScalar, int valueIndex, int count ) const
318{
319 Q_UNUSED( isScalar )
321 QVector<double> zValues( count );
322 for ( int i = valueIndex; i < valueIndex + count; ++i )
323 zValues[i - valueIndex] = mMesh.vertex( i ).z();
324 ret.setValues( zValues );
325 return ret;
326}
327
329{
330 Q_UNUSED( faceIndex );
331 Q_UNUSED( count );
333 ret.setValid( true );
334 return ret;
335}
336
337bool QgsMeshZValueDataset::isActive( int faceIndex ) const
338{
339 return ( faceIndex > 0 && faceIndex < mMesh.faceCount() );
340}
341
343{
344 return QgsMeshDatasetMetadata( 0, true, mZMinimum, mZMaximum, 0 );
345}
346
348{
349 return mMesh.vertexCount();
350}
351
353
355{
356 return QObject::tr( "Delaunay triangulation" );
357}
358
359QgsTopologicalMesh::Changes QgsMeshEditingDelaunayTriangulation::apply( QgsMeshEditor *meshEditor )
360{
361 //use only vertices that are on boundary or free, if boundary
362 QList<int> vertexIndextoTriangulate;
363
364 QList<int> removedVerticesFromTriangulation;
365
366 for ( const int vertexIndex : std::as_const( mInputVertices ) )
367 {
368 if ( meshEditor->isVertexFree( vertexIndex ) || meshEditor->isVertexOnBoundary( vertexIndex ) )
369 vertexIndextoTriangulate.append( vertexIndex );
370 else
371 removedVerticesFromTriangulation.append( vertexIndex );
372 }
373
374 bool triangulationReady = false;
375 bool giveUp = false;
376 QgsTopologicalMesh::TopologicalFaces topologicFaces;
377
378 while ( !triangulationReady )
379 {
380 QgsMeshTriangulation triangulation;
381
382 QVector<int> triangulationVertexToMeshVertex( vertexIndextoTriangulate.count() );
383 const QgsMesh *destinationMesh = meshEditor->topologicalMesh().mesh();
384
385 for ( int i = 0; i < vertexIndextoTriangulate.count(); ++i )
386 {
387 triangulationVertexToMeshVertex[i] = vertexIndextoTriangulate.at( i );
388 triangulation.addVertex( destinationMesh->vertices.at( vertexIndextoTriangulate.at( i ) ) );
389 }
390
391 QgsMesh resultingTriangulation = triangulation.triangulatedMesh();
392
393 //Transform the new mesh triangulation to destination mesh faces
394 QVector<QgsMeshFace> rawDestinationFaces = resultingTriangulation.faces;
395
396 for ( QgsMeshFace &destinationFace : rawDestinationFaces )
397 {
398 for ( int &vertexIndex : destinationFace )
399 vertexIndex = triangulationVertexToMeshVertex[vertexIndex];
400 }
401
402 //The new triangulation may contains faces that intersect existing faces, we need to remove them
403 QVector<QgsMeshFace> destinationFaces;
404 for ( const QgsMeshFace &face : rawDestinationFaces )
405 {
406 if ( meshEditor->isFaceGeometricallyCompatible( face ) )
407 destinationFaces.append( face );
408 }
409
410 bool facesReady = false;
411 QgsMeshEditingError previousError;
412 while ( !facesReady && !giveUp )
413 {
414 QgsMeshEditingError error;
415 topologicFaces = QgsTopologicalMesh::createNewTopologicalFaces( destinationFaces, true, error );
416
417 if ( error == QgsMeshEditingError() )
418 error = meshEditor->topologicalMesh().facesCanBeAdded( topologicFaces );
419
420 switch ( error.errorType )
421 {
423 facesReady = true;
424 triangulationReady = true;
425 break;
430 if ( error.elementIndex != -1 )
431 destinationFaces.remove( error.elementIndex );
432 else
433 giveUp = true; //we don't know what happens, better to give up
434 break;
437 facesReady = true;
438 if ( error.elementIndex != -1 )
439 {
440 removedVerticesFromTriangulation.append( error.elementIndex );
441 vertexIndextoTriangulate.removeOne( error.elementIndex );
442 }
443 else
444 giveUp = true; //we don't know what happens, better to give up
445 break;
446 }
447 }
448 }
449
450 Q_ASSERT( meshEditor->topologicalMesh().checkConsistency() == QgsMeshEditingError() );
451
452 if ( !removedVerticesFromTriangulation.isEmpty() )
453 mMessage = QObject::tr( "%n vertices have not been included in the triangulation", nullptr, removedVerticesFromTriangulation.count() );
454
455 mIsFinished = true;
456
457 if ( triangulationReady && !giveUp )
458 return meshEditor->topologicalMesh().addFaces( topologicFaces );
459 else
460 return QgsTopologicalMesh::Changes();
461}
@ TooManyVerticesInFace
A face has more vertices than the maximum number supported per face.
Definition qgis.h:1741
@ InvalidFace
An error occurs due to an invalid face (for example, vertex indexes are unordered).
Definition qgis.h:1740
@ UniqueSharedVertex
A least two faces share only one vertices.
Definition qgis.h:1743
@ ManifoldFace
ManifoldFace.
Definition qgis.h:1745
@ InvalidVertex
An error occurs due to an invalid vertex (for example, vertex index is out of range the available ver...
Definition qgis.h:1744
@ FlatFace
A flat face is present.
Definition qgis.h:1742
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:379
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
@ PointZ
PointZ.
Definition qgis.h:313
@ Forward
Forward transform (from source to destination).
Definition qgis.h:2765
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:60
QgsGeometry geometry
Definition qgsfeature.h:71
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:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
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:53
double x
Definition qgspoint.h:56
double y
Definition qgspoint.h:57
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:63
QVector< int > QgsMeshFace
List of vertex indexes.
QgsPoint QgsMeshVertex
xyz coords of vertex
Mesh - vertices, edges and faces.
QVector< QgsMeshVertex > vertices
QVector< QgsMeshFace > faces