Skip to content

Commit

Permalink
Implementation details for dimension and factor aggregation weighting…
Browse files Browse the repository at this point in the history
… logic
  • Loading branch information
timlinux committed Nov 22, 2024
1 parent 8594408 commit b2ed899
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 34 deletions.
8 changes: 8 additions & 0 deletions geest/core/json_tree_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,14 @@ def getStatus(self):
if not data.get("required", False):
if not float(data.get("dimension_weighting", 0.0)):
return "Excluded from analysis"
if self.isDimension():
# if the sum of the factor weightings is 0, return "Excluded from analysis"
weight_sum = 0
for child in self.childItems:
weight_sum += float(child.attribute("factor_weighting", 0.0))
if not weight_sum:
return "Excluded from analysis"

if "Error" in data.get("result", ""):
return "Workflow failed"
if "Failed" in data.get("result", ""):
Expand Down
48 changes: 34 additions & 14 deletions geest/gui/dialogs/dimension_aggregation_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def __init__(
item = self.tree_item.getItemByGuid(guid)
attributes = item.attributes()
factor_id = attributes.get("name")
dimension_weighting = attributes.get("dimension_weighting", 0)
dimension_weighting = float(attributes.get("dimension_weighting", 0.0))
default_dimension_weighting = attributes.get(
"default_dimension_weighting", 0
)
Expand All @@ -160,14 +160,14 @@ def __init__(
weighting_item = QDoubleSpinBox(self)
weighting_item.setRange(0.0, 1.0)
weighting_item.setDecimals(4)
weighting_item.setValue(float(dimension_weighting))
weighting_item.setValue(dimension_weighting)
weighting_item.setSingleStep(0.01)
weighting_item.valueChanged.connect(self.validate_weightings)
self.table.setCellWidget(row, 1, weighting_item)
self.weightings[guid] = weighting_item

# Use checkboxes
checkbox_widget = self.create_checkbox_widget(row)
checkbox_widget = self.create_checkbox_widget(row, dimension_weighting)
self.table.setCellWidget(row, 2, checkbox_widget)
if factor_required == 1:
checkbox_widget.setEnabled(False)
Expand Down Expand Up @@ -235,12 +235,15 @@ def toggle_guid_column(self):
self.guid_column_visible = not self.guid_column_visible
self.table.setColumnHidden(4, not self.guid_column_visible)

def create_checkbox_widget(self, row: int) -> QWidget:
def create_checkbox_widget(self, row: int, dimension_weighting: float) -> QWidget:
"""
Create a QWidget containing a QCheckBox for a specific row and center it.
"""
checkbox = QCheckBox()
checkbox.setChecked(True) # Initially checked
if dimension_weighting > 0:
checkbox.setChecked(True) # Initially checked
else:
checkbox.setChecked(False)
checkbox.stateChanged.connect(
lambda state, r=row: self.toggle_row_widgets(r, state)
)
Expand Down Expand Up @@ -293,7 +296,12 @@ def auto_calculate_weightings(self):
log_message("No enabled rows found, skipping auto-calculation")
return # No enabled rows, avoid division by zero

equal_weighting = 1.0 / len(enabled_rows) # Divide equally among enabled rows
if len(enabled_rows) == 0:
equal_weighting = 0.0
else:
equal_weighting = 1.0 / len(
enabled_rows
) # Divide equally among enabled rows

# Set the weighting for each enabled row
for row in enabled_rows:
Expand All @@ -305,6 +313,7 @@ def auto_calculate_weightings(self):
log_message(f"Setting zero weighting for row: {row}")
widget = self.table.cellWidget(row, 1) # Assuming weight is in column 1
widget.setValue(0)
self.validate_weightings()

def is_checkbox_checked(self, row: int) -> bool:
"""
Expand Down Expand Up @@ -333,7 +342,7 @@ def get_checkbox_in_row(self, row: int) -> QCheckBox:
return checkbox
return None

def assignWeightings(self):
def saveWeightingsToModel(self):
"""Assign new weightings to the dimensions's factors."""
for factor_guid, spin_box in self.weightings.items():
try:
Expand All @@ -353,12 +362,23 @@ def update_preview(self):

def validate_weightings(self):
"""Validate weightings to ensure they sum to 1 and are within range."""
total_weighting = sum(
float(spin_box.value() or 0) for spin_box in self.weightings.values()
)
valid_sum = (
abs(total_weighting - 1.0) < 0.001
) # Allow slight floating-point tolerance
try:
total_weighting = sum(
float(spin_box.value() or 0) for spin_box in self.weightings.values()
)
valid_sum = (
abs(total_weighting - 1.0) < 0.001
) # Allow slight floating-point tolerance
except ValueError:
valid_sum = False

# In the case that all rows are disabled, the sum is valid
enabled_rows = [
row for row in range(self.table.rowCount()) if self.is_checkbox_checked(row)
]
enabled_rows_count = len(enabled_rows)
if enabled_rows_count == 0:
valid_sum = True

# Update button state and font color for validation
for spin_box in self.weightings.values():
Expand All @@ -372,7 +392,7 @@ def validate_weightings(self):

def accept_changes(self):
"""Handle the OK button by applying changes and closing the dialog."""
self.assignWeightings() # Assign weightings when changes are accepted
self.saveWeightingsToModel() # Assign weightings when changes are accepted
if self.editing:
updated_data = self.dimension_data
updated_data["description"] = self.text_edit_left.toPlainText()
Expand Down
41 changes: 23 additions & 18 deletions geest/gui/dialogs/factor_aggregation_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,12 @@ def switch_page(self):
current_index = self.stacked_widget.currentIndex()
self.stacked_widget.setCurrentIndex(1 - current_index) # Toggle between 0 and 1

def create_checkbox_widget(self, row: int) -> QWidget:
def create_checkbox_widget(self, row: int, weighting_value: float) -> QWidget:
checkbox = QCheckBox()
checkbox.setChecked(True)
if weighting_value > 0:
checkbox.setChecked(True)
else:
checkbox.setChecked(False)
checkbox.stateChanged.connect(
lambda state, r=row: self.toggle_row_widgets(r, state)
)
Expand Down Expand Up @@ -245,18 +248,18 @@ def populate_table(self):
self.table.setItem(row, 1, name_item)

# Weighting
weighting_value = attributes.get("factor_weighting", 0)
weighting_value = float(attributes.get("factor_weighting", 0.0))
weighting_item = QDoubleSpinBox()
weighting_item.setRange(0.0, 1.0)
weighting_item.setDecimals(4)
weighting_item.setSingleStep(0.01)
weighting_item.setValue(float(weighting_value))
weighting_item.setValue(weighting_value)
weighting_item.valueChanged.connect(self.validate_weightings)
self.table.setCellWidget(row, 2, weighting_item)
self.weightings[guid] = weighting_item

# Use (Checkbox)
checkbox_widget = self.create_checkbox_widget(row)
checkbox_widget = self.create_checkbox_widget(row, weighting_value)
self.table.setCellWidget(row, 3, checkbox_widget)

# GUID
Expand Down Expand Up @@ -292,15 +295,18 @@ def auto_calculate_weightings(self):
row for row in range(self.table.rowCount()) if self.is_checkbox_checked(row)
]
if not enabled_rows:
return
equal_weighting = 1.0 / len(enabled_rows)
equal_weighting = 0.0
else:
equal_weighting = 1.0 / len(enabled_rows)

for row in enabled_rows:
widget = self.table.cellWidget(row, 2) # Weight column
widget.setValue(equal_weighting)
for row in range(self.table.rowCount()):
if row not in enabled_rows:
widget = self.table.cellWidget(row, 2)
widget.setValue(0)
self.validate_weightings()

def is_checkbox_checked(self, row: int) -> bool:
checkbox = self.get_checkbox_in_row(row)
Expand All @@ -316,7 +322,7 @@ def get_checkbox_in_row(self, row: int) -> QCheckBox:
return checkbox
return None

def assignWeightings(self):
def saveWeightingsToModel(self):
"""Assign new weightings to the factor's indicators."""
for indicator_guid, spin_box in self.weightings.items():
try:
Expand All @@ -332,18 +338,9 @@ def assignWeightings(self):

log_message(traceback.format_exc(), tag="Geest", level=Qgis.Warning)

def validate_weightings(self):
total_weighting = sum(
float(spin_box.value()) for spin_box in self.weightings.values()
)
valid_sum = abs(total_weighting - 1.0) < 0.001
for spin_box in self.weightings.values():
spin_box.setStyleSheet("color: black;" if valid_sum else "color: red;")
self.button_box.button(QDialogButtonBox.Ok).setEnabled(valid_sum)

def accept_changes(self):
"""Handle the OK button by applying changes and closing the dialog."""
self.assignWeightings()
self.saveWeightingsToModel()
if self.editing:
updated_data = self.factor_data
updated_data["description"] = self.text_edit_left.toPlainText()
Expand All @@ -367,6 +364,14 @@ def validate_weightings(self):
except ValueError:
valid_sum = False

# In the case that all rows are disabled, the sum is valid
enabled_rows = [
row for row in range(self.table.rowCount()) if self.is_checkbox_checked(row)
]
enabled_rows_count = len(enabled_rows)
if enabled_rows_count == 0:
valid_sum = True

# Update button state and cell highlighting
for spin_box in self.weightings.values():
if valid_sum:
Expand Down
4 changes: 2 additions & 2 deletions geest/gui/panels/tree_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,7 @@ def edit_dimension_aggregation(self, dimension_item):
dimension_name, dimension_data, dimension_item, editing=editing, parent=self
)
if dialog.exec_(): # If OK was clicked
dialog.assignWeightings()
dialog.saveWeightingsToModel()
self.save_json_to_working_directory() # Save changes to the JSON if necessary

def edit_factor_aggregation(self, factor_item):
Expand All @@ -830,7 +830,7 @@ def edit_factor_aggregation(self, factor_item):
factor_name, factor_data, factor_item, editing=editing, parent=self
)
if dialog.exec_(): # If OK was clicked
dialog.assignWeightings()
dialog.saveWeightingsToModel()
self.save_json_to_working_directory() # Save changes to the JSON if necessary

def show_layer_properties(self, item):
Expand Down

0 comments on commit b2ed899

Please sign in to comment.