I want to create a simple qgis processing algorithm, where input layer is copied, there are some basic operations done on attributes and the resulting layer is returned. However, I encounter following error:
The following layers were not correctly generated. • Selected_features_10143967_2a0f_4057_8aea_cb8c49a1766f You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm.
The Log Messages Panel is empty. Here is the code:
from qgis import processing
from qgis.processing import alg
from qgis.core import QgsProject, QgsField, edit
from PyQt5.QtCore import QVariant
@alg(name='somename', label='somelabel',
group='somegroup', group_label='somegroup')
# 'INPUT' is the recommended name for the main input parameter
@alg.input(type=alg.SOURCE, name='INPUT', label='in')
# 'OUTPUT' is the recommended name for the main output parameter
@alg.input(type=alg.VECTOR_LAYER_DEST, name='OUTPUT',
label='out')
def somename(instance, parameters, context, feedback, inputs):
"""
Does stuff
"""
input = instance.parameterAsVectorLayer(parameters, 'INPUT', context)
input.selectAll()
output = processing.run("native:saveselectedfeatures", {'INPUT': input, 'OUTPUT': parameters['OUTPUT']},
context=context,
feedback=feedback)['OUTPUT']
input.removeSelection()
layer_provider = output.dataProvider()
layer_provider.addAttributes([QgsField("somefield", QVariant.String, len=1)])
output.updateFields()
with edit(output):
for feature in output.getFeatures():
if not feature['someotherfield']:
feature['somefield'] = '0'
output.updateFeature(feature)
return {'OUTPUT': output}
What am I doing wrong? What is the proper way to return the result after editing?
1 Answer 1
The error is not about how you are returning the results, but that problems in your code are preventing the output layer from being correctly generated. Your main problem is that your output variable holds a layer id string, not an actual layer.
I would suggest a slightly different approach. Rather than running Save Selected Features as a child algorithm to copy your input layer, use feature source and feature sink parameters, add a field to the sink then copy your input features to the sink setting the input geometry, input attributes and appending the new attribute value if the input feature satisfies the condition.
The following should work (tested in QGIS 3.20):
from qgis.processing import alg
from qgis.core import (QgsField,
QgsFeatureSink,
QgsFeature)
from PyQt5.QtCore import QVariant
@alg(name='somename', label='somelabel',
group='somegroup', group_label='somegroup')
# 'INPUT' is the recommended name for the main input parameter
@alg.input(type=alg.SOURCE, name='INPUT', label='in')
# 'OUTPUT' is the recommended name for the main output parameter
@alg.input(type=alg.SINK, name='OUTPUT',
label='out')
def somename(instance, parameters, context, feedback, inputs):
"""
Does stuff
"""
# Declare source from input parameter
source = instance.parameterAsSource(parameters, 'INPUT', context)
# Copy source fields
flds = source.fields()
# Add new field
flds.append(QgsField("somefield", QVariant.String, len=1))
# Create feature sink from output parameter with extra field (geom type & crs same as source)
(sink, dest_id) = instance.parameterAsSink(parameters, 'OUTPUT', context,
flds, source.wkbType(), source.sourceCrs())
# Loop through source features
for in_feat in source.getFeatures():
# Create output feature
out_feat = QgsFeature()
# Set geometry from source
out_feat.setGeometry(in_feat.geometry())
# Copy source attributes
atts = in_feat.attributes()
# If input feature satisfies condition add new value to attributes
# Are you checking for an empty string here?
if not in_feat['someotherfield']:
atts.append(0)
# Set modified attributes to output feature
out_feat.setAttributes(atts)
# Add output feature to feature sink
sink.addFeature(out_feat, QgsFeatureSink.FastInsert)
# Return output layer id
return {'OUTPUT': dest_id}
-
It's a pity that you can't edit resulting layer directly. Nevertheless, it works. BTW you can do
out_feat.setFields(flds)
to refer to attributes by names.Kubson– Kubson2022年01月05日 15:53:46 +00:00Commented Jan 5, 2022 at 15:53 -
@Kubson, sorry I don't really know what you mean when you say "it's a pity you can't edit resulting layer directly". The approach in my answer is quite common in QGIS processing algorithms. If you really want to stick with your original approach you could probably get a
QgsVectorLayer
object like:layer = QgsProcessingUtils.mapLayerFromString(output, context)
. I didn't try it myself in this case because I honestly don't see the advantage of doing it that way.Ben W– Ben W2022年01月05日 23:00:59 +00:00Commented Jan 5, 2022 at 23:00