Skip to content

Commit

Permalink
Merge pull request #561 from Samweli/activity_masking
Browse files Browse the repository at this point in the history
Support for activity layer masking
  • Loading branch information
Samweli authored Nov 13, 2024
2 parents 97e4043 + 7405b21 commit ca65a19
Show file tree
Hide file tree
Showing 15 changed files with 554 additions and 79 deletions.
3 changes: 3 additions & 0 deletions src/cplus_plugin/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
NCS_CARBON_SEGMENT,
NCS_PATHWAY_SEGMENT,
NPV_COLLECTION_PROPERTY,
MASK_PATHS_SEGMENT,
PATH_ATTRIBUTE,
PATHWAYS_ATTRIBUTE,
PIXEL_VALUE_ATTRIBUTE,
Expand Down Expand Up @@ -1293,13 +1294,15 @@ 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:
ncs_pathways.append(str(ncs.uuid))

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
Expand Down
1 change: 1 addition & 0 deletions src/cplus_plugin/definitions/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
126 changes: 125 additions & 1 deletion src/cplus_plugin/gui/activity_editor_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
1 change: 1 addition & 0 deletions src/cplus_plugin/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/cplus_plugin/models/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
DESCRIPTION_ATTRIBUTE,
LAYER_TYPE_ATTRIBUTE,
MANUAL_NPV_ATTRIBUTE,
MASK_PATHS_SEGMENT,
NPV_MAPPINGS_ATTRIBUTE,
MAX_VALUE_ATTRIBUTE,
MIN_VALUE_ATTRIBUTE,
Expand Down Expand Up @@ -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]
Expand Down
Loading

0 comments on commit ca65a19

Please sign in to comment.