QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgsalgorithmfieldcalculator.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmfieldcalculator.h
3  ----------------------
4  begin : September 2020
5  copyright : (C) 2020 by Ivan Ivanov
6  email : [email protected]
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 
18 
21 #include "qgsvariantutils.h"
22 
24 
25 QString QgsFieldCalculatorAlgorithm::name() const
26 {
27  return QStringLiteral( "fieldcalculator" );
28 }
29 
30 QString QgsFieldCalculatorAlgorithm::displayName() const
31 {
32  return QObject::tr( "Field calculator" );
33 }
34 
35 QStringList QgsFieldCalculatorAlgorithm::tags() const
36 {
37  return QObject::tr( "field,calculator,vector" ).split( ',' );
38 }
39 
40 QString QgsFieldCalculatorAlgorithm::group() const
41 {
42  return QObject::tr( "Vector table" );
43 }
44 
45 QString QgsFieldCalculatorAlgorithm::groupId() const
46 {
47  return QStringLiteral( "vectortable" );
48 }
49 
50 QString QgsFieldCalculatorAlgorithm::outputName() const
51 {
52  return QObject::tr( "Calculated" );
53 }
54 
55 QList<int> QgsFieldCalculatorAlgorithm::inputLayerTypes() const
56 {
57  return QList<int>() << QgsProcessing::TypeVector;
58 }
59 
60 QgsProcessingFeatureSource::Flag QgsFieldCalculatorAlgorithm::sourceFlags() const
61 {
63 }
64 
65 void QgsFieldCalculatorAlgorithm::initParameters( const QVariantMap &configuration )
66 {
67  Q_UNUSED( configuration )
68 
69  QStringList fieldTypes;
70  QVariantList icons;
71  fieldTypes.reserve( 11 );
72  icons.reserve( 11 );
73  for ( const auto &type :
74  std::vector < std::pair< QVariant::Type, QVariant::Type > >
75 {
76  {QVariant::Double, QVariant::Invalid },
77  {QVariant::Int, QVariant::Invalid },
78  {QVariant::String, QVariant::Invalid },
79  {QVariant::Date, QVariant::Invalid },
80  {QVariant::Time, QVariant::Invalid },
81  {QVariant::DateTime, QVariant::Invalid },
82  {QVariant::Bool, QVariant::Invalid },
83  {QVariant::ByteArray, QVariant::Invalid },
84  {QVariant::StringList, QVariant::Invalid },
85  {QVariant::List, QVariant::Int },
86  {QVariant::List, QVariant::Double }
87 } )
88  {
89  fieldTypes << QgsVariantUtils::typeToDisplayString( type.first, type.second );
90  icons << QgsFields::iconForFieldType( type.first, type.second );
91  }
92 
93  std::unique_ptr< QgsProcessingParameterString > fieldName = std::make_unique< QgsProcessingParameterString > ( QStringLiteral( "FIELD_NAME" ), QObject::tr( "Field name" ), QVariant(), false );
94  std::unique_ptr< QgsProcessingParameterEnum > fieldType = std::make_unique< QgsProcessingParameterEnum > ( QStringLiteral( "FIELD_TYPE" ), QObject::tr( "Result field type" ), fieldTypes, false, 0 );
95  fieldType->setMetadata(
96  {
97  QVariantMap( {{
98  QStringLiteral( "widget_wrapper" ),
99  QVariantMap(
100  { {
101  QStringLiteral( "icons" ), icons
102  }}
103  )
104  }} )
105  } );
106 
107  std::unique_ptr< QgsProcessingParameterNumber > fieldLength = std::make_unique< QgsProcessingParameterNumber > ( QStringLiteral( "FIELD_LENGTH" ), QObject::tr( "Result field length" ), QgsProcessingParameterNumber::Integer, QVariant( 0 ), false, 0 );
108  std::unique_ptr< QgsProcessingParameterNumber > fieldPrecision = std::make_unique< QgsProcessingParameterNumber > ( QStringLiteral( "FIELD_PRECISION" ), QObject::tr( "Result field precision" ), QgsProcessingParameterNumber::Integer, QVariant( 0 ), false, 0 );
109  std::unique_ptr< QgsProcessingParameterExpression > expression = std::make_unique< QgsProcessingParameterExpression> ( QStringLiteral( "FORMULA" ), QObject::tr( "Formula" ), QVariant(), QStringLiteral( "INPUT" ), false );
110 
111  expression->setMetadata( QVariantMap( {{"inlineEditor", true}} ) );
112 
113  addParameter( fieldName.release() );
114  addParameter( fieldType.release() );
115  addParameter( fieldLength.release() );
116  addParameter( fieldPrecision.release() );
117  addParameter( expression.release() );
118 }
119 
120 QgsFields QgsFieldCalculatorAlgorithm::outputFields( const QgsFields & ) const
121 {
122  return mFields;
123 }
124 
125 QString QgsFieldCalculatorAlgorithm::shortHelpString() const
126 {
127  return QObject::tr( "This algorithm computes a new vector layer with the same features of the input layer, "
128  "but either overwriting an existing attribute or adding an additional attribute. The values of this field "
129  "are computed from each feature using an expression, based on the properties and attributes of the feature. "
130  "Note that if \"Field name\" is an existing field in the layer then all the rest of the field settings are ignored." );
131 }
132 
133 QgsFieldCalculatorAlgorithm *QgsFieldCalculatorAlgorithm::createInstance() const
134 {
135  return new QgsFieldCalculatorAlgorithm();
136 }
137 
138 
139 bool QgsFieldCalculatorAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
140 {
141  std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
142 
143  if ( !source )
144  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
145 
146  // prepare fields
147  const int fieldTypeIdx = parameterAsInt( parameters, QStringLiteral( "FIELD_TYPE" ), context );
148  const int fieldLength = parameterAsInt( parameters, QStringLiteral( "FIELD_LENGTH" ), context );
149  const int fieldPrecision = parameterAsInt( parameters, QStringLiteral( "FIELD_PRECISION" ), context );
150  const QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD_NAME" ), context );
151 
152  QVariant::Type fieldType = QVariant::Type::String;
153  QVariant::Type fieldSubType = QVariant::Type::Invalid;
154  switch ( fieldTypeIdx )
155  {
156  case 0: // Float
157  fieldType = QVariant::Double;
158  break;
159  case 1: // Integer
160  fieldType = QVariant::Int;
161  break;
162  case 2: // String
163  fieldType = QVariant::String;
164  break;
165  case 3: // Date
166  fieldType = QVariant::Date;
167  break;
168  case 4: // Time
169  fieldType = QVariant::Time;
170  break;
171  case 5: // DateTime
172  fieldType = QVariant::DateTime;
173  break;
174  case 6: // Boolean
175  fieldType = QVariant::Bool;
176  break;
177  case 7: // Binary
178  fieldType = QVariant::ByteArray;
179  break;
180  case 8: // StringList
181  fieldType = QVariant::StringList;
182  fieldSubType = QVariant::String;
183  break;
184  case 9: // IntegerList
185  fieldType = QVariant::List;
186  fieldSubType = QVariant::Int;
187  break;
188  case 10: // DoubleList
189  fieldType = QVariant::List;
190  fieldSubType = QVariant::Double;
191  break;
192  }
193 
194  if ( fieldName.isEmpty() )
195  throw QgsProcessingException( QObject::tr( "Field name must not be an empty string" ) );
196 
197  const QgsField field(
198  fieldName,
199  fieldType,
200  QString(),
201  fieldLength,
202  fieldPrecision,
203  QString(),
204  fieldSubType
205  );
206 
207  mFields = source->fields();
208 
209  const int fieldIdx = mFields.indexFromName( field.name() );
210 
211  if ( fieldIdx < 0 )
212  {
213  mFields.append( field );
214  }
215  else
216  {
217  feedback->pushWarning( QObject::tr( "Field name %1 already exists and will be replaced" ).arg( field.name() ) );
218  }
219 
220  mFieldIdx = mFields.lookupField( field.name() );
221 
222  // prepare expression
223  const QString expressionString = parameterAsString( parameters, QStringLiteral( "FORMULA" ), context );
224  mExpressionContext = createExpressionContext( parameters, context, source.get() );
225  mExpression = QgsExpression( expressionString );
226  mDa.setSourceCrs( source->sourceCrs(), context.transformContext() );
227  mDa.setEllipsoid( context.ellipsoid() );
228 
229  mExpression.setGeomCalculator( &mDa );
230  mExpression.setDistanceUnits( context.distanceUnit() );
231  mExpression.setAreaUnits( context.areaUnit() );
232 
233  if ( mExpression.hasParserError() )
234  throw QgsProcessingException( QObject::tr( "Parser error with formula expression \"%2\": %3" )
235  .arg( expressionString, mExpression.parserErrorString() ) );
236 
237  mExpression.prepare( &mExpressionContext );
238 
239  return true;
240 }
241 
242 QgsFeatureList QgsFieldCalculatorAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
243 {
244  QgsAttributes attributes( mFields.size() );
245  const QStringList fieldNames = mFields.names();
246  for ( const QString &fieldName : fieldNames )
247  {
248  const int attributeIndex = feature.fieldNameIndex( fieldName );
249 
250  if ( attributeIndex >= 0 )
251  attributes[attributeIndex] = feature.attribute( fieldName );
252  }
253 
254  if ( mExpression.isValid() )
255  {
256  mExpressionContext.setFeature( feature );
257  mExpressionContext.lastScope()->setVariable( QStringLiteral( "row_number" ), mRowNumber );
258 
259  const QVariant value = mExpression.evaluate( &mExpressionContext );
260 
261  if ( mExpression.hasEvalError() )
262  {
263  throw QgsProcessingException( QObject::tr( "Evaluation error in expression \"%1\": %2" )
264  .arg( mExpression.expression(), mExpression.evalErrorString() ) );
265  }
266 
267  attributes[mFieldIdx] = value;
268  }
269  else
270  {
271  attributes[mFieldIdx] = QVariant();
272  }
273 
274  QgsFeature f = feature;
275  f.setAttributes( attributes );
276  mRowNumber++;
277  return QgsFeatureList() << f;
278 }
279 
280 bool QgsFieldCalculatorAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
281 {
282  Q_UNUSED( layer )
283  return false;
284 }
285 
A vector of attributes.
Definition: qgsattributes.h:58
Class for parsing and evaluation of expressions (formerly called "search strings").
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
int fieldNameIndex(const QString &fieldName) const
Utility method to get attribute index from name.
Definition: qgsfeature.cpp:335
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:153
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:320
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
Container of fields for a vector layer.
Definition: qgsfields.h:45
static QIcon iconForFieldType(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns an icon corresponding to a field type.
Definition: qgsfields.cpp:294
Base class for all map layer types.
Definition: qgsmaplayer.h:73
Contains information about the context in which a processing algorithm is executed.
QgsUnitTypes::AreaUnit areaUnit() const
Returns the area unit to use for area calculations.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsUnitTypes::DistanceUnit distanceUnit() const
Returns the distance unit to use for distance calculations.
QString ellipsoid() const
Returns the ellipsoid to use for distance and area calculations.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
Flag
Flags controlling how QgsProcessingFeatureSource fetches features.
@ 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.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
@ TypeVector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:54
static QString typeToDisplayString(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns a user-friendly translated string representing a QVariant type.
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:882
const QgsField & field
Definition: qgsfield.h:463