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