QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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  QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );
92  Operation op = static_cast< Operation >( parameterAsEnum( parameters, QStringLiteral( "OPERATOR" ), context ) );
93  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  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  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  QString fieldRef = QgsExpression::quotedColumnRef( fieldName );
134  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  long count = source->featureCount();
182 
183  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  matchingSink->addFeature( f, QgsFeatureSink::FastInsert );
203 
204  feedback->setProgress( current * step );
205  current++;
206  }
207  }
208  else
209  {
210  // saving non-matching features, so we need EVERYTHING
211  expressionContext.setFields( source->fields() );
212  expression.prepare( &expressionContext );
213 
215  QgsFeature f;
216  while ( it.nextFeature( f ) )
217  {
218  if ( feedback->isCanceled() )
219  {
220  break;
221  }
222 
223  expressionContext.setFeature( f );
224  if ( expression.evaluate( &expressionContext ).toBool() )
225  {
226  matchingSink->addFeature( f, QgsFeatureSink::FastInsert );
227  }
228  else
229  {
230  nonMatchingSink->addFeature( f, QgsFeatureSink::FastInsert );
231  }
232 
233  feedback->setProgress( current * step );
234  current++;
235  }
236  }
237 
238 
239  QVariantMap outputs;
240  outputs.insert( QStringLiteral( "OUTPUT" ), matchingSinkId );
241  if ( nonMatchingSink )
242  outputs.insert( QStringLiteral( "FAIL_OUTPUT" ), nonMatchingSinkId );
243  return outputs;
244 }
245 
247 
248 
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
Base class for providing feedback from a processing algorithm.
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
A vector layer or feature source field parameter for processing algorithms.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
A feature sink output for processing algorithms.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
An enum based parameter for processing algorithms, allowing for selection from predefined values...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
void setCreateByDefault(bool createByDefault)
Sets whether the destination should be created by default.
An input feature source (such as vector layers) parameter for processing algorithms.
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:53
bool nextFeature(QgsFeature &f)
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required...
Contains information about the context in which a processing algorithm is executed.
A string parameter for processing algorithms.
Any vector layer with geometry.
Definition: qgsprocessing.h:47