diff --git a/src/cplus_plugin/conf.py b/src/cplus_plugin/conf.py
index 51b640af4..750cc2592 100644
--- a/src/cplus_plugin/conf.py
+++ b/src/cplus_plugin/conf.py
@@ -21,6 +21,7 @@
NCS_CARBON_SEGMENT,
NCS_PATHWAY_SEGMENT,
NPV_COLLECTION_PROPERTY,
+ MASK_PATHS_SEGMENT,
PATH_ATTRIBUTE,
PATHWAYS_ATTRIBUTE,
PIXEL_VALUE_ATTRIBUTE,
@@ -1293,6 +1294,7 @@ def save_activity(self, activity: typing.Union[Activity, dict]):
priority_layers = activity.priority_layers
layer_styles = activity.layer_styles
style_pixel_value = activity.style_pixel_value
+ mask_paths = activity.mask_paths
ncs_pathways = []
for ncs in activity.pathways:
@@ -1300,6 +1302,7 @@ def save_activity(self, activity: typing.Union[Activity, dict]):
activity = layer_component_to_dict(activity)
activity[PRIORITY_LAYERS_SEGMENT] = priority_layers
+ activity[MASK_PATHS_SEGMENT] = mask_paths
activity[PATHWAYS_ATTRIBUTE] = ncs_pathways
activity[STYLE_ATTRIBUTE] = layer_styles
activity[PIXEL_VALUE_ATTRIBUTE] = style_pixel_value
diff --git a/src/cplus_plugin/definitions/constants.py b/src/cplus_plugin/definitions/constants.py
index 8232fb465..3ff2bf229 100644
--- a/src/cplus_plugin/definitions/constants.py
+++ b/src/cplus_plugin/definitions/constants.py
@@ -7,6 +7,7 @@
NCS_PATHWAY_SEGMENT = "ncs_pathways"
NCS_CARBON_SEGMENT = "ncs_carbon"
PRIORITY_LAYERS_SEGMENT = "priority_layers"
+MASK_PATHS_SEGMENT = "mask_paths"
NPV_PRIORITY_LAYERS_SEGMENT = "npv"
COMPARISON_REPORT_SEGMENT = "comparison_reports"
diff --git a/src/cplus_plugin/gui/activity_editor_dialog.py b/src/cplus_plugin/gui/activity_editor_dialog.py
index 8f078b67f..cb6edba48 100644
--- a/src/cplus_plugin/gui/activity_editor_dialog.py
+++ b/src/cplus_plugin/gui/activity_editor_dialog.py
@@ -17,7 +17,7 @@
)
from qgis.gui import QgsGui, QgsMessageBar
-from qgis.PyQt import QtGui, QtWidgets
+from qgis.PyQt import QtCore, QtGui, QtWidgets
from qgis.PyQt.uic import loadUiType
@@ -74,6 +74,7 @@ def __init__(self, parent=None, activity=None, excluded_names=None):
self._edit_mode = False
self._layer = None
+ self._mask_layer = None
self._excluded_names = excluded_names
if excluded_names is None:
@@ -93,6 +94,32 @@ def __init__(self, parent=None, activity=None, excluded_names=None):
# Hide map layer handling
self.layer_gb.setVisible(False)
+ # Mask layers
+ add_icon = FileUtils.get_icon("symbologyAdd.svg")
+ self.btn_add_mask.setIcon(add_icon)
+ self.btn_add_mask.clicked.connect(self._on_add_mask_layer)
+
+ remove_icon = FileUtils.get_icon("symbologyRemove.svg")
+ self.btn_delete_mask.setIcon(remove_icon)
+ self.btn_delete_mask.setEnabled(False)
+ self.btn_delete_mask.clicked.connect(self._on_remove_mask_layer)
+
+ edit_icon = FileUtils.get_icon("mActionToggleEditing.svg")
+ self.btn_edit_mask.setIcon(edit_icon)
+ self.btn_edit_mask.setEnabled(False)
+ self.btn_edit_mask.clicked.connect(self._on_edit_mask_layer)
+
+ if self._activity is not None:
+ mask_paths_list = self._activity.mask_paths
+
+ for mask_path in mask_paths_list or []:
+ if mask_path == "":
+ continue
+ item = QtWidgets.QListWidgetItem()
+ item.setData(QtCore.Qt.DisplayRole, mask_path)
+ self.lst_mask_layers.addItem(item)
+ self.mask_layers_changed()
+
@property
def activity(self) -> Activity:
"""Returns a reference to the activity object.
@@ -186,6 +213,88 @@ def _add_layer_path(self, layer_path: str):
else:
self.cbo_layer.setCurrentIndex(matching_index)
+ def _on_add_mask_layer(self, activated: bool):
+ """Slot raised to add a mask layer."""
+ data_dir = settings_manager.get_value(Settings.LAST_MASK_DIR, default=None)
+
+ if not data_dir:
+ data_dir = os.path.expanduser("~")
+
+ mask_path = self._show_mask_path_selector(data_dir)
+ if not mask_path:
+ return
+
+ item = QtWidgets.QListWidgetItem()
+ item.setData(QtCore.Qt.DisplayRole, mask_path)
+
+ if self.lst_mask_layers.findItems(mask_path, QtCore.Qt.MatchExactly):
+ error_tr = tr("The selected mask layer already exists.")
+ self.message_bar.pushMessage(error_tr, qgis.core.Qgis.MessageLevel.Warning)
+ return
+
+ self.lst_mask_layers.addItem(item)
+ settings_manager.set_value(Settings.LAST_MASK_DIR, os.path.dirname(mask_path))
+
+ self.mask_layers_changed()
+
+ def _on_edit_mask_layer(self, activated: bool):
+ """Slot raised to edit a mask layer."""
+
+ item = self.lst_mask_layers.currentItem()
+ if not item:
+ error_tr = tr("Select a mask layer first.")
+ self.message_bar.pushMessage(error_tr, qgis.core.Qgis.MessageLevel.Warning)
+ return
+ mask_path = self._show_mask_path_selector(item.data(QtCore.Qt.DisplayRole))
+ if not mask_path:
+ return
+
+ if self.lst_mask_layers.findItems(mask_path, QtCore.Qt.MatchExactly):
+ error_tr = tr("The selected mask layer already exists.")
+ self.message_bar.pushMessage(error_tr, qgis.core.Qgis.MessageLevel.Warning)
+ return
+
+ item.setData(QtCore.Qt.DisplayRole, mask_path)
+
+ def _on_remove_mask_layer(self, activated: bool):
+ """Slot raised to remove one or more selected mask layers."""
+ items = self.lst_mask_layers.selectedItems()
+ if not items:
+ error_tr = tr("Select the target mask layer first, before removing it.")
+ self.message_bar.pushMessage(error_tr, qgis.core.Qgis.MessageLevel.Warning)
+ return
+
+ reply = QtWidgets.QMessageBox.warning(
+ self,
+ tr("QGIS CPLUS PLUGIN | Settings"),
+ tr("Remove the selected mask layer(s)?"),
+ QtWidgets.QMessageBox.Yes,
+ QtWidgets.QMessageBox.No,
+ )
+
+ if reply == QtWidgets.QMessageBox.Yes:
+ for item in items:
+ item_row = self.lst_mask_layers.row(item)
+ self.lst_mask_layers.takeItem(item_row)
+
+ self.mask_layers_changed()
+
+ def _show_mask_path_selector(self, layer_dir: str) -> str:
+ """Show file selector dialog for selecting a mask layer."""
+ filter_tr = tr("Shapefiles")
+
+ layer_path, _ = QtWidgets.QFileDialog.getOpenFileName(
+ self,
+ self.tr("Select mask Layer"),
+ layer_dir,
+ f"{filter_tr} (*.shp)",
+ options=QtWidgets.QFileDialog.DontResolveSymlinks,
+ )
+ if not layer_path:
+ return ""
+
+ return layer_path
+
def validate(self) -> bool:
"""Validates if name has been specified.
@@ -266,6 +375,15 @@ def _create_activity(self):
ACTIVITY_LAYER_STYLE_ATTRIBUTE
] = color_ramp_info
+ # Mask layers settings
+ mask_paths = []
+ for row in range(0, self.lst_mask_layers.count()):
+ item = self.lst_mask_layers.item(row)
+ item_path = item.data(QtCore.Qt.DisplayRole)
+ mask_paths.append(item_path)
+
+ self._activity.mask_paths = mask_paths
+
def _get_selected_map_layer(self) -> typing.Union[QgsRasterLayer, None]:
"""Returns the currently selected map layer or None if there is
no item in the combobox.
@@ -358,3 +476,9 @@ def _on_select_file(self, activated: bool):
self._add_layer_path(layer_path)
settings_manager.set_value(Settings.LAST_DATA_DIR, os.path.dirname(layer_path))
+
+ def mask_layers_changed(self):
+ contains_items = self.lst_mask_layers.count() > 0
+
+ self.btn_edit_mask.setEnabled(contains_items)
+ self.btn_delete_mask.setEnabled(contains_items)
diff --git a/src/cplus_plugin/models/base.py b/src/cplus_plugin/models/base.py
index e915d7a7c..91ae2e317 100644
--- a/src/cplus_plugin/models/base.py
+++ b/src/cplus_plugin/models/base.py
@@ -390,6 +390,7 @@ class Activity(LayerModelComponent):
pathways: typing.List[NcsPathway] = dataclasses.field(default_factory=list)
priority_layers: typing.List[typing.Dict] = dataclasses.field(default_factory=list)
layer_styles: dict = dataclasses.field(default_factory=dict)
+ mask_paths: typing.List[str] = dataclasses.field(default_factory=list)
style_pixel_value: int = -1
@classmethod
diff --git a/src/cplus_plugin/models/helpers.py b/src/cplus_plugin/models/helpers.py
index 9792c676e..84dd8ab7e 100644
--- a/src/cplus_plugin/models/helpers.py
+++ b/src/cplus_plugin/models/helpers.py
@@ -37,6 +37,7 @@
DESCRIPTION_ATTRIBUTE,
LAYER_TYPE_ATTRIBUTE,
MANUAL_NPV_ATTRIBUTE,
+ MASK_PATHS_SEGMENT,
NPV_MAPPINGS_ATTRIBUTE,
MAX_VALUE_ATTRIBUTE,
MIN_VALUE_ATTRIBUTE,
@@ -198,6 +199,9 @@ def create_activity(source_dict) -> typing.Union[Activity, None]:
if PRIORITY_LAYERS_SEGMENT in source_dict.keys():
activity.priority_layers = source_dict[PRIORITY_LAYERS_SEGMENT]
+ if MASK_PATHS_SEGMENT in source_dict.keys():
+ activity.mask_paths = source_dict[MASK_PATHS_SEGMENT]
+
# Set style
if STYLE_ATTRIBUTE in source_dict.keys():
activity.layer_styles = source_dict[STYLE_ATTRIBUTE]
diff --git a/src/cplus_plugin/tasks.py b/src/cplus_plugin/tasks.py
index f07c30d2b..62d5fe872 100644
--- a/src/cplus_plugin/tasks.py
+++ b/src/cplus_plugin/tasks.py
@@ -323,6 +323,12 @@ def run(self):
extent_string,
)
+ # Run internal masking of the activities layers
+ self.run_internal_activities_masking(
+ self.analysis_activities,
+ extent_string,
+ )
+
# TODO enable the sieve functionality
sieve_enabled = self.get_settings_value(
Settings.SIEVE_ENABLED, default=False, setting_type=bool
@@ -1269,8 +1275,11 @@ def run_activities_masking(
)
return False
+ # see https://qgis.org/pyqgis/master/core/Qgis.html#qgis.core.Qgis.GeometryType
if Qgis.versionInt() < 33000:
- layer_check = initial_mask_layer.geometryType() == QgsWkbTypes.Polygon
+ layer_check = (
+ initial_mask_layer.geometryType() == QgsWkbTypes.PolygonGeometry
+ )
else:
layer_check = (
initial_mask_layer.geometryType() == Qgis.GeometryType.Polygon
@@ -1375,6 +1384,161 @@ def run_activities_masking(
return True
+ def run_internal_activities_masking(
+ self, activities, extent, temporary_output=False
+ ):
+ """Applies the mask layers into the passed activities
+
+ :param activities: List of the selected activities
+ :type activities: typing.List[Activity]
+
+ :param extent: selected extent from user
+ :type extent: str
+
+ :param temporary_output: Whether to save the processing outputs as temporary
+ files
+ :type temporary_output: bool
+
+ :returns: Whether the task operations was successful
+ :rtype: bool
+ """
+ if self.processing_cancelled:
+ # Will not proceed if processing has been cancelled by the user
+ return False
+
+ self.set_status_message(
+ tr("Masking activities using their respective mask layers.")
+ )
+
+ try:
+ for activity in activities:
+ masking_layers = activity.mask_paths
+
+ if len(masking_layers) < 1:
+ return False
+ if len(masking_layers) > 1:
+ initial_mask_layer = self.merge_vector_layers(masking_layers)
+ else:
+ mask_layer_path = masking_layers[0]
+ initial_mask_layer = QgsVectorLayer(mask_layer_path, "mask", "ogr")
+
+ if not initial_mask_layer.isValid():
+ self.log_message(
+ f"Skipping activities masking "
+ f"using layer {mask_layer_path}, not a valid layer."
+ )
+ return False
+
+ # see https://qgis.org/pyqgis/master/core/Qgis.html#qgis.core.Qgis.GeometryType
+ if Qgis.versionInt() < 33000:
+ layer_check = (
+ initial_mask_layer.geometryType() == QgsWkbTypes.PolygonGeometry
+ )
+ else:
+ layer_check = (
+ initial_mask_layer.geometryType() == Qgis.GeometryType.Polygon
+ )
+
+ if not layer_check:
+ self.log_message(
+ f"Skipping activities masking "
+ f"using layer {mask_layer_path}, not a polygon layer."
+ )
+ return False
+
+ extent_layer = self.layer_extent(extent)
+ mask_layer = self.mask_layer_difference(
+ initial_mask_layer, extent_layer
+ )
+
+ if isinstance(mask_layer, str):
+ mask_layer = QgsVectorLayer(mask_layer, "ogr")
+
+ if not mask_layer.isValid():
+ self.log_message(
+ f"Skipping activities masking "
+ f"the created difference mask layer {mask_layer.source()},"
+ f" not a valid layer."
+ )
+ return False
+ if activity.path is None or activity.path == "":
+ if not self.processing_cancelled:
+ self.set_info_message(
+ tr(
+ f"Problem when masking activities, "
+ f"there is no map layer for the activity {activity.name}"
+ ),
+ level=Qgis.Critical,
+ )
+ self.log_message(
+ f"Problem when masking activities, "
+ f"there is no map layer for the activity {activity.name}"
+ )
+ else:
+ # If the user cancelled the processing
+ self.set_info_message(
+ tr(f"Processing has been cancelled by the user."),
+ level=Qgis.Critical,
+ )
+ self.log_message(f"Processing has been cancelled by the user.")
+
+ return False
+
+ masked_activities_directory = os.path.join(
+ self.scenario_directory, "final_masked_activities"
+ )
+ FileUtils.create_new_dir(masked_activities_directory)
+ file_name = clean_filename(activity.name.replace(" ", "_"))
+
+ output_file = os.path.join(
+ masked_activities_directory,
+ f"{file_name}_{str(uuid.uuid4())[:4]}.tif",
+ )
+
+ output = (
+ QgsProcessing.TEMPORARY_OUTPUT if temporary_output else output_file
+ )
+
+ activity_layer = QgsRasterLayer(activity.path, "activity_layer")
+
+ # Actual processing calculation
+ alg_params = {
+ "INPUT": activity.path,
+ "MASK": mask_layer,
+ "SOURCE_CRS": activity_layer.crs(),
+ "DESTINATION_CRS": activity_layer.crs(),
+ "TARGET_EXTENT": extent,
+ "OUTPUT": output,
+ "NO_DATA": -9999,
+ }
+
+ self.log_message(
+ f"Used parameters for masking the activities: {alg_params} \n"
+ )
+
+ feedback = QgsProcessingFeedback()
+
+ feedback.progressChanged.connect(self.update_progress)
+
+ if self.processing_cancelled:
+ return False
+
+ results = processing.run(
+ "gdal:cliprasterbymasklayer",
+ alg_params,
+ context=self.processing_context,
+ feedback=self.feedback,
+ )
+ activity.path = results["OUTPUT"]
+
+ except Exception as e:
+ self.log_message(f"Problem masking activities layers, {e} \n")
+ self.cancel_task(e)
+
+ return False
+
+ return True
+
def merge_vector_layers(self, layers):
"""Merges the passed vector layers into a single layer
diff --git a/src/cplus_plugin/ui/activity_editor_dialog.ui b/src/cplus_plugin/ui/activity_editor_dialog.ui
index 9a812ff23..bdd38c7ea 100644
--- a/src/cplus_plugin/ui/activity_editor_dialog.ui
+++ b/src/cplus_plugin/ui/activity_editor_dialog.ui
@@ -6,8 +6,8 @@
0
0
- 436
- 456
+ 477
+ 588
@@ -20,6 +20,32 @@
Activity Editor
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 198
+ 20
+
+
+
+
+ -
+
+
+ Description
+
+
+
+ -
+
+
+ -
+
+
-
@@ -85,53 +111,26 @@
- -
-
-
- -
-
+
-
+
- Name
-
-
-
- -
-
-
- 200
+ Help
- -
-
+
-
+
- Description
+ Name
- -
-
-
- -
+
-
Style
-
-
-
-
- Output activity layer
-
-
-
- -
-
-
- Scenario layer
-
-
-
-
@@ -170,10 +169,47 @@
+ -
+
+
+ Output activity layer
+
+
+
+ -
+
+
+ Scenario layer
+
+
+
- -
+
-
+
+
+
+ 30
+ 0
+
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ 200
+
+
+
+ -
@@ -197,13 +233,6 @@
false
-
-
-
-
- false
-
-
-
-
@@ -214,43 +243,67 @@
+ -
+
+
+ false
+
+
+
- -
-
-
- Help
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 198
- 20
-
-
-
-
- -
-
-
-
- 30
- 0
-
-
-
- Qt::Horizontal
+
-
+
+
+ Activity Mask
-
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+ true
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ QAbstractItemView::MultiSelection
+
+
+
+
diff --git a/test/data/mask/layers/test_mask_1.cpg b/test/data/mask/layers/test_mask_1.cpg
new file mode 100644
index 000000000..3ad133c04
--- /dev/null
+++ b/test/data/mask/layers/test_mask_1.cpg
@@ -0,0 +1 @@
+UTF-8
\ No newline at end of file
diff --git a/test/data/mask/layers/test_mask_1.dbf b/test/data/mask/layers/test_mask_1.dbf
new file mode 100644
index 000000000..03910b828
Binary files /dev/null and b/test/data/mask/layers/test_mask_1.dbf differ
diff --git a/test/data/mask/layers/test_mask_1.prj b/test/data/mask/layers/test_mask_1.prj
new file mode 100644
index 000000000..f45cbadf0
--- /dev/null
+++ b/test/data/mask/layers/test_mask_1.prj
@@ -0,0 +1 @@
+GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
\ No newline at end of file
diff --git a/test/data/mask/layers/test_mask_1.qix b/test/data/mask/layers/test_mask_1.qix
new file mode 100644
index 000000000..588b9f49e
Binary files /dev/null and b/test/data/mask/layers/test_mask_1.qix differ
diff --git a/test/data/mask/layers/test_mask_1.qmd b/test/data/mask/layers/test_mask_1.qmd
new file mode 100644
index 000000000..ae78cd98c
--- /dev/null
+++ b/test/data/mask/layers/test_mask_1.qmd
@@ -0,0 +1,27 @@
+
+
+
+
+
+ dataset
+
+
+
+
+
+
+
+
+ GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]]
+ +proj=longlat +datum=WGS84 +no_defs
+ 3452
+ 4326
+ EPSG:4326
+ WGS 84
+ longlat
+ EPSG:7030
+ true
+
+
+
+
diff --git a/test/data/mask/layers/test_mask_1.shp b/test/data/mask/layers/test_mask_1.shp
new file mode 100644
index 000000000..e903c909e
Binary files /dev/null and b/test/data/mask/layers/test_mask_1.shp differ
diff --git a/test/data/mask/layers/test_mask_1.shx b/test/data/mask/layers/test_mask_1.shx
new file mode 100644
index 000000000..b996e1ce2
Binary files /dev/null and b/test/data/mask/layers/test_mask_1.shx differ
diff --git a/test/test_scenario_tasks.py b/test/test_scenario_tasks.py
index 62a7849d8..d78b76968 100644
--- a/test/test_scenario_tasks.py
+++ b/test/test_scenario_tasks.py
@@ -12,7 +12,7 @@
from processing.core.Processing import Processing
-from qgis.core import QgsRasterLayer
+from qgis.core import Qgis, QgsRasterLayer, QgsVectorLayer, QgsWkbTypes
from cplus_plugin.conf import settings_manager, Settings
@@ -618,5 +618,101 @@ def test_scenario_activities_weighting(self):
self.assertEqual(stat.minimumValue, 5.0)
self.assertEqual(stat.maximumValue, 27.0)
+ def test_scenario_activities_masking(self):
+ activities_layer_directory = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), "data", "activities", "layers"
+ )
+
+ mask_layers_directory = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), "data", "mask", "layers"
+ )
+
+ activity_layer_path_1 = os.path.join(
+ activities_layer_directory, "test_activity_1.tif"
+ )
+ mask_layer_path_1 = os.path.join(mask_layers_directory, "test_mask_1.shp")
+
+ test_activity = Activity(
+ uuid=uuid.uuid4(),
+ name="test_activity",
+ description="test_description",
+ pathways=[],
+ path=activity_layer_path_1,
+ mask_paths=[mask_layer_path_1],
+ )
+
+ settings_manager.save_activity(test_activity)
+
+ activity_layer = QgsRasterLayer(test_activity.path, test_activity.name)
+
+ test_extent = activity_layer.extent()
+
+ scenario = Scenario(
+ uuid=uuid.uuid4(),
+ name="Scenario",
+ description="Scenario description",
+ activities=[test_activity],
+ extent=test_extent,
+ weighted_activities=[],
+ priority_layer_groups=[],
+ )
+
+ analysis_task = ScenarioAnalysisTask(
+ "test_scenario_activities_masking",
+ "test_scenario_activities_masking_description",
+ [test_activity],
+ [],
+ test_extent,
+ scenario,
+ )
+
+ extent_string = (
+ f"{test_extent.xMinimum()},{test_extent.xMaximum()},"
+ f"{test_extent.yMinimum()},{test_extent.yMaximum()}"
+ f" [{activity_layer.crs().authid()}]"
+ )
+
+ base_dir = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data",
+ "activities",
+ )
+
+ scenario_directory = os.path.join(
+ f"{base_dir}",
+ f'scenario_{datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")}'
+ f"_{str(uuid.uuid4())[:4]}",
+ )
+
+ analysis_task.scenario_directory = scenario_directory
+
+ settings_manager.set_value(Settings.BASE_DIR, base_dir)
+
+ # Before masking, check if the activity layer stats are correct
+ activity_layer = QgsRasterLayer(test_activity.path, test_activity.name)
+ first_layer_stat = activity_layer.dataProvider().bandStatistics(1)
+
+ self.assertEqual(first_layer_stat.minimumValue, 1.0)
+ self.assertEqual(first_layer_stat.maximumValue, 19.0)
+
+ results = analysis_task.run_internal_activities_masking(
+ [test_activity], extent_string, temporary_output=True
+ )
+
+ self.assertTrue(results)
+
+ self.assertIsInstance(results, bool)
+ self.assertTrue(results)
+
+ self.assertIsNotNone(test_activity.path)
+
+ result_layer = QgsRasterLayer(test_activity.path, test_activity.name)
+
+ result_stat = result_layer.dataProvider().bandStatistics(1)
+ self.assertEqual(result_stat.minimumValue, 1.0)
+ self.assertEqual(result_stat.maximumValue, 18.0)
+
+ self.assertTrue(result_layer.isValid())
+
def tearDown(self):
pass