QGIS API Documentation 4.3.0-Master (d3b565c628d)
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 QGS_MARK_ALGORITHM_SOURCE
108
109 if ( feedback->isCanceled() )
110 return QVariantMap();
111 feedback->setProgress( 0 );
112 feedback->pushInfo( QObject::tr( "Creating output vector layer" ) );
113
114 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, u"CRS_OUTPUT"_s, context );
115 QString identifier;
116 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, identifier, QgsFields(), Qgis::WkbType::MultiPolygon, outputCrs ) );
117 if ( !sink )
118 return QVariantMap();
119
120 if ( feedback->isCanceled() )
121 return QVariantMap();
122 feedback->setProgress( 0 );
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 feedback->setProgressText( QObject::tr( "Parsing mesh faces to extract edges." ) );
130
131 for ( int i = 0; i < mNativeMesh.faceCount(); i++ )
132 {
133 if ( feedback->isCanceled() )
134 return QVariantMap();
135
136 face = mNativeMesh.face( i );
137
138 for ( int j = 0; j < face.size(); j++ )
139 {
140 int indexEnd;
141 if ( j == face.size() - 1 )
142 indexEnd = 0;
143 else
144 indexEnd = j + 1;
145 int edgeFirstVertex = face.at( j );
146 int edgeSecondVertex = face.at( indexEnd );
147
148 // make vertex sorted to avoid have 1,2 and 2,1 as different keys
149 if ( edgeSecondVertex < edgeFirstVertex )
150 edge = std::make_pair( edgeSecondVertex, edgeFirstVertex );
151 else
152 edge = std::make_pair( edgeFirstVertex, edgeSecondVertex );
153
154 // if edge exist in map increase its count otherwise set count to 1
155 auto it = edges.find( edge );
156 if ( it != edges.end() )
157 {
158 int count = edges.take( edge ) + 1;
159 edges.insert( edge, count );
160 }
161 else
162 {
163 edges.insert( edge, 1 );
164 }
165 }
166
167 feedback->setProgress( 100.0 * static_cast<double>( i ) / mNativeMesh.faceCount() );
168 }
169
170 feedback->setProgress( 0 );
171 feedback->setProgressText( QObject::tr( "Parsing mesh edges." ) );
172
173 auto multiLineString = std::make_unique<QgsMultiLineString>();
174
175 int i = 0;
176 for ( auto it = edges.begin(); it != edges.end(); it++ )
177 {
178 if ( feedback->isCanceled() )
179 return QVariantMap();
180
181 // only consider edges with count 1 which are on the edge of mesh surface
182 if ( it.value() == 1 )
183 {
184 auto line = std::make_unique<QgsLineString>( mNativeMesh.vertex( it.key().first ), mNativeMesh.vertex( it.key().second ) );
185 multiLineString->addGeometry( line.release() );
186 }
187
188 feedback->setProgress( 100.0 * static_cast<double>( i ) / edges.size() );
189
190 i++;
191 }
192
193 feedback->setProgressText( QObject::tr( "Creating final geometry." ) );
194 if ( feedback->isCanceled() )
195 return QVariantMap();
196
197 // merge lines
198 QgsGeometry mergedLines = QgsGeometry( multiLineString.release() );
199 mergedLines = mergedLines.mergeLines();
200 QgsAbstractGeometry *multiLinesAbstract = mergedLines.get();
201
202 // set of polygons to construct result
203 QVector<QgsAbstractGeometry *> polygons;
204
205 // for every part create polygon and add to resulting multipolygon
206 for ( auto pit = multiLinesAbstract->const_parts_begin(); pit != multiLinesAbstract->const_parts_end(); ++pit )
207 {
208 if ( feedback->isCanceled() )
209 return QVariantMap();
210
211 // individula polygon - can be either polygon or hole in polygon
212 QgsPolygon *polygon = new QgsPolygon();
213 polygon->setExteriorRing( qgsgeometry_cast<const QgsLineString *>( *pit )->clone() );
214
215 // add first polygon, no need to check anything
216 if ( polygons.empty() )
217 {
218 polygons.push_back( polygon );
219 continue;
220 }
221
222 // engine for spatial relations
223 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( polygon ) );
224
225 // need to check if polygon is not either contained (hole) or covering (main polygon) with another
226 // this solves meshes with holes
227 bool isHole = false;
228
229 for ( int i = 0; i < polygons.count(); i++ )
230 {
231 QgsPolygon *p = qgsgeometry_cast<QgsPolygon *>( polygons.at( i ) );
232
233 // polygon covers another, turn contained polygon into interior ring
234 if ( engine->contains( p ) )
235 {
236 polygons.removeAt( i );
237 polygon->addInteriorRing( p->exteriorRing()->clone() );
238 break;
239 }
240 // polygon is within another, make it interior rind and do not add it
241 else if ( engine->within( p ) )
242 {
243 p->addInteriorRing( polygon->exteriorRing()->clone() );
244 isHole = true;
245 break;
246 }
247 }
248
249 // if is not a hole polygon add it to the vector of polygons
250 if ( !isHole )
251 polygons.append( polygon );
252 else
253 // polygon was used as a hole, it is not needed anymore, delete it to avoid memory leak
254 delete polygon;
255 }
256
257 // create resulting multipolygon
258 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
259 multiPolygon->addGeometries( polygons );
260
261 if ( feedback->isCanceled() )
262 return QVariantMap();
263
264 // create final geom and transform it
265 QgsGeometry resultGeom = QgsGeometry( multiPolygon.release() );
266
267 try
268 {
269 resultGeom.transform( mTransform );
270 }
271 catch ( QgsCsException & )
272 {
273 feedback->reportError( QObject::tr( "Could not transform point to destination CRS" ) );
274 }
275
276 QgsFeature feat;
277 feat.setGeometry( resultGeom );
278
279 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
280 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
281 else
282 feedback->featureAddedToSink( u"OUTPUT"_s );
283
284 sink->finalize();
285 feedback->featureSinkFinalized( u"OUTPUT"_s );
286
287 feedback->pushInfo( QObject::tr( "Output vector layer created" ) );
288 if ( feedback->isCanceled() )
289 return QVariantMap();
290
291 QVariantMap ret;
292 ret[u"OUTPUT"_s] = identifier;
293
294 return ret;
295}
296
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3752
@ 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.