QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 
qgsexpressioncontextutils.h
QgsProcessingContext::distanceUnit
QgsUnitTypes::DistanceUnit distanceUnit() const
Returns the distance unit to use for distance calculations.
Definition: qgsprocessingcontext.cpp:195
QgsProcessingFeedback
Base class for providing feedback from a processing algorithm.
Definition: qgsprocessingfeedback.h:37
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:44
qgsalgorithmfieldcalculator.h
QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks
@ FlagSkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
Definition: qgsprocessingutils.h:584
field
const QgsField & field
Definition: qgsfield.h:463
QgsField::name
QString name
Definition: qgsfield.h:60
QgsFeature::fieldNameIndex
int fieldNameIndex(const QString &fieldName) const
Utility method to get attribute index from name.
Definition: qgsfeature.cpp:342
QgsProcessing::TypeVector
@ TypeVector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:54
QgsProcessingContext
Contains information about the context in which a processing algorithm is executed.
Definition: qgsprocessingcontext.h:46
QgsProcessingContext::ellipsoid
QString ellipsoid() const
Returns the ellipsoid to use for distance and area calculations.
Definition: qgsprocessingcontext.cpp:185
QgsFeatureList
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:882
qgsvariantutils.h
QgsFeature::attribute
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:327
QgsProcessingContext::transformContext
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Definition: qgsprocessingcontext.h:165
QgsProcessingParameterNumber::Integer
@ Integer
Integer values.
Definition: qgsprocessingparameters.h:2186
QgsFields::iconForFieldType
static QIcon iconForFieldType(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns an icon corresponding to a field type.
Definition: qgsfields.cpp:294
QgsVariantUtils::typeToDisplayString
static QString typeToDisplayString(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns a user-friendly translated string representing a QVariant type.
Definition: qgsvariantutils.cpp:19
QgsProcessingFeatureSource::Flag
Flag
Flags controlling how QgsProcessingFeatureSource fetches features.
Definition: qgsprocessingutils.h:582
QgsProcessingContext::areaUnit
QgsUnitTypes::AreaUnit areaUnit() const
Returns the area unit to use for area calculations.
Definition: qgsprocessingcontext.cpp:205
QgsMapLayer
Base class for all map layer types. This is the base class for all map layer types (vector,...
Definition: qgsmaplayer.h:72
QgsAttributes
A vector of attributes. Mostly equal to QVector<QVariant>.
Definition: qgsattributes.h:57
QgsFeature
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:55
QgsFeature::setAttributes
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:160
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings")....
Definition: qgsexpression.h:102
QgsProcessingException
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
QgsProcessingFeedback::pushWarning
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
Definition: qgsprocessingfeedback.cpp:68
QgsField
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:50