QGIS API Documentation  3.0.2-Girona (307d082)
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  addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Selection attribute" ), QVariant(), QStringLiteral( "INPUT" ) ) );
51  addParameter( new QgsProcessingParameterEnum( QStringLiteral( "OPERATOR" ), QObject::tr( "Operator" ), QStringList()
52  << QObject::tr( "=" )
53  << QObject::tr( "≠" )
54  << QObject::tr( ">" )
55  << QObject::tr( ">=" )
56  << QObject::tr( "<" )
57  << QObject::tr( "<=" )
58  << QObject::tr( "begins with" )
59  << QObject::tr( "contains" )
60  << QObject::tr( "is null" )
61  << QObject::tr( "is not null" )
62  << QObject::tr( "does not contain" ) ) );
63  addParameter( new QgsProcessingParameterString( QStringLiteral( "VALUE" ), QObject::tr( "Value" ), QVariant(), false, true ) );
64 
65  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Extracted (attribute)" ) ) );
66  QgsProcessingParameterFeatureSink *failOutput = new QgsProcessingParameterFeatureSink( QStringLiteral( "FAIL_OUTPUT" ), QObject::tr( "Extracted (non-matching)" ),
67  QgsProcessing::TypeVectorAnyGeometry, QVariant(), true );
68  failOutput->setCreateByDefault( false );
69  addParameter( failOutput );
70 }
71 
72 QString QgsExtractByAttributeAlgorithm::shortHelpString() const
73 {
74  return QObject::tr( "This algorithm creates a new vector layer that only contains matching features from an input layer. "
75  "The criteria for adding features to the resulting layer is defined based on the values "
76  "of an attribute from the input layer." );
77 }
78 
79 QgsExtractByAttributeAlgorithm *QgsExtractByAttributeAlgorithm::createInstance() const
80 {
81  return new QgsExtractByAttributeAlgorithm();
82 }
83 
84 QVariantMap QgsExtractByAttributeAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
85 {
86  std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
87  if ( !source )
88  throw QgsProcessingException( QObject::tr( "Could not load source layer for INPUT" ) );
89 
90  QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );
91  Operation op = static_cast< Operation >( parameterAsEnum( parameters, QStringLiteral( "OPERATOR" ), context ) );
92  QString value = parameterAsString( parameters, QStringLiteral( "VALUE" ), context );
93 
94  QString matchingSinkId;
95  std::unique_ptr< QgsFeatureSink > matchingSink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, matchingSinkId, source->fields(),
96  source->wkbType(), source->sourceCrs() ) );
97  if ( !matchingSink )
98  throw QgsProcessingException( QObject::tr( "Could not create destination layer for OUTPUT" ) );;
99 
100  QString nonMatchingSinkId;
101  std::unique_ptr< QgsFeatureSink > nonMatchingSink( parameterAsSink( parameters, QStringLiteral( "FAIL_OUTPUT" ), context, nonMatchingSinkId, source->fields(),
102  source->wkbType(), source->sourceCrs() ) );
103 
104 
105  int idx = source->fields().lookupField( fieldName );
106  QVariant::Type fieldType = source->fields().at( idx ).type();
107 
108  if ( fieldType != QVariant::String && ( op == BeginsWith || op == Contains || op == DoesNotContain ) )
109  {
110  QString method;
111  switch ( op )
112  {
113  case BeginsWith:
114  method = QObject::tr( "begins with" );
115  break;
116  case Contains:
117  method = QObject::tr( "contains" );
118  break;
119  case DoesNotContain:
120  method = QObject::tr( "does not contain" );
121  break;
122 
123  default:
124  break;
125  }
126 
127  throw QgsProcessingException( QObject::tr( "Operator '%1' can be used only with string fields." ).arg( method ) );
128  }
129 
130  QString fieldRef = QgsExpression::quotedColumnRef( fieldName );
131  QString quotedVal = QgsExpression::quotedValue( value );
132  QString expr;
133  switch ( op )
134  {
135  case Equals:
136  expr = QStringLiteral( "%1 = %3" ).arg( fieldRef, quotedVal );
137  break;
138  case NotEquals:
139  expr = QStringLiteral( "%1 != %3" ).arg( fieldRef, quotedVal );
140  break;
141  case GreaterThan:
142  expr = QStringLiteral( "%1 > %3" ).arg( fieldRef, quotedVal );
143  break;
144  case GreaterThanEqualTo:
145  expr = QStringLiteral( "%1 >= %3" ).arg( fieldRef, quotedVal );
146  break;
147  case LessThan:
148  expr = QStringLiteral( "%1 < %3" ).arg( fieldRef, quotedVal );
149  break;
150  case LessThanEqualTo:
151  expr = QStringLiteral( "%1 <= %3" ).arg( fieldRef, quotedVal );
152  break;
153  case BeginsWith:
154  expr = QStringLiteral( "%1 LIKE '%2%'" ).arg( fieldRef, value );
155  break;
156  case Contains:
157  expr = QStringLiteral( "%1 LIKE '%%2%'" ).arg( fieldRef, value );
158  break;
159  case IsNull:
160  expr = QStringLiteral( "%1 IS NULL" ).arg( fieldRef );
161  break;
162  case IsNotNull:
163  expr = QStringLiteral( "%1 IS NOT NULL" ).arg( fieldRef );
164  break;
165  case DoesNotContain:
166  expr = QStringLiteral( "%1 NOT LIKE '%%2%'" ).arg( fieldRef, value );
167  break;
168  }
169 
170  QgsExpression expression( expr );
171  if ( expression.hasParserError() )
172  {
173  throw QgsProcessingException( expression.parserErrorString() );
174  }
175 
176  QgsExpressionContext expressionContext = createExpressionContext( parameters, context, dynamic_cast< QgsProcessingFeatureSource * >( source.get() ) );
177 
178  long count = source->featureCount();
179 
180  double step = count > 0 ? 100.0 / count : 1;
181  int current = 0;
182 
183  if ( !nonMatchingSink )
184  {
185  // not saving failing features - so only fetch good features
186  QgsFeatureRequest req;
187  req.setFilterExpression( expr );
188  req.setExpressionContext( expressionContext );
189 
190  QgsFeatureIterator it = source->getFeatures( req );
191  QgsFeature f;
192  while ( it.nextFeature( f ) )
193  {
194  if ( feedback->isCanceled() )
195  {
196  break;
197  }
198 
199  matchingSink->addFeature( f, QgsFeatureSink::FastInsert );
200 
201  feedback->setProgress( current * step );
202  current++;
203  }
204  }
205  else
206  {
207  // saving non-matching features, so we need EVERYTHING
208  expressionContext.setFields( source->fields() );
209  expression.prepare( &expressionContext );
210 
211  QgsFeatureIterator it = source->getFeatures();
212  QgsFeature f;
213  while ( it.nextFeature( f ) )
214  {
215  if ( feedback->isCanceled() )
216  {
217  break;
218  }
219 
220  expressionContext.setFeature( f );
221  if ( expression.evaluate( &expressionContext ).toBool() )
222  {
223  matchingSink->addFeature( f, QgsFeatureSink::FastInsert );
224  }
225  else
226  {
227  nonMatchingSink->addFeature( f, QgsFeatureSink::FastInsert );
228  }
229 
230  feedback->setProgress( current * step );
231  current++;
232  }
233  }
234 
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 
244 
245 
Wrapper for iterator of features from vector data provider or vector layer.
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.
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:62
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.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
An input feature source (such as vector layers) parameter for processing algorithms.
bool nextFeature(QgsFeature &f)
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:48