QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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
19
20#include "qgsgeometryengine.h"
21#include "qgslinestring.h"
22#include "qgsmeshlayer.h"
23#include "qgsmultilinestring.h"
24#include "qgsmultipolygon.h"
25#include "qgspolygon.h"
27
28#include <QTextStream>
29
31
32
33QString QgsMeshSurfaceToPolygonAlgorithm::shortHelpString() const
34{
35 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." );
36}
37
38QString QgsMeshSurfaceToPolygonAlgorithm::shortDescription() const
39{
40 return QObject::tr( "Exports a polygon layer containing a mesh layer's boundary." );
41}
42
43QString QgsMeshSurfaceToPolygonAlgorithm::name() const
44{
45 return QStringLiteral( "surfacetopolygon" );
46}
47
48QString QgsMeshSurfaceToPolygonAlgorithm::displayName() const
49{
50 return QObject::tr( "Surface to polygon" );
51}
52
53QString QgsMeshSurfaceToPolygonAlgorithm::group() const
54{
55 return QObject::tr( "Mesh" );
56}
57
58QString QgsMeshSurfaceToPolygonAlgorithm::groupId() const
59{
60 return QStringLiteral( "mesh" );
61}
62
63QgsProcessingAlgorithm *QgsMeshSurfaceToPolygonAlgorithm::createInstance() const
64{
65 return new QgsMeshSurfaceToPolygonAlgorithm();
66}
67
68void QgsMeshSurfaceToPolygonAlgorithm::initAlgorithm( const QVariantMap &configuration )
69{
70 Q_UNUSED( configuration );
71
72 addParameter( new QgsProcessingParameterMeshLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input mesh layer" ) ) );
73
74 addParameter( new QgsProcessingParameterCrs( QStringLiteral( "CRS_OUTPUT" ), QObject::tr( "Output coordinate system" ), QVariant(), true ) );
75
76 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Output vector layer" ), Qgis::ProcessingSourceType::VectorPolygon ) );
77}
78
79bool QgsMeshSurfaceToPolygonAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
80{
81 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
82
83 if ( !meshLayer || !meshLayer->isValid() )
84 return false;
85
86 if ( meshLayer->isEditable() )
87 throw QgsProcessingException( QObject::tr( "Input mesh layer in edit mode is not supported" ) );
88
89 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
90 if ( !outputCrs.isValid() )
91 outputCrs = meshLayer->crs();
92 mTransform = QgsCoordinateTransform( meshLayer->crs(), outputCrs, context.transformContext() );
93 if ( !meshLayer->nativeMesh() )
94 meshLayer->updateTriangularMesh( mTransform ); //necessary to load the native mesh
95
96 mNativeMesh = *meshLayer->nativeMesh();
97
98 return true;
99}
100
101
102QVariantMap QgsMeshSurfaceToPolygonAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
103{
104 if ( feedback )
105 {
106 if ( feedback->isCanceled() )
107 return QVariantMap();
108 feedback->setProgress( 0 );
109 feedback->pushInfo( QObject::tr( "Creating output vector layer" ) );
110 }
111
112 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
113 QString identifier;
114 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, identifier, QgsFields(), Qgis::WkbType::MultiPolygon, outputCrs ) );
115 if ( !sink )
116 return QVariantMap();
117
118 if ( feedback )
119 {
120 if ( feedback->isCanceled() )
121 return QVariantMap();
122 feedback->setProgress( 0 );
123 }
124
125 QgsGeometry lines;
126 QgsMeshFace face;
127 QMap<std::pair<int, int>, int> edges; // edge as key and count of edge usage as value
128 std::pair<int, int> edge;
129
130 if ( feedback )
131 feedback->setProgressText( QObject::tr( "Parsing mesh faces to extract edges." ) );
132
133 for ( int i = 0; i < mNativeMesh.faceCount(); i++ )
134 {
135 if ( feedback )
136 {
137 if ( feedback->isCanceled() )
138 return QVariantMap();
139 }
140
141 face = mNativeMesh.face( i );
142
143 for ( int j = 0; j < face.size(); j++ )
144 {
145 int indexEnd;
146 if ( j == face.size() - 1 )
147 indexEnd = 0;
148 else
149 indexEnd = j + 1;
150 int edgeFirstVertex = face.at( j );
151 int edgeSecondVertex = face.at( indexEnd );
152
153 // make vertex sorted to avoid have 1,2 and 2,1 as different keys
154 if ( edgeSecondVertex < edgeFirstVertex )
155 edge = std::make_pair( edgeSecondVertex, edgeFirstVertex );
156 else
157 edge = std::make_pair( edgeFirstVertex, edgeSecondVertex );
158
159 // if edge exist in map increase its count otherwise set count to 1
160 auto it = edges.find( edge );
161 if ( it != edges.end() )
162 {
163 int count = edges.take( edge ) + 1;
164 edges.insert( edge, count );
165 }
166 else
167 {
168 edges.insert( edge, 1 );
169 }
170 }
171
172 if ( feedback )
173 feedback->setProgress( 100.0 * static_cast<double>( i ) / mNativeMesh.faceCount() );
174 }
175
176 if ( feedback )
177 {
178 feedback->setProgress( 0 );
179 feedback->setProgressText( QObject::tr( "Parsing mesh edges." ) );
180 }
181
182 auto multiLineString = std::make_unique<QgsMultiLineString>();
183
184 int i = 0;
185 for ( auto it = edges.begin(); it != edges.end(); it++ )
186 {
187 if ( feedback )
188 {
189 if ( feedback->isCanceled() )
190 return QVariantMap();
191 }
192
193 // only consider edges with count 1 which are on the edge of mesh surface
194 if ( it.value() == 1 )
195 {
196 auto line = std::make_unique<QgsLineString>( mNativeMesh.vertex( it.key().first ), mNativeMesh.vertex( it.key().second ) );
197 multiLineString->addGeometry( line.release() );
198 }
199
200 if ( feedback )
201 feedback->setProgress( 100.0 * static_cast<double>( i ) / edges.size() );
202
203 i++;
204 }
205
206 if ( feedback )
207 {
208 feedback->setProgressText( QObject::tr( "Creating final geometry." ) );
209 if ( feedback->isCanceled() )
210 return QVariantMap();
211 }
212
213 // merge lines
214 QgsGeometry mergedLines = QgsGeometry( multiLineString.release() );
215 mergedLines = mergedLines.mergeLines();
216 QgsAbstractGeometry *multiLinesAbstract = mergedLines.get();
217
218 // set of polygons to construct result
219 QVector<QgsAbstractGeometry *> polygons;
220
221 // for every part create polygon and add to resulting multipolygon
222 for ( auto pit = multiLinesAbstract->const_parts_begin(); pit != multiLinesAbstract->const_parts_end(); ++pit )
223 {
224 if ( feedback )
225 {
226 if ( feedback->isCanceled() )
227 return QVariantMap();
228 }
229
230 // individula polygon - can be either polygon or hole in polygon
231 QgsPolygon *polygon = new QgsPolygon();
232 polygon->setExteriorRing( qgsgeometry_cast<const QgsLineString *>( *pit )->clone() );
233
234 // add first polygon, no need to check anything
235 if ( polygons.empty() )
236 {
237 polygons.push_back( polygon );
238 continue;
239 }
240
241 // engine for spatial relations
242 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( polygon ) );
243
244 // need to check if polygon is not either contained (hole) or covering (main polygon) with another
245 // this solves meshes with holes
246 bool isHole = false;
247
248 for ( int i = 0; i < polygons.count(); i++ )
249 {
250 QgsPolygon *p = qgsgeometry_cast<QgsPolygon *>( polygons.at( i ) );
251
252 // polygon covers another, turn contained polygon into interior ring
253 if ( engine->contains( p ) )
254 {
255 polygons.removeAt( i );
256 polygon->addInteriorRing( p->exteriorRing()->clone() );
257 break;
258 }
259 // polygon is within another, make it interior rind and do not add it
260 else if ( engine->within( p ) )
261 {
262 p->addInteriorRing( polygon->exteriorRing()->clone() );
263 isHole = true;
264 break;
265 }
266 }
267
268 // if is not a hole polygon add it to the vector of polygons
269 if ( !isHole )
270 polygons.append( polygon );
271 else
272 // polygon was used as a hole, it is not needed anymore, delete it to avoid memory leak
273 delete polygon;
274 }
275
276 // create resulting multipolygon
277 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
278 multiPolygon->addGeometries( polygons );
279
280 if ( feedback )
281 {
282 if ( feedback->isCanceled() )
283 return QVariantMap();
284 }
285
286 // create final geom and transform it
287 QgsGeometry resultGeom = QgsGeometry( multiPolygon.release() );
288
289 try
290 {
291 resultGeom.transform( mTransform );
292 }
293 catch ( QgsCsException & )
294 {
295 if ( feedback )
296 feedback->reportError( QObject::tr( "Could not transform point to destination CRS" ) );
297 }
298
299 QgsFeature feat;
300 feat.setGeometry( resultGeom );
301
302 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
303 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
304
305 if ( feedback )
306 {
307 feedback->pushInfo( QObject::tr( "Output vector layer created" ) );
308 if ( feedback->isCanceled() )
309 return QVariantMap();
310 }
311
312 QVariantMap ret;
313 ret[QStringLiteral( "OUTPUT" )] = identifier;
314
315 return ret;
316}
317
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3536
@ MultiPolygon
MultiPolygon.
Definition qgis.h:285
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.
QgsGeometry mergeLines(const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
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.
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:87
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.
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< int > QgsMeshFace
List of vertex indexes.