QGIS API Documentation 4.3.0-Master (ffcfc20b9b4)
Loading...
Searching...
No Matches
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
19
24#include "qgsvectorfilewriter.h"
25#include "qgsvectorlayer.h"
26
27#include <QString>
28
29using namespace Qt::StringLiterals;
30
32
33QString QgsFixGeometryMultipartAlgorithm::name() const
34{
35 return u"fixgeometrymultipart"_s;
36}
37
38QString QgsFixGeometryMultipartAlgorithm::displayName() const
39{
40 return QObject::tr( "Convert to strictly multipart" );
41}
42
43QString QgsFixGeometryMultipartAlgorithm::shortDescription() const
44{
45 return QObject::tr( "Converts features detected with the \"Strictly multipart\" algorithm from the \"Check geometry\" section to singlepart." );
46}
47
48QStringList QgsFixGeometryMultipartAlgorithm::tags() const
49{
50 return QObject::tr( "fix,multipart,singlepart" ).split( ',' );
51}
52
53QString QgsFixGeometryMultipartAlgorithm::group() const
54{
55 return QObject::tr( "Fix geometry" );
56}
57
58QString QgsFixGeometryMultipartAlgorithm::groupId() const
59{
60 return u"fixgeometry"_s;
61}
62
63QString QgsFixGeometryMultipartAlgorithm::shortHelpString() const
64{
65 return QObject::tr(
66 "This algorithm converts multipart geometries that consists of only one geometry "
67 "into singlepart geometries, based on an error layer from the \"Strict multipart\" algorithm in the \"Check geometry\" section.\n\n"
68 "This algorithm does not change the layer geometry type, which will remain multipart."
69 );
70}
71
72QgsFixGeometryMultipartAlgorithm *QgsFixGeometryMultipartAlgorithm::createInstance() const
73{
74 return new QgsFixGeometryMultipartAlgorithm();
75}
76
77void QgsFixGeometryMultipartAlgorithm::initAlgorithm( const QVariantMap &configuration )
78{
79 Q_UNUSED( configuration )
80
81 addParameter(
82 new QgsProcessingParameterFeatureSource( u"INPUT"_s, QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) << static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) )
83 );
84 addParameter( new QgsProcessingParameterFeatureSource( u"ERRORS"_s, QObject::tr( "Error layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) ) );
85 addParameter( new QgsProcessingParameterField( u"UNIQUE_ID"_s, QObject::tr( "Field of original feature unique identifier" ), u"id"_s, u"ERRORS"_s ) );
86
87 addParameter( new QgsProcessingParameterFeatureSink( u"OUTPUT"_s, QObject::tr( "Strictly-multipart layer" ), Qgis::ProcessingSourceType::VectorAnyGeometry ) );
88 addParameter( new QgsProcessingParameterFeatureSink( u"REPORT"_s, QObject::tr( "Report layer from fixing multiparts" ), Qgis::ProcessingSourceType::VectorPoint ) );
89
90 auto tolerance = std::make_unique<QgsProcessingParameterNumber>( u"TOLERANCE"_s, QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 );
91 tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
92 tolerance->setHelp(
93 QObject::tr(
94 "The \"Tolerance\" advanced parameter defines the numerical precision of geometric operations, "
95 "given as an integer n, meaning that any difference smaller than 10⁻ⁿ (in map units) is considered zero."
96 )
97 );
98 addParameter( tolerance.release() );
99}
100
101QVariantMap QgsFixGeometryMultipartAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
102{
103 QGS_MARK_ALGORITHM_SOURCE
104
105 const std::unique_ptr<QgsProcessingFeatureSource> input( parameterAsSource( parameters, u"INPUT"_s, context ) );
106 if ( !input )
107 throw QgsProcessingException( invalidSourceError( parameters, u"INPUT"_s ) );
108
109 const std::unique_ptr<QgsProcessingFeatureSource> errors( parameterAsSource( parameters, u"ERRORS"_s, context ) );
110 if ( !errors )
111 throw QgsProcessingException( invalidSourceError( parameters, u"ERRORS"_s ) );
112
113 QgsProcessingMultiStepFeedback multiStepFeedback( 2, feedback );
114
115 const QString featIdFieldName = parameterAsString( parameters, u"UNIQUE_ID"_s, 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 const 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( parameters, u"OUTPUT"_s, context, dest_output, input->fields(), input->wkbType(), input->sourceCrs() ) );
130 if ( !sink_output )
131 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
132
133 QString dest_report;
134 QgsFields reportFields = errors->fields();
135 reportFields.append( QgsField( u"report"_s, QMetaType::QString ) );
136 reportFields.append( QgsField( u"error_fixed"_s, QMetaType::Bool ) );
137 const std::unique_ptr<QgsFeatureSink> sink_report( parameterAsSink( parameters, u"REPORT"_s, context, dest_report, reportFields, errors->wkbType(), errors->sourceCrs() ) );
138 if ( !sink_report )
139 throw QgsProcessingException( invalidSinkError( parameters, u"REPORT"_s ) );
140
141 QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project() );
142
143 const QgsGeometryMultipartCheck check( &checkContext, QVariantMap() );
144
145 QgsVectorLayer *fixedLayer = input->materialize( QgsFeatureRequest() );
147 QMap<QString, QgsFeaturePool *> featurePools;
148 featurePools.insert( fixedLayer->id(), &featurePool );
149
150 QgsFeature errorFeature, inputFeature, testDuplicateIdFeature;
151 QgsFeatureIterator errorFeaturesIt = errors->getFeatures();
152 QList<QgsGeometryCheck::Changes> changesList;
153 QgsFeature reportFeature;
154 reportFeature.setFields( reportFields );
155 long long progression = 0;
156 long long totalProgression = errors->featureCount();
157 multiStepFeedback.setCurrentStep( 1 );
158 multiStepFeedback.setProgressText( QObject::tr( "Fixing errors..." ) );
159 while ( errorFeaturesIt.nextFeature( errorFeature ) )
160 {
161 if ( feedback->isCanceled() )
162 break;
163
164 progression++;
165 multiStepFeedback.setProgress( static_cast<double>( static_cast<long double>( progression ) / totalProgression ) * 100 );
166 reportFeature.setGeometry( errorFeature.geometry() );
167
168 QString idValue = errorFeature.attribute( featIdFieldName ).toString();
169 if ( inputFeatIdField.type() == QMetaType::QString )
170 idValue = "'" + idValue + "'";
171
172 QgsFeatureIterator it = fixedLayer->getFeatures( QgsFeatureRequest().setFilterExpression( "\"" + featIdFieldName + "\" = " + idValue ) );
173 if ( !it.nextFeature( inputFeature ) || !inputFeature.isValid() )
174 reportFeature.setAttributes( errorFeature.attributes() << QObject::tr( "Source feature not found or invalid" ) << false );
175
176 else if ( it.nextFeature( testDuplicateIdFeature ) )
177 throw QgsProcessingException( QObject::tr( "More than one feature found in input layer with value \"%1\" in unique field \"%2\"" ).arg( idValue, featIdFieldName ) );
178
179 else if ( inputFeature.geometry().isNull() )
180 reportFeature.setAttributes( errorFeature.attributes() << QObject::tr( "Feature geometry is null" ) << false );
181
182 else
183 {
184 QgsGeometryCheckError checkError
185 = QgsGeometryCheckError( &check, QgsGeometryCheckerUtils::LayerFeature( &featurePool, inputFeature, &checkContext, false ), errorFeature.geometry().asPoint(), QgsVertexId() );
186 for ( const QgsGeometryCheck::Changes &changes : std::as_const( changesList ) )
187 checkError.handleChanges( changes );
188
190 check.fixError( featurePools, &checkError, QgsGeometryMultipartCheck::ResolutionMethod::ConvertToSingle, QMap<QString, int>(), changes );
191 changesList << changes;
192 QString resolutionMessage = checkError.resolutionMessage();
193 if ( checkError.status() == QgsGeometryCheckError::StatusObsolete )
194 resolutionMessage = QObject::tr( "Error is obsolete" );
195 reportFeature.setAttributes( errorFeature.attributes() << resolutionMessage << ( checkError.status() == QgsGeometryCheckError::StatusFixed ) );
196 }
197
198 if ( !sink_report->addFeature( reportFeature, QgsFeatureSink::FastInsert ) )
199 throw QgsProcessingException( writeFeatureError( sink_report.get(), parameters, u"REPORT"_s ) );
200 else
201 feedback->featureAddedToSink( u"REPORT"_s );
202 }
203 multiStepFeedback.setProgress( 100 );
204
205 progression = 0;
206 totalProgression = fixedLayer->featureCount();
207 multiStepFeedback.setCurrentStep( 2 );
208 multiStepFeedback.setProgressText( QObject::tr( "Exporting fixed layer..." ) );
209 QgsFeature fixedFeature;
210 QgsFeatureIterator fixedFeaturesIt = fixedLayer->getFeatures();
211 while ( fixedFeaturesIt.nextFeature( fixedFeature ) )
212 {
213 if ( feedback->isCanceled() )
214 break;
215
216 progression++;
217 multiStepFeedback.setProgress( static_cast<double>( static_cast<long double>( progression ) / totalProgression ) * 100 );
218 if ( !sink_output->addFeature( fixedFeature, QgsFeatureSink::FastInsert ) )
219 throw QgsProcessingException( writeFeatureError( sink_output.get(), parameters, u"OUTPUT"_s ) );
220 else
221 feedback->featureAddedToSink( u"OUTPUT"_s );
222 }
223 multiStepFeedback.setProgress( 100 );
224
225 sink_report->finalize();
226 feedback->featureSinkFinalized( u"REPORT"_s );
227 sink_output->finalize();
228 feedback->featureSinkFinalized( u"OUTPUT"_s );
229
230 QVariantMap outputs;
231 outputs.insert( u"OUTPUT"_s, dest_output );
232 outputs.insert( u"REPORT"_s, dest_report );
233
234 return outputs;
235}
236
237bool QgsFixGeometryMultipartAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
238{
239 mTolerance = parameterAsInt( parameters, u"TOLERANCE"_s, context );
240
241 return true;
242}
243
244Qgis::ProcessingAlgorithmFlags QgsFixGeometryMultipartAlgorithm::flags() const
245{
247}
248
@ VectorAnyGeometry
Any vector layer with geometry.
Definition qgis.h:3749
@ VectorPoint
Vector point layers.
Definition qgis.h:3750
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3752
@ VectorLine
Vector line layers.
Definition qgis.h:3751
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition qgis.h:3826
@ NoThreading
Algorithm is not thread safe and cannot be run in a background thread, e.g. for algorithms which mani...
Definition qgis.h:3805
@ RequiresProject
The algorithm requires that a valid QgsProject is available from the processing context in order to e...
Definition qgis.h:3813
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3982
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:60
QgsAttributes attributes
Definition qgsfeature.h:64
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:66
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:56
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QMetaType::Type type
Definition qgsfield.h:63
Container of fields for a vector layer.
Definition qgsfields.h:45
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:75
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.
virtual bool handleChanges(const QgsGeometryCheck::Changes &changes)
Apply a list of changes.
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:86
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.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsProject * project() const
Returns the project in which the algorithm is being 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.
void featureSinkFinalized(const QString &output)
Reports that a feature sink has been finalized.
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.
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:35