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