QGIS API Documentation 3.43.0-Master (e01d6d7c4c0)
qgsalgorithmfixgeometrymultipart.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmfixgeometrymultipart.cpp
3 ---------------------
4 begin : April 2025
5 copyright : (C) 2025 by Jacky Volpes
6 email : jacky dot volpes at oslandia 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
23#include "qgsvectorlayer.h"
24#include "qgsvectorfilewriter.h"
25
27
28QString QgsFixGeometryMultipartAlgorithm::name() const
29{
30 return QStringLiteral( "fixgeometrymultipart" );
31}
32
33QString QgsFixGeometryMultipartAlgorithm::displayName() const
34{
35 return QObject::tr( "Convert to strictly multipart" );
36}
37
38QString QgsFixGeometryMultipartAlgorithm::shortDescription() const
39{
40 return QObject::tr( "Converts features detected with the \"Strictly multipart\" algorithm from the \"Check geometry\" section to singlepart." );
41}
42
43QStringList QgsFixGeometryMultipartAlgorithm::tags() const
44{
45 return QObject::tr( "fix,multipart,singlepart" ).split( ',' );
46}
47
48QString QgsFixGeometryMultipartAlgorithm::group() const
49{
50 return QObject::tr( "Fix geometry" );
51}
52
53QString QgsFixGeometryMultipartAlgorithm::groupId() const
54{
55 return QStringLiteral( "fixgeometry" );
56}
57
58QString QgsFixGeometryMultipartAlgorithm::shortHelpString() const
59{
60 return QObject::tr( "This algorithm converts multipart geometries that consists of only one geometry "
61 "into singlepart geometries, based on an error layer from the \"Strict multipart\" algorithm in the \"Check geometry\" section.\n\n"
62 "This algorithm does not change the layer geometry type, which will remain multipart." );
63}
64
65QgsFixGeometryMultipartAlgorithm *QgsFixGeometryMultipartAlgorithm::createInstance() const
66{
67 return new QgsFixGeometryMultipartAlgorithm();
68}
69
70void QgsFixGeometryMultipartAlgorithm::initAlgorithm( const QVariantMap &configuration )
71{
72 Q_UNUSED( configuration )
73
74 // Inputs
76 QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) << static_cast<int>( Qgis::ProcessingSourceType::VectorLine )
77 ) );
79 QStringLiteral( "ERRORS" ), QObject::tr( "Error layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint )
80 ) );
81 addParameter( new QgsProcessingParameterField(
82 QStringLiteral( "UNIQUE_ID" ), QObject::tr( "Field of original feature unique identifier" ),
83 QStringLiteral( "id" ), QStringLiteral( "ERRORS" )
84 ) );
85
86 // Outputs
87 addParameter( new QgsProcessingParameterFeatureSink(
88 QStringLiteral( "OUTPUT" ), QObject::tr( "Strictly-multipart layer" ), Qgis::ProcessingSourceType::VectorAnyGeometry
89 ) );
90 addParameter( new QgsProcessingParameterFeatureSink(
91 QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing multiparts" ), Qgis::ProcessingSourceType::VectorPoint
92 ) );
93
94 std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
95 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
96 );
97 tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
98 tolerance->setHelp( QObject::tr( "The \"Tolerance\" advanced parameter defines the numerical precision of geometric operations, "
99 "given as an integer n, meaning that any difference smaller than 10⁻ⁿ (in map units) is considered zero." ) );
100 addParameter( tolerance.release() );
101}
102
103QVariantMap QgsFixGeometryMultipartAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
104{
105 const std::unique_ptr<QgsProcessingFeatureSource> input( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
106 if ( !input )
107 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
108
109 const std::unique_ptr<QgsProcessingFeatureSource> errors( parameterAsSource( parameters, QStringLiteral( "ERRORS" ), context ) );
110 if ( !errors )
111 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "ERRORS" ) ) );
112
113 QgsProcessingMultiStepFeedback multiStepFeedback( 2, feedback );
114
115 const QString featIdFieldName = parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context );
116
117 // Verify that input fields exists
118 if ( errors->fields().indexFromName( featIdFieldName ) == -1 )
119 throw QgsProcessingException( QObject::tr( "Field \"%1\" does not exist in the error layer." ).arg( featIdFieldName ) );
120 int inputIdFieldIndex = input->fields().indexFromName( featIdFieldName );
121 if ( inputIdFieldIndex == -1 )
122 throw QgsProcessingException( QObject::tr( "Field \"%1\" does not exist in input layer." ).arg( featIdFieldName ) );
123
124 QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
125 if ( inputFeatIdField.type() != errors->fields().at( errors->fields().indexFromName( featIdFieldName ) ).type() )
126 throw QgsProcessingException( QObject::tr( "Field \"%1\" does not have the same type as in the error layer." ).arg( featIdFieldName ) );
127
128 QString dest_output;
129 const std::unique_ptr<QgsFeatureSink> sink_output( parameterAsSink(
130 parameters, QStringLiteral( "OUTPUT" ), context, dest_output, input->fields(), input->wkbType(), input->sourceCrs()
131 ) );
132 if ( !sink_output )
133 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
134
135 QString dest_report;
136 QgsFields reportFields = errors->fields();
137 reportFields.append( QgsField( QStringLiteral( "report" ), QMetaType::QString ) );
138 reportFields.append( QgsField( QStringLiteral( "error_fixed" ), QMetaType::Bool ) );
139 const std::unique_ptr<QgsFeatureSink> sink_report( parameterAsSink(
140 parameters, QStringLiteral( "REPORT" ), context, dest_report, reportFields, errors->wkbType(), errors->sourceCrs()
141 ) );
142 if ( !sink_report )
143 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "REPORT" ) ) );
144
145 const QgsProject *project = QgsProject::instance();
146 QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
147 QStringList messages;
148
149 const QgsGeometryMultipartCheck check( &checkContext, QVariantMap() );
150
151 QgsVectorLayer *fixedLayer = input->materialize( QgsFeatureRequest() );
153 QMap<QString, QgsFeaturePool *> featurePools;
154 featurePools.insert( fixedLayer->id(), &featurePool );
155
156 QgsFeature errorFeature, inputFeature, testDuplicateIdFeature;
157 QgsFeatureIterator errorFeaturesIt = errors->getFeatures();
158 QList<QgsGeometryCheck::Changes> changesList;
159 QgsFeature reportFeature;
160 reportFeature.setFields( reportFields );
161 long long progression = 0;
162 long long totalProgression = errors->featureCount();
163 multiStepFeedback.setCurrentStep( 1 );
164 multiStepFeedback.setProgressText( QObject::tr( "Fixing errors..." ) );
165 while ( errorFeaturesIt.nextFeature( errorFeature ) )
166 {
167 if ( feedback->isCanceled() )
168 break;
169
170 progression++;
171 multiStepFeedback.setProgress( static_cast<double>( static_cast<long double>( progression ) / totalProgression ) * 100 );
172 reportFeature.setGeometry( errorFeature.geometry() );
173
174 QString idValue = errorFeature.attribute( featIdFieldName ).toString();
175 if ( inputFeatIdField.type() == QMetaType::QString )
176 idValue = "'" + idValue + "'";
177
178 QgsFeatureIterator it = fixedLayer->getFeatures( QgsFeatureRequest().setFilterExpression( "\"" + featIdFieldName + "\" = " + idValue ) );
179 if ( !it.nextFeature( inputFeature ) || !inputFeature.isValid() )
180 reportFeature.setAttributes( errorFeature.attributes() << QObject::tr( "Source feature not found or invalid" ) << false );
181
182 else if ( it.nextFeature( testDuplicateIdFeature ) )
183 throw QgsProcessingException( QObject::tr( "More than one feature found in input layer with value \"%1\" in unique field \"%2\"" ).arg( idValue ).arg( featIdFieldName ) );
184
185 else if ( inputFeature.geometry().isNull() )
186 reportFeature.setAttributes( errorFeature.attributes() << QObject::tr( "Feature geometry is null" ) << false );
187
188 else
189 {
191 &check,
192 QgsGeometryCheckerUtils::LayerFeature( &featurePool, inputFeature, &checkContext, false ),
193 errorFeature.geometry().asPoint(),
195 );
196 for ( QgsGeometryCheck::Changes changes : changesList )
197 checkError.handleChanges( changes );
198
200 check.fixError( featurePools, &checkError, QgsGeometryMultipartCheck::ResolutionMethod::ConvertToSingle, QMap<QString, int>(), changes );
201 changesList << changes;
202 QString resolutionMessage = checkError.resolutionMessage();
203 if ( checkError.status() == QgsGeometryCheckError::StatusObsolete )
204 resolutionMessage = QObject::tr( "Error is obsolete" );
205 reportFeature.setAttributes( errorFeature.attributes() << resolutionMessage << ( checkError.status() == QgsGeometryCheckError::StatusFixed ) );
206 }
207
208 if ( !sink_report->addFeature( reportFeature, QgsFeatureSink::FastInsert ) )
209 throw QgsProcessingException( writeFeatureError( sink_report.get(), parameters, QStringLiteral( "REPORT" ) ) );
210 }
211 multiStepFeedback.setProgress( 100 );
212
213 progression = 0;
214 totalProgression = fixedLayer->featureCount();
215 multiStepFeedback.setCurrentStep( 2 );
216 multiStepFeedback.setProgressText( QObject::tr( "Exporting fixed layer..." ) );
217 QgsFeature fixedFeature;
218 QgsFeatureIterator fixedFeaturesIt = fixedLayer->getFeatures();
219 while ( fixedFeaturesIt.nextFeature( fixedFeature ) )
220 {
221 if ( feedback->isCanceled() )
222 break;
223
224 progression++;
225 multiStepFeedback.setProgress( static_cast<double>( static_cast<long double>( progression ) / totalProgression ) * 100 );
226 if ( !sink_output->addFeature( fixedFeature, QgsFeatureSink::FastInsert ) )
227 throw QgsProcessingException( writeFeatureError( sink_output.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
228 }
229 multiStepFeedback.setProgress( 100 );
230
231 QVariantMap outputs;
232 outputs.insert( QStringLiteral( "OUTPUT" ), dest_output );
233 outputs.insert( QStringLiteral( "REPORT" ), dest_report );
234
235 return outputs;
236}
237
238bool QgsFixGeometryMultipartAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
239{
240 mTolerance = parameterAsInt( parameters, QStringLiteral( "TOLERANCE" ), context );
241
242 return true;
243}
244
245Qgis::ProcessingAlgorithmFlags QgsFixGeometryMultipartAlgorithm::flags() const
246{
248}
249
@ VectorAnyGeometry
Any vector layer with geometry.
@ VectorPoint
Vector point layers.
@ VectorPolygon
Vector polygon layers.
@ VectorLine
Vector line layers.
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition qgis.h:3476
@ NoThreading
Algorithm is not thread safe and cannot be run in a background thread, e.g. for algorithms which mani...
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
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...
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
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.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
QgsGeometry geometry
Definition qgsfeature.h:69
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
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
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QMetaType::Type type
Definition qgsfield.h:60
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
Base configuration for geometry checks.
This represents an error reported by a geometry check.
@ StatusFixed
The error is fixed.
@ StatusObsolete
The error is obsolete because of other modifications.
Status status() const
The status of the error.
QString resolutionMessage() const
A message with details, how the error has been resolved.
QMap< QString, QMap< QgsFeatureId, QList< QgsGeometryCheck::Change > > > Changes
A collection of changes.
A layer feature combination to uniquely identify and access a feature in a set of layers.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
QString id
Definition qgsmaplayer.h:80
virtual Qgis::ProcessingAlgorithmFlags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
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.
Processing feedback object for multi-step operations.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
A vector layer or feature source field parameter for processing algorithms.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:107
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:113
A feature pool based on a vector data provider.
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30