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