QGIS API Documentation 4.3.0-Master (3b7b3208a79)
Loading...
Searching...
No Matches
qgsprocessingpostprocessor.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingpostprocessor.cpp
3 ------------------------------
4 begin : October 2026
5 copyright : (C) 2026 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
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
19
20#include "qgsapplication.h"
21#include "qgslayertree.h"
22#include "qgslayertreegroup.h"
24#include "qgsmaplayer.h"
28#include "qgsvariantutils.h"
29#include "qgsvectorlayer.h"
30
31#include <QString>
32
33using namespace Qt::StringLiterals;
34
36
38{
39 if ( !layer )
40 return;
41
42 QString styleFilePath;
43
44 if ( !outputName.isEmpty() && algorithm )
45 {
47 }
48
49 if ( styleFilePath.isEmpty() )
50 {
51 QgsSettings settings;
52
53 switch ( layer->type() )
54 {
56 {
57 QgsVectorLayer *vectorLayer = qobject_cast< QgsVectorLayer * >( layer );
58 switch ( vectorLayer->geometryType() )
59 {
61 styleFilePath = settings.value( u"Processing/Configuration/VECTOR_POINT_STYLE"_s, QString() ).toString();
62 break;
64 styleFilePath = settings.value( u"Processing/Configuration/VECTOR_LINE_STYLE"_s, QString() ).toString();
65 break;
67 styleFilePath = settings.value( u"Processing/Configuration/VECTOR_POLYGON_STYLE"_s, QString() ).toString();
68 break;
71 break;
72 }
73 break;
74 }
76 {
77 styleFilePath = settings.value( u"Processing/Configuration/RASTER_STYLE"_s, QString() ).toString();
78 break;
79 }
87 break;
88 }
89 }
90
91 if ( !styleFilePath.isEmpty() )
92 {
93 bool result = false;
94 layer->loadNamedStyle( styleFilePath, result );
95 }
96}
97
98//
99// QgsProcessingResultsHandler
100//
101
103
105 const QString &destinationId, const QgsProcessingContext::LayerDetails &details, const QgsProcessingAlgorithm *algorithm, QgsProcessingContext &context, const QVariantMap &parameters
106)
107{
108 if ( !algorithm )
109 return details.outputName;
110
111 const QgsProcessingOutputDefinitions outputs = algorithm->outputDefinitions();
112 for ( const QgsProcessingOutputDefinition *output : std::as_const( outputs ) )
113 {
114 auto it = parameters.constFind( output->name() );
115 if ( it == parameters.constEnd() || QgsVariantUtils::isNull( it.value() ) )
116 {
117 continue;
118 }
119
120 QVariant outputValue = it.value();
121 if ( outputValue.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition >() )
122 {
123 QgsProperty sink = outputValue.value< QgsProcessingOutputLayerDefinition >().sink;
124 outputValue = sink.valueAsString( context.expressionContext() );
125 }
126 else
127 {
128 outputValue = outputValue.toString();
129 }
130 if ( outputValue.toString() == destinationId )
131 {
132 return output->name();
133 }
134 }
135
136 return details.outputName;
137}
138
140{
141 QgsProject *destinationProject = layerDetails.project ? layerDetails.project : context.project();
142 if ( !destinationProject )
143 return nullptr;
144
145 QgsLayerTreeGroup *resultsGroup = nullptr;
146
147 // if a specific results group is specified in Processing settings,
148 // respect it (and create if necessary)
149 QgsSettings settings;
150 const QString resultsGroupName = settings.value( u"Processing/Configuration/RESULTS_GROUP_NAME"_s, QString() ).toString();
151
152 if ( !resultsGroupName.isEmpty() )
153 {
154 resultsGroup = destinationProject->layerTreeRoot()->findGroup( resultsGroupName );
155 if ( !resultsGroup )
156 {
157 resultsGroup = destinationProject->layerTreeRoot()->insertGroup( 0, resultsGroupName );
158 resultsGroup->setExpanded( true );
159 }
160 }
161
162 // if this particular output layer has a specific output group assigned,
163 // find or create it now
164 QgsLayerTreeGroup *group = nullptr;
165 if ( !layerDetails.groupName.isEmpty() )
166 {
167 if ( !resultsGroup )
168 {
169 resultsGroup = destinationProject->layerTreeRoot();
170 }
171
172 group = resultsGroup->findGroup( layerDetails.groupName );
173 if ( !group )
174 {
175 group = resultsGroup->insertGroup( 0, layerDetails.groupName );
176 group->setExpanded( true );
177 }
178 }
179 else
180 {
181 group = resultsGroup;
182 }
183
184 return group;
185}
186
188{
189 const QgsMapLayer *layer = layerTreeLayer->layer();
190 if ( layer && layer->type() == Qgis::LayerType::Vector )
191 {
192 // post-process vector layer
193 QgsSettings settings;
194 if ( settings.value( u"Processing/Configuration/VECTOR_FEATURE_COUNT"_s, false ).toBool() )
195 {
196 layerTreeLayer->setCustomProperty( u"showFeatureCount"_s, true );
197 }
198 }
199}
200
202{
203 std::unique_ptr< QgsProcessingFeedback > localFeedback;
204 if ( !feedback )
205 {
206 localFeedback = std::make_unique< QgsProcessingFeedback >();
207 feedback = localFeedback.get();
208 }
209
210 return handleAlgorithmResultsProtected( algorithm, context, parameters, feedback ).succeeded;
211}
212
214 const QgsProcessingAlgorithm *algorithm, QgsProcessingContext &context, const QVariantMap &parameters, QgsProcessingFeedback *feedback
215)
216{
218
219 feedback->setProgressText( QObject::tr( "Loading resulting layers" ) );
220
221 ResultDetails result;
222 QStringList invalidLayers;
223
224
225 struct LayerToPostProcess
226 {
227 QgsMapLayer *layer;
229 };
230
231 QList< LayerToPostProcess > layersToPostProcess;
232
233 const QMap< QString, QgsProcessingContext::LayerDetails > layersToLoad = context.layersToLoadOnCompletion();
234 int i = 0;
235 for ( auto it = layersToLoad.constBegin(); it != layersToLoad.constEnd(); ++it, ++i )
236 {
237 if ( feedback->isCanceled() )
238 return result;
239
240 if ( layersToLoad.size() > 2 )
241 {
242 // only show progress feedback if we're loading a bunch of layers
243 feedback->setProgress( 100 * i / static_cast< float >( layersToLoad.size() ) );
244 }
245
246 const QString destId = it.key();
247 QgsProcessingContext::LayerDetails details = it.value();
248
249 QgsMapLayer *layer = QgsProcessingUtils::mapLayerFromString( destId, context, true, details.layerTypeHint );
250 if ( layer )
251 {
252 details.setOutputLayerName( layer );
253 const QString outputName = determineOutputName( destId, details, algorithm, context, parameters );
254 layerPostProcessor->postProcessLayer( layer, outputName, algorithm );
255
256 // Load layer to layer tree root or to a specific group
257 QgsLayerTreeGroup *resultsGroup = layerTreeResultsGroup( details, context );
258
259 // note here that we may not retrieve an owned layer -- eg if the
260 // output layer already exists in the destination project
261 std::unique_ptr< QgsMapLayer> ownedMapLayer( context.temporaryLayerStore()->takeMapLayer( layer ) );
262 if ( ownedMapLayer )
263 {
264 // we don't add the layer to the tree yet -- that's done
265 // later, after we've sorted all added layers
266 ResultLayerDetails resultLayerDetails = ResultLayerDetails( ownedMapLayer.release() );
267 resultLayerDetails.targetLayerTreeGroup = resultsGroup;
268 resultLayerDetails.sortKey = details.layerSortKey;
269 resultLayerDetails.destinationProject = details.project;
270 result.addedLayers.append( resultLayerDetails );
271 }
272
273 if ( details.postProcessor() )
274 {
275 // we defer calling the postProcessor set in the context
276 // until the layer has been added to the project's layer
277 // tree, just in case the postProcessor contains logic
278 // relating to layer tree handling
279 layersToPostProcess.append( LayerToPostProcess { layer, details } );
280 }
281 }
282 else
283 {
284 invalidLayers.append( destId );
285 }
286 }
287
288 result.succeeded = invalidLayers.empty();
289
290 QgsMapLayer *newActiveLayer = handleAddResultLayers( result, context );
291
292 // all layers have been added to the layer tree, so safe to call
293 // postProcessors now
294 for ( const LayerToPostProcess &layer : std::as_const( layersToPostProcess ) )
295 {
296 layer.details.postProcessor()->postProcessLayer( layer.layer, context, feedback );
297 }
298
299 setNewActiveLayer( newActiveLayer );
300
301 feedback->setProgress( 100 );
302
303 if ( !invalidLayers.empty() )
304 {
305 QString msg = QObject::tr( "The following layers were not correctly generated." );
306 QStringList layerList;
307 layerList.reserve( invalidLayers.size() );
308 for ( const QString &layerId : std::as_const( invalidLayers ) )
309 {
310 layerList.append( u"• %1"_s.arg( layerId ) );
311 }
312 msg += u"\n%1\n"_s.arg( layerList.join( '\n' ) );
313 msg += QObject::tr( "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm." );
314 feedback->reportError( msg );
315 }
316
317 return result;
318}
319
321{
322 return addResultLayers( details.addedLayers, context, nullptr );
323}
324
327
328QgsMapLayer *QgsProcessingResultsHandler::addResultLayers( const QVector<ResultLayerDetails> &layers, const QgsProcessingContext &context, QgsLayerTreeNode *currentSelectedNode )
329{
330 // sort added layer tree layers
331 QVector<QgsProcessingResultsHandler::ResultLayerDetails> sortedLayers = layers;
332 std::sort( sortedLayers.begin(), sortedLayers.end(), []( const QgsProcessingResultsHandler::ResultLayerDetails &a, const QgsProcessingResultsHandler::ResultLayerDetails &b ) {
333 return a.sortKey < b.sortKey;
334 } );
335
336 bool haveSetActiveLayer = false;
337 QgsLayerTreeGroup *defaultTargetGroup = nullptr;
338 qsizetype defaultTargetGroupIndex = 0;
339 if ( auto currentSelectedLayer = qobject_cast< QgsLayerTreeLayer * >( currentSelectedNode ) )
340 {
341 defaultTargetGroup = qobject_cast< QgsLayerTreeGroup * >( currentSelectedLayer->parent() );
342 if ( defaultTargetGroup )
343 defaultTargetGroupIndex = defaultTargetGroup->children().indexOf( currentSelectedNode );
344 }
345 if ( auto currentSelectedGroup = qobject_cast< QgsLayerTreeGroup * >( currentSelectedNode ) )
346 {
347 defaultTargetGroup = currentSelectedGroup;
348 }
349
350 QgsMapLayer *resultLayer = nullptr;
351 for ( const QgsProcessingResultsHandler::ResultLayerDetails &layerDetails : std::as_const( sortedLayers ) )
352 {
353 QgsProject *project = layerDetails.destinationProject;
354 if ( !project )
355 project = context.project();
356
357 // store the current insertion point to restore it later
358 std::optional< QgsLayerTreeRegistryBridge::InsertionPoint > previousInsertionPoint;
359 if ( project )
360 {
361 previousInsertionPoint.emplace( project->layerTreeRegistryBridge()->layerInsertionPoint() );
362 }
363
364 std::optional< QgsLayerTreeRegistryBridge::InsertionPoint > insertionPoint;
365 if ( layerDetails.targetLayerTreeGroup )
366 {
367 insertionPoint.emplace( QgsLayerTreeRegistryBridge::InsertionPoint( layerDetails.targetLayerTreeGroup, 0 ) );
368 }
369 else
370 {
371 // no destination group for this layer, so should be placed
372 // above the current layer if one was selected, or at top of group if a group was selected
373 if ( defaultTargetGroup )
374 {
375 insertionPoint.emplace( QgsLayerTreeRegistryBridge::InsertionPoint( defaultTargetGroup, static_cast< int >( defaultTargetGroupIndex ) ) );
376 }
377 else if ( project )
378 {
379 insertionPoint.emplace( QgsLayerTreeRegistryBridge::InsertionPoint( project->layerTreeRoot(), 0 ) );
380 }
381 }
382
383 if ( project && insertionPoint.has_value() )
384 {
385 project->layerTreeRegistryBridge()->setLayerInsertionPoint( *insertionPoint );
386 }
387
388 if ( project )
389 {
390 project->addMapLayer( layerDetails.layer );
391 QgsLayerTreeLayer *layerTreeLayer = project->layerTreeRoot()->findLayer( layerDetails.layer );
393 }
394
395 if ( !haveSetActiveLayer )
396 {
397 resultLayer = layerDetails.layer;
398 haveSetActiveLayer = true;
399 }
400
401 // reset to the previous insertion point
402 if ( project && previousInsertionPoint.has_value() )
403 {
404 project->layerTreeRegistryBridge()->setLayerInsertionPoint( *previousInsertionPoint );
405 }
406 }
407 return resultLayer;
408}
@ Point
Points.
Definition qgis.h:400
@ Line
Lines.
Definition qgis.h:401
@ Polygon
Polygons.
Definition qgis.h:402
@ Unknown
Unknown types.
Definition qgis.h:403
@ Null
No geometry.
Definition qgis.h:404
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:214
@ Plugin
Plugin based layer.
Definition qgis.h:209
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:215
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:212
@ Vector
Vector layer.
Definition qgis.h:207
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:211
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:210
@ Raster
Raster layer.
Definition qgis.h:208
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:213
static QgsProcessingRegistry * processingRegistry()
Returns the application's processing registry, used for managing processing providers,...
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
Layer tree group node serves as a container for layers and further groups.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
QgsLayerTreeGroup * insertGroup(int index, const QString &name)
Insert a new group node with given name at specified position.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
Base class for nodes in a layer tree.
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void setExpanded(bool expanded)
Sets whether the node should be shown as expanded or collapsed in GUI.
Q_DECL_DEPRECATED void setLayerInsertionPoint(QgsLayerTreeGroup *parentGroup, int index)
Set where the new layers should be inserted - can be used to follow current selection.
InsertionPoint layerInsertionPoint() const
Returns the insertion point used to add layers to the tree.
QgsMapLayer * takeMapLayer(QgsMapLayer *layer)
Takes a layer from the store.
Base class for all map layer types.
Definition qgsmaplayer.h:83
virtual QString loadNamedStyle(const QString &theURI, bool &resultFlag, bool loadFromLocalDb, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories, Qgis::LoadStyleFlags flags=Qgis::LoadStyleFlags())
Loads a named style from file/local db/datasource db.
Qgis::LayerType type
Definition qgsmaplayer.h:93
Abstract base class for processing algorithms.
Details for layers to load into projects.
int layerSortKey
Optional sorting key for sorting output layers when loading them into a project.
QString groupName
Optional name for a layer tree group under which to place the layer when loading it into a project.
QgsProcessingLayerPostProcessorInterface * postProcessor() const
Layer post-processor.
QgsProject * project
Destination project.
QString outputName
Associated output name from algorithm which generated the layer.
QgsProcessingUtils::LayerHint layerTypeHint
Layer type hint.
void setOutputLayerName(QgsMapLayer *layer) const
Sets a layer name to match this output, respecting any local user settings which affect this name.
Contains information about the context in which a processing algorithm is executed.
QgsExpressionContext & expressionContext()
Returns the expression context.
QgsProject * project() const
Returns the project in which the algorithm is being executed.
QMap< QString, QgsProcessingContext::LayerDetails > layersToLoadOnCompletion() const
Returns a map of layers (by ID or datasource) to LayerDetails, to load into the canvas upon completio...
QgsMapLayerStore * temporaryLayerStore()
Returns a reference to the layer store used for storing temporary layers during algorithm execution.
QString defaultStyleForOutput(const QString &algorithmId, const QString &outputName) const
Returns the QML path to the default style to use for the specified output from an algorithm,...
Base class for providing feedback from a processing algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
virtual void setProgressText(const QString &text)
Sets a progress report text string.
A post-processor for result layers created by Processing algorithms.
virtual void postProcessLayer(QgsMapLayer *layer, const QString &outputName, const QgsProcessingAlgorithm *algorithm)
Post-processes a layer, e.g.
Base class for the definition of processing outputs.
Encapsulates settings relating to a feature sink or output raster layer for a processing algorithm.
QgsProcessingLayerPostProcessor * layerPostProcessor()
Returns the post-processor to use for all map layers generated by Processing algorithms.
QgsProcessingDefaultStyleRegistry * defaultStyleRegistry() const
Returns the default style registry, responsible for styling layers generated by Processing tools.
Contains details of a layer result from running an algorithm.
int sortKey
Sort order key for ordering output layers in the layer tree.
QgsLayerTreeGroup * targetLayerTreeGroup
Optional target layer tree group, where the layer should be placed.
QgsProject * destinationProject
Destination QGIS project.
static void configureResultLayerTreeLayer(QgsLayerTreeLayer *layerTreeLayer)
Applies post-processing steps to the QgsLayerTreeLayer created for an algorithm's output.
static QgsMapLayer * addResultLayers(const QVector< QgsProcessingResultsHandler::ResultLayerDetails > &layers, const QgsProcessingContext &context, QgsLayerTreeNode *currentSelectedNode)
Responsible for adding layers created by an algorithm to a project and the project's layer tree in th...
static QgsLayerTreeGroup * layerTreeResultsGroup(const QgsProcessingContext::LayerDetails &layerDetails, const QgsProcessingContext &context)
Returns the destination layer tree group to store results in, or nullptr if there is no specific dest...
virtual QgsMapLayer * handleAddResultLayers(const QgsProcessingResultsHandler::ResultDetails &details, const QgsProcessingContext &context)
Calls addResultLayers(), allowing for extra logic to be injected in subclasses.
virtual void setNewActiveLayer(QgsMapLayer *layer)
Handles setting the new project active layer after processing the results.
static QString determineOutputName(const QString &destinationId, const QgsProcessingContext::LayerDetails &details, const QgsProcessingAlgorithm *algorithm, QgsProcessingContext &context, const QVariantMap &parameters)
Determines the desired layer name for a map layer output.
bool handleAlgorithmResults(const QgsProcessingAlgorithm *algorithm, QgsProcessingContext &context, const QVariantMap &parameters, QgsProcessingFeedback *feedback=nullptr)
Handles the results from running an algorithm.
virtual QgsProcessingResultsHandler::ResultDetails handleAlgorithmResultsProtected(const QgsProcessingAlgorithm *algorithm, QgsProcessingContext &context, const QVariantMap &parameters, QgsProcessingFeedback *feedback)
Handles the results from running an algorithm.
virtual ~QgsProcessingResultsHandler()
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true, QgsProcessingUtils::LayerHint typeHint=QgsProcessingUtils::LayerHint::UnknownType, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Interprets a string as a map layer within the supplied context.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
QgsLayerTreeRegistryBridge * layerTreeRegistryBridge() const
Returns pointer to the helper class that synchronizes map layer registry with layer tree.
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
QgsMapLayer * addMapLayer(QgsMapLayer *mapLayer, bool addToLegend=true, bool takeOwnership=true)
Add a layer to the map of loaded layers.
A store for object properties.
QString valueAsString(const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property and interprets it as a string.
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
Stores settings for use within QGIS.
Definition qgssettings.h:68
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based dataset.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
QList< const QgsProcessingOutputDefinition * > QgsProcessingOutputDefinitions
List of processing parameters.
A structure to define the insertion point to the layer tree.
Stores results of running the handleAlgorithmResultsProtected method.
bool succeeded
true if all layers could be successfully loaded
QList< QgsProcessingResultsHandler::ResultLayerDetails > addedLayers
Details of newly added layers.