24 QString QgsAggregateAlgorithm::name()
const
26 return QStringLiteral(
"aggregate" );
29 QString QgsAggregateAlgorithm::displayName()
const
31 return QObject::tr(
"Aggregate" );
34 QString QgsAggregateAlgorithm::shortHelpString()
const
36 return QObject::tr(
"This algorithm take a vector or table layer and aggregate features based on a group by expression. Features for which group by expression return the same value are grouped together.\n\n"
37 "It is possible to group all source features together using constant value in group by parameter, example: NULL.\n\n"
38 "It is also possible to group features using multiple fields using Array function, example: Array(\"Field1\", \"Field2\").\n\n"
39 "Geometries (if present) are combined into one multipart geometry for each group.\n\n"
40 "Output attributes are computed depending on each given aggregate definition." );
43 QStringList QgsAggregateAlgorithm::tags()
const
45 return QObject::tr(
"attributes,sum,mean,collect,dissolve,statistics" ).split(
',' );
48 QString QgsAggregateAlgorithm::group()
const
50 return QObject::tr(
"Vector geometry" );
53 QString QgsAggregateAlgorithm::groupId()
const
55 return QStringLiteral(
"vectorgeometry" );
58 QgsAggregateAlgorithm *QgsAggregateAlgorithm::createInstance()
const
60 return new QgsAggregateAlgorithm();
63 void QgsAggregateAlgorithm::initAlgorithm(
const QVariantMap & )
66 addParameter(
new QgsProcessingParameterExpression( QStringLiteral(
"GROUP_BY" ), QObject::tr(
"Group by expression (NULL to group all features)" ), QStringLiteral(
"NULL" ), QStringLiteral(
"INPUT" ) ) );
73 mSource.reset( parameterAsSource( parameters, QStringLiteral(
"INPUT" ), context ) );
77 mGroupBy = parameterAsExpression( parameters, QStringLiteral(
"GROUP_BY" ), context );
82 mGroupByExpression = createExpression( mGroupBy, context );
83 mGeometryExpression = createExpression( QStringLiteral(
"collect($geometry, %1)" ).arg( mGroupBy ), context );
85 const QVariantList aggregates = parameters.value( QStringLiteral(
"AGGREGATES" ) ).toList();
86 int currentAttributeIndex = 0;
87 for (
const QVariant &aggregate : aggregates )
89 const QVariantMap aggregateDef = aggregate.toMap();
91 const QString name = aggregateDef.value( QStringLiteral(
"name" ) ).toString();
95 const QVariant::Type type =
static_cast< QVariant::Type
>( aggregateDef.value( QStringLiteral(
"type" ) ).toInt() );
97 const int length = aggregateDef.value( QStringLiteral(
"length" ), 0 ).toInt();
98 const int precision = aggregateDef.value( QStringLiteral(
"precision" ), 0 ).toInt();
103 const QString aggregateType = aggregateDef.value( QStringLiteral(
"aggregate" ) ).toString();
104 const QString source = aggregateDef.value( QStringLiteral(
"input" ) ).toString();
105 const QString delimiter = aggregateDef.value( QStringLiteral(
"delimiter" ) ).toString();
108 if ( aggregateType == QLatin1String(
"first_value" ) )
112 else if ( aggregateType == QLatin1String(
"last_value" ) )
115 mAttributesRequireLastFeature << currentAttributeIndex;
117 else if ( aggregateType == QLatin1String(
"concatenate" ) || aggregateType == QLatin1String(
"concatenate_unique" ) )
119 expression = QStringLiteral(
"%1(%2, %3, %4, \'%5\')" ).arg( aggregateType,
122 QStringLiteral(
"TRUE" ),
127 expression = QStringLiteral(
"%1(%2, %3)" ).arg( aggregateType, source, mGroupBy );
129 mExpressions.append( createExpression( expression, context ) );
130 currentAttributeIndex++;
138 QgsExpressionContext expressionContext = createExpressionContext( parameters, context, mSource.get() );
139 mGroupByExpression.prepare( &expressionContext );
142 const long long count = mSource->featureCount();
143 double progressStep = count > 0 ? 50.0 / count : 1;
144 long long current = 0;
146 QHash< QVariantList, Group > groups;
147 QVector< QVariantList > keys;
150 std::vector< std::unique_ptr< QgsFeatureSink > > groupSinks;
156 const QVariant groupByValue = mGroupByExpression.evaluate( &expressionContext );
157 if ( mGroupByExpression.hasEvalError() )
159 throw QgsProcessingException( QObject::tr(
"Evaluation error in group by expression \"%1\": %2" ).arg( mGroupByExpression.expression(),
160 mGroupByExpression.evalErrorString() ) );
164 const QVariantList key = groupByValue.type() == QVariant::List ? groupByValue.toList() : ( QVariantList() << groupByValue );
166 auto groupIt = groups.find( key );
167 if ( groupIt == groups.end() )
169 QString
id = QStringLiteral(
"memory:" );
174 mSource->sourceCrs() ) );
181 group.sink = sink.get();
183 groupSinks.emplace_back( std::move( sink ) );
185 group.firstFeature = feature;
186 group.lastFeature = feature;
193 groupIt->lastFeature = feature;
206 std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral(
"OUTPUT" ), context, destId, mFields,
QgsWkbTypes::multiType( mSource->wkbType() ), mSource->sourceCrs() ) );
212 progressStep = 50.0 / keys.size();
215 for (
const QVariantList &key : keys )
217 Group &group = groups[ key ];
224 if ( mGeometryExpression.hasEvalError() )
226 throw QgsProcessingException( QObject::tr(
"Evaluation error in geometry expression \"%1\": %2" ).arg( mGeometryExpression.expression(),
227 mGeometryExpression.evalErrorString() ) );
235 QStringList keyString;
236 for (
const QVariant &v : key )
237 keyString << v.toString();
239 throw QgsProcessingException( QObject::tr(
"Impossible to combine geometries for %1 = %2" ).arg( mGroupBy, keyString.join(
',' ) ) );
244 attributes.reserve( mExpressions.size() );
245 int currentAttributeIndex = 0;
246 for (
auto it = mExpressions.begin(); it != mExpressions.end(); ++it )
248 exprContext.
setFeature( mAttributesRequireLastFeature.contains( currentAttributeIndex ) ? group.lastFeature : group.firstFeature );
251 const QVariant value = it->evaluate( &exprContext );
252 if ( it->hasEvalError() )
254 throw QgsProcessingException( QObject::tr(
"Evaluation error in expression \"%1\": %2" ).arg( it->expression(), it->evalErrorString() ) );
256 attributes.append( value );
260 attributes.append( QVariant() );
262 currentAttributeIndex++;
272 feedback->
setProgress( 50 + current * progressStep );
278 results.insert( QStringLiteral(
"OUTPUT" ), destId );
282 bool QgsAggregateAlgorithm::supportInPlaceEdit(
const QgsMapLayer *layer )
const
291 expr.setGeomCalculator( &mDa );
293 expr.setAreaUnits( context.
areaUnit() );
294 if ( expr.hasParserError() )
297 QObject::tr(
"Parser error in expression \"%1\": %2" ).arg( expressionString, expr.parserErrorString() ) );
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
bool isValid() const
Will return if this iterator is valid.
This class wraps a request for features to a vector layer (or directly its vector data provider).
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
void setProgress(double progress)
Sets the current progress for the feedback object.
Encapsulate a field in an attribute table or data source.
A geometry is the spatial representation of a feature.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries)
Compute the unary union on a list of geometries.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
Base class for all map layer types.
Contains information about the context in which a processing algorithm is executed.
QgsUnitTypes::AreaUnit areaUnit() const
Returns the area unit to use for area calculations.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsUnitTypes::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.
A parameter for "aggregate" configurations, which consist of a definition of desired output fields,...
An expression parameter for processing algorithms.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
static QgsFeatureSink * createFeatureSink(QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions=QVariantMap(), const QStringList &datasourceOptions=QStringList(), const QStringList &layerOptions=QStringList(), QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QgsRemappingSinkDefinition *remappingDefinition=nullptr)
Creates a feature sink ready for adding features.
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true, QgsProcessingUtils::LayerHint typeHint=QgsProcessingUtils::LayerHint::UnknownType)
Interprets a string as a map layer within the supplied context.
@ TypeVector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
static Type multiType(Type type) SIP_HOLDGIL
Returns the multi type for a WKB type.