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