QGIS API Documentation 4.3.0-Master (18b5e825726)
Loading...
Searching...
No Matches
qgsalgorithmcoverageclean.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmcoverageclean.h
3 ---------------------
4 begin : June 2026
5 copyright : (C) 2026 by Juho Ervasti
6 email : juho dot ervasti at gispo dot fi
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
18
20
22#include "qgsgeos.h"
23
24#include <QString>
25
26using namespace Qt::StringLiterals;
27
29
30QString QgsCoverageCleanAlgorithm::name() const
31{
32 return u"coverageclean"_s;
33}
34
35QString QgsCoverageCleanAlgorithm::displayName() const
36{
37 return QObject::tr( "Clean coverage" );
38}
39
40QStringList QgsCoverageCleanAlgorithm::tags() const
41{
42 return QObject::tr( "topological,boundary,repair" ).split( ',' );
43}
44
45QString QgsCoverageCleanAlgorithm::group() const
46{
47 return QObject::tr( "Vector coverage" );
48}
49
50QString QgsCoverageCleanAlgorithm::groupId() const
51{
52 return u"vectorcoverage"_s;
53}
54
55void QgsCoverageCleanAlgorithm::initAlgorithm( const QVariantMap & )
56{
57 addParameter( new QgsProcessingParameterFeatureSource( u"INPUT"_s, QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) ) );
58 auto gapWidthParam = std::make_unique<QgsProcessingParameterDistance>( u"MAXIMUM_GAP_WIDTH"_s, QObject::tr( "Maximum gap width" ), 0.0, u"INPUT"_s, false, 0, 10000000.0 );
59 gapWidthParam->setHelp(
60 QObject::tr(
61 "Gaps which are narrower than this distance are merged with an adjacent polygon. Polygon width is determined as twice the radius of the maximum inscribed circle of the gap polygon. Empty holes "
62 "in input polygons are treated as gaps, and may be filled in. Gaps which are not fully enclosed ('inlets') are not removed."
63 )
64 );
65 auto snappingDistanceParam = std::make_unique<QgsProcessingParameterDistance>( u"SNAPPING_DISTANCE"_s, QObject::tr( "Snapping distance" ), QVariant(), u"INPUT"_s, true, 0.0, 10000000.0 );
66 snappingDistanceParam->setHelp(
67 QObject::tr(
68 "Snapping to nearby vertices and line segment snapping is used to improve noding robustness and eliminate small errors in an efficient way. By default the snapping distance is not set, which "
69 "means that the clean operation uses a very small snapping distance based on the extent of the input data. A distance of zero prevents snapping from being used."
70 )
71 );
72 auto mergeStrategyParam = std::make_unique<QgsProcessingParameterEnum>(
73 u"OVERLAP_MERGE_STRATEGY"_s,
74 QObject::tr( "Overlap merge strategy" ),
75 QStringList() << QObject::tr( "Longest border" ) << QObject::tr( "Maximum area" ) << QObject::tr( "Minimum area" ) << QObject::tr( "Minimum index" ),
76 false,
77 0
78 );
79 mergeStrategyParam->setHelp(
80 QObject::tr(
81 "Determines the strategy used to merge gaps with adjacent polygons. Strategies include merging with the polygon sharing the longest border, the polygon with the maximum or minimum area, or the "
82 "first encountered polygon (Minimum Index)."
83 )
84 );
85
86 addParameter( gapWidthParam.release() );
87 addParameter( snappingDistanceParam.release() );
88 addParameter( mergeStrategyParam.release() );
89
90 addParameter( new QgsProcessingParameterFeatureSink( u"OUTPUT"_s, QObject::tr( "Cleaned" ), Qgis::ProcessingSourceType::VectorPolygon ) );
91}
92
93QString QgsCoverageCleanAlgorithm::shortDescription() const
94{
95 return QObject::tr( "Cleans a coverage of polygon features to fix cases where the geometries do not exactly align." );
96}
97
98QString QgsCoverageCleanAlgorithm::shortHelpString() const
99{
100 return QObject::tr(
101 "This algorithm operates on a coverage (represented as a list of polygon features) "
102 "to fix cases where the geometry does not in fact exactly match, repairing small "
103 "overlaps and gaps to restore a valid topological coverage.\n\n"
104 "It uses a combination of snapping and gap/overlap merging. Snapping to nearby vertices "
105 "and line segments improves noding robustness and eliminates small errors. If the snapping "
106 "distance is not specified, an automatic distance based on the data extent is used. A distance "
107 "of 0 disables snapping.\n\n"
108 "Gaps and holes that are narrower than the Maximum Gap Width are merged with an adjacent polygon. "
109 "Gaps which are not fully enclosed ('inlets') are not removed.\n\n"
110 "When repairing overlaps and gaps, the Overlap Merge Strategy determines which adjacent "
111 "polygon the problematic area is merged into (e.g., merging with the polygon that shares "
112 "the longest border, or the one with the maximum/minimum area).\n\n"
113 "Polygons that have collapsed during cleaning will be returned as empty polygons."
114 );
115}
116
117QgsCoverageCleanAlgorithm *QgsCoverageCleanAlgorithm::createInstance() const
118{
119 return new QgsCoverageCleanAlgorithm();
120}
121
122bool QgsCoverageCleanAlgorithm::prepareAlgorithm( const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
123{
124#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 14
125 throw QgsProcessingException( QObject::tr( "This algorithm requires a QGIS build based on GEOS 3.14 or later" ) );
126#endif
127 return true;
128}
129
130QVariantMap QgsCoverageCleanAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
131{
132 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, u"INPUT"_s, context ) );
133 if ( !source )
134 throw QgsProcessingException( invalidSourceError( parameters, u"INPUT"_s ) );
135
136 QString sinkId;
137 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, sinkId, source->fields(), source->wkbType(), source->sourceCrs() ) );
138 if ( !sink )
139 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
140
141 // build list of features in advance
142 QVector<QgsFeature> featuresWithGeom;
143 QVector<QgsFeature> featuresWithoutGeom;
144 QgsGeometryCollection collection;
145
146 const long count = source->featureCount();
147 if ( count > 0 )
148 {
149 featuresWithGeom.reserve( count );
150 collection.reserve( count );
151 }
152
153 const double step = count > 0 ? 100.0 / count : 1;
154 int current = 0;
155
156 feedback->pushInfo( QObject::tr( "Collecting features" ) );
157
158 QgsFeature inFeature;
160 while ( features.nextFeature( inFeature ) )
161 {
162 if ( feedback->isCanceled() )
163 {
164 break;
165 }
166
167 if ( inFeature.hasGeometry() )
168 {
169 featuresWithGeom.append( inFeature );
170 collection.addGeometry( inFeature.geometry().constGet()->clone() );
171 }
172 else
173 {
174 featuresWithoutGeom.append( inFeature );
175 }
176
177 feedback->setProgress( current * step * 0.2 );
178 current++;
179 }
180
181 QString error;
182 QgsGeos geos( &collection );
183
184 QgsCoverageCleanParameters cleanParameters;
185 if ( parameters.value( u"SNAPPING_DISTANCE"_s ).isValid() )
186 {
187 cleanParameters.setSnappingDistance( parameterAsDouble( parameters, u"SNAPPING_DISTANCE"_s, context ) );
188 }
189 cleanParameters.setMaximumGapWidth( parameterAsDouble( parameters, u"MAXIMUM_GAP_WIDTH"_s, context ) );
190 switch ( parameterAsEnum( parameters, u"OVERLAP_MERGE_STRATEGY"_s, context ) )
191 {
192 case 0:
194 break;
195 case 1:
197 break;
198 case 2:
200 break;
201 case 3:
203 break;
204 default:
205 break;
206 }
207
208 feedback->pushInfo( QObject::tr( "Cleaning coverage" ) );
209 std::unique_ptr<QgsAbstractGeometry> cleaned;
210 try
211 {
212 cleaned = geos.cleanCoverage( cleanParameters, &error, feedback );
213 }
214 catch ( QgsNotSupportedException &e )
215 {
216 throw QgsProcessingException( e.what() );
217 }
218
219 if ( !cleaned )
220 {
221 if ( !error.isEmpty() )
222 throw QgsProcessingException( error );
223 else
224 throw QgsProcessingException( QObject::tr( "No geometry was returned for cleaned coverage" ) );
225 }
226
227 feedback->setProgress( 80 );
228
229 feedback->pushInfo( QObject::tr( "Storing features" ) );
230 long long featureIndex = 0;
231 for ( auto partsIt = cleaned->const_parts_begin(); partsIt != cleaned->const_parts_end(); ++partsIt )
232 {
233 QgsFeature outFeature = featuresWithGeom.value( featureIndex );
234 outFeature.setGeometry( QgsGeometry( *partsIt ? ( *partsIt )->clone() : nullptr ) );
235 if ( !sink->addFeature( outFeature, QgsFeatureSink::FastInsert ) )
236 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
237 else
238 feedback->featureAddedToSink( u"OUTPUT"_s );
239
240 feedback->setProgress( featureIndex * step * 0.2 + 80 );
241 featureIndex++;
242 }
243
244 for ( const QgsFeature &feature : featuresWithoutGeom )
245 {
246 QgsFeature outFeature = feature;
247 if ( !sink->addFeature( outFeature, QgsFeatureSink::FastInsert ) )
248 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
249 else
250 feedback->featureAddedToSink( u"OUTPUT"_s );
251 }
252
253 sink->finalize();
254 feedback->featureSinkFinalized( u"OUTPUT"_s );
255
256 QVariantMap outputs;
257 outputs.insert( u"OUTPUT"_s, sinkId );
258 return outputs;
259}
260
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3731
@ LongestBorder
Polygon with longest common border is selected to merge overlapping polygons into.
Definition qgis.h:6921
@ MaximumArea
Polygon with largest area is selected to merge overlapping polygons into.
Definition qgis.h:6922
@ MinimumArea
Polygon with minimum area is selected to merge overlapping polygons into.
Definition qgis.h:6923
@ MinimumIndex
Polygon with smallest input index is selected to merge overlapping polygons into.
Definition qgis.h:6924
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
Definition qgis.h:3909
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
Encapsulates parameters for a coverage cleaning operation.
void setSnappingDistance(double distance)
Sets the snapping distance.
void setOverlapMergeStrategy(Qgis::CoverageCleanOverlapMergeStrategy strategy)
Sets the overlap merge strategy to use during cleaning.
void setMaximumGapWidth(double width)
Sets the maximum gap width.
QString what() const
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
@ 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
QgsGeometry geometry
Definition qgsfeature.h:66
bool hasGeometry() const
Returns true if the feature has an associated geometry.
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
void reserve(int size)
Attempts to allocate memory for at least size geometries.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:175
Custom exception class which is raised when an operation is not supported.
Contains information about the context in which a processing algorithm is executed.
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.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
Contains geos related utilities and functions.
Definition qgsgeos.h:112