QGIS API Documentation 4.3.0-Master (45633be667c)
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
56QStringList QgsMeshSurfaceToPolygonAlgorithm::tags() const
57{
58 return QObject::tr( "mesh,export,polygon,vector,boundary,bounds,surface" ).split( ',' );
59}
60
61QString QgsMeshSurfaceToPolygonAlgorithm::group() const
62{
63 return QObject::tr( "Mesh" );
64}
65
66QString QgsMeshSurfaceToPolygonAlgorithm::groupId() const
67{
68 return u"mesh"_s;
69}
70
71QgsProcessingAlgorithm *QgsMeshSurfaceToPolygonAlgorithm::createInstance() const
72{
73 return new QgsMeshSurfaceToPolygonAlgorithm();
74}
75
76void QgsMeshSurfaceToPolygonAlgorithm::initAlgorithm( const QVariantMap &configuration )
77{
78 Q_UNUSED( configuration );
79
80 addParameter( new QgsProcessingParameterMeshLayer( u"INPUT"_s, QObject::tr( "Input mesh layer" ) ) );
81
82 addParameter( new QgsProcessingParameterCrs( u"CRS_OUTPUT"_s, QObject::tr( "Output coordinate system" ), QVariant(), true ) );
83
84 addParameter( new QgsProcessingParameterFeatureSink( u"OUTPUT"_s, QObject::tr( "Output vector layer" ), Qgis::ProcessingSourceType::VectorPolygon ) );
85}
86
87bool QgsMeshSurfaceToPolygonAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
88{
89 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, u"INPUT"_s, context );
90
91 if ( !meshLayer || !meshLayer->isValid() )
92 return false;
93
94 if ( meshLayer->isEditable() )
95 throw QgsProcessingException( QObject::tr( "Input mesh layer in edit mode is not supported" ) );
96
97 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, u"CRS_OUTPUT"_s, context );
98 if ( !outputCrs.isValid() )
99 outputCrs = meshLayer->crs();
100 mTransform = QgsCoordinateTransform( meshLayer->crs(), outputCrs, context.transformContext() );
101 if ( !meshLayer->nativeMesh() )
102 meshLayer->updateTriangularMesh( mTransform ); //necessary to load the native mesh
103
104 mNativeMesh = *meshLayer->nativeMesh();
105
106 return true;
107}
108
109
110QVariantMap QgsMeshSurfaceToPolygonAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
111{
112 QGS_MARK_ALGORITHM_SOURCE
113
114 if ( feedback->isCanceled() )
115 return QVariantMap();
116 feedback->setProgress( 0 );
117 feedback->pushInfo( QObject::tr( "Creating output vector layer" ) );
118
119 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, u"CRS_OUTPUT"_s, context );
120 QString identifier;
121 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, identifier, QgsFields(), Qgis::WkbType::MultiPolygon, outputCrs ) );
122 if ( !sink )
123 return QVariantMap();
124
125 if ( feedback->isCanceled() )
126 return QVariantMap();
127 feedback->setProgress( 0 );
128
129 QgsGeometry lines;
130 QgsMeshFace face;
131 QMap<std::pair<int, int>, int> edges; // edge as key and count of edge usage as value
132 std::pair<int, int> edge;
133
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->isCanceled() )
139 return QVariantMap();
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 feedback->setProgress( 100.0 * static_cast<double>( i ) / mNativeMesh.faceCount() );
173 }
174
175 feedback->setProgress( 0 );
176 feedback->setProgressText( QObject::tr( "Parsing mesh edges." ) );
177
178 auto multiLineString = std::make_unique<QgsMultiLineString>();
179
180 int i = 0;
181 for ( auto it = edges.begin(); it != edges.end(); it++ )
182 {
183 if ( feedback->isCanceled() )
184 return QVariantMap();
185
186 // only consider edges with count 1 which are on the edge of mesh surface
187 if ( it.value() == 1 )
188 {
189 auto line = std::make_unique<QgsLineString>( mNativeMesh.vertex( it.key().first ), mNativeMesh.vertex( it.key().second ) );
190 multiLineString->addGeometry( line.release() );
191 }
192
193 feedback->setProgress( 100.0 * static_cast<double>( i ) / edges.size() );
194
195 i++;
196 }
197
198 feedback->setProgressText( QObject::tr( "Creating final geometry." ) );
199 if ( feedback->isCanceled() )
200 return QVariantMap();
201
202 // merge lines
203 QgsGeometry mergedLines = QgsGeometry( multiLineString.release() );
204 mergedLines = mergedLines.mergeLines();
205 QgsAbstractGeometry *multiLinesAbstract = mergedLines.get();
206
207 // set of polygons to construct result
208 QVector<QgsAbstractGeometry *> polygons;
209
210 // for every part create polygon and add to resulting multipolygon
211 for ( auto pit = multiLinesAbstract->const_parts_begin(); pit != multiLinesAbstract->const_parts_end(); ++pit )
212 {
213 if ( feedback->isCanceled() )
214 return QVariantMap();
215
216 // individula polygon - can be either polygon or hole in polygon
217 QgsPolygon *polygon = new QgsPolygon();
218 polygon->setExteriorRing( qgsgeometry_cast<const QgsLineString *>( *pit )->clone() );
219
220 // add first polygon, no need to check anything
221 if ( polygons.empty() )
222 {
223 polygons.push_back( polygon );
224 continue;
225 }
226
227 // engine for spatial relations
228 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( polygon ) );
229
230 // need to check if polygon is not either contained (hole) or covering (main polygon) with another
231 // this solves meshes with holes
232 bool isHole = false;
233
234 for ( int i = 0; i < polygons.count(); i++ )
235 {
236 QgsPolygon *p = qgsgeometry_cast<QgsPolygon *>( polygons.at( i ) );
237
238 // polygon covers another, turn contained polygon into interior ring
239 if ( engine->contains( p ) )
240 {
241 polygons.removeAt( i );
242 polygon->addInteriorRing( p->exteriorRing()->clone() );
243 break;
244 }
245 // polygon is within another, make it interior rind and do not add it
246 else if ( engine->within( p ) )
247 {
248 p->addInteriorRing( polygon->exteriorRing()->clone() );
249 isHole = true;
250 break;
251 }
252 }
253
254 // if is not a hole polygon add it to the vector of polygons
255 if ( !isHole )
256 polygons.append( polygon );
257 else
258 // polygon was used as a hole, it is not needed anymore, delete it to avoid memory leak
259 delete polygon;
260 }
261
262 // create resulting multipolygon
263 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
264 multiPolygon->addGeometries( polygons );
265
266 if ( feedback->isCanceled() )
267 return QVariantMap();
268
269 // create final geom and transform it
270 QgsGeometry resultGeom = QgsGeometry( multiPolygon.release() );
271
272 try
273 {
274 resultGeom.transform( mTransform );
275 }
276 catch ( QgsCsException & )
277 {
278 feedback->reportError( QObject::tr( "Could not transform point to destination CRS" ) );
279 }
280
281 QgsFeature feat;
282 feat.setGeometry( resultGeom );
283
284 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
285 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
286 else
287 feedback->featureAddedToSink( u"OUTPUT"_s );
288
289 sink->finalize();
290 feedback->featureSinkFinalized( u"OUTPUT"_s );
291
292 feedback->pushInfo( QObject::tr( "Output vector layer created" ) );
293 if ( feedback->isCanceled() )
294 return QVariantMap();
295
296 QVariantMap ret;
297 ret[u"OUTPUT"_s] = identifier;
298
299 return ret;
300}
301
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3754
@ MultiPolygon
MultiPolygon.
Definition qgis.h:302
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:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
Container of fields for a vector layer.
Definition qgsfields.h:45
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.
void featureAddedToSink(const QString &output)
Reports that a feature was added to the the sink associated with the specified algorithm output.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
void featureSinkFinalized(const QString &output)
Reports that a feature sink has been finalized.
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.