Branch data Line data Source code
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 : :
19 : : #include "qgsalgorithmfieldcalculator.h"
20 : : #include "qgsexpressioncontextutils.h"
21 : :
22 : : ///@cond PRIVATE
23 : :
24 : 0 : QString QgsFieldCalculatorAlgorithm::name() const
25 : : {
26 : 0 : return QStringLiteral( "fieldcalculator" );
27 : : }
28 : :
29 : 0 : QString QgsFieldCalculatorAlgorithm::displayName() const
30 : : {
31 : 0 : return QObject::tr( "Field calculator" );
32 : : }
33 : :
34 : 0 : QStringList QgsFieldCalculatorAlgorithm::tags() const
35 : : {
36 : 0 : return QObject::tr( "field,calculator,vector" ).split( ',' );
37 : 0 : }
38 : :
39 : 0 : QString QgsFieldCalculatorAlgorithm::group() const
40 : : {
41 : 0 : return QObject::tr( "Vector table" );
42 : : }
43 : :
44 : 0 : QString QgsFieldCalculatorAlgorithm::groupId() const
45 : : {
46 : 0 : return QStringLiteral( "vectortable" );
47 : : }
48 : :
49 : 0 : QString QgsFieldCalculatorAlgorithm::outputName() const
50 : : {
51 : 0 : return QObject::tr( "Calculated" );
52 : : }
53 : :
54 : 0 : QList<int> QgsFieldCalculatorAlgorithm::inputLayerTypes() const
55 : : {
56 : 0 : return QList<int>() << QgsProcessing::TypeVector;
57 : 0 : }
58 : :
59 : 0 : QgsProcessingFeatureSource::Flag QgsFieldCalculatorAlgorithm::sourceFlags() const
60 : : {
61 : 0 : return QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks;
62 : : }
63 : :
64 : 0 : void QgsFieldCalculatorAlgorithm::initParameters( const QVariantMap &configuration )
65 : : {
66 : 0 : Q_UNUSED( configuration );
67 : :
68 : 0 : QStringList fieldTypes = QStringList( {QObject::tr( "Float" ), QObject::tr( "Integer" ), QObject::tr( "String" ), QObject::tr( "Date" ) } );
69 : :
70 : 0 : std::unique_ptr< QgsProcessingParameterString > fieldName = std::make_unique< QgsProcessingParameterString > ( QStringLiteral( "FIELD_NAME" ), QObject::tr( "Field name" ), QVariant(), false );
71 : 0 : std::unique_ptr< QgsProcessingParameterEnum > fieldType = std::make_unique< QgsProcessingParameterEnum > ( QStringLiteral( "FIELD_TYPE" ), QObject::tr( "Result field type" ), fieldTypes, false, 0 );
72 : 0 : std::unique_ptr< QgsProcessingParameterNumber > fieldLength = std::make_unique< QgsProcessingParameterNumber > ( QStringLiteral( "FIELD_LENGTH" ), QObject::tr( "Result field length" ), QgsProcessingParameterNumber::Integer, QVariant( 0 ), false, 0 );
73 : 0 : std::unique_ptr< QgsProcessingParameterNumber > fieldPrecision = std::make_unique< QgsProcessingParameterNumber > ( QStringLiteral( "FIELD_PRECISION" ), QObject::tr( "Result field precision" ), QgsProcessingParameterNumber::Integer, QVariant( 0 ), false, 0 );
74 : 0 : std::unique_ptr< QgsProcessingParameterExpression > expression = std::make_unique< QgsProcessingParameterExpression> ( QStringLiteral( "FORMULA" ), QObject::tr( "Formula" ), QVariant(), QStringLiteral( "INPUT" ), false );
75 : :
76 : 0 : expression->setMetadata( QVariantMap( {{"inlineEditor", true}} ) );
77 : :
78 : 0 : addParameter( fieldName.release() );
79 : 0 : addParameter( fieldType.release() );
80 : 0 : addParameter( fieldLength.release() );
81 : 0 : addParameter( fieldPrecision.release() );
82 : 0 : addParameter( expression.release() );
83 : 0 : }
84 : :
85 : 0 : QgsFields QgsFieldCalculatorAlgorithm::outputFields( const QgsFields & ) const
86 : : {
87 : 0 : return mFields;
88 : : }
89 : :
90 : 0 : QString QgsFieldCalculatorAlgorithm::shortHelpString() const
91 : : {
92 : 0 : return QObject::tr( "This algorithm computes a new vector layer with the same features of the input layer, "
93 : : "but either overwriting an existing attribute or adding an additional attribute. The values of this field "
94 : : "are computed from each feature using an expression, based on the properties and attributes of the feature. "
95 : : "Note that if \"Field name\" is an existing field in the layer then all the rest of the field settings are ignored." );
96 : : }
97 : :
98 : 0 : QgsFieldCalculatorAlgorithm *QgsFieldCalculatorAlgorithm::createInstance() const
99 : : {
100 : 0 : return new QgsFieldCalculatorAlgorithm();
101 : 0 : }
102 : :
103 : :
104 : 0 : bool QgsFieldCalculatorAlgorithm::prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
105 : : {
106 : 0 : std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
107 : :
108 : 0 : if ( !source )
109 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
110 : :
111 : 0 : QList<QVariant::Type> fieldTypes( {QVariant::Double, QVariant::Int, QVariant::String, QVariant::Date} );
112 : :
113 : : // prepare fields
114 : 0 : const int fieldTypeIdx = parameterAsInt( parameters, QStringLiteral( "FIELD_TYPE" ), context );
115 : 0 : const int fieldLength = parameterAsInt( parameters, QStringLiteral( "FIELD_LENGTH" ), context );
116 : 0 : const int fieldPrecision = parameterAsInt( parameters, QStringLiteral( "FIELD_PRECISION" ), context );
117 : 0 : const QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD_NAME" ), context );
118 : :
119 : 0 : QVariant::Type fieldType = fieldTypes[fieldTypeIdx];
120 : :
121 : 0 : if ( fieldName.isEmpty() )
122 : 0 : throw QgsProcessingException( QObject::tr( "Field name must not be an empty string" ) );
123 : :
124 : 0 : const QgsField field(
125 : : fieldName,
126 : 0 : fieldType,
127 : 0 : QString(),
128 : 0 : fieldLength,
129 : 0 : fieldPrecision
130 : : );
131 : :
132 : 0 : mFields = source->fields();
133 : :
134 : 0 : int fieldIdx = mFields.indexFromName( field.name() );
135 : :
136 : 0 : if ( fieldIdx < 0 )
137 : : {
138 : 0 : mFields.append( field );
139 : 0 : }
140 : : else
141 : : {
142 : 0 : feedback->pushWarning( QObject::tr( "Field name %1 already exists and will be replaced" ).arg( field.name() ) );
143 : : }
144 : :
145 : 0 : QString dest;
146 : :
147 : 0 : mFieldIdx = mFields.lookupField( field.name() );
148 : :
149 : : // prepare expression
150 : 0 : QString expressionString = parameterAsString( parameters, QStringLiteral( "FORMULA" ), context );
151 : 0 : mExpressionContext = createExpressionContext( parameters, context, source.get() );
152 : 0 : mExpression = QgsExpression( expressionString );
153 : 0 : mDa.setSourceCrs( source->sourceCrs(), context.transformContext() );
154 : 0 : mDa.setEllipsoid( context.ellipsoid() );
155 : :
156 : 0 : mExpression.setGeomCalculator( &mDa );
157 : 0 : mExpression.setDistanceUnits( context.distanceUnit() );
158 : 0 : mExpression.setAreaUnits( context.areaUnit() );
159 : :
160 : 0 : if ( mExpression.hasParserError() )
161 : 0 : throw QgsProcessingException( QObject::tr( "Parser error with formula expression \"%2\": %3" )
162 : 0 : .arg( expressionString, mExpression.parserErrorString() ) );
163 : :
164 : 0 : mExpression.prepare( &mExpressionContext );
165 : :
166 : : return true;
167 : 0 : }
168 : :
169 : 0 : QgsFeatureList QgsFieldCalculatorAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
170 : : {
171 : 0 : QgsAttributes attributes( mFields.size() );
172 : 0 : const QStringList fieldNames = mFields.names();
173 : 0 : for ( const QString &fieldName : fieldNames )
174 : : {
175 : 0 : const int attributeIndex = feature.fieldNameIndex( fieldName );
176 : :
177 : 0 : if ( attributeIndex >= 0 )
178 : 0 : attributes[attributeIndex] = feature.attribute( fieldName );
179 : : }
180 : :
181 : 0 : if ( mExpression.isValid() )
182 : : {
183 : 0 : mExpressionContext.setFeature( feature );
184 : 0 : mExpressionContext.lastScope()->setVariable( QStringLiteral( "row_number" ), mRowNumber );
185 : :
186 : 0 : const QVariant value = mExpression.evaluate( &mExpressionContext );
187 : :
188 : 0 : if ( mExpression.hasEvalError() )
189 : : {
190 : 0 : throw QgsProcessingException( QObject::tr( "Evaluation error in expression \"%1\": %2" )
191 : 0 : .arg( mExpression.expression(), mExpression.evalErrorString() ) );
192 : : }
193 : :
194 : 0 : attributes[mFieldIdx] = value;
195 : 0 : }
196 : : else
197 : : {
198 : 0 : attributes[mFieldIdx] = QVariant();
199 : : }
200 : :
201 : 0 : QgsFeature f = feature;
202 : 0 : f.setAttributes( attributes );
203 : 0 : mRowNumber++;
204 : 0 : return QgsFeatureList() << f;
205 : 0 : }
206 : :
207 : 0 : bool QgsFieldCalculatorAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
208 : : {
209 : : Q_UNUSED( layer )
210 : 0 : return false;
211 : : }
212 : :
213 : : ///@endcond
|