QGIS API Documentation 3.43.0-Master (c4a2e9c6d2f)
qgsalgorithmcheckgeometrycontained.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmcheckgeometrycontained.cpp
3 ---------------------
4 begin : January 2025
5 copyright : (C) 2024 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
22#include "qgspoint.h"
23#include "qgsvectorlayer.h"
25
27
28QString QgsGeometryCheckContainedAlgorithm::name() const
29{
30 return QStringLiteral( "checkgeometrycontained" );
31}
32
33QString QgsGeometryCheckContainedAlgorithm::displayName() const
34{
35 return QObject::tr( "Check Geometry (Contained)" );
36}
37
38QStringList QgsGeometryCheckContainedAlgorithm::tags() const
39{
40 return QObject::tr( "check,geometry,contained" ).split( ',' );
41}
42
43QString QgsGeometryCheckContainedAlgorithm::group() const
44{
45 return QObject::tr( "Check geometry" );
46}
47
48QString QgsGeometryCheckContainedAlgorithm::groupId() const
49{
50 return QStringLiteral( "checkgeometry" );
51}
52
53QString QgsGeometryCheckContainedAlgorithm::shortHelpString() const
54{
55 return QObject::tr( "This algorithm checks the input geometries contained in the polygon layers features.\n"
56 "A polygon layer can be checked against itself.\n"
57 "Input features contained in the polygon layers features are errors.\n" );
58}
59
60Qgis::ProcessingAlgorithmFlags QgsGeometryCheckContainedAlgorithm::flags() const
61{
63}
64
65QgsGeometryCheckContainedAlgorithm *QgsGeometryCheckContainedAlgorithm::createInstance() const
66{
67 return new QgsGeometryCheckContainedAlgorithm();
68}
69
70void QgsGeometryCheckContainedAlgorithm::initAlgorithm( const QVariantMap &configuration )
71{
72 Q_UNUSED( configuration )
73
74 // inputs
76 QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ),
77 QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint )
78 << static_cast<int>( Qgis::ProcessingSourceType::VectorLine )
80 ) );
81 addParameter( new QgsProcessingParameterField(
82 QStringLiteral( "UNIQUE_ID" ), QObject::tr( "Unique feature identifier" ), QString(), QStringLiteral( "INPUT" )
83 ) );
85 QStringLiteral( "POLYGONS" ), QObject::tr( "Polygon layers" ), Qgis::ProcessingSourceType::VectorPolygon
86 ) );
87
88 // outputs
89 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Output layer" ) ) );
90 addParameter( new QgsProcessingParameterFeatureSink(
91 QStringLiteral( "ERRORS" ), QObject::tr( "Errors layer" ), 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
98 tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
99 addParameter( tolerance.release() );
100}
101
102bool QgsGeometryCheckContainedAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
103{
104 mTolerance = parameterAsInt( parameters, QStringLiteral( "TOLERANCE" ), context );
105 return true;
106}
107
108QgsFields QgsGeometryCheckContainedAlgorithm::outputFields()
109{
110 QgsFields fields;
111 fields.append( QgsField( QStringLiteral( "gc_layerid" ), QMetaType::QString ) );
112 fields.append( QgsField( QStringLiteral( "gc_layername" ), QMetaType::QString ) );
113 fields.append( QgsField( QStringLiteral( "gc_partidx" ), QMetaType::Int ) );
114 fields.append( QgsField( QStringLiteral( "gc_ringidx" ), QMetaType::Int ) );
115 fields.append( QgsField( QStringLiteral( "gc_vertidx" ), QMetaType::Int ) );
116 fields.append( QgsField( QStringLiteral( "gc_errorx" ), QMetaType::Double ) );
117 fields.append( QgsField( QStringLiteral( "gc_errory" ), QMetaType::Double ) );
118 fields.append( QgsField( QStringLiteral( "gc_error" ), QMetaType::QString ) );
119 return fields;
120}
121
122
123QVariantMap QgsGeometryCheckContainedAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
124{
125 const std::unique_ptr<QgsProcessingFeatureSource> input( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
126 if ( !input )
127 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
128
129 QList<QgsMapLayer *> polygonLayers = parameterAsLayerList( parameters, QStringLiteral( "POLYGONS" ), context );
130 if ( polygonLayers.isEmpty() )
131 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "POLYGONS" ) ) );
132
133
134 const QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
135 const int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
136 if ( uniqueIdFieldIdx == -1 )
137 throw QgsProcessingException( QObject::tr( "Missing field %1 in input layer" ).arg( uniqueIdFieldName ) );
138
139 const QgsField uniqueIdField = input->fields().at( uniqueIdFieldIdx );
140
141 QgsFields fields = outputFields();
142 fields.append( uniqueIdField );
143
144 QString dest_output, dest_errors;
145 const std::unique_ptr<QgsFeatureSink> sink_output( parameterAsSink(
146 parameters, QStringLiteral( "OUTPUT" ), context, dest_output, fields, input->wkbType(), input->sourceCrs()
147 ) );
148 if ( !sink_output )
149 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
150
151 std::unique_ptr<QgsFeatureSink> sink_errors( parameterAsSink(
152 parameters, QStringLiteral( "ERRORS" ), context, dest_errors, fields, Qgis::WkbType::Point, input->sourceCrs()
153 ) );
154 if ( !sink_errors )
155 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "ERRORS" ) ) );
156
157 QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
158
159 const QgsProject *project = QgsProject::instance();
160
161 QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
162
163 // Test detection
164 QList<QgsGeometryCheckError *> checkErrors;
165 QStringList messages;
166
167 const QgsGeometryContainedCheck check( &checkContext, QVariantMap() );
168
169 multiStepFeedback.setCurrentStep( 1 );
170 feedback->setProgressText( QObject::tr( "Preparing features…" ) );
171 QMap<QString, QgsFeaturePool *> checkerFeaturePools;
172 QList<std::shared_ptr<QgsVectorDataProviderFeaturePool>> featurePools;
173
174 std::unique_ptr<QgsVectorLayer> inputLayer( input->materialize( QgsFeatureRequest() ) );
175 featurePools << std::make_shared<QgsVectorDataProviderFeaturePool>( inputLayer.get() );
176 checkerFeaturePools.insert( inputLayer->id(), featurePools.last().get() );
177
178 for ( QgsMapLayer *polygonLayer : polygonLayers )
179 {
180 auto vlayer = dynamic_cast<QgsVectorLayer *>( polygonLayer );
181 if ( vlayer && vlayer->geometryType() == Qgis::GeometryType::Polygon )
182 {
183 featurePools << std::make_shared<QgsVectorDataProviderFeaturePool>( vlayer );
184 checkerFeaturePools.insert( vlayer->id(), featurePools.last().get() );
185 }
186 }
187
188 multiStepFeedback.setCurrentStep( 2 );
189 feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
190 check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
191
192 multiStepFeedback.setCurrentStep( 3 );
193 feedback->setProgressText( QObject::tr( "Exporting errors…" ) );
194 const double step { checkErrors.size() > 0 ? 100.0 / checkErrors.size() : 1 };
195 long i = 0;
196 feedback->setProgress( 0.0 );
197
198 QVariantList uniqueIds;
199 for ( QgsGeometryCheckError *error : checkErrors )
200 {
201 if ( feedback->isCanceled() )
202 {
203 break;
204 }
205 QgsFeature f;
206 QgsAttributes attrs = f.attributes();
207
208 // the geometry_checker adds errors from input + polygon layers comapred together, here
209 // we only want errors from the input layer.
210 if ( error->layerId() != inputLayer->id() )
211 {
212 continue;
213 }
214
215 // if the user wants to check a polygon layer against itself we avoid adding duplicate errors.
216 QVariant uniqueId = inputLayer->getFeature( error->featureId() ).attribute( uniqueIdField.name() );
217 if ( uniqueIds.contains( uniqueId ) )
218 {
219 continue;
220 }
221 else
222 {
223 uniqueIds << uniqueId;
224 }
225
226 attrs << error->layerId()
227 << inputLayer->name()
228 << error->vidx().part
229 << error->vidx().ring
230 << error->vidx().vertex
231 << error->location().x()
232 << error->location().y()
233 << error->value().toString()
234 << uniqueId;
235 f.setAttributes( attrs );
236
237 f.setGeometry( error->geometry() );
238 if ( !sink_output->addFeature( f, QgsFeatureSink::FastInsert ) )
239 throw QgsProcessingException( writeFeatureError( sink_output.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
240
241 f.setGeometry( QgsGeometry::fromPoint( QgsPoint( error->location().x(), error->location().y() ) ) );
242 if ( !sink_errors->addFeature( f, QgsFeatureSink::FastInsert ) )
243 throw QgsProcessingException( writeFeatureError( sink_errors.get(), parameters, QStringLiteral( "ERRORS" ) ) );
244
245 i++;
246 feedback->setProgress( 100.0 * step * static_cast<double>( i ) );
247 }
248
249 // Place the point layer above the other layer
250 if ( context.willLoadLayerOnCompletion( dest_output ) && context.willLoadLayerOnCompletion( dest_errors ) )
251 {
252 context.layerToLoadOnCompletionDetails( dest_errors ).layerSortKey = 0;
253 context.layerToLoadOnCompletionDetails( dest_output ).layerSortKey = 1;
254 }
255
256 QVariantMap outputs;
257 outputs.insert( QStringLiteral( "OUTPUT" ), dest_output );
258 outputs.insert( QStringLiteral( "ERRORS" ), dest_errors );
259
260 return outputs;
261}
262
@ VectorPoint
Vector point layers.
@ VectorPolygon
Vector polygon layers.
@ VectorLine
Vector line layers.
@ Polygon
Polygons.
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.
A vector of attributes.
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.
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
QString name
Definition qgsfield.h:62
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.
static QgsGeometry fromPoint(const QgsPoint &point)
Creates a new geometry from a QgsPoint object.
Base class for all map layer types.
Definition qgsmaplayer.h:77
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
virtual Qgis::ProcessingAlgorithmFlags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
int layerSortKey
Optional sorting key for sorting output layers when loading them into a project.
Contains information about the context in which a processing algorithm is executed.
QgsProcessingContext::LayerDetails & layerToLoadOnCompletionDetails(const QString &layer)
Returns a reference to the details for a given layer which is loaded on completion of the algorithm o...
bool willLoadLayerOnCompletion(const QString &layer) const
Returns true if the given layer (by ID or datasource) will be loaded into the current project upon co...
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void setProgressText(const QString &text)
Sets a progress report text string.
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 parameter for processing algorithms which accepts multiple map layers.
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
Represents a vector layer which manages a vector based dataset.