I am trying to perform some categorization from an existing vector layer such that after performing the calculations, a new vector layer is created with just the new fields created. After running my script, no error message shows up, but then no layer is seen either. What is missing so that I can see the newly created layer and for it to get loaded up to the Mapcancas? Simply: Nothing appears after it runs. No new layer was seen. How do I fix it? Here is my code:
def processAlgorithm(self, parameters, context, feedback):
rawz_layer = self.parameterAsVectorLayer(parameters, self.INPUT_LAYER, context)
if rawz_layer is None:
raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))
rawz_fields = rawz_layer.fields()
'''Counter for the progress bar'''
total = rawz_layer.featureCount()
parts = 100/total
# Get indices of P_SIZES and LENGTH fields
p_size_index = rawz_fields.indexFromName(self.parameterAsString(parameters, self.PIPE_KEY, context))
length_index = rawz_fields.indexFromName(self.parameterAsString(parameters, self.LENGTH_KEY, context))
# Pipe Sizes
size_pipe = [4, 5, 6, 8, 10, 12, 15, 18, 21, 24, 30, 36, 42]
# Pipe Types with Prices
single_wall_price = [2, 3, 4, 5, 8, 10, 20, 30, 40, 50, 80, 100, 150]
smooth_wall_price = [3, 4, 5, 7, 10, 13, 20, 30, 45, 60, 90, 150, 250]
clay_wall_price = [12, 15, 18, 25, 30, 40, 55, 80, 110, 140, 250, 450, 600]
concrete_wall_price = [15, 20, 25, 40, 50, 70, 100, 150, 200, 250, 400, 600, 800]
#define new fields
out_fields = QgsFields()
#append fields
out_fields.append(QgsField('N_SIZES', QVariant.Int))
out_fields.append(QgsField('FREQUENCY', QVariant.Int))
out_fields.append(QgsField('TOTAL_FEET', QVariant.Double))
out_fields.append(QgsField('SING_EST', QVariant.Double))
out_fields.append(QgsField('SING_POSB', QVariant.Double))
out_fields.append(QgsField('SMOT_EST', QVariant.Double))
out_fields.append(QgsField('SMOT_POSB', QVariant.Double))
out_fields.append(QgsField('CLAY_EST', QVariant.Double))
out_fields.append(QgsField('CLAY_POSB', QVariant.Double))
out_fields.append(QgsField('CONC_EST', QVariant.Double))
out_fields.append(QgsField('CONC_POSB', QVariant.Double))
'''load data from layer "raw_layer" '''
feedback.setProgressText(self.tr("Loading network layer\n "))
# Initialize lists for output attributes
n_sizes = []
n_freq = []
total_length = []
est_sing = []
posb_sing = []
est_smt = []
posb_smt = []
est_clay = []
posb_clay = []
est_conc = []
posb_conc = []
# Loop through features and group by pipe size
groups = {}
for feature in rawz_layer.getFeatures():
p_size = feature.attributes()[p_size_index]
length = feature.attributes()[length_index]
# Add feature to corresponding pipe size group
if p_size not in groups:
groups[p_size] = {'freq': 0, 'length': 0}
groups[p_size]['freq'] += 1
groups[p_size]['length'] += length
# Loop through pipe size groups and calculate output attributes
for size in size_pipe:
if size in groups:
n_sizes.append(size)
n_freq.append(groups[size]['freq'])
total_length.append(groups[size]['length'])
est_sing.append(groups[size]['length'] * single_wall_price[size_pipe.index(size)])
posb_sing.append(est_sing[-1] * 0.9)
est_smt.append(groups[size]['length'] * smooth_wall_price[size_pipe.index(size)])
posb_smt.append(est_smt[-1] * 0.9)
est_clay.append(groups[size]['length'] * clay_wall_price[size_pipe.index(size)])
posb_clay.append(est_clay[-1] * 0.9)
est_conc.append(groups[size]['length'] * concrete_wall_price[size_pipe.index(size)])
posb_conc.append(est_conc[-1] * 0.9)
# Create output layer and add output fields
output_layer = QgsVectorLayer('Point?crs=' + rawz_layer.crs().authid(), self.OUTPUT_LAYER, 'memory')
output_layer_provider = output_layer.dataProvider()
output_layer_provider.addAttributes(out_fields)
output_layer.updateFields()
# Use random colors to create categorized symbol for each group/category
color_list = ['#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#00FFFF', '#FF00FF', '#800000', '#008000', '#000080', '#808000', '#008080', '#800080', '#C0C0C0']
categories = []
i = 0
for n_size in n_sizes:
symbol = QgsSymbol.defaultSymbol(output_layer.geometryType())
symbol.setColor(QColor(color_list[i % len(color_list)]))
category = QgsRendererCategory(n_size, symbol, str(n_size))
categories.append(category)
i += 1
renderer = QgsCategorizedSymbolRenderer('n_sizes', categories)
output_layer.setRenderer(renderer)
# Add features to output layer with new fields of attribute values
features = []
for i, n_size in enumerate(n_sizes):
feature = QgsFeature()
feature.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(0, i)))
feature.setAttributes([
n_sizes[i],
n_freq[i],
total_length[i],
est_sing[i],
posb_sing[i],
est_smt[i],
posb_smt[i],
est_clay[i],
posb_clay[i],
est_conc[i],
posb_conc[i]
])
features.append(feature)
output_layer_provider.addFeatures(features)
# Add layer to the map canvas
QgsProject.instance().addMapLayer(output_layer)
# iface.setActiveLayer(output_layer)
# canvas = iface.mapCanvas()
# canvas.zoomToFullExtent()
# canvas.refresh()
canvas = iface.mapCanvas()
canvas.setExtent(output_layer.extent())
canvas.refresh()
iface.layerTreeView().refreshLayerSymbology(output_layer.id())
# Return result
return {self.OUTPUT_LAYER: output_layer}
processAlgorithm()
is not thread safe. 2. If you declare and use an ouput parameter properly you don't need to load the layer yourself- QGIS will handle it. 3. to style the layer, useQgsProcessingLayerPostProcessorInterface
class, example here. Also read: docs.qgis.org/3.28/en/docs/user_manual/processing/scripts.html#