QGIS API Documentation 3.41.0-Master (45a0abf3bec)
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
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{
76 {QMetaType::Type::Double, QMetaType::Type::UnknownType },
77 {QMetaType::Type::Int, QMetaType::Type::UnknownType },
78 {QMetaType::Type::QString, QMetaType::Type::UnknownType },
79 {QMetaType::Type::QDate, QMetaType::Type::UnknownType },
80 {QMetaType::Type::QTime, QMetaType::Type::UnknownType },
81 {QMetaType::Type::QDateTime, QMetaType::Type::UnknownType },
82 {QMetaType::Type::Bool, QMetaType::Type::UnknownType },
83 {QMetaType::Type::QByteArray, QMetaType::Type::UnknownType },
84 {QMetaType::Type::QStringList, QMetaType::Type::UnknownType },
85 {QMetaType::Type::QVariantList, QMetaType::Type::Int },
86 {QMetaType::Type::QVariantList, QMetaType::Type::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" ), Qgis::ProcessingNumberParameterType::Integer, QVariant( 0 ), false, 0 );
108 std::unique_ptr< QgsProcessingParameterNumber > fieldPrecision = std::make_unique< QgsProcessingParameterNumber > ( QStringLiteral( "FIELD_PRECISION" ), QObject::tr( "Result field precision" ), Qgis::ProcessingNumberParameterType::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
120QgsFields QgsFieldCalculatorAlgorithm::outputFields( const QgsFields & ) const
121{
122 return mFields;
123}
124
125QString 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
133QgsFieldCalculatorAlgorithm *QgsFieldCalculatorAlgorithm::createInstance() const
134{
135 return new QgsFieldCalculatorAlgorithm();
136}
137
138
139bool 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 QMetaType::Type fieldType = QMetaType::Type::QString;
153 QMetaType::Type fieldSubType = QMetaType::Type::UnknownType;
154 switch ( fieldTypeIdx )
155 {
156 case 0: // Float
157 fieldType = QMetaType::Type::Double;
158 break;
159 case 1: // Integer
160 fieldType = QMetaType::Type::Int;
161 break;
162 case 2: // String
163 fieldType = QMetaType::Type::QString;
164 break;
165 case 3: // Date
166 fieldType = QMetaType::Type::QDate;
167 break;
168 case 4: // Time
169 fieldType = QMetaType::Type::QTime;
170 break;
171 case 5: // DateTime
172 fieldType = QMetaType::Type::QDateTime;
173 break;
174 case 6: // Boolean
175 fieldType = QMetaType::Type::Bool;
176 break;
177 case 7: // Binary
178 fieldType = QMetaType::Type::QByteArray;
179 break;
180 case 8: // StringList
181 fieldType = QMetaType::Type::QStringList;
182 fieldSubType = QMetaType::Type::QString;
183 break;
184 case 9: // IntegerList
185 fieldType = QMetaType::Type::QVariantList;
186 fieldSubType = QMetaType::Type::Int;
187 break;
188 case 10: // DoubleList
189 fieldType = QMetaType::Type::QVariantList;
190 fieldSubType = QMetaType::Type::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
242QgsFeatureList 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
280bool QgsFieldCalculatorAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
281{
282 Q_UNUSED( layer )
283 return false;
284}
285
@ 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:3444
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