0

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

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}
2
  • 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
    Commented 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
    Commented Jan 5, 2022 at 23:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.