diff --git a/qgis2fds_export_algo.py b/qgis2fds_export_algo.py index b964945..b1a097d 100644 --- a/qgis2fds_export_algo.py +++ b/qgis2fds_export_algo.py @@ -105,7 +105,6 @@ def initAlgorithm(self, config=None): def processAlgorithm(self, parameters, context, model_feedback): feedback = QgsProcessingMultiStepFeedback(9, model_feedback) # FIXME results, outputs, project = {}, {}, QgsProject.instance() - time0 = time.time() # Load parameter values @@ -358,9 +357,7 @@ def processAlgorithm(self, parameters, context, model_feedback): # Interpolate UTM DEM points to DEM raster layer (Grid, IDW with nearest neighbor searching) # aligned to UTM sampling grid # Spatial index does not improve the performance here - feedback.setProgressText( - "\nInterpolate UTM DEM points to DEM raster layer (IDW)..." - ) + feedback.setProgressText("\nInterpolate UTM DEM points (IDW)...") t0 = time.time() radius = max(dem_layer_rx, dem_layer_ry) e = utm_extent @@ -452,9 +449,9 @@ def processAlgorithm(self, parameters, context, model_feedback): return {} # Sample landuse values from landuse layer - feedback.setProgressText("\nSample landuse values from landuse layer...") - t0 = time.time() if landuse_layer and landuse_type_filepath: + feedback.setProgressText("\nSample landuse values from landuse layer...") + t0 = time.time() alg_params = { "COLUMN_PREFIX": "landuse", "INPUT": outputs["SetZFromDem"]["OUTPUT"], @@ -474,9 +471,9 @@ def processAlgorithm(self, parameters, context, model_feedback): fds_grid_layer = context.getMapLayer( outputs["SampleRasterValues"]["OUTPUT"] ) + feedback.setProgressText(f"time: {time.time()-t0:.1f}s") else: fds_grid_layer = context.getMapLayer(outputs["SetZFromDem"]["OUTPUT"]) - feedback.setProgressText(f"time: {time.time()-t0:.1f}s") feedback.setCurrentStep(9) if feedback.isCanceled(): @@ -489,6 +486,78 @@ def processAlgorithm(self, parameters, context, model_feedback): filepath=landuse_type_filepath, ) + # Get UTM fire layer and buffered UTM fire layer + # and sample bc values from them + utm_fire_layer, utm_b_fire_layer = None, None + if fire_layer: + feedback.setProgressText("\nGet UTM fire layer...") + t0 = time.time() + # Reproject fire layer to UTM + alg_params = { + "INPUT": fire_layer, + "TARGET_CRS": utm_crs, + "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT, + } + outputs["UTMFireLayer"] = processing.run( + "native:reprojectlayer", + alg_params, + context=context, + feedback=feedback, + is_child_algorithm=True, + ) + utm_fire_layer = context.getMapLayer(outputs["UTMFireLayer"]["OUTPUT"]) + # Buffer UTM fire layer + alg_params = { + "INPUT": utm_fire_layer, + "DISTANCE": pixel_size, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": False, + "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT, + } + outputs["UTMBufferedFireLayer"] = processing.run( + "native:buffer", + alg_params, + context=context, + feedback=feedback, + is_child_algorithm=True, + ) + utm_b_fire_layer = context.getMapLayer( + outputs["UTMBufferedFireLayer"]["OUTPUT"] + ) + # Sample them + _load_fire_layer_bc( + context, + feedback, + fds_grid_layer=fds_grid_layer, + fire_layer=utm_b_fire_layer, + bc_field="bc_out", + bc_default=landuse_type.bc_out_default, + ) + _load_fire_layer_bc( + context, + feedback, + fds_grid_layer=fds_grid_layer, + fire_layer=utm_fire_layer, + bc_field="bc_in", + bc_default=landuse_type.bc_in_default, + ) + feedback.setProgressText(f"time: {time.time()-t0:.1f}s") + + feedback.setCurrentStep(9) + if feedback.isCanceled(): + return {} + + utm_fire_layer, utm_b_fire_layer = None, None + if fire_layer: + feedback.setProgressText("\nGet UTM fire layer...") + t0 = time.time() + fds_grid_layer = fds_grid_layer # FIXME + + feedback.setProgressText(f"time: {time.time()-t0:.1f}s") + # Get texture texture = Texture( feedback=feedback, @@ -512,7 +581,7 @@ def processAlgorithm(self, parameters, context, model_feedback): Terrain = GEOMTerrain terrain = Terrain( feedback=feedback, - sampling_layer=fds_grid_layer, # utm_grid_layer, + sampling_layer=fds_grid_layer, utm_origin=utm_origin, landuse_layer=landuse_layer, landuse_type=landuse_type, @@ -588,3 +657,61 @@ def _show_extent(self, e, c, parameters, outputs, context, feedback): ) text = f"\nextent: {extent_str}" feedback.setProgressText(text) + + +# FIXME move elsewhere + +from qgis.PyQt.QtCore import QVariant +from qgis.core import ( + QgsProcessing, + QgsProcessingException, + QgsField, + NULL, + edit, + QgsFeatureRequest, +) + + +def _load_fire_layer_bc( + context, + feedback, + fds_grid_layer, + fire_layer, + bc_field, + bc_default, +): + feedback.pushInfo(f"Load fire layer bc ({bc_field})...") + + # Edit fds grid layer + with edit(fds_grid_layer): + # Add new data field + if fds_grid_layer.dataProvider().fieldNameIndex("bc") == -1: + attributes = list((QgsField("bc", QVariant.Int),)) + fds_grid_layer.dataProvider().addAttributes(attributes) + fds_grid_layer.updateFields() + output_bc_idx = fds_grid_layer.dataProvider().fieldNameIndex("bc") + + if fire_layer: + # For all fire layer features + bc_idx = fire_layer.fields().indexOf(bc_field) + for fire_feat in fire_layer.getFeatures(): + # Check if user specified per feature bc available + if bc_idx != -1: + bc = fire_feat[bc_idx] + else: + bc = bc_default + + # Set bc in sampling layer + # for speed, preselect points + fire_geom = fire_feat.geometry() + fire_geom_bbox = fire_geom.boundingBox() + for f in fds_grid_layer.getFeatures(QgsFeatureRequest(fire_geom_bbox)): + g = f.geometry() + if fire_geom.contains(g): + if bc != NULL: + fds_grid_layer.changeAttributeValue( + f.id(), output_bc_idx, bc + ) + feedback.pushInfo( + f" applyed from fire layer <{fire_feat.id()}> feature" + ) diff --git a/qgis2fds_params.py b/qgis2fds_params.py index abcdce8..c4b88bf 100644 --- a/qgis2fds_params.py +++ b/qgis2fds_params.py @@ -199,7 +199,7 @@ def get(cls, algo, parameters, context, feedback, project): url = value.source() if not os.path.isfile(url): raise QgsProcessingException( - "DEM layer data is not saved locally, cannot proceed." + f"DEM layer data is not saved locally, cannot proceed.\n{url}" ) # Check valid if not value.crs().isValid(): @@ -238,7 +238,7 @@ def get(cls, algo, parameters, context, feedback, project): url = value.source() if not os.path.isfile(url): raise QgsProcessingException( - "Landuse layer data is not saved locally, cannot proceed." + f"Landuse layer data is not saved locally, cannot proceed.\n{url}" ) # Check valid if not value.crs().isValid(): @@ -309,18 +309,11 @@ def get(cls, algo, parameters, context, feedback, project): value = None if parameters.get(cls.label): value = algo.parameterAsVectorLayer(parameters, cls.label, context) - if value: - # Check local - url = value.source() - if not os.path.isfile(url): - raise QgsProcessingException( - "Fire layer data is not saved locally, cannot proceed." - ) - # Check valid - if not value.crs().isValid(): - raise QgsProcessingException( - f"Fire layer CRS <{value.crs().description()}> not valid, cannot proceed." - ) + # Check valid + if value and not value.crs().isValid(): + raise QgsProcessingException( + f"Fire layer CRS <{value.crs().description()}> not valid, cannot proceed." + ) project.writeEntry("qgis2fds", cls.label, parameters.get(cls.label)) # protect feedback.setProgressText(f"{cls.desc}: <{value}>") return value diff --git a/types/domain.py b/types/domain.py index 7fa9f16..99806cb 100644 --- a/types/domain.py +++ b/types/domain.py @@ -24,7 +24,7 @@ def __init__( cell_size, nmesh, ) -> None: - feedback.pushInfo("Init MESH...") + feedback.pushInfo("\nInit MESH...") self.feedback = feedback self.utm_extent = utm_extent diff --git a/types/landuse.py b/types/landuse.py index 9910720..5cc5689 100644 --- a/types/landuse.py +++ b/types/landuse.py @@ -30,12 +30,12 @@ def __init__(self, feedback, project_path, filepath) -> None: self.feedback = feedback self.filepath = filepath and os.path.join(project_path, filepath) or str() if filepath: - self.feedback.pushInfo(f"Import landuse type *.csv file: <{self.filepath}>") + self.feedback.pushInfo(f"\nImport landuse type *.csv file: <{self.filepath}>") self.surf_dict = dict() self.surf_id_dict = dict() self._import() else: - self.feedback.pushInfo(f"No landuse type *.csv file.") + self.feedback.pushInfo(f"\nNo landuse type *.csv file.") self.surf_dict = {} # INERT is predefined, FDS SURF not needed self.surf_id_dict = {0: "INERT"} self.feedback.pushInfo( @@ -91,6 +91,6 @@ def bc_out_default(self) -> str: @property def bc_in_default(self) -> str: try: - return list(self.surf_id_dict)[-1] # eg. burned + return list(self.surf_id_dict)[-1] # eg. Burned except KeyError: return 0