QGIS API Documentation 3.43.0-Master (e01d6d7c4c0)
qgsalgorithmcheckvalidity.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmcheckvalidity.cpp
3 ---------------------
4 begin : February 2025
5 copyright : (C) 2025 by Alexander Bruy
6 email : alexander dot bruy at gmail 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#include "qgsvectorlayer.h"
21
23
24QString QgsCheckValidityAlgorithm::name() const
25{
26 return QStringLiteral( "checkvalidity" );
27}
28
29QString QgsCheckValidityAlgorithm::displayName() const
30{
31 return QObject::tr( "Check validity" );
32}
33
34QStringList QgsCheckValidityAlgorithm::tags() const
35{
36 return QObject::tr( "valid,invalid,detect,error" ).split( ',' );
37}
38
39QString QgsCheckValidityAlgorithm::group() const
40{
41 return QObject::tr( "Vector geometry" );
42}
43
44QString QgsCheckValidityAlgorithm::groupId() const
45{
46 return QStringLiteral( "vectorgeometry" );
47}
48
49QString QgsCheckValidityAlgorithm::shortHelpString() const
50{
51 return QObject::tr( "This algorithm performs a validity check on the geometries of a vector layer.\n\n"
52 "The geometries are classified in three groups (valid, invalid and error), and a vector layer "
53 "is generated with the features in each of these categories.\n\n"
54 "By default the algorithm uses the strict OGC definition of polygon validity, where a polygon "
55 "is marked as invalid if a self-intersecting ring causes an interior hole. If the 'Ignore "
56 "ring self intersections' option is checked, then this rule will be ignored and a more "
57 "lenient validity check will be performed.\n\n"
58 "The GEOS method is faster and performs better on larger geometries, but is limited to only "
59 "returning the first error encountered in a geometry. The QGIS method will be slower but "
60 "reports all errors encountered in the geometry, not just the first." );
61}
62
63QString QgsCheckValidityAlgorithm::shortDescription() const
64{
65 return QObject::tr( "Performs a validity check on the geometries of a vector layer "
66 "and classifies them in three groups (valid, invalid and error)." );
67}
68
69QgsCheckValidityAlgorithm *QgsCheckValidityAlgorithm::createInstance() const
70{
71 return new QgsCheckValidityAlgorithm();
72}
73
74void QgsCheckValidityAlgorithm::initAlgorithm( const QVariantMap & )
75{
76 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT_LAYER" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) );
77
78 const QStringList options = QStringList()
79 << QObject::tr( "The one selected in digitizing settings" )
80 << QStringLiteral( "QGIS" )
81 << QStringLiteral( "GEOS" );
82 auto methodParam = std::make_unique<QgsProcessingParameterEnum>( QStringLiteral( "METHOD" ), QObject::tr( "Method" ), options, false, 2 );
83 QVariantMap methodParamMetadata;
84 QVariantMap widgetMetadata;
85 widgetMetadata.insert( QStringLiteral( "useCheckBoxes" ), true );
86 widgetMetadata.insert( QStringLiteral( "columns" ), 3 );
87 methodParamMetadata.insert( QStringLiteral( "widget_wrapper" ), widgetMetadata );
88 methodParam->setMetadata( methodParamMetadata );
89 addParameter( methodParam.release() );
90
91 addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "IGNORE_RING_SELF_INTERSECTION" ), QObject::tr( "Ignore ring self intersections" ), false ) );
92 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "VALID_OUTPUT" ), QObject::tr( "Valid output" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true ) );
93 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "INVALID_OUTPUT" ), QObject::tr( "Invalid output" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true ) );
94 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "ERROR_OUTPUT" ), QObject::tr( "Error output" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true ) );
95 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "VALID_COUNT" ), QObject::tr( "Count of valid features" ) ) );
96 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "INVALID_COUNT" ), QObject::tr( "Count of invalid features" ) ) );
97 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "ERROR_COUNT" ), QObject::tr( "Count of errors" ) ) );
98}
99
100QVariantMap QgsCheckValidityAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
101{
102 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT_LAYER" ), context ) );
103 if ( !source )
104 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT_LAYER" ) ) );
105
106 int method = parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context );
107 if ( method == 0 )
108 {
109 const int methodFromSettings = QgsSettingsRegistryCore::settingsDigitizingValidateGeometries->value() - 1;
110 method = methodFromSettings > 0 ? methodFromSettings : 0;
111 }
112 else
113 {
114 method--;
115 }
116
117 const bool ignoreRingSelfIntersection = parameterAsBool( parameters, QStringLiteral( "IGNORE_RING_SELF_INTERSECTION" ), context );
118
120
121 QString validSinkId;
122 std::unique_ptr<QgsFeatureSink> validSink( parameterAsSink( parameters, QStringLiteral( "VALID_OUTPUT" ), context, validSinkId, source->fields(), source->wkbType(), source->sourceCrs() ) );
123
124 QgsFields invalidFields = source->fields();
125 invalidFields.append( QgsField( QStringLiteral( "_errors" ), QMetaType::Type::QString, QStringLiteral( "string" ), 255 ) );
126 QString invalidSinkId;
127 std::unique_ptr<QgsFeatureSink> invalidSink( parameterAsSink( parameters, QStringLiteral( "INVALID_OUTPUT" ), context, invalidSinkId, invalidFields, source->wkbType(), source->sourceCrs() ) );
128
129 QgsFields errorFields;
130 errorFields.append( QgsField( QStringLiteral( "message" ), QMetaType::Type::QString, QStringLiteral( "string" ), 255 ) );
131 QString errorSinkId;
132 std::unique_ptr<QgsFeatureSink> errorSink( parameterAsSink( parameters, QStringLiteral( "ERROR_OUTPUT" ), context, errorSinkId, errorFields, Qgis::WkbType::Point, source->sourceCrs() ) );
133
134 int validCount = 0;
135 int invalidCount = 0;
136 int errorCount = 0;
137
138 const long count = source->featureCount();
139 const double step = count > 0 ? 100.0 / count : 1;
140 long long current = 0;
141
143 QgsFeature f;
144 while ( it.nextFeature( f ) )
145 {
146 if ( feedback->isCanceled() )
147 {
148 break;
149 }
150
151 const QgsGeometry geom = f.geometry();
152 QgsAttributes attrs = f.attributes();
153
154 bool isValid = true;
155
156 if ( !geom.isNull() && !geom.isEmpty() )
157 {
158 QVector< QgsGeometry::Error > errors;
159 geom.validateGeometry( errors, Qgis::GeometryValidationEngine( method ), flags );
160 if ( errors.count() > 0 )
161 {
162 isValid = false;
163 QStringList reasons;
164 reasons.reserve( errors.count() );
165 for ( const QgsGeometry::Error &error : std::as_const( errors ) )
166 {
167 if ( errorSink )
168 {
169 QgsFeature f;
170 f.setGeometry( QgsGeometry::fromPointXY( error.where() ) );
171 f.setAttributes( QVector< QVariant >() << error.what() );
172 if ( !errorSink->addFeature( f, QgsFeatureSink::FastInsert ) )
173 {
174 throw QgsProcessingException( writeFeatureError( errorSink.get(), parameters, QStringLiteral( "ERROR_OUTPUT" ) ) );
175 }
176 }
177 errorCount++;
178 reasons.append( error.what() );
179 }
180 QString reason = reasons.join( '\n' );
181 if ( reason.size() > 255 )
182 {
183 reason = reason.left( 252 ) + QStringLiteral( "…" );
184 }
185 attrs.append( reason );
186 }
187 }
188
189 QgsFeature f;
190 f.setGeometry( geom );
191 f.setAttributes( attrs );
192
193 if ( isValid )
194 {
195 if ( validSink && !validSink->addFeature( f, QgsFeatureSink::FastInsert ) )
196 {
197 throw QgsProcessingException( writeFeatureError( validSink.get(), parameters, QStringLiteral( "VALID_OUTPUT" ) ) );
198 }
199 validCount++;
200 }
201 else
202 {
203 if ( invalidSink && !invalidSink->addFeature( f, QgsFeatureSink::FastInsert ) )
204 {
205 throw QgsProcessingException( writeFeatureError( invalidSink.get(), parameters, QStringLiteral( "INVALID_OUTPUT" ) ) );
206 }
207 invalidCount++;
208 }
209
210 feedback->setProgress( current * step );
211 current++;
212 }
213
214 if ( validSink )
215 {
216 validSink->finalize();
217 }
218 if ( invalidSink )
219 {
220 invalidSink->finalize();
221 }
222 if ( errorSink )
223 {
224 errorSink->finalize();
225 }
226
227 QVariantMap outputs;
228 outputs.insert( QStringLiteral( "VALID_COUNT" ), validCount );
229 outputs.insert( QStringLiteral( "INVALID_COUNT" ), invalidCount );
230 outputs.insert( QStringLiteral( "ERROR_COUNT" ), errorCount );
231
232 if ( validSink )
233 {
234 outputs.insert( QStringLiteral( "VALID_OUTPUT" ), validSinkId );
235 }
236 if ( invalidSink )
237 {
238 outputs.insert( QStringLiteral( "INVALID_OUTPUT" ), invalidSinkId );
239 }
240 if ( errorSink )
241 {
242 outputs.insert( QStringLiteral( "ERROR_OUTPUT" ), errorSinkId );
243 }
244
245 return outputs;
246}
247
@ VectorAnyGeometry
Any vector layer with geometry.
@ AllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:2038
GeometryValidationEngine
Available engines for validating geometries.
Definition qgis.h:2047
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
A vector of attributes.
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:58
QgsAttributes attributes
Definition qgsfeature.h:67
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
QgsGeometry geometry
Definition qgsfeature.h:69
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:70
A geometry error.
A geometry is the spatial representation of a feature.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
void validateGeometry(QVector< QgsGeometry::Error > &errors, Qgis::GeometryValidationEngine method=Qgis::GeometryValidationEngine::QgisInternal, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Validates geometry and produces a list of geometry errors.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
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.
A numeric output for processing algorithms.
A boolean parameter for processing algorithms.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryInteger * settingsDigitizingValidateGeometries
Settings entry digitizing validate geometries.