2014-07-14 14:19:09 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
"""
|
|
|
|
***************************************************************************
|
|
|
|
AlgorithmExecutor.py
|
|
|
|
---------------------
|
|
|
|
Date : August 2012
|
|
|
|
Copyright : (C) 2012 by Victor Olaya
|
|
|
|
Email : volayaf at gmail dot com
|
|
|
|
***************************************************************************
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
|
|
* (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
***************************************************************************
|
|
|
|
"""
|
2016-07-21 06:55:47 +02:00
|
|
|
|
2014-07-14 14:19:09 +02:00
|
|
|
__author__ = 'Victor Olaya'
|
|
|
|
__date__ = 'August 2012'
|
|
|
|
__copyright__ = '(C) 2012, Victor Olaya'
|
|
|
|
|
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
|
|
|
import sys
|
2017-03-03 20:39:17 +01:00
|
|
|
from qgis.PyQt.QtCore import QCoreApplication
|
2018-02-05 22:11:34 -04:00
|
|
|
from qgis.core import (Qgis,
|
2021-11-29 13:00:00 +01:00
|
|
|
QgsApplication,
|
2018-02-15 22:30:52 +01:00
|
|
|
QgsFeatureSink,
|
2017-03-03 20:39:17 +01:00
|
|
|
QgsProcessingFeedback,
|
2017-04-24 14:35:50 +10:00
|
|
|
QgsProcessingUtils,
|
2017-06-12 13:35:31 +10:00
|
|
|
QgsMessageLog,
|
2017-07-13 09:01:28 +03:00
|
|
|
QgsProcessingException,
|
2018-07-23 13:58:11 +10:00
|
|
|
QgsProcessingFeatureSourceDefinition,
|
2018-12-21 13:20:41 +01:00
|
|
|
QgsProcessingFeatureSource,
|
2018-09-10 08:09:52 +02:00
|
|
|
QgsProcessingParameters,
|
|
|
|
QgsProject,
|
|
|
|
QgsFeatureRequest,
|
|
|
|
QgsFeature,
|
|
|
|
QgsExpression,
|
2018-09-10 18:14:03 +02:00
|
|
|
QgsWkbTypes,
|
2018-09-11 16:53:21 +02:00
|
|
|
QgsGeometry,
|
2018-10-05 10:10:20 +02:00
|
|
|
QgsVectorLayerUtils,
|
|
|
|
QgsVectorLayer)
|
2014-07-14 14:19:09 +02:00
|
|
|
from processing.gui.Postprocessing import handleAlgorithmResults
|
|
|
|
from processing.tools import dataobjects
|
2018-07-23 13:58:11 +10:00
|
|
|
from qgis.utils import iface
|
2014-07-14 14:19:09 +02:00
|
|
|
|
2016-08-04 09:10:08 +02:00
|
|
|
|
2020-10-08 10:19:28 +10:00
|
|
|
def execute(alg, parameters, context=None, feedback=None, catch_exceptions=True):
|
2014-07-14 14:19:09 +02:00
|
|
|
"""Executes a given algorithm, showing its progress in the
|
|
|
|
progress object passed along.
|
|
|
|
|
|
|
|
Return true if everything went OK, false if the algorithm
|
|
|
|
could not be completed.
|
|
|
|
"""
|
2017-01-07 07:31:31 +10:00
|
|
|
|
|
|
|
if feedback is None:
|
|
|
|
feedback = QgsProcessingFeedback()
|
2017-04-25 14:55:38 +10:00
|
|
|
if context is None:
|
2017-06-23 10:32:51 +10:00
|
|
|
context = dataobjects.createContext(feedback)
|
2017-01-07 07:31:31 +10:00
|
|
|
|
2020-10-08 10:19:28 +10:00
|
|
|
if catch_exceptions:
|
|
|
|
try:
|
|
|
|
results, ok = alg.run(parameters, context, feedback)
|
|
|
|
return ok, results
|
|
|
|
except QgsProcessingException as e:
|
|
|
|
QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical)
|
|
|
|
if feedback is not None:
|
|
|
|
feedback.reportError(e.msg)
|
|
|
|
return False, {}
|
|
|
|
else:
|
|
|
|
results, ok = alg.run(parameters, context, feedback, {}, False)
|
2017-06-23 09:27:39 +10:00
|
|
|
return ok, results
|
2018-07-23 13:58:11 +10:00
|
|
|
|
|
|
|
|
2018-10-05 10:10:20 +02:00
|
|
|
def execute_in_place_run(alg, parameters, context=None, feedback=None, raise_exceptions=False):
|
2018-09-10 08:09:52 +02:00
|
|
|
"""Executes an algorithm modifying features in-place in the input layer.
|
|
|
|
|
|
|
|
:param alg: algorithm to run
|
|
|
|
:type alg: QgsProcessingAlgorithm
|
|
|
|
:param parameters: parameters of the algorithm
|
|
|
|
:type parameters: dict
|
|
|
|
:param context: context, defaults to None
|
2018-10-04 11:36:50 +02:00
|
|
|
:type context: QgsProcessingContext, optional
|
2018-09-10 08:09:52 +02:00
|
|
|
:param feedback: feedback, defaults to None
|
2018-10-04 11:36:50 +02:00
|
|
|
:type feedback: QgsProcessingFeedback, optional
|
|
|
|
:param raise_exceptions: useful for testing, if True exceptions are raised, normally exceptions will be forwarded to the feedback
|
|
|
|
:type raise_exceptions: boo, default to False
|
|
|
|
:raises QgsProcessingException: raised when there is no active layer, or it cannot be made editable
|
2018-09-10 08:09:52 +02:00
|
|
|
:return: a tuple with true if success and results
|
|
|
|
:rtype: tuple
|
|
|
|
"""
|
2018-07-23 13:58:11 +10:00
|
|
|
|
|
|
|
if feedback is None:
|
|
|
|
feedback = QgsProcessingFeedback()
|
|
|
|
if context is None:
|
|
|
|
context = dataobjects.createContext(feedback)
|
|
|
|
|
2018-12-21 13:20:41 +01:00
|
|
|
# Only feature based algs have sourceFlags
|
|
|
|
try:
|
|
|
|
if alg.sourceFlags() & QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks:
|
|
|
|
context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck)
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
2018-12-21 11:47:36 +01:00
|
|
|
|
2021-02-03 08:21:33 +10:00
|
|
|
in_place_input_parameter_name = 'INPUT'
|
|
|
|
if hasattr(alg, 'inputParameterName'):
|
|
|
|
in_place_input_parameter_name = alg.inputParameterName()
|
|
|
|
|
|
|
|
active_layer = parameters[in_place_input_parameter_name]
|
2018-10-05 10:10:20 +02:00
|
|
|
|
2021-06-17 10:51:23 +10:00
|
|
|
# prepare expression context for feature iteration
|
|
|
|
alg_context = context.expressionContext()
|
|
|
|
alg_context.appendScope(active_layer.createExpressionContextScope())
|
|
|
|
context.setExpressionContext(alg_context)
|
|
|
|
|
2018-10-04 11:36:50 +02:00
|
|
|
# Run some checks and prepare the layer for in-place execution by:
|
|
|
|
# - getting the active layer and checking that it is a vector
|
|
|
|
# - making the layer editable if it was not already
|
|
|
|
# - selecting all features if none was selected
|
|
|
|
# - checking in-place support for the active layer/alg/parameters
|
|
|
|
# If one of the check fails and raise_exceptions is True an exception
|
|
|
|
# is raised, else the execution is aborted and the error reported in
|
|
|
|
# the feedback
|
|
|
|
try:
|
|
|
|
if active_layer is None:
|
2020-10-25 22:00:37 +01:00
|
|
|
raise QgsProcessingException(tr("There is no active layer."))
|
2018-10-04 11:36:50 +02:00
|
|
|
|
2018-10-05 10:10:20 +02:00
|
|
|
if not isinstance(active_layer, QgsVectorLayer):
|
|
|
|
raise QgsProcessingException(tr("Active layer is not a vector layer."))
|
|
|
|
|
2018-10-04 11:36:50 +02:00
|
|
|
if not active_layer.isEditable():
|
|
|
|
if not active_layer.startEditing():
|
|
|
|
raise QgsProcessingException(tr("Active layer is not editable (and editing could not be turned on)."))
|
|
|
|
|
|
|
|
if not alg.supportInPlaceEdit(active_layer):
|
|
|
|
raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."))
|
|
|
|
except QgsProcessingException as e:
|
|
|
|
if raise_exceptions:
|
|
|
|
raise e
|
|
|
|
QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical)
|
|
|
|
if feedback is not None:
|
|
|
|
feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True)
|
|
|
|
return False, {}
|
2018-09-10 18:14:03 +02:00
|
|
|
|
2018-10-04 11:36:50 +02:00
|
|
|
if not active_layer.selectedFeatureIds():
|
|
|
|
active_layer.selectAll()
|
2018-07-23 13:58:11 +10:00
|
|
|
|
2018-10-18 17:43:00 +02:00
|
|
|
# Make sure we are working on selected features only
|
2021-02-03 08:21:33 +10:00
|
|
|
parameters[in_place_input_parameter_name] = QgsProcessingFeatureSourceDefinition(active_layer.id(), True)
|
2018-07-23 13:58:11 +10:00
|
|
|
parameters['OUTPUT'] = 'memory:'
|
|
|
|
|
2018-10-18 17:43:00 +02:00
|
|
|
req = QgsFeatureRequest(QgsExpression(r"$id < 0"))
|
|
|
|
req.setFlags(QgsFeatureRequest.NoGeometry)
|
|
|
|
req.setSubsetOfAttributes([])
|
|
|
|
|
2018-10-04 11:36:50 +02:00
|
|
|
# Start the execution
|
|
|
|
# If anything goes wrong and raise_exceptions is True an exception
|
|
|
|
# is raised, else the execution is aborted and the error reported in
|
|
|
|
# the feedback
|
2018-07-23 13:58:11 +10:00
|
|
|
try:
|
2018-09-10 08:09:52 +02:00
|
|
|
new_feature_ids = []
|
2018-07-23 13:58:11 +10:00
|
|
|
|
2018-09-18 08:26:40 +10:00
|
|
|
active_layer.beginEditCommand(alg.displayName())
|
2018-09-10 08:09:52 +02:00
|
|
|
|
|
|
|
# Checks whether the algorithm has a processFeature method
|
|
|
|
if hasattr(alg, 'processFeature'): # in-place feature editing
|
2018-09-10 18:14:03 +02:00
|
|
|
# Make a clone or it will crash the second time the dialog
|
|
|
|
# is opened and run
|
2020-07-27 13:48:45 +10:00
|
|
|
alg = alg.create({'IN_PLACE': True})
|
2018-09-13 18:27:12 +02:00
|
|
|
if not alg.prepare(parameters, context, feedback):
|
|
|
|
raise QgsProcessingException(tr("Could not prepare selected algorithm."))
|
2018-09-10 18:14:03 +02:00
|
|
|
# Check again for compatibility after prepare
|
|
|
|
if not alg.supportInPlaceEdit(active_layer):
|
|
|
|
raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."))
|
2019-09-05 15:19:47 +10:00
|
|
|
|
|
|
|
# some algorithms have logic in outputFields/outputCrs/outputWkbType which they require to execute before
|
|
|
|
# they can start processing features
|
|
|
|
_ = alg.outputFields(active_layer.fields())
|
|
|
|
_ = alg.outputWkbType(active_layer.wkbType())
|
|
|
|
_ = alg.outputCrs(active_layer.crs())
|
|
|
|
|
2018-09-10 08:09:52 +02:00
|
|
|
field_idxs = range(len(active_layer.fields()))
|
2018-10-18 17:43:00 +02:00
|
|
|
iterator_req = QgsFeatureRequest(active_layer.selectedFeatureIds())
|
|
|
|
iterator_req.setInvalidGeometryCheck(context.invalidGeometryCheck())
|
|
|
|
feature_iterator = active_layer.getFeatures(iterator_req)
|
2018-10-02 14:32:57 +10:00
|
|
|
step = 100 / len(active_layer.selectedFeatureIds()) if active_layer.selectedFeatureIds() else 1
|
Fix calling in place with empty iterator
```
File "/home/mkuhn/.local/share/QGIS/QGIS3/profiles/default/python/plugins/autocurve/plugin.py", line 127, in curvify
AlgorithmExecutor.execute_in_place(alg, {})
File "/usr/share/qgis/python/plugins/processing/gui/AlgorithmExecutor.py", line 305, in execute_in_place
ok, results = execute_in_place_run(alg, parameters, context=context, feedback=feedback)
File "/usr/share/qgis/python/plugins/processing/gui/AlgorithmExecutor.py", line 214, in execute_in_place_run
results, ok = {'__count': current + 1}, True
UnboundLocalError: local variable 'current' referenced before assignment
```
2021-02-22 15:04:10 +01:00
|
|
|
current = 0
|
2018-10-02 14:32:57 +10:00
|
|
|
for current, f in enumerate(feature_iterator):
|
|
|
|
if feedback.isCanceled():
|
|
|
|
break
|
|
|
|
|
2018-09-21 16:41:46 +10:00
|
|
|
# need a deep copy, because python processFeature implementations may return
|
|
|
|
# a shallow copy from processFeature
|
|
|
|
input_feature = QgsFeature(f)
|
2021-06-17 10:51:23 +10:00
|
|
|
|
|
|
|
context.expressionContext().setFeature(input_feature)
|
|
|
|
|
2018-09-21 16:41:46 +10:00
|
|
|
new_features = alg.processFeature(input_feature, context, feedback)
|
2018-09-24 10:52:13 +02:00
|
|
|
new_features = QgsVectorLayerUtils.makeFeaturesCompatible(new_features, active_layer)
|
2018-10-18 17:43:00 +02:00
|
|
|
|
2018-09-10 08:09:52 +02:00
|
|
|
if len(new_features) == 0:
|
|
|
|
active_layer.deleteFeature(f.id())
|
|
|
|
elif len(new_features) == 1:
|
|
|
|
new_f = new_features[0]
|
|
|
|
if not f.geometry().equals(new_f.geometry()):
|
|
|
|
active_layer.changeGeometry(f.id(), new_f.geometry())
|
|
|
|
if f.attributes() != new_f.attributes():
|
|
|
|
active_layer.changeAttributeValues(f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes())))
|
|
|
|
new_feature_ids.append(f.id())
|
|
|
|
else:
|
|
|
|
active_layer.deleteFeature(f.id())
|
|
|
|
# Get the new ids
|
2018-10-19 16:27:18 +02:00
|
|
|
old_ids = set([f.id() for f in active_layer.getFeatures(req)])
|
2019-09-13 18:41:02 +02:00
|
|
|
# If multiple new features were created, we need to pass
|
|
|
|
# them to createFeatures to manage constraints correctly
|
|
|
|
features_data = []
|
|
|
|
for f in new_features:
|
|
|
|
features_data.append(QgsVectorLayerUtils.QgsFeatureData(f.geometry(), dict(enumerate(f.attributes()))))
|
|
|
|
new_features = QgsVectorLayerUtils.createFeatures(active_layer, features_data, context.expressionContext())
|
2018-09-10 18:14:03 +02:00
|
|
|
if not active_layer.addFeatures(new_features):
|
|
|
|
raise QgsProcessingException(tr("Error adding processed features back into the layer."))
|
2018-09-10 08:09:52 +02:00
|
|
|
new_ids = set([f.id() for f in active_layer.getFeatures(req)])
|
|
|
|
new_feature_ids += list(new_ids - old_ids)
|
|
|
|
|
2020-04-26 20:49:07 +03:00
|
|
|
feedback.setProgress(int((current + 1) * step))
|
|
|
|
|
2020-09-14 18:22:24 +02:00
|
|
|
results, ok = {'__count': current + 1}, True
|
2018-09-10 08:09:52 +02:00
|
|
|
|
|
|
|
else: # Traditional 'run' with delete and add features cycle
|
2018-10-18 17:43:00 +02:00
|
|
|
|
|
|
|
# There is no way to know if some features have been skipped
|
|
|
|
# due to invalid geometries
|
|
|
|
if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid:
|
|
|
|
selected_ids = active_layer.selectedFeatureIds()
|
|
|
|
else:
|
|
|
|
selected_ids = []
|
|
|
|
|
2020-07-27 13:48:45 +10:00
|
|
|
results, ok = alg.run(parameters, context, feedback, configuration={'IN_PLACE': True})
|
2018-09-10 08:09:52 +02:00
|
|
|
|
2018-09-11 16:53:21 +02:00
|
|
|
if ok:
|
|
|
|
result_layer = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context)
|
|
|
|
# TODO: check if features have changed before delete/add cycle
|
2018-10-18 17:43:00 +02:00
|
|
|
|
2018-09-11 16:53:21 +02:00
|
|
|
new_features = []
|
2018-10-18 17:43:00 +02:00
|
|
|
|
|
|
|
# Check if there are any skipped features
|
|
|
|
if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid:
|
|
|
|
missing_ids = list(set(selected_ids) - set(result_layer.allFeatureIds()))
|
|
|
|
if missing_ids:
|
|
|
|
for f in active_layer.getFeatures(QgsFeatureRequest(missing_ids)):
|
|
|
|
if not f.geometry().isGeosValid():
|
|
|
|
new_features.append(f)
|
|
|
|
|
|
|
|
active_layer.deleteFeatures(active_layer.selectedFeatureIds())
|
|
|
|
|
2020-10-14 09:42:43 +10:00
|
|
|
regenerate_primary_key = result_layer.customProperty('OnConvertFormatRegeneratePrimaryKey', False)
|
|
|
|
sink_flags = QgsFeatureSink.SinkFlags(QgsFeatureSink.RegeneratePrimaryKey) if regenerate_primary_key \
|
|
|
|
else QgsFeatureSink.SinkFlags()
|
|
|
|
|
2018-09-11 16:53:21 +02:00
|
|
|
for f in result_layer.getFeatures():
|
2018-09-24 10:52:13 +02:00
|
|
|
new_features.extend(QgsVectorLayerUtils.
|
2020-10-14 09:42:43 +10:00
|
|
|
makeFeaturesCompatible([f], active_layer, sink_flags))
|
2018-09-10 08:09:52 +02:00
|
|
|
|
2018-09-11 16:53:21 +02:00
|
|
|
# Get the new ids
|
|
|
|
old_ids = set([f.id() for f in active_layer.getFeatures(req)])
|
2018-09-13 16:37:31 +02:00
|
|
|
if not active_layer.addFeatures(new_features):
|
|
|
|
raise QgsProcessingException(tr("Error adding processed features back into the layer."))
|
2018-09-11 16:53:21 +02:00
|
|
|
new_ids = set([f.id() for f in active_layer.getFeatures(req)])
|
|
|
|
new_feature_ids += list(new_ids - old_ids)
|
2020-09-14 18:22:24 +02:00
|
|
|
results['__count'] = len(new_feature_ids)
|
2018-09-10 08:09:52 +02:00
|
|
|
|
2018-09-10 18:14:03 +02:00
|
|
|
active_layer.endEditCommand()
|
|
|
|
|
2018-09-10 08:09:52 +02:00
|
|
|
if ok and new_feature_ids:
|
|
|
|
active_layer.selectByIds(new_feature_ids)
|
|
|
|
elif not ok:
|
2018-09-11 16:53:21 +02:00
|
|
|
active_layer.rollBack()
|
2018-09-10 08:09:52 +02:00
|
|
|
|
2018-07-23 13:58:11 +10:00
|
|
|
return ok, results
|
2018-09-10 08:09:52 +02:00
|
|
|
|
2018-07-23 13:58:11 +10:00
|
|
|
except QgsProcessingException as e:
|
2018-09-10 18:14:03 +02:00
|
|
|
active_layer.endEditCommand()
|
2018-09-11 16:53:21 +02:00
|
|
|
active_layer.rollBack()
|
2018-09-10 18:14:03 +02:00
|
|
|
if raise_exceptions:
|
|
|
|
raise e
|
2018-07-23 13:58:11 +10:00
|
|
|
QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical)
|
|
|
|
if feedback is not None:
|
2018-10-04 11:36:50 +02:00
|
|
|
feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True)
|
2018-09-10 08:09:52 +02:00
|
|
|
|
2018-09-10 18:14:03 +02:00
|
|
|
return False, {}
|
2014-07-14 14:19:09 +02:00
|
|
|
|
2015-08-22 14:29:41 +02:00
|
|
|
|
2018-09-10 08:09:52 +02:00
|
|
|
def execute_in_place(alg, parameters, context=None, feedback=None):
|
2018-10-05 10:10:20 +02:00
|
|
|
"""Executes an algorithm modifying features in-place, if the INPUT
|
|
|
|
parameter is not defined, the current active layer will be used as
|
|
|
|
INPUT.
|
2018-09-10 08:09:52 +02:00
|
|
|
|
|
|
|
:param alg: algorithm to run
|
|
|
|
:type alg: QgsProcessingAlgorithm
|
|
|
|
:param parameters: parameters of the algorithm
|
|
|
|
:type parameters: dict
|
|
|
|
:param context: context, defaults to None
|
|
|
|
:param context: QgsProcessingContext, optional
|
|
|
|
:param feedback: feedback, defaults to None
|
|
|
|
:param feedback: QgsProcessingFeedback, optional
|
|
|
|
:raises QgsProcessingException: raised when the layer is not editable or the layer cannot be found in the current project
|
|
|
|
:return: a tuple with true if success and results
|
|
|
|
:rtype: tuple
|
|
|
|
"""
|
|
|
|
|
2018-10-20 10:05:48 +02:00
|
|
|
if feedback is None:
|
|
|
|
feedback = QgsProcessingFeedback()
|
|
|
|
if context is None:
|
|
|
|
context = dataobjects.createContext(feedback)
|
|
|
|
|
2021-02-03 08:21:33 +10:00
|
|
|
in_place_input_parameter_name = 'INPUT'
|
|
|
|
if hasattr(alg, 'inputParameterName'):
|
|
|
|
in_place_input_parameter_name = alg.inputParameterName()
|
|
|
|
in_place_input_layer_name = 'INPUT'
|
|
|
|
if hasattr(alg, 'inputParameterDescription'):
|
|
|
|
in_place_input_layer_name = alg.inputParameterDescription()
|
|
|
|
|
|
|
|
if in_place_input_parameter_name not in parameters or not parameters[in_place_input_parameter_name]:
|
|
|
|
parameters[in_place_input_parameter_name] = iface.activeLayer()
|
2018-10-05 10:10:20 +02:00
|
|
|
ok, results = execute_in_place_run(alg, parameters, context=context, feedback=feedback)
|
2018-09-10 08:09:52 +02:00
|
|
|
if ok:
|
2021-02-03 08:21:33 +10:00
|
|
|
if isinstance(parameters[in_place_input_parameter_name], QgsProcessingFeatureSourceDefinition):
|
|
|
|
layer = alg.parameterAsVectorLayer({in_place_input_parameter_name: parameters[in_place_input_parameter_name].source}, in_place_input_layer_name, context)
|
|
|
|
elif isinstance(parameters[in_place_input_parameter_name], QgsVectorLayer):
|
|
|
|
layer = parameters[in_place_input_parameter_name]
|
2018-10-18 17:43:00 +02:00
|
|
|
if layer:
|
|
|
|
layer.triggerRepaint()
|
2018-09-10 08:09:52 +02:00
|
|
|
return ok, results
|
|
|
|
|
|
|
|
|
2017-05-15 16:19:46 +10:00
|
|
|
def executeIterating(alg, parameters, paramToIter, context, feedback):
|
2014-07-14 14:19:09 +02:00
|
|
|
# Generate all single-feature layers
|
2017-06-12 13:35:31 +10:00
|
|
|
parameter_definition = alg.parameterDefinition(paramToIter)
|
|
|
|
if not parameter_definition:
|
|
|
|
return False
|
|
|
|
|
|
|
|
iter_source = QgsProcessingParameters.parameterAsSource(parameter_definition, parameters, context)
|
|
|
|
sink_list = []
|
2017-06-12 13:39:33 +10:00
|
|
|
if iter_source.featureCount() == 0:
|
|
|
|
return False
|
|
|
|
|
2020-04-26 20:49:07 +03:00
|
|
|
step = 100.0 / iter_source.featureCount()
|
2017-06-12 13:39:33 +10:00
|
|
|
for current, feat in enumerate(iter_source.getFeatures()):
|
2017-06-12 13:35:31 +10:00
|
|
|
if feedback.isCanceled():
|
|
|
|
return False
|
|
|
|
|
|
|
|
sink, sink_id = QgsProcessingUtils.createFeatureSink('memory:', context, iter_source.fields(), iter_source.wkbType(), iter_source.sourceCrs())
|
|
|
|
sink_list.append(sink_id)
|
2017-06-23 14:34:38 +10:00
|
|
|
sink.addFeature(feat, QgsFeatureSink.FastInsert)
|
2017-06-12 13:35:31 +10:00
|
|
|
del sink
|
2014-07-14 14:19:09 +02:00
|
|
|
|
2020-04-26 20:49:07 +03:00
|
|
|
feedback.setProgress(int((current + 1) * step))
|
2017-06-12 13:39:33 +10:00
|
|
|
|
2014-07-14 14:19:09 +02:00
|
|
|
# store output values to use them later as basenames for all outputs
|
2017-06-12 13:35:31 +10:00
|
|
|
outputs = {}
|
|
|
|
for out in alg.destinationParameterDefinitions():
|
2018-10-01 14:33:39 +10:00
|
|
|
if out.name() in parameters:
|
|
|
|
outputs[out.name()] = parameters[out.name()]
|
2014-07-14 14:19:09 +02:00
|
|
|
|
|
|
|
# now run all the algorithms
|
2017-06-12 13:35:31 +10:00
|
|
|
for i, f in enumerate(sink_list):
|
|
|
|
if feedback.isCanceled():
|
|
|
|
return False
|
|
|
|
|
2017-05-15 16:19:46 +10:00
|
|
|
parameters[paramToIter] = f
|
2017-06-12 13:35:31 +10:00
|
|
|
for out in alg.destinationParameterDefinitions():
|
2018-10-01 14:33:39 +10:00
|
|
|
if out.name() not in outputs:
|
|
|
|
continue
|
|
|
|
|
2017-06-12 13:35:31 +10:00
|
|
|
o = outputs[out.name()]
|
|
|
|
parameters[out.name()] = QgsProcessingUtils.generateIteratingDestination(o, i, context)
|
2019-02-23 15:30:57 +01:00
|
|
|
feedback.setProgressText(QCoreApplication.translate('AlgorithmExecutor', 'Executing iteration {0}/{1}…').format(i + 1, len(sink_list)))
|
2020-04-26 20:49:07 +03:00
|
|
|
feedback.setProgress(int((i + 1) * 100 / len(sink_list)))
|
2017-06-12 13:35:31 +10:00
|
|
|
ret, results = execute(alg, parameters, context, feedback)
|
|
|
|
if not ret:
|
2014-07-14 14:19:09 +02:00
|
|
|
return False
|
|
|
|
|
2017-06-12 13:35:31 +10:00
|
|
|
handleAlgorithmResults(alg, context, feedback, False)
|
2014-07-14 14:19:09 +02:00
|
|
|
return True
|
2014-10-03 14:44:01 +03:00
|
|
|
|
2015-08-22 14:29:41 +02:00
|
|
|
|
2014-10-03 14:44:01 +03:00
|
|
|
def tr(string, context=''):
|
|
|
|
if context == '':
|
|
|
|
context = 'AlgorithmExecutor'
|
|
|
|
return QCoreApplication.translate(context, string)
|