Skip to content

Commit

Permalink
Merge pull request #649 from kartoza/timlinux/issue620
Browse files Browse the repository at this point in the history
Fix safety widget
  • Loading branch information
timlinux authored Dec 1, 2024
2 parents f00fecd + e633c76 commit e105901
Show file tree
Hide file tree
Showing 15 changed files with 152 additions and 58 deletions.
31 changes: 27 additions & 4 deletions geest/core/json_tree_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,10 @@ def getStatus(self):
analysis_mode = data.get("analysis_mode", "")
qgis_layer_source_key = analysis_mode.replace("use_", "") + "_layer_source"
qgis_layer_shapefile_key = analysis_mode.replace("use_", "") + "_shapefile"
qgis_layer_raster_key = analysis_mode.replace("use_", "") + "_raster"
status = ""

if "Workflow Completed" in data.get("result", ""):
return "Completed successfully"
# First check if the item weighting is 0, or its parent factor is zero
# If so, return "Excluded from analysis"
if self.isIndicator():
Expand Down Expand Up @@ -269,10 +271,17 @@ def getStatus(self):
return "Excluded from analysis"
# If the sum of the indicator weightings is zero, return "Excluded from analysis"
weight_sum = 0
unconfigured_child_count = 0
for child in self.childItems:
weight_sum += float(child.attribute("factor_weighting", 0.0))
if child.getStatus() == "Not configured (optional)":
unconfigured_child_count += 1
if child.getStatus() == "Required and not configured":
unconfigured_child_count += 1
if not weight_sum:
return "Excluded from analysis"
if unconfigured_child_count:
return "Required and not configured"
#
# Note we avoid infinite recursion by NOT doing the checks below using the getStatus
# method of the parent.
Expand Down Expand Up @@ -323,12 +332,27 @@ def getStatus(self):
):
return "Not configured (optional)"
if (
# Test for algs requiring vector inputs
self.isIndicator()
and analysis_mode != "use_default_index_score"
and analysis_mode
not in ["use_default_index_score", "use_environmental_hazards"]
and not data.get(qgis_layer_source_key, False)
and not data.get(qgis_layer_shapefile_key, False)
):
return "Not configured (optional)"
if (
# Test for algs requiring raster inputs
self.isIndicator()
and analysis_mode not in ["use_default_index_score"]
and analysis_mode in ["use_environmental_hazards"]
and not data.get(qgis_layer_source_key, False)
and not data.get(qgis_layer_raster_key, False)
):
# log_message(f"Indicator {data.get('id')} is missing a raster input")
# log_message(f"analysis_mode in use_default_index_score, use_environmental_hazards: {analysis_mode in ['use_default_index_score', 'use_environmental_hazards']}")
# log_message(f"qgis_layer_source_key: {qgis_layer_source_key}: {data.get(qgis_layer_source_key, False)}")
# log_message(f"qgis_layer_raster_key: {qgis_layer_raster_key}: {data.get(qgis_layer_raster_key, False)}")
return "Not configured (optional)"
if "Not Run" in data.get("result", "") and not data.get("result_file", ""):
return "Configured, not run"
if not data.get("result", False):
Expand All @@ -339,8 +363,7 @@ def getStatus(self):
"result_file", ""
):
return "Workflow failed"
if "Workflow Completed" in data.get("result", ""):
return "Completed successfully"

return "WRITE TOOL TIP"

except Exception as e:
Expand Down
10 changes: 4 additions & 6 deletions geest/core/workflows/raster_reclassification_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,18 @@ def __init__(
else:
self.range_boundaries = 0 # default value for range boundaries

layer_name = self.attributes.get("use_environmental_hazards_raster", None)
layer_name = self.attributes.get("environmental_hazards_raster", None)

if not layer_name:
log_message(
"Invalid layer found in use_environmental_hazards_raster, trying use_environmental_hazards_layer_source.",
"Invalid layer found in environmental_hazards_raster, trying environmental_hazards_layer_source.",
tag="Geest",
level=Qgis.Warning,
)
layer_name = self.attributes.get(
"use_environmental_hazards_layer_source", None
)
layer_name = self.attributes.get("environmental_hazards_layer_source", None)
if not layer_name:
log_message(
"No layer found in use_environmental_hazards_layer_source.",
"No layer found in environmental_hazards_layer_source.",
tag="Geest",
level=Qgis.Warning,
)
Expand Down
8 changes: 4 additions & 4 deletions geest/core/workflows/safety_raster_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,18 @@ def __init__(
item, cell_size_m, feedback, context
) # ⭐️ Item is a reference - whatever you change in this item will directly update the tree
self.workflow_name = "use_nighttime_lights"
layer_name = self.attributes.get("use_nighttime_lights_raster", None)
layer_name = self.attributes.get("nighttime_lights_raster", None)

if not layer_name:
log_message(
"Invalid raster found in use_nighttime_lights_raster, trying use_nighttime_lights_layer_source.",
"Invalid raster found in nighttime_lights_raster, trying nighttime_lights_layer_source.",
tag="Geest",
level=Qgis.Warning,
)
layer_name = self.attributes.get("use_nighttime_lights_layer_source", None)
layer_name = self.attributes.get("nighttime_lights_layer_source", None)
if not layer_name:
log_message(
"No points layer found in use_nighttime_lights_layer_source.",
"No points layer found in nighttime_lights_layer_source.",
tag="Geest",
level=Qgis.Warning,
)
Expand Down
12 changes: 7 additions & 5 deletions geest/gui/datasource_widget_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,12 @@ def create_widget(
widget_key=cleaned_key, attributes=attributes
)
if widget_key == "use_csv_point_per_cell" and value == 1:
return CsvDataSourceWidget(widget_key=widget_key, attributes=attributes)
return CsvDataSourceWidget(
cleaned_key=widget_key, attributes=attributes
)
if widget_key == "use_csv_to_point_layer" and value == 1:
return AcledCsvDataSourceWidget(
widget_key=widget_key, attributes=attributes
widget_key=cleaned_key, attributes=attributes
)
if widget_key == "use_classify_polygon_into_classes" and value == 1:
return VectorAndFieldDataSourceWidget(
Expand All @@ -79,15 +81,15 @@ def create_widget(
)
if widget_key == "use_nighttime_lights" and value == 1:
return RasterDataSourceWidget(
widget_key=widget_key, attributes=attributes
widget_key=cleaned_key, attributes=attributes
)
if widget_key == "use_environmental_hazards" and value == 1:
return RasterDataSourceWidget(
widget_key=widget_key, attributes=attributes
widget_key=cleaned_key, attributes=attributes
)
if widget_key == "use_street_lights" and value == 1:
return VectorDataSourceWidget(
widget_key=widget_key, attributes=attributes
widget_key=cleaned_key, attributes=attributes
)
else:
log_message(
Expand Down
14 changes: 9 additions & 5 deletions geest/gui/indicator_configuration_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,23 @@ def __init__(self, attributes: dict) -> None:
self.create_radio_buttons(attributes)
except Exception as e:
log_message(f"Error in create_radio_buttons: {e}", level=Qgis.Critical)
import traceback

log_message(traceback.format_exc(), level=Qgis.Critical)

self.setLayout(self.layout)

def create_radio_buttons(self, attributes: dict) -> None:
"""
Uses the factory to create radio buttons from attributes dictionary.
"""
# make a deep copy of the dictionary in case it changes while we
# are using it
attributes = attributes.copy()
analysis_mode = attributes.get("analysis_mode", "")

for key, value in attributes.items():
analysis_mode = attributes.get("analysis_mode", "")
# We iterate over a list to defend against changes to the dictionary
# See issue #620. This is a workaround until we can refactor the code
# The issue is that the dictionary is being modified while we are iterating over it
# by the safety_polygon_configuration_widget.py file I think.
for key, value in list(attributes.items()):
radio_button_widget = CombinedWidgetFactory.create_radio_button(
key, value, attributes
)
Expand Down
27 changes: 25 additions & 2 deletions geest/gui/panels/tree_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,8 @@ def show_attributes(self, item):
table.horizontalHeader().setSectionResizeMode(
1, QHeaderView.Stretch
) # Only the second column stretches

# Track if "error_file" exists
error_file_content = None
# Populate the table with the sorted data
for row, (key, value) in enumerate(sorted_data.items()):
key_item = QTableWidgetItem(key)
Expand All @@ -637,6 +638,15 @@ def show_attributes(self, item):
value_item = QTableWidgetItem(str(value))
value_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
table.setItem(row, 1, value_item)
# Check if this row contains "error_file"
if key == "error_file" and isinstance(value, str):
try:
with open(value, "r") as file:
error_file_content = file.read()
except (OSError, IOError):
error_file_content = f"Unable to read file: {value}"
except Exception as e:
error_file_content = f"Error reading file: {e}"

layout.addWidget(table)

Expand All @@ -658,6 +668,13 @@ def show_attributes(self, item):
copy_button.clicked.connect(lambda: self.copy_to_clipboard_as_markdown(table))
button_layout.addWidget(copy_button)

# Show Error File button
if error_file_content is not None:
show_error_file_button = QPushButton("Show Error File")
button_layout.addWidget(show_error_file_button)
show_error_file_button.clicked.connect(
lambda: self.show_error_file_popup(error_file_content)
)
layout.addLayout(button_layout)

# Enable custom context menu for the table
Expand All @@ -668,7 +685,13 @@ def show_attributes(self, item):

dialog.exec_()

log_message("----------------------------")
def show_error_file_popup(self, error_file_content):
"""Show a popup message with the contents of the error file."""
msg_box = QMessageBox()
msg_box.setWindowTitle("Error File Contents")
msg_box.setText(error_file_content)
msg_box.setStandardButtons(QMessageBox.Ok)
msg_box.exec_()

def copy_to_clipboard_as_markdown(self, table: QTableWidget):
"""Copy the table content as Markdown to the clipboard."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def add_internal_widgets(self) -> None:
"""
try:
self.main_layout = QVBoxLayout()
self.widget_key = "use_csv_to_point_layer"
self.widget_key = "csv_to_point_layer"

# impact distance input
self.buffer_distance_layout = QHBoxLayout()
Expand Down
26 changes: 13 additions & 13 deletions geest/gui/widgets/combined_widgets/multi_buffer_distances_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def add_internal_widgets(self) -> None:
"""
try:
self.main_layout = QVBoxLayout()

self.widget_key = "multi_buffer_point"
# Point Layer Combobox - Filtered to point layers
self.point_layer_label = QLabel(
"Point Layer - shapefile will have preference"
Expand All @@ -38,12 +38,12 @@ def add_internal_widgets(self) -> None:
self.main_layout.addWidget(self.layer_combo)

# Set the selected QgsVectorLayer in QgsMapLayerComboBox
layer_id = self.attributes.get("multi_buffer_point_layer_id", None)
layer_id = self.attributes.get(f"{self.widget_key}_layer_id", None)
if layer_id:
layer = QgsProject.instance().mapLayer(layer_id)
if layer:
self.layer_combo.setLayer(layer)
layer_id = self.attributes.get("multi_buffer_point_layer_id")
layer_id = self.attributes.get(f"{self.widget_key}_layer_id")
layer = QgsProject.instance().mapLayer(layer_id)

if layer and isinstance(layer, QgsVectorLayer):
Expand All @@ -55,9 +55,9 @@ def add_internal_widgets(self) -> None:
self.shapefile_button = QToolButton()
self.shapefile_button.setText("...")
self.shapefile_button.clicked.connect(self.select_shapefile)
if self.attributes.get("multi_buffer_point_shapefile", False):
if self.attributes.get(f"{self.widget_key}_shapefile", False):
self.shapefile_line_edit.setText(
self.attributes["multi_buffer_point_shapefile"]
self.attributes[f"{self.widget_key}_shapefile"]
)
self.shapefile_layout.addWidget(self.shapefile_line_edit)
self.shapefile_layout.addWidget(self.shapefile_button)
Expand Down Expand Up @@ -157,20 +157,20 @@ def get_data(self) -> dict:

layer = self.layer_combo.currentLayer()
if not layer:
self.attributes["multi_buffer_point_layer"] = None
self.attributes[f"{self.widget_key}_layer"] = None
else:
self.attributes["multi_buffer_point_layer_name"] = layer.name()
self.attributes["multi_buffer_point_layer_source"] = layer.source()
self.attributes["multi_buffer_point_layer_provider_type"] = (
self.attributes[f"{self.widget_key}_layer_name"] = layer.name()
self.attributes[f"{self.widget_key}_layer_source"] = layer.source()
self.attributes[f"{self.widget_key}_layer_provider_type"] = (
layer.providerType()
)
self.attributes["multi_buffer_point_layer_crs"] = (
self.attributes[f"{self.widget_key}_layer_crs"] = (
layer.crs().authid()
) # Coordinate Reference System
self.attributes["multi_buffer_point_layer_wkb_type"] = (
self.attributes[f"{self.widget_key}_layer_wkb_type"] = (
layer.wkbType()
) # Geometry type (e.g., Point, Polygon)
self.attributes["multi_buffer_point_layer_id"] = (
self.attributes[f"{self.widget_key}_layer_id"] = (
layer.id()
) # Unique ID of the layer

Expand All @@ -185,7 +185,7 @@ def get_data(self) -> dict:
self.attributes["multi_buffer_travel_units"] = "Time"

self.attributes["multi_buffer_travel_distances"] = self.increments_input.text()
self.attributes["multi_buffer_point_shapefile"] = (
self.attributes[f"{self.widget_key}_shapefile"] = (
self.shapefile_line_edit.text()
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from qgis.core import (
QgsMapLayerProxyModel,
QgsProject,
Qgis,
)
from qgis.PyQt.QtCore import QSettings
from .base_indicator_widget import BaseIndicatorWidget
Expand Down Expand Up @@ -39,7 +40,7 @@ def add_internal_widgets(self) -> None:
"""
try:
self.main_layout = QVBoxLayout()
self.widget_key = "use_environmental_hazards"
self.widget_key = "environmental_hazards"
self.settings = QSettings()

# Raster Layer Section
Expand Down
3 changes: 2 additions & 1 deletion geest/gui/widgets/combined_widgets/safety_raster_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from qgis.core import (
QgsMapLayerProxyModel,
QgsProject,
Qgis,
)
from qgis.PyQt.QtCore import QSettings
from geest.utilities import log_message
Expand Down Expand Up @@ -39,7 +40,7 @@ def add_internal_widgets(self) -> None:
"""
try:
self.main_layout = QVBoxLayout()
self.widget_key = "use_nighttime_lights"
self.widget_key = "nighttime_lights"
self.settings = QSettings()

# Raster Layer Section
Expand Down
Loading

0 comments on commit e105901

Please sign in to comment.