Skip to content

Commit 165c189

Browse files
author
Daniel
committed
introduce postProcess to handle layers
1 parent 0f838a8 commit 165c189

File tree

4 files changed

+225
-313
lines changed

4 files changed

+225
-313
lines changed

geovita_processing_plugin/algorithms/BegrensSkadeExcavation.py

Lines changed: 87 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import traceback
3434
from pathlib import Path
35+
from datetime import datetime
3536

3637
from qgis.core import (
3738
Qgis,
@@ -49,11 +50,12 @@
4950
QgsProcessingParameterRasterLayer,
5051
QgsProcessingParameterString,
5152
QgsProject,
53+
QgsVectorLayer,
54+
QgsProcessingOutputFile,
5255
)
5356
from qgis.PyQt.QtCore import QCoreApplication
5457

5558
from ..REMEDY_GIS_RiskTool.BegrensSkade import mainBegrensSkade_Excavation
56-
from ..utilities.AddLayersTask import AddLayersTask
5759
from ..utilities.gui import GuiUtils
5860
from ..utilities.logger import CustomLogger
5961
from ..utilities.methodslib import (
@@ -117,7 +119,6 @@ def __init__(self):
117119
self.feature_name = None # Default value
118120
self.layers_info = {}
119121
self.styles_dir_path = Path()
120-
self.add_layers_task = AddLayersTask()
121122
self.logger.info("__INIT__ - Finished initialize BegrensSkadeExcavation ")
122123

123124
def tr(self, string):
@@ -449,8 +450,9 @@ def initAlgorithm(self, config):
449450
self.OUTPUT_FEATURE_NAME,
450451
self.tr(
451452
"Naming Conventions for Analysis and Features (Output feature name appended to file-names)"
452-
),
453-
)
453+
)
454+
),
455+
createOutput=True
454456
)
455457
self.addParameter(
456458
QgsProcessingParameterCrs(
@@ -465,6 +467,24 @@ def initAlgorithm(self, config):
465467
self.tr("Output Folder"),
466468
)
467469
)
470+
self.addOutput(
471+
QgsProcessingOutputFile(
472+
self.OUTPUT_BUILDING,
473+
self.tr("Output Buildings Shapefile"),
474+
)
475+
)
476+
self.addOutput(
477+
QgsProcessingOutputFile(
478+
self.OUTPUT_WALL,
479+
self.tr("Output Walls Shapefile"),
480+
)
481+
)
482+
self.addOutput(
483+
QgsProcessingOutputFile(
484+
self.OUTPUT_CORNER,
485+
self.tr("Output Corners Shapefile"),
486+
)
487+
)
468488

469489
self.logger.info("initAlgorithm - Done setting up the inputs.")
470490

@@ -801,7 +821,7 @@ def processAlgorithm(self, parameters, context, feedback):
801821
return {}
802822

803823
#################### HANDLE THE RESULT ###############################
804-
feedback.setProgress(80)
824+
feedback.setProgress(90)
805825
self.logger.info(f"PROCESS - OUTPUT BUILDINGS: {output_shapefiles[0]}")
806826
self.logger.info(f"PROCESS - OUTPUT WALL: {output_shapefiles[1]}")
807827
self.logger.info(f"PROCESS - OUTPUT CORNER: {output_shapefiles[2]}")
@@ -820,80 +840,89 @@ def processAlgorithm(self, parameters, context, feedback):
820840
"shape_path": output_shapefiles[1],
821841
"style_name": "WALL-ANGLE.qml",
822842
},
823-
"BUILDING-TOTAL-SETTLMENT": {
824-
"shape_path": output_shapefiles[0],
825-
"style_name": "BUILDING-TOTAL-SETTLMENT_sv_tot.qml",
826-
},
827843
"BUILDING-TOTAL-ANGLE": {
828844
"shape_path": output_shapefiles[0],
829845
"style_name": "BUILDING-TOTAL-ANGLE_max_angle.qml",
830846
},
847+
"BUILDING-TOTAL-SETTLMENT": {
848+
"shape_path": output_shapefiles[0],
849+
"style_name": "BUILDING-TOTAL-SETTLMENT_sv_tot.qml",
850+
}
831851
}
832852
if bVulnerability:
833853
self.layers_info.update(
834854
{
835-
"BUILDING-RISK-SETTLMENT": {
836-
"shape_path": output_shapefiles[0],
837-
"style_name": "BUILDING-TOTAL-RISK-SELLMENT_risk_tots.qml",
838-
},
839855
"BUILDING-RISK-ANGLE": {
840856
"shape_path": output_shapefiles[0],
841857
"style_name": "BUILDING-TOTAL-RISK-ANGLE_risk_angle.qml",
842858
},
859+
"BUILDING-RISK-SETTLMENT": {
860+
"shape_path": output_shapefiles[0],
861+
"style_name": "BUILDING-TOTAL-RISK-SELLMENT_risk_tots.qml",
862+
}
843863
}
844864
)
845865

846-
feedback.setProgress(90)
866+
feedback.setProgress(100)
847867
feedback.pushInfo("PROCESS - Finished processing!")
868+
848869
# Return the results of the algorithm.
849-
return {
850-
self.OUTPUT_BUILDING: output_shapefiles[0],
851-
self.OUTPUT_WALL: output_shapefiles[1],
852-
self.OUTPUT_CORNER: output_shapefiles[2],
853-
}
854-
870+
return {self.OUTPUT_BUILDING: output_shapefiles[0],
871+
self.OUTPUT_WALL: output_shapefiles[1],
872+
self.OUTPUT_CORNER: output_shapefiles[2],
873+
874+
}
875+
855876
def postProcessAlgorithm(self, context, feedback):
856877
"""
857-
Handles the post-processing steps of the algorithm, specifically adding output layers to the QGIS project.
858-
859-
This method creates and executes a process to add layers to the QGIS interface, applying predefined styles
860-
and organizing them within a specified group. It leverages the `AddLayersTask` class to manage layer
861-
addition in a way that ensures thread safety and proper GUI updates.
862-
863-
Parameters:
864-
- context (QgsProcessingContext): The context of the processing, providing access to the QGIS project and other relevant settings.
865-
- feedback (QgsProcessingFeedback): The object used to report progress and log messages back to the user.
866-
867-
Returns:
868-
- dict: An empty dictionary. This method does not produce output parameters but instead focuses on the side effect of adding layers to the project.
869-
870-
Note:
871-
This method sets up a task for layer addition, defining success and failure callbacks to provide user feedback.
872-
It manually starts the process and handles its completion.
878+
This method is called after processAlgorithm finishes.
879+
Here, we manually load the output shapefiles, apply QML styles,
880+
and place them under a custom group in the layer tree.
873881
"""
874-
######### EXPERIMENTAL ADD LAYERS TO GUI #########
875-
# Create the task
876-
self.add_layers_task.setParameters(
877-
self.layers_info,
878-
self.feature_name,
879-
self.styles_dir_path,
880-
self.logger,
881-
)
882-
883-
# Define a slot to handle the task completion
884-
def onTaskCompleted(success):
885-
if success:
886-
feedback.pushInfo("POSTPROCESS - Layers added successfully.")
887-
feedback.setProgress(100)
882+
project = context.project()
883+
root = project.layerTreeRoot()
884+
885+
# Create (or find) a group at the top level named by 'self.feature_name'.
886+
group_name = self.feature_name
887+
group = root.findGroup(group_name)
888+
if not group:
889+
group = root.insertGroup(0, group_name)
890+
891+
# 2) Loop through the entries stored in 'self.layers_info' defined during processAlgorithm
892+
for layer_label, layer_info in self.layers_info.items():
893+
shape_path = layer_info["shape_path"] # e.g. "C:/somefolder/buildings.shp"
894+
style_name = layer_info["style_name"] # e.g. "BUILDING-TOTAL-SETTLMENT_sv_tot.qml"
895+
896+
# Generate a unique layer name with timestamp
897+
timestamp = datetime.now().strftime("%Y%m%d_%H%M")
898+
final_layer_name = f"{layer_label}_{timestamp}"
899+
900+
# Create the QgsVectorLayer from the file path
901+
layer = QgsVectorLayer(shape_path, final_layer_name, "ogr")
902+
if not layer.isValid():
903+
feedback.reportError(f"Could not load layer from file: {shape_path}")
904+
continue
905+
906+
# Load the QML style if it exists
907+
style_path = self.styles_dir_path / style_name # e.g. /path/to/styles/BUILDING-TOTAL-SETTLMENT_sv_tot.qml
908+
if style_path.is_file():
909+
layer.loadNamedStyle(str(style_path))
910+
layer.triggerRepaint()
888911
else:
889-
feedback.reportError("POSTPROCESS - Failed to add layers.")
890-
feedback.setProgress(100)
912+
feedback.reportError(f"Style file not found: {style_path}")
913+
914+
# Add the layer to the project (layer registry) *without* adding to the root TOC
915+
QgsProject.instance().addMapLayer(layer, False)
916+
917+
# Place the layer under the group at the bottom
918+
group.insertLayer(-1, layer)
891919

892-
# Connect the task's completed signal to the slot
893-
self.add_layers_task.taskCompleted.connect(onTaskCompleted)
920+
# Ensure the new layer node is visible
921+
node = group.findLayer(layer.id())
922+
if node:
923+
node.setItemVisibilityChecked(True)
894924

895-
# Start the task
896-
success = self.add_layers_task.run()
897-
self.add_layers_task.finished(success)
925+
feedback.pushInfo(f"Loaded and styled layer '{final_layer_name}' in group '{group_name}'.")
898926

899-
return {}
927+
feedback.pushInfo("postProcessAlgorithm complete.")
928+
return {}

geovita_processing_plugin/algorithms/BegrensSkadeImpactMap.py

Lines changed: 59 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,15 @@
4545
QgsProcessingParameterDefinition,
4646
QgsProcessingParameterRasterLayer,
4747
QgsMessageLog,
48+
QgsProcessingOutputFile,
49+
QgsRasterLayer
4850
)
4951

5052
import traceback
53+
from datetime import datetime
5154
from pathlib import Path
5255

5356
from .base_algorithm import GvBaseProcessingAlgorithms
54-
from ..utilities.AddLayersTask import AddLayersTask
5557
from ..utilities.gui import GuiUtils
5658
from ..utilities.logger import CustomLogger
5759
from ..utilities.methodslib import (
@@ -116,7 +118,6 @@ def __init__(self):
116118
self.feature_name = None # Default value
117119
self.layers_info = {}
118120
self.styles_dir_path = Path()
119-
self.add_layers_task = AddLayersTask()
120121
self.logger.info("__INIT__ - Finished initialize BegrensSkadeImpactMap ")
121122

122123
def name(self):
@@ -375,6 +376,14 @@ def initAlgorithm(self, config):
375376
)
376377
param.setFlags(QgsProcessingParameterDefinition.FlagAdvanced)
377378
self.addParameter(param)
379+
380+
# We add the output definition
381+
self.addOutput(
382+
QgsProcessingOutputFile(
383+
self.OUTPUT_RASTER,
384+
self.tr("Output Raster impact map"),
385+
)
386+
)
378387

379388
def processAlgorithm(self, parameters, context, feedback):
380389
"""
@@ -617,49 +626,62 @@ def processAlgorithm(self, parameters, context, feedback):
617626
}
618627
}
619628

620-
feedback.setProgress(90)
629+
feedback.setProgress(100)
621630
feedback.pushInfo("PROCESS - Finished processing!")
622631
# Return the results of the algorithm.
623-
return {self.OUTPUT_FOLDER: output_raster_path}
632+
return {self.OUTPUT_RASTER: output_raster_path}
624633

625634
def postProcessAlgorithm(self, context, feedback):
626635
"""
627-
Handles the post-processing steps of the algorithm, specifically adding output layers to the QGIS project.
628-
629-
This method creates and executes a process to add layers to the QGIS interface, applying predefined styles
630-
and organizing them within a specified group. It leverages the `AddLayersTask` class to manage layer
631-
addition in a way that ensures thread safety and proper GUI updates.
632-
633-
Parameters:
634-
- context (QgsProcessingContext): The context of the processing, providing access to the QGIS project and other relevant settings.
635-
- feedback (QgsProcessingFeedback): The object used to report progress and log messages back to the user.
636+
After processAlgorithm finishes, load the produced raster (output_raster_path),
637+
apply a QML style, and place it into a custom group in the TOC.
638+
"""
639+
project = context.project()
640+
root = project.layerTreeRoot()
641+
642+
# 1) Define or find the group at the top level
643+
group_name = self.feature_name
644+
group = root.findGroup(group_name)
645+
if not group:
646+
group = root.insertGroup(0, group_name)
647+
648+
# The self.layers_info just have one key: "IMPACT-MAP",
649+
# but let's loop in case there are added more raster layers in future
650+
for layer_label, layer_info in self.layers_info.items():
651+
raster_path = layer_info["shape_path"]
652+
style_name = layer_info["style_name"]
653+
style_path = self.styles_dir_path / style_name
654+
655+
# Build a unique name with timestamp
656+
timestamp = datetime.now().strftime("%Y%m%d_%H%M")
657+
final_layer_name = f"{layer_label}_{timestamp}"
658+
659+
# Create a QgsRasterLayer
660+
# Use "gdal" or an appropriate provider for your raster format
661+
raster_layer = QgsRasterLayer(raster_path, final_layer_name, "gdal")
662+
if not raster_layer.isValid():
663+
feedback.reportError(f"Invalid raster layer from file: {raster_path}")
664+
continue
665+
666+
# Attempt to load a QML style (if it references valid raster symbology)
667+
if style_path.is_file():
668+
raster_layer.loadNamedStyle(str(style_path))
669+
raster_layer.triggerRepaint()
670+
else:
671+
feedback.reportError(f"Style file not found: {style_path}")
636672

637-
Returns:
638-
- dict: An empty dictionary. This method does not produce output parameters but instead focuses on the side effect of adding layers to the project.
673+
# Add the layer to the project, but do *not* place it in the root
674+
QgsProject.instance().addMapLayer(raster_layer, False)
639675

640-
Note:
641-
This method sets up a task for layer addition, defining success and failure callbacks to provide user feedback.
642-
It manually starts the process and handles its completion.
643-
"""
644-
######### EXPERIMENTAL ADD LAYERS TO GUI #########
645-
# Create the task
646-
self.add_layers_task.setParameters(
647-
self.layers_info, self.feature_name, self.styles_dir_path, self.logger
648-
)
676+
# Insert it in our custom group
677+
group.insertLayer(0, raster_layer)
649678

650-
# Define a slot to handle the task completion
651-
def onTaskCompleted(success):
652-
if success:
653-
feedback.pushInfo("POSTPROCESS - Layers added successfully.")
654-
feedback.setProgress(100)
655-
else:
656-
feedback.reportError("POSTPROCESS - Failed to add layers.")
657-
feedback.setProgress(100)
679+
# Make sure it's visible
680+
node = group.findLayer(raster_layer.id())
681+
if node:
682+
node.setItemVisibilityChecked(True)
658683

659-
# Connect the task's completed signal to the slot
660-
self.add_layers_task.taskCompleted.connect(onTaskCompleted)
684+
feedback.pushInfo(f"Loaded and styled raster layer '{final_layer_name}' in group '{group_name}'.")
661685

662-
# Start the task
663-
success = self.add_layers_task.run()
664-
self.add_layers_task.finished(success)
686+
feedback.pushInfo("postProcessAlgorithm complete.")
665687
return {}

0 commit comments

Comments
 (0)