QGIS API Documentation 3.43.0-Master (52e71951ee5)
qgsalgorithmmeshsurfacetopolygon.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmmeshsurfacetopolygon.cpp
3 ---------------------------
4 begin : September 2024
5 copyright : (C) 2024 by Jan Caha
6 email : jan.caha at outlook 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
20#include "qgsmeshlayer.h"
21#include "qgspolygon.h"
22#include "qgslinestring.h"
23#include "qgsmultilinestring.h"
24#include "qgsmultipolygon.h"
25#include "qgsgeometryengine.h"
26
27#include <QTextStream>
28
30
31
32QString QgsMeshSurfaceToPolygonAlgorithm::shortHelpString() const
33{
34 return QObject::tr( "This algorithm exports a polygon layer containing a mesh layer's boundary. It may contain holes and it may be a multi-part polygon." );
35}
36
37QString QgsMeshSurfaceToPolygonAlgorithm::shortDescription() const
38{
39 return QObject::tr( "Exports a polygon layer containing a mesh layer's boundary." );
40}
41
42QString QgsMeshSurfaceToPolygonAlgorithm::name() const
43{
44 return QStringLiteral( "surfacetopolygon" );
45}
46
47QString QgsMeshSurfaceToPolygonAlgorithm::displayName() const
48{
49 return QObject::tr( "Surface to polygon" );
50}
51
52QString QgsMeshSurfaceToPolygonAlgorithm::group() const
53{
54 return QObject::tr( "Mesh" );
55}
56
57QString QgsMeshSurfaceToPolygonAlgorithm::groupId() const
58{
59 return QStringLiteral( "mesh" );
60}
61
62QgsProcessingAlgorithm *QgsMeshSurfaceToPolygonAlgorithm::createInstance() const
63{
64 return new QgsMeshSurfaceToPolygonAlgorithm();
65}
66
67void QgsMeshSurfaceToPolygonAlgorithm::initAlgorithm( const QVariantMap &configuration )
68{
69 Q_UNUSED( configuration );
70
71 addParameter( new QgsProcessingParameterMeshLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input mesh layer" ) ) );
72
73 addParameter( new QgsProcessingParameterCrs( QStringLiteral( "CRS_OUTPUT" ), QObject::tr( "Output coordinate system" ), QVariant(), true ) );
74
75 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Output vector layer" ), Qgis::ProcessingSourceType::VectorPolygon ) );
76}
77
78bool QgsMeshSurfaceToPolygonAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
79{
80 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
81
82 if ( !meshLayer || !meshLayer->isValid() )
83 return false;
84
85 if ( meshLayer->isEditable() )
86 throw QgsProcessingException( QObject::tr( "Input mesh layer in edit mode is not supported" ) );
87
88 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
89 if ( !outputCrs.isValid() )
90 outputCrs = meshLayer->crs();
91 mTransform = QgsCoordinateTransform( meshLayer->crs(), outputCrs, context.transformContext() );
92 if ( !meshLayer->nativeMesh() )
93 meshLayer->updateTriangularMesh( mTransform ); //necessary to load the native mesh
94
95 mNativeMesh = *meshLayer->nativeMesh();
96
97 return true;
98}
99
100
101QVariantMap QgsMeshSurfaceToPolygonAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
102{
103 if ( feedback )
104 {
105 if ( feedback->isCanceled() )
106 return QVariantMap();
107 feedback->setProgress( 0 );
108 feedback->pushInfo( QObject::tr( "Creating output vector layer" ) );
109 }
110
111 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
112 QString identifier;
113 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, identifier, QgsFields(), Qgis::WkbType::MultiPolygon, outputCrs ) );
114 if ( !sink )
115 return QVariantMap();
116
117 if ( feedback )
118 {
119 if ( feedback->isCanceled() )
120 return QVariantMap();
121 feedback->setProgress( 0 );
122 }
123
124 QgsGeometry lines;
125 QgsMeshFace face;
126 QMap<std::pair<int, int>, int> edges; // edge as key and count of edge usage as value
127 std::pair<int, int> edge;
128
129 if ( feedback )
130 feedback->setProgressText( QObject::tr( "Parsing mesh faces to extract edges." ) );
131
132 for ( int i = 0; i < mNativeMesh.faceCount(); i++ )
133 {
134 if ( feedback )
135 {
136 if ( feedback->isCanceled() )
137 return QVariantMap();
138 }
139
140 face = mNativeMesh.face( i );
141
142 for ( int j = 0; j < face.size(); j++ )
143 {
144 int indexEnd;
145 if ( j == face.size() - 1 )
146 indexEnd = 0;
147 else
148 indexEnd = j + 1;
149 int edgeFirstVertex = face.at( j );
150 int edgeSecondVertex = face.at( indexEnd );
151
152 // make vertex sorted to avoid have 1,2 and 2,1 as different keys
153 if ( edgeSecondVertex < edgeFirstVertex )
154 edge = std::make_pair( edgeSecondVertex, edgeFirstVertex );
155 else
156 edge = std::make_pair( edgeFirstVertex, edgeSecondVertex );
157
158 // if edge exist in map increase its count otherwise set count to 1
159 auto it = edges.find( edge );
160 if ( it != edges.end() )
161 {
162 int count = edges.take( edge ) + 1;
163 edges.insert( edge, count );
164 }
165 else
166 {
167 edges.insert( edge, 1 );
168 }
169 }
170
171 if ( feedback )
172 feedback->setProgress( 100.0 * static_cast<double>( i ) / mNativeMesh.faceCount() );
173 }
174
175 if ( feedback )
176 {
177 feedback->setProgress( 0 );
178 feedback->setProgressText( QObject::tr( "Parsing mesh edges." ) );
179 }
180
181 auto multiLineString = std::make_unique<QgsMultiLineString>();
182
183 int i = 0;
184 for ( auto it = edges.begin(); it != edges.end(); it++ )
185 {
186 if ( feedback )
187 {
188 if ( feedback->isCanceled() )
189 return QVariantMap();
190 }
191
192 // only consider edges with count 1 which are on the edge of mesh surface
193 if ( it.value() == 1 )
194 {
195 auto line = std::make_unique<QgsLineString>( mNativeMesh.vertex( it.key().first ), mNativeMesh.vertex( it.key().second ) );
196 multiLineString->addGeometry( line.release() );
197 }
198
199 if ( feedback )
200 feedback->setProgress( 100.0 * static_cast<double>( i ) / edges.size() );
201
202 i++;
203 }
204
205 if ( feedback )
206 {
207 feedback->setProgressText( QObject::tr( "Creating final geometry." ) );
208 if ( feedback->isCanceled() )
209 return QVariantMap();
210 }
211
212 // merge lines
213 QgsGeometry mergedLines = QgsGeometry( multiLineString.release() );
214 mergedLines = mergedLines.mergeLines();
215 QgsAbstractGeometry *multiLinesAbstract = mergedLines.get();
216
217 // set of polygons to construct result
218 QVector<QgsAbstractGeometry *> polygons;
219
220 // for every part create polygon and add to resulting multipolygon
221 for ( auto pit = multiLinesAbstract->const_parts_begin(); pit != multiLinesAbstract->const_parts_end(); ++pit )
222 {
223 if ( feedback )
224 {
225 if ( feedback->isCanceled() )
226 return QVariantMap();
227 }
228
229 // individula polygon - can be either polygon or hole in polygon
230 QgsPolygon *polygon = new QgsPolygon();
231 polygon->setExteriorRing( qgsgeometry_cast<const QgsLineString *>( *pit )->clone() );
232
233 // add first polygon, no need to check anything
234 if ( polygons.empty() )
235 {
236 polygons.push_back( polygon );
237 continue;
238 }
239
240 // engine for spatial relations
241 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( polygon ) );
242
243 // need to check if polygon is not either contained (hole) or covering (main polygon) with another
244 // this solves meshes with holes
245 bool isHole = false;
246
247 for ( int i = 0; i < polygons.count(); i++ )
248 {
249 QgsPolygon *p = qgsgeometry_cast<QgsPolygon *>( polygons.at( i ) );
250
251 // polygon covers another, turn contained polygon into interior ring
252 if ( engine->contains( p ) )
253 {
254 polygons.removeAt( i );
255 polygon->addInteriorRing( p->exteriorRing()->clone() );
256 break;
257 }
258 // polygon is within another, make it interior rind and do not add it
259 else if ( engine->within( p ) )
260 {
261 p->addInteriorRing( polygon->exteriorRing()->clone() );
262 isHole = true;
263 break;
264 }
265 }
266
267 // if is not a hole polygon add it to the vector of polygons
268 if ( !isHole )
269 polygons.append( polygon );
270 else
271 // polygon was used as a hole, it is not needed anymore, delete it to avoid memory leak
272 delete polygon;
273 }
274
275 // create resulting multipolygon
276 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
277 multiPolygon->addGeometries( polygons );
278
279 if ( feedback )
280 {
281 if ( feedback->isCanceled() )
282 return QVariantMap();
283 }
284
285 // create final geom and transform it
286 QgsGeometry resultGeom = QgsGeometry( multiPolygon.release() );
287
288 try
289 {
290 resultGeom.transform( mTransform );
291 }
292 catch ( QgsCsException & )
293 {
294 if ( feedback )
295 feedback->reportError( QObject::tr( "Could not transform point to destination CRS" ) );
296 }
297
298 QgsFeature feat;
299 feat.setGeometry( resultGeom );
300
301 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
302 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
303
304 if ( feedback )
305 {
306 feedback->pushInfo( QObject::tr( "Output vector layer created" ) );
307 if ( feedback->isCanceled() )
308 return QVariantMap();
309 }
310
311 QVariantMap ret;
312 ret[QStringLiteral( "OUTPUT" )] = identifier;
313
314 return ret;
315}
316
@ VectorPolygon
Vector polygon layers.
@ MultiPolygon
MultiPolygon.
Abstract base class for all geometries.
const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary const part after the last part of the geometry.
const_part_iterator const_parts_begin() const
Returns STL-style iterator pointing to the const first part of the geometry.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
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
Container of fields for a vector layer.
Definition qgsfields.h:46
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.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:84
Represents a mesh layer supporting display of data on structured or unstructured meshes.
void updateTriangularMesh(const QgsCoordinateTransform &transform=QgsCoordinateTransform())
Gets native mesh and updates (creates if it doesn't exist) the base triangular mesh.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh)
bool isEditable() const override
Returns true if the layer can be edited.
Polygon geometry type.
Definition qgspolygon.h:33
void setExteriorRing(QgsCurve *ring) override
Sets the exterior ring of the polygon.
void addInteriorRing(QgsCurve *ring) override
Adds an interior ring to the geometry (takes ownership)
Abstract base class for processing algorithms.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
virtual void setProgressText(const QString &text)
Sets a progress report text string.
A coordinate reference system parameter for processing algorithms.
A feature sink output for processing algorithms.
A mesh layer parameter for processing algorithms.
QVector< int > QgsMeshFace
List of vertex indexes.
const QgsCoordinateReferenceSystem & outputCrs