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