1

My processing script works fine from the scripts menu and also from a model if the input is on disk or in memory. However, if it's part of a model and uses an Algorithm output, it can't find the data source

enter image description here

The error is

Input Parameters:
{ INPUT: 'memory://MultiPolygon?crs=EPSG:28355&field=OBJECTID:integer(0,0)&field=LOT:string(5,0)&field=PLAN:string(10,0)&field=LOTPLAN:string(15,0)&field=SEG_NUM:integer(0,0)&field=PAR_NUM:integer(0,0)&field=SEGPAR:integer(0,0)&field=PAR_IND:integer(0,0)&field=LOT_AREA:double(0,0)&field=EXCL_AREA:double(0,0)&field=LOT_VOLUME:double(0,0)&field=SURV_IND:string(1,0)&field=TENURE:string(40,0)&field=PRC:integer(0,0)&field=PARISH:string(20,0)&field=COUNTY:string(16,0)&field=LAC:integer(0,0)&field=SHIRE_NAME:string(40,0)&field=FEAT_NAME:string(60,0)&field=ALIAS_NAME:string(254,0)&field=LOC:integer(0,0)&field=LOCALITY:string(30,0)&field=PARCEL_TYP:string(24,0)&field=COVER_TYP:string(10,0)&field=ACC_CODE:string(40,0)&field=CA_AREA_SQM:double(0,0)&field=O_SHAPE_AREA:double(0,0)&field=O_SHAPE_LEN:double(0,0)&field=Shape_Length:double(0,0)&field=Shape_Area:double(0,0)&field=OBJECTID_2:string(0,0)&field=LOTPLAN_2:string(0,0)&field=BUP_LOT:string(0,0)&field=BUP_PLAN:string(0,0)&field=BUP_LOTPLAN:string(0,0)&field=LOT_AREA_AM:string(0,0)&uid={b0e54775-764d-415f-a701-e3cfdb95e12d}', In_Attrib: 'LOTPLAN', OUTPUT: 'C:/Users/support.mapping1/AppData/Local/Temp/processing_BMkWiy/6e8f8a6eaddc41739e79b18699fa4f7e/OUTPUT.gpkg', Out_Attrib: 'LOTPLAN', OutputS: "'SPSP'", RegexS: 'SP' }
Traceback (most recent call last):
File "C:\PROGRA~1/QGIS/apps/qgis/./python\qgis\processing\algfactory.py", line 276, in processAlgorithm
output = self._func(self, parameters, context, feedback, values)
File "C:\Users\support.mapping1\AppData\Roaming\QGIS\QGIS3\profiles\testing\processing\scripts\Search_Update.py", line 36, in searchupdatealg
layerf = QgsProject().instance().mapLayersByName(in_layer.sourceName())[0]
IndexError: list index out of range

Error encountered while running Search and update (alg)
Execution failed after 1.99 seconds

How do I define the input and how do I return the updated data as an output since it's updating the existing file?

The processing script code is

from qgis import processing
from qgis.processing import alg
from qgis.core import QgsProject
import operator,re

@alg(name='searchupdatealg', label='Search and update (alg)',
     group='mgm', group_label='Custom Scripts')
# 'INPUT' is the recommended name for the main input parameter
@alg.input(type=alg.SOURCE, name='INPUT', label='Input vector')
@alg.input(type=alg.FIELD, name='In_Attrib', label='Attribute to search',
           parentLayerParameterName='INPUT')
@alg.input(type=alg.STRING, name='RegexS', label='Regex String')
@alg.input(type=alg.FIELD, name='Out_Attrib', label='Attribute to change',
           parentLayerParameterName='INPUT')
@alg.input(type=alg.EXPRESSION, name='OutputS', label='Output expression')
# 'OUTPUT' is the recommended name for the main output parameter
@alg.input(type=alg.VECTOR_LAYER_DEST, name='OUTPUT',
           label='Vector output')
           

def searchupdatealg(instance, parameters, context, feedback, inputs):
    """
    Search using Regex and output an expression based value
    Use standard regex to find the text to replace (it will replace all occurances as per the regex)
    Use feat['ColumnName'] in the output expression to get a value from the field.
    """
    in_layer = instance.parameterAsSource(parameters, 'INPUT', context)
    regex_s = instance.parameterAsString(parameters,'RegexS', context)
    numfeatures = in_layer.featureCount()
    feats = [f for f in in_layer.getFeatures()]
    in_attrib = instance.parameterAsString(parameters,'In_Attrib', context)
    out_attrib = instance.parameterAsString(parameters,'Out_Attrib', context)
    out_val = instance.parameterAsExpression(parameters,'OutputS', context)
    print (in_layer)
    layerf = QgsProject().instance().mapLayersByName(in_layer.sourceName())[0]
    i=0
    print (layerf)
    layerf.startEditing()
    for feat in feats:
        in_val = feat[in_attrib]
        if in_val!='NULL':
            if len(re.findall(regex_s, in_val))>0:
                UpdateRecord(layerf,feat,out_attrib,in_val,out_val)
                i+=1
    layerf.commitChanges()
    if feedback.isCanceled():
        return {}
    return {'NUMBEROFFEATURES': numfeatures, 'Number changed': i}
    
def UpdateRecord(layerf,feat,out_attrib,in_val,out_val):
    out_attrib_index = feat.fields().indexFromName(out_attrib)
    print(feat.id(),out_attrib_index,in_val,str(eval(out_val))) #testing only. Comment out in production
    layerf.changeAttributeValue(feat.id(), out_attrib_index, str(eval(out_val)))

I have also tried alg.VECTOR_LAYER for the input.

1 Answer 1

2

You don't need to get the layer from QgsProject since you already have the source of the input layer as an input of the processing algorithm, use:

if os.path.isfile(parameters["INPUT"]):
    layerf = QgsVectorLayer(parameters["INPUT"], "Input layer", "ogr") # Shapefile/GeoPackage/... layer
else:
    layerf = context.getMapLayer(parameters["INPUT"])  # Memory layer

PS: context.getMapLayer(parameters["INPUT"]) probably works for all sources I haven't used it in a while so I am not positive

To export your results to the output layer you can do the following:

results = {}
(sink, dest_id) = self.parameterAsSink(
    parameters,
    "OUTPUT",
    context,
    layerf.fields(),
    layerf.wkbType(),
    layerf.sourceCrs()
)
if sink is None:
    raise QgsProcessingException(self.invalidSinkError(parameters, "OUTPUT"))
features = layer_sites.getFeatures()
for current, feature in enumerate(features):
    # Stop the algorithm if cancel button has been clicked
    if feedback.isCanceled():
        break

    # Create a copy of the feature and
    # do some operations on the feature

    # Add a feature in the sink
    sink.addFeature(feature, QgsFeatureSink.FastInsert)

results["OUTPUT"] = dest_id
return results

PS2 : If you have multiple questions try to ask them in seperate questions.

3
  • thanks - and how would I set this as an output if I don't want it to alter the input table?
    – GeorgeC
    Sep 1, 2022 at 11:30
  • 1
    Updated my answer
    – Kalak
    Sep 2, 2022 at 11:42
  • thanks but where is this placed in the code? Currently, it updates the input dataset in place, I want to be able to have the choice of either updating the input data or updating a copy of it in memory and then saving that data output.
    – GeorgeC
    Sep 5, 2022 at 1: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.