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