QGIS API Documentation 4.3.0-Master (d3b565c628d)
Loading...
Searching...
No Matches
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
20#include <QString>
21
22using namespace Qt::StringLiterals;
23
25
26QString QgsExtractByAttributeAlgorithm::name() const
27{
28 return u"extractbyattribute"_s;
29}
30
31QString QgsExtractByAttributeAlgorithm::displayName() const
32{
33 return QObject::tr( "Extract by attribute" );
34}
35
36QStringList QgsExtractByAttributeAlgorithm::tags() const
37{
38 return QObject::tr( "extract,filter,attribute,value,contains,null,field" ).split( ',' );
39}
40
41QString QgsExtractByAttributeAlgorithm::group() const
42{
43 return QObject::tr( "Vector selection" );
44}
45
46QString QgsExtractByAttributeAlgorithm::groupId() const
47{
48 return u"vectorselection"_s;
49}
50
51void QgsExtractByAttributeAlgorithm::initAlgorithm( const QVariantMap & )
52{
53 addParameter( new QgsProcessingParameterFeatureSource( u"INPUT"_s, QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::Vector ) ) );
54 addParameter( new QgsProcessingParameterField( u"FIELD"_s, QObject::tr( "Selection attribute" ), QVariant(), u"INPUT"_s ) );
55 addParameter( new QgsProcessingParameterEnum(
56 u"OPERATOR"_s,
57 QObject::tr( "Operator" ),
58 QStringList()
59 << QObject::tr( "=" )
60 << QObject::tr( "≠" )
61 << QObject::tr( ">" )
62 << QObject::tr( "≥" )
63 << QObject::tr( "<" )
64 << QObject::tr( "≤" )
65 << QObject::tr( "begins with" )
66 << QObject::tr( "contains" )
67 << QObject::tr( "is null" )
68 << QObject::tr( "is not null" )
69 << QObject::tr( "does not contain" ),
70 false,
71 0
72 ) );
73 addParameter( new QgsProcessingParameterString( u"VALUE"_s, QObject::tr( "Value" ), QVariant(), false, true ) );
74
75 addParameter( new QgsProcessingParameterFeatureSink( u"OUTPUT"_s, QObject::tr( "Extracted (attribute)" ) ) );
77 = new QgsProcessingParameterFeatureSink( u"FAIL_OUTPUT"_s, QObject::tr( "Extracted (non-matching)" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true );
78 failOutput->setCreateByDefault( false );
79 addParameter( failOutput );
80}
81
82QString QgsExtractByAttributeAlgorithm::shortHelpString() const
83{
84 return QObject::tr(
85 "This algorithm creates a new vector layer that only contains matching features from an input layer. "
86 "The criteria for adding features to the resulting layer is defined based on the values "
87 "of an attribute from the input layer."
88 );
89}
90
91QString QgsExtractByAttributeAlgorithm::shortDescription() const
92{
93 return QObject::tr( "Creates a vector layer that only contains features matching an attribute value from an input layer." );
94}
95
96QgsExtractByAttributeAlgorithm *QgsExtractByAttributeAlgorithm::createInstance() const
97{
98 return new QgsExtractByAttributeAlgorithm();
99}
100
101QVariantMap QgsExtractByAttributeAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
102{
103 QGS_MARK_ALGORITHM_SOURCE
104
105 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, u"INPUT"_s, context ) );
106 if ( !source )
107 throw QgsProcessingException( invalidSourceError( parameters, u"INPUT"_s ) );
108
109 const QString fieldName = parameterAsString( parameters, u"FIELD"_s, context );
110 const Operation op = static_cast<Operation>( parameterAsEnum( parameters, u"OPERATOR"_s, context ) );
111 const QString value = parameterAsString( parameters, u"VALUE"_s, context );
112
113 QString matchingSinkId;
114 std::unique_ptr<QgsFeatureSink> matchingSink( parameterAsSink( parameters, u"OUTPUT"_s, context, matchingSinkId, source->fields(), source->wkbType(), source->sourceCrs() ) );
115 if ( !matchingSink )
116 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
117
118 QString nonMatchingSinkId;
119 std::unique_ptr<QgsFeatureSink> nonMatchingSink( parameterAsSink( parameters, u"FAIL_OUTPUT"_s, context, nonMatchingSinkId, source->fields(), source->wkbType(), source->sourceCrs() ) );
120
121 const int idx = source->fields().lookupField( fieldName );
122 if ( idx < 0 )
123 throw QgsProcessingException( QObject::tr( "Field '%1' was not found in INPUT source" ).arg( fieldName ) );
124
125 const QMetaType::Type fieldType = source->fields().at( idx ).type();
126
127 if ( fieldType != QMetaType::Type::QString && ( op == BeginsWith || op == Contains || op == DoesNotContain ) )
128 {
129 QString method;
130 switch ( op )
131 {
132 case BeginsWith:
133 method = QObject::tr( "begins with" );
134 break;
135 case Contains:
136 method = QObject::tr( "contains" );
137 break;
138 case DoesNotContain:
139 method = QObject::tr( "does not contain" );
140 break;
141
142 default:
143 break;
144 }
145
146 throw QgsProcessingException( QObject::tr( "Operator '%1' can be used only with string fields." ).arg( method ) );
147 }
148
149 const QString fieldRef = QgsExpression::quotedColumnRef( fieldName );
150 const QString quotedVal = QgsExpression::quotedValue( value );
151 QString expr;
152 switch ( op )
153 {
154 case Equals:
155 expr = u"%1 = %3"_s.arg( fieldRef, quotedVal );
156 break;
157 case NotEquals:
158 expr = u"%1 != %3"_s.arg( fieldRef, quotedVal );
159 break;
160 case GreaterThan:
161 expr = u"%1 > %3"_s.arg( fieldRef, quotedVal );
162 break;
163 case GreaterThanEqualTo:
164 expr = u"%1 >= %3"_s.arg( fieldRef, quotedVal );
165 break;
166 case LessThan:
167 expr = u"%1 < %3"_s.arg( fieldRef, quotedVal );
168 break;
169 case LessThanEqualTo:
170 expr = u"%1 <= %3"_s.arg( fieldRef, quotedVal );
171 break;
172 case BeginsWith:
173 expr = u"%1 LIKE '%2%'"_s.arg( fieldRef, value );
174 break;
175 case Contains:
176 expr = u"%1 LIKE '%%2%'"_s.arg( fieldRef, value );
177 break;
178 case IsNull:
179 expr = u"%1 IS NULL"_s.arg( fieldRef );
180 break;
181 case IsNotNull:
182 expr = u"%1 IS NOT NULL"_s.arg( fieldRef );
183 break;
184 case DoesNotContain:
185 expr = u"%1 NOT LIKE '%%2%'"_s.arg( fieldRef, value );
186 break;
187 }
188
189 QgsExpression expression( expr );
190 if ( expression.hasParserError() )
191 {
192 throw QgsProcessingException( expression.parserErrorString() );
193 }
194
195 QgsExpressionContext expressionContext = createExpressionContext( parameters, context, source.get() );
196
197 const long count = source->featureCount();
198
199 const double step = count > 0 ? 100.0 / count : 1;
200 int current = 0;
201
202 if ( !nonMatchingSink )
203 {
204 // not saving failing features - so only fetch good features
206 req.setFilterExpression( expr );
207 req.setExpressionContext( expressionContext );
208
210 QgsFeature f;
211 while ( it.nextFeature( f ) )
212 {
213 if ( feedback->isCanceled() )
214 {
215 break;
216 }
217
218 if ( !matchingSink->addFeature( f, QgsFeatureSink::FastInsert ) )
219 throw QgsProcessingException( writeFeatureError( matchingSink.get(), parameters, u"OUTPUT"_s ) );
220 else
221 feedback->featureAddedToSink( u"OUTPUT"_s );
222
223 feedback->setProgress( current * step );
224 current++;
225 }
226 }
227 else
228 {
229 // saving non-matching features, so we need EVERYTHING
230 expressionContext.setFields( source->fields() );
231 expression.prepare( &expressionContext );
232
234 QgsFeature f;
235 while ( it.nextFeature( f ) )
236 {
237 if ( feedback->isCanceled() )
238 {
239 break;
240 }
241
242 expressionContext.setFeature( f );
243 if ( expression.evaluate( &expressionContext ).toBool() )
244 {
245 if ( !matchingSink->addFeature( f, QgsFeatureSink::FastInsert ) )
246 throw QgsProcessingException( writeFeatureError( matchingSink.get(), parameters, u"OUTPUT"_s ) );
247 else
248 feedback->featureAddedToSink( u"OUTPUT"_s );
249 }
250 else
251 {
252 if ( !nonMatchingSink->addFeature( f, QgsFeatureSink::FastInsert ) )
253 throw QgsProcessingException( writeFeatureError( nonMatchingSink.get(), parameters, u"FAIL_OUTPUT"_s ) );
254 else
255 feedback->featureAddedToSink( u"FAIL_OUTPUT"_s );
256 }
257
258 feedback->setProgress( current * step );
259 current++;
260 }
261 }
262
263 if ( matchingSink )
264 {
265 matchingSink->finalize();
266 feedback->featureSinkFinalized( u"OUTPUT"_s );
267 }
268 if ( nonMatchingSink )
269 {
270 nonMatchingSink->finalize();
271 feedback->featureSinkFinalized( u"FAIL_OUTPUT"_s );
272 }
273
274 QVariantMap outputs;
275 outputs.insert( u"OUTPUT"_s, matchingSinkId );
276 if ( nonMatchingSink )
277 outputs.insert( u"FAIL_OUTPUT"_s, nonMatchingSinkId );
278 return outputs;
279}
280
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition qgis.h:3755
@ VectorAnyGeometry
Any vector layer with geometry.
Definition qgis.h:3749
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
Definition qgis.h:3930
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:60
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
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.
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.
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.