QGIS API Documentation 3.43.0-Master (e01d6d7c4c0)
qgsalgorithmextractbyattribute.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmextractbyattribute.cpp
3 ----------------------------------
4 begin : April 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson 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
21
22QString QgsExtractByAttributeAlgorithm::name() const
23{
24 return QStringLiteral( "extractbyattribute" );
25}
26
27QString QgsExtractByAttributeAlgorithm::displayName() const
28{
29 return QObject::tr( "Extract by attribute" );
30}
31
32QStringList QgsExtractByAttributeAlgorithm::tags() const
33{
34 return QObject::tr( "extract,filter,attribute,value,contains,null,field" ).split( ',' );
35}
36
37QString QgsExtractByAttributeAlgorithm::group() const
38{
39 return QObject::tr( "Vector selection" );
40}
41
42QString QgsExtractByAttributeAlgorithm::groupId() const
43{
44 return QStringLiteral( "vectorselection" );
45}
46
47void QgsExtractByAttributeAlgorithm::initAlgorithm( const QVariantMap & )
48{
49 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::Vector ) ) );
50 addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Selection attribute" ), QVariant(), QStringLiteral( "INPUT" ) ) );
51 addParameter( new QgsProcessingParameterEnum( QStringLiteral( "OPERATOR" ), QObject::tr( "Operator" ), QStringList() << QObject::tr( "=" ) << QObject::tr( "≠" ) << QObject::tr( ">" ) << QObject::tr( "≥" ) << QObject::tr( "<" ) << QObject::tr( "≤" ) << QObject::tr( "begins with" ) << QObject::tr( "contains" ) << QObject::tr( "is null" ) << QObject::tr( "is not null" ) << QObject::tr( "does not contain" ), false, 0 ) );
52 addParameter( new QgsProcessingParameterString( QStringLiteral( "VALUE" ), QObject::tr( "Value" ), QVariant(), false, true ) );
53
54 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Extracted (attribute)" ) ) );
55 QgsProcessingParameterFeatureSink *failOutput = new QgsProcessingParameterFeatureSink( QStringLiteral( "FAIL_OUTPUT" ), QObject::tr( "Extracted (non-matching)" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true );
56 failOutput->setCreateByDefault( false );
57 addParameter( failOutput );
58}
59
60QString QgsExtractByAttributeAlgorithm::shortHelpString() const
61{
62 return QObject::tr( "This algorithm creates a new vector layer that only contains matching features from an input layer. "
63 "The criteria for adding features to the resulting layer is defined based on the values "
64 "of an attribute from the input layer." );
65}
66
67QString QgsExtractByAttributeAlgorithm::shortDescription() const
68{
69 return QObject::tr( "Creates a vector layer that only contains features matching an attribute value from an input layer." );
70}
71
72QgsExtractByAttributeAlgorithm *QgsExtractByAttributeAlgorithm::createInstance() const
73{
74 return new QgsExtractByAttributeAlgorithm();
75}
76
77QVariantMap QgsExtractByAttributeAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
78{
79 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
80 if ( !source )
81 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
82
83 const QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );
84 const Operation op = static_cast<Operation>( parameterAsEnum( parameters, QStringLiteral( "OPERATOR" ), context ) );
85 const QString value = parameterAsString( parameters, QStringLiteral( "VALUE" ), context );
86
87 QString matchingSinkId;
88 std::unique_ptr<QgsFeatureSink> matchingSink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, matchingSinkId, source->fields(), source->wkbType(), source->sourceCrs() ) );
89 if ( !matchingSink )
90 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
91
92 QString nonMatchingSinkId;
93 std::unique_ptr<QgsFeatureSink> nonMatchingSink( parameterAsSink( parameters, QStringLiteral( "FAIL_OUTPUT" ), context, nonMatchingSinkId, source->fields(), source->wkbType(), source->sourceCrs() ) );
94
95 const int idx = source->fields().lookupField( fieldName );
96 if ( idx < 0 )
97 throw QgsProcessingException( QObject::tr( "Field '%1' was not found in INPUT source" ).arg( fieldName ) );
98
99 const QMetaType::Type fieldType = source->fields().at( idx ).type();
100
101 if ( fieldType != QMetaType::Type::QString && ( op == BeginsWith || op == Contains || op == DoesNotContain ) )
102 {
103 QString method;
104 switch ( op )
105 {
106 case BeginsWith:
107 method = QObject::tr( "begins with" );
108 break;
109 case Contains:
110 method = QObject::tr( "contains" );
111 break;
112 case DoesNotContain:
113 method = QObject::tr( "does not contain" );
114 break;
115
116 default:
117 break;
118 }
119
120 throw QgsProcessingException( QObject::tr( "Operator '%1' can be used only with string fields." ).arg( method ) );
121 }
122
123 const QString fieldRef = QgsExpression::quotedColumnRef( fieldName );
124 const QString quotedVal = QgsExpression::quotedValue( value );
125 QString expr;
126 switch ( op )
127 {
128 case Equals:
129 expr = QStringLiteral( "%1 = %3" ).arg( fieldRef, quotedVal );
130 break;
131 case NotEquals:
132 expr = QStringLiteral( "%1 != %3" ).arg( fieldRef, quotedVal );
133 break;
134 case GreaterThan:
135 expr = QStringLiteral( "%1 > %3" ).arg( fieldRef, quotedVal );
136 break;
137 case GreaterThanEqualTo:
138 expr = QStringLiteral( "%1 >= %3" ).arg( fieldRef, quotedVal );
139 break;
140 case LessThan:
141 expr = QStringLiteral( "%1 < %3" ).arg( fieldRef, quotedVal );
142 break;
143 case LessThanEqualTo:
144 expr = QStringLiteral( "%1 <= %3" ).arg( fieldRef, quotedVal );
145 break;
146 case BeginsWith:
147 expr = QStringLiteral( "%1 LIKE '%2%'" ).arg( fieldRef, value );
148 break;
149 case Contains:
150 expr = QStringLiteral( "%1 LIKE '%%2%'" ).arg( fieldRef, value );
151 break;
152 case IsNull:
153 expr = QStringLiteral( "%1 IS NULL" ).arg( fieldRef );
154 break;
155 case IsNotNull:
156 expr = QStringLiteral( "%1 IS NOT NULL" ).arg( fieldRef );
157 break;
158 case DoesNotContain:
159 expr = QStringLiteral( "%1 NOT LIKE '%%2%'" ).arg( fieldRef, value );
160 break;
161 }
162
163 QgsExpression expression( expr );
164 if ( expression.hasParserError() )
165 {
166 throw QgsProcessingException( expression.parserErrorString() );
167 }
168
169 QgsExpressionContext expressionContext = createExpressionContext( parameters, context, source.get() );
170
171 const long count = source->featureCount();
172
173 const double step = count > 0 ? 100.0 / count : 1;
174 int current = 0;
175
176 if ( !nonMatchingSink )
177 {
178 // not saving failing features - so only fetch good features
180 req.setFilterExpression( expr );
181 req.setExpressionContext( expressionContext );
182
184 QgsFeature f;
185 while ( it.nextFeature( f ) )
186 {
187 if ( feedback->isCanceled() )
188 {
189 break;
190 }
191
192 if ( !matchingSink->addFeature( f, QgsFeatureSink::FastInsert ) )
193 throw QgsProcessingException( writeFeatureError( matchingSink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
194
195 feedback->setProgress( current * step );
196 current++;
197 }
198 }
199 else
200 {
201 // saving non-matching features, so we need EVERYTHING
202 expressionContext.setFields( source->fields() );
203 expression.prepare( &expressionContext );
204
206 QgsFeature f;
207 while ( it.nextFeature( f ) )
208 {
209 if ( feedback->isCanceled() )
210 {
211 break;
212 }
213
214 expressionContext.setFeature( f );
215 if ( expression.evaluate( &expressionContext ).toBool() )
216 {
217 if ( !matchingSink->addFeature( f, QgsFeatureSink::FastInsert ) )
218 throw QgsProcessingException( writeFeatureError( matchingSink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
219 }
220 else
221 {
222 if ( !nonMatchingSink->addFeature( f, QgsFeatureSink::FastInsert ) )
223 throw QgsProcessingException( writeFeatureError( nonMatchingSink.get(), parameters, QStringLiteral( "FAIL_OUTPUT" ) ) );
224 }
225
226 feedback->setProgress( current * step );
227 current++;
228 }
229 }
230
231 if ( matchingSink )
232 matchingSink->finalize();
233 if ( nonMatchingSink )
234 nonMatchingSink->finalize();
235
236 QVariantMap outputs;
237 outputs.insert( QStringLiteral( "OUTPUT" ), matchingSinkId );
238 if ( nonMatchingSink )
239 outputs.insert( QStringLiteral( "FAIL_OUTPUT" ), nonMatchingSinkId );
240 return outputs;
241}
242
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
@ VectorAnyGeometry
Any vector layer with geometry.
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
Handles parsing and evaluation of expressions (formerly called "search strings").
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
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).
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
@ 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
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
Contains information about the context in which a processing algorithm is executed.
void setCreateByDefault(bool createByDefault)
Sets whether the destination should be created by default.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
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 string parameter for processing algorithms.