QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
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
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 std::unique_ptr<QgsProcessingParameterString> fieldName = std::make_unique<QgsProcessingParameterString>( QStringLiteral( "FIELD_NAME" ), QObject::tr( "Field name" ), QVariant(), false );
93 std::unique_ptr<QgsProcessingParameterEnum> 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 std::unique_ptr<QgsProcessingParameterNumber> fieldLength = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "FIELD_LENGTH" ), QObject::tr( "Result field length" ), Qgis::ProcessingNumberParameterType::Integer, QVariant( 0 ), false, 0 );
100 std::unique_ptr<QgsProcessingParameterNumber> fieldPrecision = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "FIELD_PRECISION" ), QObject::tr( "Result field precision" ), Qgis::ProcessingNumberParameterType::Integer, QVariant( 0 ), false, 0 );
101 std::unique_ptr<QgsProcessingParameterExpression> 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
125QgsFieldCalculatorAlgorithm *QgsFieldCalculatorAlgorithm::createInstance() const
126{
127 return new QgsFieldCalculatorAlgorithm();
128}
129
130
131bool QgsFieldCalculatorAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
132{
133 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
134
135 if ( !source )
136 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
137
138 // prepare fields
139 const int fieldTypeIdx = parameterAsInt( parameters, QStringLiteral( "FIELD_TYPE" ), context );
140 const int fieldLength = parameterAsInt( parameters, QStringLiteral( "FIELD_LENGTH" ), context );
141 const int fieldPrecision = parameterAsInt( parameters, QStringLiteral( "FIELD_PRECISION" ), context );
142 const QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD_NAME" ), context );
143
144 QMetaType::Type fieldType = QMetaType::Type::QString;
145 QMetaType::Type fieldSubType = QMetaType::Type::UnknownType;
146 switch ( fieldTypeIdx )
147 {
148 case 0: // Float
149 fieldType = QMetaType::Type::Double;
150 break;
151 case 1: // Integer
152 fieldType = QMetaType::Type::Int;
153 break;
154 case 2: // String
155 fieldType = QMetaType::Type::QString;
156 break;
157 case 3: // Date
158 fieldType = QMetaType::Type::QDate;
159 break;
160 case 4: // Time
161 fieldType = QMetaType::Type::QTime;
162 break;
163 case 5: // DateTime
164 fieldType = QMetaType::Type::QDateTime;
165 break;
166 case 6: // Boolean
167 fieldType = QMetaType::Type::Bool;
168 break;
169 case 7: // Binary
170 fieldType = QMetaType::Type::QByteArray;
171 break;
172 case 8: // StringList
173 fieldType = QMetaType::Type::QStringList;
174 fieldSubType = QMetaType::Type::QString;
175 break;
176 case 9: // IntegerList
177 fieldType = QMetaType::Type::QVariantList;
178 fieldSubType = QMetaType::Type::Int;
179 break;
180 case 10: // DoubleList
181 fieldType = QMetaType::Type::QVariantList;
182 fieldSubType = QMetaType::Type::Double;
183 break;
184 }
185
186 if ( fieldName.isEmpty() )
187 throw QgsProcessingException( QObject::tr( "Field name must not be an empty string" ) );
188
189 const QgsField field(
190 fieldName,
191 fieldType,
192 QString(),
193 fieldLength,
194 fieldPrecision,
195 QString(),
196 fieldSubType
197 );
198
199 mFields = source->fields();
200
201 const int fieldIdx = mFields.indexFromName( field.name() );
202
203 if ( fieldIdx < 0 )
204 {
205 mFields.append( field );
206 }
207 else
208 {
209 feedback->pushWarning( QObject::tr( "Field name %1 already exists and will be replaced" ).arg( field.name() ) );
210 }
211
212 mFieldIdx = mFields.lookupField( field.name() );
213
214 // prepare expression
215 const QString expressionString = parameterAsString( parameters, QStringLiteral( "FORMULA" ), context );
216 mExpressionContext = createExpressionContext( parameters, context, source.get() );
217 mExpression = QgsExpression( expressionString );
218 mDa.setSourceCrs( source->sourceCrs(), context.transformContext() );
219 mDa.setEllipsoid( context.ellipsoid() );
220
221 mExpression.setGeomCalculator( &mDa );
222 mExpression.setDistanceUnits( context.distanceUnit() );
223 mExpression.setAreaUnits( context.areaUnit() );
224
225 if ( mExpression.hasParserError() )
226 throw QgsProcessingException( QObject::tr( "Parser error with formula expression \"%2\": %3" )
227 .arg( expressionString, mExpression.parserErrorString() ) );
228
229 mExpression.prepare( &mExpressionContext );
230
231 return true;
232}
233
234QgsFeatureList QgsFieldCalculatorAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
235{
236 QgsAttributes attributes( mFields.size() );
237 const QStringList fieldNames = mFields.names();
238 for ( const QString &fieldName : fieldNames )
239 {
240 const int attributeIndex = feature.fieldNameIndex( fieldName );
241
242 if ( attributeIndex >= 0 )
243 attributes[attributeIndex] = feature.attribute( fieldName );
244 }
245
246 if ( mExpression.isValid() )
247 {
248 mExpressionContext.setFeature( feature );
249 mExpressionContext.lastScope()->setVariable( QStringLiteral( "row_number" ), mRowNumber );
250
251 const QVariant value = mExpression.evaluate( &mExpressionContext );
252
253 if ( mExpression.hasEvalError() )
254 {
255 throw QgsProcessingException( QObject::tr( "Evaluation error in expression \"%1\": %2" )
256 .arg( mExpression.expression(), mExpression.evalErrorString() ) );
257 }
258
259 attributes[mFieldIdx] = value;
260 }
261 else
262 {
263 attributes[mFieldIdx] = QVariant();
264 }
265
266 QgsFeature f = feature;
267 f.setAttributes( attributes );
268 mRowNumber++;
269 return QgsFeatureList() << f;
270}
271
272bool QgsFieldCalculatorAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
273{
274 Q_UNUSED( layer )
275 return false;
276}
277
@ 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:3507
A vector of attributes.
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: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:76
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