def processAlgorithm(self, parameters, context: QgsProcessingContext, feedback: QgsProcessingFeedback): """Here is where the processing itself takes place.""" feedback.setProgress(0) # init params reach_layer = self.parameterAsVectorLayer(parameters, self.REACH_LAYER, context) wastewater_node_layer = self.parameterAsVectorLayer( parameters, self.WASTEWATER_NODE_LAYER, context) value_expression = self.parameterAsExpression(parameters, self.VALUE_EXPRESSION, context) reach_pk_name = self.parameterAsFields(parameters, self.REACH_PK_NAME, context)[0] node_pk_name = self.parameterAsFields(parameters, self.NODE_PK_NAME, context)[0] node_from_fk_name = self.parameterAsFields(parameters, self.NODE_FROM_FK_NAME, context)[0] node_to_fk_name = self.parameterAsFields(parameters, self.NODE_TO_FK_NAME, context)[0] branch_behavior = self.parameterAsEnum(parameters, self.BRANCH_BEHAVIOR, context) create_loop_layer = self.parameterAsBool(parameters, self.CREATE_LOOP_LAYER, context) if branch_behavior == 0: aggregate_method = lambda values: min(values) if values else 0 elif branch_behavior == 1: aggregate_method = lambda values: max(values) if values else 0 elif branch_behavior == 2: aggregate_method = lambda values: statistics.mean( values) if values else 0 else: aggregate_method = lambda values: feedback.pushError( 'Aggregate method not implemented') # create feature sink fields = wastewater_node_layer.fields() fields.append(QgsField('value', QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, reach_layer.sourceCrs()) loop_sink = None loop_dest_id = None if create_loop_layer: (loop_sink, loop_dest_id) = self.parameterAsSink(parameters, self.LOOP_OUTPUT, context, fields, QgsWkbTypes.Point, reach_layer.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) feature_count = reach_layer.featureCount() reaches_by_from_node = dict() reaches_by_id = dict() expression = QgsExpression(value_expression) context = QgsExpressionContext( QgsExpressionContextUtils.globalProjectLayerScopes(reach_layer)) expression.prepare(context) progress = 0 feedback.setProgressText(self.tr('Indexing reaches')) for reach in reach_layer.getFeatures(QgsFeatureRequest()): if reach[node_from_fk_name] == NULL: continue context.setFeature(reach) value = expression.evaluate(context) reach_obj = Reach(reach[node_from_fk_name], reach[node_to_fk_name], value, reach.geometry()) reaches_by_from_node.setdefault(reach_obj.from_id, []).append(reach_obj) reaches_by_id[reach[reach_pk_name]] = reach_obj feedback.setProgress(progress / feature_count * 10) progress += 1 loop_nodes = [] current_feature = 0 calculated_values = {} feedback.setProgressText(self.tr('Analyzing network')) for node in wastewater_node_layer.getFeatures(): from_node_id = node[node_pk_name] processed_nodes = [] times = [] if from_node_id in reaches_by_from_node.keys(): for reach in reaches_by_from_node[from_node_id]: times.append( self.calculate_branch(reach, reaches_by_from_node, reaches_by_id, list(processed_nodes), calculated_values, aggregate_method, loop_nodes, feedback)) if times: time = aggregate_method(times) else: time = 0 current_feature += 1 calculated_values[node[node_pk_name]] = time new_node = QgsFeature(node) new_node.setFields(fields) new_node.setAttributes(node.attributes() + [time]) sink.addFeature(new_node, QgsFeatureSink.FastInsert) if create_loop_layer and from_node_id in loop_nodes: loop_sink.addFeature(node, QgsFeatureSink.FastInsert) feedback.setProgress(10 + current_feature / feature_count * 90) result = {self.OUTPUT: dest_id} if create_loop_layer: result[self.LOOP_OUTPUT] = loop_dest_id return result