From 8ea5df02e741e8edbce9cb610fa452b8c2bcc7a0 Mon Sep 17 00:00:00 2001 From: Tim Sutton Date: Tue, 19 Nov 2024 16:27:29 +0000 Subject: [PATCH 1/2] Fix poly classification for Digital Inclusion and Education WIP Fixes #612 --- geest/core/generate_model.py | 22 +- geest/core/workflow_factory.py | 7 +- geest/core/workflows/__init__.py | 1 + .../workflows/classified_polygon_workflow.py | 177 +++++++++++ .../workflows/polygon_per_cell_workflow.py | 2 +- .../core/workflows/safety_polygon_workflow.py | 18 +- geest/gui/combined_widget_factory.py | 7 +- geest/gui/configuration_widget_factory.py | 19 +- geest/gui/datasource_widget_factory.py | 8 +- .../gui/widgets/combined_widgets/__init__.py | 1 + .../classified_polygon_widget.py | 279 ++++++++++++++++++ .../combined_widgets/safety_polygon_widget.py | 119 +++++++- .../widgets/configuration_widgets/__init__.py | 3 + ...classified_polygon_configuration_widget.py | 68 +++++ .../safety_polygon_configuration_widget.py | 20 +- .../vector_and_field_datasource_widget.py | 4 +- geest/resources/README-DEVELOPERS.md | 4 +- geest/resources/geest2.ods | Bin 33131 -> 35177 bytes geest/resources/model.json | 130 ++++---- geest/resources/schema.json | 8 +- 20 files changed, 793 insertions(+), 104 deletions(-) create mode 100644 geest/core/workflows/classified_polygon_workflow.py create mode 100644 geest/gui/widgets/combined_widgets/classified_polygon_widget.py create mode 100644 geest/gui/widgets/configuration_widgets/classified_polygon_configuration_widget.py diff --git a/geest/core/generate_model.py b/geest/core/generate_model.py index 1df191a..86c4177 100755 --- a/geest/core/generate_model.py +++ b/geest/core/generate_model.py @@ -53,9 +53,10 @@ def load_spreadsheet(self): "Use Mapillary Downloader", "Use Other Downloader", "Use Add Layers Manually", - "Use Classify Poly into Classes", + "Use Classify Polygon into Classes", + "Use Classify Safety Polygon into Classes", "Use CSV to Point Layer", - "Use Poly per Cell", + "Use Polygon per Cell", "Use Polyline per Cell", "Use Point per Cell", "Use Nighttime Lights", @@ -242,9 +243,14 @@ def parse_to_json(self): if not pd.isna(row["Use Add Layers Manually"]) else "" ), - "use_classify_poly_into_classes": ( - row["Use Classify Poly into Classes"] - if not pd.isna(row["Use Classify Poly into Classes"]) + "use_classify_polygon_into_classes": ( + row["Use Classify Polygon into Classes"] + if not pd.isna(row["Use Classify Polygon into Classes"]) + else "" + ), + "use_classify_safety_polygon_into_classes": ( + row["Use Classify Safety Polygon into Classes"] + if not pd.isna(row["Use Classify Safety Polygon into Classes"]) else "" ), "use_csv_to_point_layer": ( @@ -252,9 +258,9 @@ def parse_to_json(self): if not pd.isna(row["Use CSV to Point Layer"]) else "" ), - "use_poly_per_cell": ( - row["Use Poly per Cell"] - if not pd.isna(row["Use Poly per Cell"]) + "use_polygon_per_cell": ( + row["Use Polygon per Cell"] + if not pd.isna(row["Use Polygon per Cell"]) else "" ), "use_polyline_per_cell": ( diff --git a/geest/core/workflow_factory.py b/geest/core/workflow_factory.py index 2f636ba..df86694 100644 --- a/geest/core/workflow_factory.py +++ b/geest/core/workflow_factory.py @@ -20,6 +20,7 @@ SafetyRasterWorkflow, RasterReclassificationWorkflow, StreetLightsBufferWorkflow, + ClassifiedPolygonWorkflow, ) from .json_tree_item import JsonTreeItem @@ -79,7 +80,7 @@ def create_workflow( elif analysis_mode == "use_polyline_per_cell": return PolylinePerCellWorkflow(item, cell_size_m, feedback, context) # TODO fix inconsistent abbreviation below for Poly - elif analysis_mode == "use_poly_per_cell": + elif analysis_mode == "use_polygon_per_cell": return PolygonPerCellWorkflow(item, cell_size_m, feedback, context) elif analysis_mode == "factor_aggregation": return FactorAggregationWorkflow(item, cell_size_m, feedback, context) @@ -91,7 +92,9 @@ def create_workflow( return AnalysisAggregationWorkflow(item, cell_size_m, feedback, context) elif analysis_mode == "use_csv_to_point_layer": return AcledImpactWorkflow(item, cell_size_m, feedback, context) - elif analysis_mode == "use_classify_poly_into_classes": + elif analysis_mode == "use_classify_polygon_into_classes": + return ClassifiedPolygonWorkflow(item, cell_size_m, feedback, context) + elif analysis_mode == "use_classify_safety_polygon_into_classes": return SafetyPolygonWorkflow(item, cell_size_m, feedback, context) elif analysis_mode == "use_nighttime_lights": return SafetyRasterWorkflow(item, cell_size_m, feedback, context) diff --git a/geest/core/workflows/__init__.py b/geest/core/workflows/__init__.py index 4c3b151..775eb2c 100644 --- a/geest/core/workflows/__init__.py +++ b/geest/core/workflows/__init__.py @@ -13,3 +13,4 @@ from .safety_raster_workflow import SafetyRasterWorkflow from .raster_reclassification_workflow import RasterReclassificationWorkflow from .street_lights_buffer_workflow import StreetLightsBufferWorkflow +from .classified_polygon_workflow import ClassifiedPolygonWorkflow diff --git a/geest/core/workflows/classified_polygon_workflow.py b/geest/core/workflows/classified_polygon_workflow.py new file mode 100644 index 0000000..436940d --- /dev/null +++ b/geest/core/workflows/classified_polygon_workflow.py @@ -0,0 +1,177 @@ +import os +from qgis.core import ( + Qgis, + QgsFeedback, + QgsGeometry, + QgsVectorLayer, + QgsProcessingContext, + edit, + QgsField, +) +from qgis.PyQt.QtCore import QVariant +from .workflow_base import WorkflowBase +from geest.core import JsonTreeItem +from geest.utilities import log_message + + +class ClassifiedPolygonWorkflow(WorkflowBase): + """ + Concrete implementation of a 'use_classify_polygon_into_classes' workflow. + """ + + def __init__( + self, + item: JsonTreeItem, + cell_size_m: float, + feedback: QgsFeedback, + context: QgsProcessingContext, + ): + """ + Initialize the workflow with attributes and feedback. + :param item: Item containing workflow parameters. + :param cell_size_m: Cell size in meters. + :param feedback: QgsFeedback object for progress reporting and cancellation. + :param context: QgsProcessingContext object for processing. This can be used to pass objects to the thread. e.g. the QgsProject Instance + """ + super().__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_classify_polygon_into_classes" + layer_path = self.attributes.get( + "use_classify_polygon_into_classes_shapefile", None + ) + + if not layer_path: + log_message( + "Invalid layer found in use_classify_polygon_into_classes_shapefile, trying use_classify_polygon_into_classes_source.", + tag="Geest", + level=Qgis.Warning, + ) + layer_path = self.attributes.get( + "use_classify_polygon_into_classes_layer_source", None + ) + if not layer_path: + log_message( + "No points layer found in use_classify_polygon_into_classes_layer_source.", + tag="Geest", + level=Qgis.Warning, + ) + return False + + self.features_layer = QgsVectorLayer(layer_path, "features_layer", "ogr") + + self.selected_field = self.attributes.get( + "classify_polygon_into_classes_selected_field", "" + ) + + def _process_features_for_area( + self, + current_area: QgsGeometry, + current_bbox: QgsGeometry, + area_features: QgsVectorLayer, + index: int, + ) -> str: + """ + Executes the actual workflow logic for a single area + Must be implemented by subclasses. + + :current_area: Current polygon from our study area. + :current_bbox: Bounding box of the above area. + :area_features: A vector layer of features to analyse that includes only features in the study area. + :index: Iteration / number of area being processed. + + :return: A raster layer file path if processing completes successfully, False if canceled or failed. + """ + area_features_count = area_features.featureCount() + log_message( + f"Features layer for area {index+1} loaded with {area_features_count} features.", + tag="Geest", + level=Qgis.Info, + ) + # Step 1: Assign reclassification values based on perceived safety + reclassified_layer = self._assign_reclassification_to_safety(area_features) + + # Step 2: Rasterize the data + raster_output = self._rasterize( + reclassified_layer, + current_bbox, + index, + value_field="value", + default_value=255, + ) + return raster_output + + def _assign_reclassification_to_safety( + self, layer: QgsVectorLayer + ) -> QgsVectorLayer: + """ + Assign reclassification values to polygons based on thresholds. + """ + with edit(layer): + # Remove all other columns except the selected field and the new 'value' field + fields_to_keep = {self.selected_field, "value"} + fields_to_remove = [ + field.name() + for field in layer.fields() + if field.name() not in fields_to_keep + ] + layer.dataProvider().deleteAttributes( + [layer.fields().indexFromName(field) for field in fields_to_remove] + ) + layer.updateFields() + if layer.fields().indexFromName("value") == -1: + layer.dataProvider().addAttributes([QgsField("value", QVariant.Int)]) + layer.updateFields() + + for feature in layer.getFeatures(): + score = feature[self.selected_field] + # Scale values between 0 and 5 + reclass_val = self._scale_value(score, 0, 100, 0, 5) + log_message(f"Scaled {score} to: {reclass_val}") + feature.setAttribute("value", reclass_val) + layer.updateFeature(feature) + return layer + + def _scale_value(self, value, min_in, max_in, min_out, max_out): + """ + Scale value from input range (min_in, max_in) to output range (min_out, max_out). + """ + try: + result = (value - min_in) / (max_in - min_in) * ( + max_out - min_out + ) + min_out + return result + except: + log_message(f"Invalid value, returning 0: {value}") + return 0 + + # Default implementation of the abstract method - not used in this workflow + def _process_raster_for_area( + self, + current_area: QgsGeometry, + current_bbox: QgsGeometry, + area_raster: str, + index: int, + ): + """ + Executes the actual workflow logic for a single area using a raster. + + :current_area: Current polygon from our study area. + :current_bbox: Bounding box of the above area. + :area_raster: A raster layer of features to analyse that includes only bbox pixels in the study area. + :index: Index of the current area. + + :return: Path to the reclassified raster. + """ + pass + + def _process_aggregate_for_area( + self, + current_area: QgsGeometry, + current_bbox: QgsGeometry, + index: int, + ): + """ + Executes the workflow, reporting progress through the feedback object and checking for cancellation. + """ + pass diff --git a/geest/core/workflows/polygon_per_cell_workflow.py b/geest/core/workflows/polygon_per_cell_workflow.py index dae681f..226190e 100644 --- a/geest/core/workflows/polygon_per_cell_workflow.py +++ b/geest/core/workflows/polygon_per_cell_workflow.py @@ -37,7 +37,7 @@ def __init__( item, cell_size_m, feedback, context ) # ⭐️ Item is a reference - whatever you change in this item will directly update the tree # TODO fix inconsistent abbreviation below for Poly - self.workflow_name = "use_poly_per_cell" + self.workflow_name = "use_polygon_per_cell" layer_path = self.attributes.get("polygon_per_cell_shapefile", None) diff --git a/geest/core/workflows/safety_polygon_workflow.py b/geest/core/workflows/safety_polygon_workflow.py index fa24751..e298a14 100644 --- a/geest/core/workflows/safety_polygon_workflow.py +++ b/geest/core/workflows/safety_polygon_workflow.py @@ -16,7 +16,7 @@ class SafetyPolygonWorkflow(WorkflowBase): """ - Concrete implementation of a 'use_classify_poly_into_classes' workflow. + Concrete implementation of a 'use_classify_polygon_into_classes' workflow. """ def __init__( @@ -36,21 +36,23 @@ def __init__( super().__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_classify_poly_into_classes" - layer_path = self.attributes.get("classify_poly_into_classes_shapefile", None) + self.workflow_name = "use_classify_safety_polygon_into_classes" + layer_path = self.attributes.get( + "classify_safety_polygon_into_classes_shapefile", None + ) if not layer_path: log_message( - "Invalid raster found in classify_poly_into_classes_shapefile, trying classify_poly_into_classes_layer_source.", + "Invalid layer found in classify_safety_polygon_into_classes_shapefile, trying classify_safety_polygon_into_classes_layer_source.", tag="Geest", level=Qgis.Warning, ) layer_path = self.attributes.get( - "classify_poly_into_classes_layer_source", None + "classify_safety_polygon_into_classes_layer_source", None ) if not layer_path: log_message( - "No points layer found in classify_poly_into_classes_layer_source.", + "No layer found in classify_safety_polygon_into_classes_layer_source.", tag="Geest", level=Qgis.Warning, ) @@ -59,12 +61,12 @@ def __init__( self.features_layer = QgsVectorLayer(layer_path, "features_layer", "ogr") self.selected_field = self.attributes.get( - "classify_poly_into_classes_selected_field", "" + "classify_safety_polygon_into_classes_selected_field", "" ) # This is a dict with keys being unique values from the selected field # and values from the aggregation dialog configuration table self.safety_mapping_table = self.attributes.get( - "classify_poly_into_classes_unique_values", None + "classify_safety_polygon_into_classes_unique_values", None ) if not isinstance(self.safety_mapping_table, dict): raise Exception("Safety scoring table not configured.") diff --git a/geest/gui/combined_widget_factory.py b/geest/gui/combined_widget_factory.py index e6e00a1..ce65c54 100644 --- a/geest/gui/combined_widget_factory.py +++ b/geest/gui/combined_widget_factory.py @@ -13,6 +13,7 @@ SafetyRasterWidget, RasterReclassificationWidget, StreetLightsWidget, + ClassifiedPolygonWidget, ) from geest.core import setting from geest.utilities import log_message @@ -49,7 +50,7 @@ def create_radio_button( return MultiBufferDistancesWidget(label_text=key, attributes=attributes) if key == "use_single_buffer_point" and value == 1: return SingleBufferDistanceWidget(label_text=key, attributes=attributes) - if key == "use_poly_per_cell" and value == 1: + if key == "use_polygon_per_cell" and value == 1: return PolygonWidget(label_text=key, attributes=attributes) if key == "use_polyline_per_cell" and value == 1: return PolylineWidget(label_text=key, attributes=attributes) @@ -57,7 +58,9 @@ def create_radio_button( return PointLayerWidget(label_text=key, attributes=attributes) if key == "use_csv_to_point_layer" and value == 1: return AcledCsvLayerWidget(label_text=key, attributes=attributes) - if key == "use_classify_poly_into_classes" and value == 1: + if key == "use_classify_polygon_into_classes" and value == 1: + return ClassifiedPolygonWidget(label_text=key, attributes=attributes) + if key == "use_classify_safety_polygon_into_classes" and value == 1: return SafetyPolygonWidget(label_text=key, attributes=attributes) if key == "use_nighttime_lights" and value == 1: return SafetyRasterWidget(label_text=key, attributes=attributes) diff --git a/geest/gui/configuration_widget_factory.py b/geest/gui/configuration_widget_factory.py index 5b661d6..3cba120 100644 --- a/geest/gui/configuration_widget_factory.py +++ b/geest/gui/configuration_widget_factory.py @@ -1,16 +1,17 @@ from qgis.core import Qgis from geest.gui.widgets.configuration_widgets import ( + AcledCsvConfigurationWidget, BaseConfigurationWidget, + ClassifiedPolygonConfigurationWidget, DontUseConfigurationWidget, - AcledCsvConfigurationWidget, + FeaturePerCellConfigurationWidget, IndexScoreConfigurationWidget, MultiBufferConfigurationWidget, - SingleBufferConfigurationWidget, - FeaturePerCellConfigurationWidget, - SafetyPolygonConfigurationWidget, - StreetLightsConfigurationWidget, RasterReclassificationConfigurationWidget, + SafetyPolygonConfigurationWidget, SafetyRasterConfigurationWidget, + SingleBufferConfigurationWidget, + StreetLightsConfigurationWidget, ) from geest.core import setting from geest.utilities import log_message @@ -57,7 +58,7 @@ def create_radio_button( # ------------------------------------------------ # These three all use the same configuration widgets # but will have different datasource widgets generated as appropriate - if key == "use_poly_per_cell" and value == 1: # poly = polygon + if key == "use_polygon_per_cell" and value == 1: # poly = polygon return FeaturePerCellConfigurationWidget( analysis_mode=key, attributes=attributes ) @@ -74,7 +75,11 @@ def create_radio_button( analysis_mode=key, attributes=attributes ) # ------------------------------------------------ - if key == "use_classify_poly_into_classes" and value == 1: + if key == "use_classify_polygon_into_classes" and value == 1: + return ClassifiedPolygonConfigurationWidget( + analysis_mode=key, attributes=attributes + ) + if key == "use_classify_safety_polygon_into_classes" and value == 1: return SafetyPolygonConfigurationWidget( analysis_mode=key, attributes=attributes ) diff --git a/geest/gui/datasource_widget_factory.py b/geest/gui/datasource_widget_factory.py index 18a3408..4873383 100644 --- a/geest/gui/datasource_widget_factory.py +++ b/geest/gui/datasource_widget_factory.py @@ -51,7 +51,7 @@ def create_widget( return VectorDataSourceWidget( widget_key=cleaned_key, attributes=attributes ) - if widget_key == "use_poly_per_cell" and value == 1: + if widget_key == "use_polygon_per_cell" and value == 1: return VectorDataSourceWidget( widget_key=cleaned_key, attributes=attributes ) @@ -69,7 +69,11 @@ def create_widget( return AcledCsvDataSourceWidget( widget_key=widget_key, attributes=attributes ) - if widget_key == "use_classify_poly_into_classes" and value == 1: + if widget_key == "use_classify_polygon_into_classes" and value == 1: + return VectorAndFieldDataSourceWidget( + widget_key=cleaned_key, attributes=attributes + ) + if widget_key == "use_classify_safety_polygon_into_classes" and value == 1: return VectorAndFieldDataSourceWidget( widget_key=cleaned_key, attributes=attributes ) diff --git a/geest/gui/widgets/combined_widgets/__init__.py b/geest/gui/widgets/combined_widgets/__init__.py index 58ef43d..132e6e3 100644 --- a/geest/gui/widgets/combined_widgets/__init__.py +++ b/geest/gui/widgets/combined_widgets/__init__.py @@ -12,3 +12,4 @@ from .safety_raster_widget import SafetyRasterWidget from .raster_reclassification_widget import RasterReclassificationWidget from .street_lights_widget import StreetLightsWidget +from .classified_polygon_widget import ClassifiedPolygonWidget diff --git a/geest/gui/widgets/combined_widgets/classified_polygon_widget.py b/geest/gui/widgets/combined_widgets/classified_polygon_widget.py new file mode 100644 index 0000000..9898fc3 --- /dev/null +++ b/geest/gui/widgets/combined_widgets/classified_polygon_widget.py @@ -0,0 +1,279 @@ +import os +from qgis.PyQt.QtWidgets import ( + QLabel, + QVBoxLayout, + QHBoxLayout, + QLineEdit, + QToolButton, + QFileDialog, +) +from qgis.gui import QgsMapLayerComboBox, QgsFieldComboBox +from qgis.core import ( + QgsMapLayerProxyModel, + QgsProject, + QgsVectorLayer, + QgsFieldProxyModel, + Qgis, +) +from qgis.PyQt.QtCore import QSettings +from .base_indicator_widget import BaseIndicatorWidget +from geest.utilities import log_message + + +class ClassifiedPolygonWidget(BaseIndicatorWidget): + """ + + A widget for selecting a polygon (area) layer with options for shapefile inputs. + + This widget provides one `QgsMapLayerComboBox` components for selecting polygon layers, + as well as `QLineEdit` and `QToolButton` components to allow the user to specify shapefile paths for + each layer. + + The user can choose layers either from the QGIS project or provide external shapefiles. + + The user can choose a numeric field from the selected polygon layer to classify the polygons into classes. + + Attributes: + widget_key (str): The key identifier for this widget. + polygon_layer_combo (QgsMapLayerComboBox): A combo box for selecting the polygon layer. + polygon_shapefile_line_edit (QLineEdit): Line edit for entering/selecting a polygon layer shapefile. + """ + + def add_internal_widgets(self) -> None: + """ + Adds the internal widgets required for selecting polygon layers and their corresponding shapefiles. + This method is called during the widget initialization and sets up the layout for the UI components. + """ + try: + self.main_layout = QVBoxLayout() + self.widget_key = "classify_polygon_into_classes" + self.settings = QSettings() + + # Polygon Layer Section + self._add_polygon_layer_widgets() + + # Add Field Selection Dropdown + self._add_field_selection_widgets() + + # Add the main layout to the widget's layout + self.layout.addLayout(self.main_layout) + + # Connect signals to update the data when user changes selections + self.polygon_layer_combo.currentIndexChanged.connect(self.update_data) + self.polygon_shapefile_line_edit.textChanged.connect(self.update_data) + # Connect signals to update the fields when user changes selections + self.polygon_layer_combo.layerChanged.connect(self.update_field_combo) + self.polygon_shapefile_line_edit.textChanged.connect( + self.update_field_combo + ) + + # Connect the field combo box to update the attributes when a field is selected + self.field_selection_combo.currentIndexChanged.connect( + self.update_selected_field + ) + + self.update_field_combo() # Populate fields for the initially selected layer + + except Exception as e: + log_message(f"Error in add_internal_widgets: {e}", level=Qgis.Critical) + import traceback + + log_message(traceback.format_exc(), level=Qgis.Critical) + + def _add_polygon_layer_widgets(self) -> None: + """ + Adds the widgets for selecting the polygon layer, including a `QgsMapLayerComboBox` and shapefile input. + """ + self.polygon_layer_label = QLabel( + "Polygon Layer - shapefile will have preference" + ) + self.main_layout.addWidget(self.polygon_layer_label) + # Polygon Layer ComboBox (Filtered to polygon layers) + self.polygon_layer_combo = QgsMapLayerComboBox() + self.polygon_layer_combo.setFilters(QgsMapLayerProxyModel.PolygonLayer) + self.main_layout.addWidget(self.polygon_layer_combo) + + # Restore previously selected polygon layer + polygon_layer_id = self.attributes.get(f"{self.widget_key}_layer_id", None) + if polygon_layer_id: + polygon_layer = QgsProject.instance().mapLayer(polygon_layer_id) + if polygon_layer: + self.polygon_layer_combo.setLayer(polygon_layer) + + # _shapefile Input for Polygon Layer + self.polygon_shapefile_layout = QHBoxLayout() + self.polygon_shapefile_line_edit = QLineEdit() + self.polygon_shapefile_button = QToolButton() + self.polygon_shapefile_button.setText("...") + self.polygon_shapefile_button.clicked.connect(self.select_polygon_shapefile) + if self.attributes.get(f"{self.widget_key}_shapefile", False): + self.polygon_shapefile_line_edit.setText( + self.attributes[f"{self.widget_key}_shapefile"] + ) + self.polygon_shapefile_layout.addWidget(self.polygon_shapefile_line_edit) + self.polygon_shapefile_layout.addWidget(self.polygon_shapefile_button) + self.main_layout.addLayout(self.polygon_shapefile_layout) + + def _add_field_selection_widgets(self) -> None: + """ + Adds a dropdown to select a specific field from the selected shapefile. + """ + self.field_selection_label = QLabel("Select Field") + self.main_layout.addWidget(self.field_selection_label) + + self.field_selection_combo = QgsFieldComboBox() + self.field_selection_combo.setFilters(QgsFieldProxyModel.Numeric) + self.field_selection_combo.setEnabled( + False + ) # Disable initially until a layer is selected + self.main_layout.addWidget(self.field_selection_combo) + + def select_polygon_shapefile(self) -> None: + """ + Opens a file dialog to select a shapefile for the polygon (paths) layer and updates the QLineEdit with the file path. + """ + try: + last_dir = self.settings.value("Geest/lastShapefileDir", "") + + file_path, _ = QFileDialog.getOpenFileName( + self, "Select Polygon Shapefile", last_dir, "Shapefiles (*.shp)" + ) + if file_path: + self.polygon_shapefile_line_edit.setText(file_path) + self.settings.setValue( + "Geest/lastShapefileDir", os.path.dirname(file_path) + ) + + # Load the shapefile as a QgsVectorLayer and populate the fields dropdown + self._populate_field_combo(file_path) + + except Exception as e: + log_message( + f"Error selecting polygon shapefile: {e}", + tag="Geest", + level=Qgis.Critical, + ) + + def _populate_field_combo(self, shapefile_path: str) -> None: + """ + Loads the shapefile and populates the field selection combo box with the field names. + + Args: + shapefile_path (str): The path to the shapefile. + """ + try: + # Store the currently selected field + previous_field = self.settings.value( + f"{self.widget_key}_selected_field", None + ) + + vector_layer = QgsVectorLayer(shapefile_path, "polygon_layer", "ogr") + if not vector_layer.isValid(): + log_message(f"Failed to load shapefile: {shapefile_path}", "Geest") + return + + # Set the vector layer on the field selection combo box, which will automatically populate it + QgsProject.instance().addMapLayer(vector_layer, False) + self.field_selection_combo.setLayer(vector_layer) + self.field_selection_combo.setEnabled(True) # Enable once layer is valid + + # Reapply the previously selected field if it exists + if ( + previous_field + and self.field_selection_combo.findText(previous_field) != -1 + ): + self.field_selection_combo.setCurrentText(previous_field) + + except Exception as e: + log_message(f"Error populating field combo: {e}", level=Qgis.Critical) + + def update_field_combo(self) -> None: + """ + Updates the field combo box when the polygon layer or shapefile is changed. + """ + # Store the currently selected field + previous_field = self.settings.value(f"{self.widget_key}_selected_field", None) + + if self.polygon_layer_combo.currentLayer(): + # Populate field combo from the selected polygon layer + self.field_selection_combo.setLayer(self.polygon_layer_combo.currentLayer()) + self.field_selection_combo.setEnabled(True) + elif self.polygon_shapefile_line_edit.text(): + # If shapefile is provided, populate the field combo + self._populate_field_combo(self.polygon_shapefile_line_edit.text()) + + # After the field combo is repopulated, re-select the previously selected field if it exists + if previous_field and self.field_selection_combo.findText(previous_field) != -1: + self.field_selection_combo.setCurrentText(previous_field) + + def get_data(self) -> dict: + """ + Retrieves and returns the current state of the widget, including selected polygon layers or shapefiles. + + Returns: + dict: A dictionary containing the current attributes of the polygon layers and/or shapefiles. + """ + if not self.isChecked(): + return None + + # Collect data for the polygon layer + polygon_layer = self.polygon_layer_combo.currentLayer() + if polygon_layer: + self.attributes[f"{self.widget_key}_layer_name"] = polygon_layer.name() + self.attributes[f"{self.widget_key}_layer_source"] = polygon_layer.source() + self.attributes[f"{self.widget_key}_layer_provider_type"] = ( + polygon_layer.providerType() + ) + self.attributes[f"{self.widget_key}_layer_crs"] = ( + polygon_layer.crs().authid() + ) + self.attributes[f"{self.widget_key}_layer_wkb_type"] = ( + polygon_layer.wkbType() + ) + self.attributes[f"{self.widget_key}_layer_id"] = polygon_layer.id() + self.attributes[f"{self.widget_key}_shapefile"] = ( + self.polygon_shapefile_line_edit.text() + ) + # Get the selected field if field combo box is enabled + selected_field = ( + self.field_selection_combo.currentText() + if self.field_selection_combo.isEnabled() + else None + ) + self.attributes[f"{self.widget_key}_selected_field"] = selected_field + + return self.attributes + + def update_selected_field(self) -> None: + """ + Updates the selected field in the attributes dictionary when the field selection changes. + """ + if self.field_selection_combo.isEnabled(): + selected_field = self.field_selection_combo.currentText() + self.attributes[f"{self.widget_key}_selected_field"] = selected_field + + # Store the selected field in QSettings + self.settings.setValue(f"{self.widget_key}_selected_field", selected_field) + else: + self.attributes[f"{self.widget_key}_selected_field"] = None + + def set_internal_widgets_enabled(self, enabled: bool) -> None: + """ + Enables or disables the internal widgets (polygon layers) based on the state of the radio button. + + Args: + enabled (bool): Whether to enable or disable the internal widgets. + """ + try: + self.polygon_layer_combo.setEnabled(enabled) + self.polygon_shapefile_line_edit.setEnabled(enabled) + self.polygon_shapefile_button.setEnabled(enabled) + self.field_selection_combo.setEnabled( + enabled and self.field_selection_combo.count() > 0 + ) + except Exception as e: + log_message( + f"Error in set_internal_widgets_enabled: {e}", + tag="Geest", + level=Qgis.Critical, + ) diff --git a/geest/gui/widgets/combined_widgets/safety_polygon_widget.py b/geest/gui/widgets/combined_widgets/safety_polygon_widget.py index efa3d61..e32cd37 100644 --- a/geest/gui/widgets/combined_widgets/safety_polygon_widget.py +++ b/geest/gui/widgets/combined_widgets/safety_polygon_widget.py @@ -1,11 +1,15 @@ import os from qgis.PyQt.QtWidgets import ( - QLabel, - QVBoxLayout, + QFileDialog, QHBoxLayout, + QLabel, QLineEdit, + QSizePolicy, + QSpinBox, + QTableWidget, + QTableWidgetItem, QToolButton, - QFileDialog, + QVBoxLayout, ) from qgis.gui import QgsMapLayerComboBox, QgsFieldComboBox from qgis.core import ( @@ -42,7 +46,7 @@ def add_internal_widgets(self) -> None: """ try: self.main_layout = QVBoxLayout() - self.widget_key = "classify_poly_into_classes" + self.widget_key = "classify_safety_polygon_into_classes" self.settings = QSettings() # Polygon Layer Section @@ -54,6 +58,72 @@ def add_internal_widgets(self) -> None: # Add the main layout to the widget's layout self.layout.addLayout(self.main_layout) + self.table_widget = QTableWidget() + self.table_widget.setSizePolicy( + QSizePolicy.Expanding, QSizePolicy.Expanding + ) + # Stop the label being editable + self.table_widget.setEditTriggers(QTableWidget.NoEditTriggers) + self.layout.addWidget(self.table_widget) + self.table_widget.setColumnCount(2) + self.table_widget.setHorizontalHeaderLabels(["Name", "Value 0-100"]) + self.table_widget.setColumnWidth(1, 80) + self.table_widget.horizontalHeader().setStretchLastSection(False) + self.table_widget.horizontalHeader().setSectionResizeMode( + 0, self.table_widget.horizontalHeader().Stretch + ) + + safety_classes = self.attributes.get( + f"classify_safety_polygon_into_classes_unique_values", {} + ) + if not isinstance(safety_classes, dict): + safety_classes = {} + # remove any item from the safety_classes where the key is not a string + safety_classes = { + k: v for k, v in safety_classes.items() if isinstance(k, str) + } + self.table_widget.setRowCount(len(safety_classes)) + + def validate_value(value): + return 0 <= value <= 100 + + log_message(f"Classes: {safety_classes}") + # iterate over the dict and populate the table + for row, (class_name, value) in enumerate(safety_classes.items()): + if row >= self.table_widget.rowCount(): + continue + + if not isinstance(class_name, str): + continue + + if not isinstance(value, (int, float)) or not 0 <= value <= 100: + value = 0 + + name_item = QTableWidgetItem(class_name) + value_item = QSpinBox() + self.table_widget.setItem(row, 0, name_item) + value_item.setRange(0, 100) # Set spinner range + value_item.setValue(value) # Default value + self.table_widget.setCellWidget(row, 1, value_item) + + def on_value_changed(value): + # Color handling for current cell + if value is None or not (0 <= value <= 100): + value_item.setStyleSheet("color: red;") + value_item.setValue(0) + else: + value_item.setStyleSheet("color: black;") + self.update_cell_colors() + self.update_data() + + value_item.valueChanged.connect(on_value_changed) + + # Call update_cell_colors after all rows are created + self.update_cell_colors() + + value_item.valueChanged.connect(on_value_changed) + self.layout.addWidget(self.table_widget) + # Connect signals to update the data when user changes selections self.polygon_layer_combo.currentIndexChanged.connect(self.update_data) self.polygon_shapefile_line_edit.textChanged.connect(self.update_data) @@ -76,6 +146,37 @@ def add_internal_widgets(self) -> None: log_message(traceback.format_exc(), level=Qgis.Critical) + def update_cell_colors(self): + # Check if all values are zero + all_zeros = True + for r in range(self.table_widget.rowCount()): + spin_widget = self.table_widget.cellWidget(r, 1) + if spin_widget and spin_widget.value() != 0: + all_zeros = False + break + + # Color all cells based on all-zeros check + for r in range(self.table_widget.rowCount()): + spin_widget = self.table_widget.cellWidget(r, 1) + if spin_widget: + spin_widget.setStyleSheet( + "color: red;" if all_zeros else "color: black;" + ) + + def table_to_dict(self): + updated_attributes = {} + for row in range(self.table_widget.rowCount()): + spin_widget = self.table_widget.cellWidget(row, 1) + value = None + if spin_widget and spin_widget.value(): + value = spin_widget.value() + name_item = self.table_widget.item(row, 0) + class_name = str(name_item.text()) + updated_attributes[class_name] = value + + log_message(f"Updated attributes {updated_attributes}") + return updated_attributes + def _add_polygon_layer_widgets(self) -> None: """ Adds the widgets for selecting the polygon layer, including a `QgsMapLayerComboBox` and shapefile input. @@ -114,13 +215,11 @@ def _add_field_selection_widgets(self) -> None: """ Adds a dropdown to select a specific field from the selected shapefile. """ - self.field_selection_label = QLabel("Select Field:") + self.field_selection_label = QLabel("Select Field") self.main_layout.addWidget(self.field_selection_label) self.field_selection_combo = QgsFieldComboBox() - self.field_selection_combo.setFilters( - QgsFieldProxyModel.String - ) # Filter for numeric fields + self.field_selection_combo.setFilters(QgsFieldProxyModel.String) self.field_selection_combo.setEnabled( False ) # Disable initially until a layer is selected @@ -213,7 +312,11 @@ def get_data(self) -> dict: """ if not self.isChecked(): return None + updated_attributes = self.table_to_dict() + self.attributes["classify_safety_polygon_into_classes_unique_values"] = ( + updated_attributes + ) # Collect data for the polygon layer polygon_layer = self.polygon_layer_combo.currentLayer() if polygon_layer: diff --git a/geest/gui/widgets/configuration_widgets/__init__.py b/geest/gui/widgets/configuration_widgets/__init__.py index 54b656b..8b815a5 100644 --- a/geest/gui/widgets/configuration_widgets/__init__.py +++ b/geest/gui/widgets/configuration_widgets/__init__.py @@ -10,4 +10,7 @@ from .raster_reclassification_configuration_widget import ( RasterReclassificationConfigurationWidget, ) +from .classified_polygon_configuration_widget import ( + ClassifiedPolygonConfigurationWidget, +) from .safety_raster_configuration_widget import SafetyRasterConfigurationWidget diff --git a/geest/gui/widgets/configuration_widgets/classified_polygon_configuration_widget.py b/geest/gui/widgets/configuration_widgets/classified_polygon_configuration_widget.py new file mode 100644 index 0000000..1bc04f5 --- /dev/null +++ b/geest/gui/widgets/configuration_widgets/classified_polygon_configuration_widget.py @@ -0,0 +1,68 @@ +import os +from qgis.PyQt.QtWidgets import ( + QLabel, + QTableWidget, + QTableWidgetItem, + QSpinBox, +) +from qgis.PyQt.QtCore import QVariant +from qgis.PyQt.QtGui import QBrush, QColor +from qgis.core import Qgis +from .base_configuration_widget import BaseConfigurationWidget +from geest.utilities import log_message + + +class ClassifiedPolygonConfigurationWidget(BaseConfigurationWidget): + """ + + A widget for selecting a polygon (area) that will be classified based on the percentage scores. + + Attributes: + widget_key (str): The key identifier for this widget. + polygon_layer_combo (QgsMapLayerComboBox): A combo box for selecting the polygon layer. + polygon_shapefile_line_edit (QLineEdit): Line edit for entering/selecting a polygon layer shapefile. + """ + + def add_internal_widgets(self) -> None: + """ + Adds the internal widgets required for selecting polygon layers and their corresponding shapefiles. + This method is called during the widget initialization and sets up the layout for the UI components. + """ + try: + self.info_label = QLabel("Classify polygons accoring to percentage scores") + self.layout.addWidget(self.info_label) + + except Exception as e: + log_message(f"Error in add_internal_widgets: {e}", level=Qgis.Critical) + import traceback + + log_message(traceback.format_exc(), level=Qgis.Critical) + + def get_data(self) -> dict: + """ + Retrieves and returns the current state of the widget, including selected polygon layers or shapefiles. + + Returns: + dict: A dictionary containing the current attributes of the polygon layers and/or shapefiles. + """ + if not self.isChecked(): + return None + + # There are no configurable options in this widget (see hte accompanying vector and field data source widget) + return self.attributes + + def set_internal_widgets_enabled(self, enabled: bool) -> None: + """ + Enables or disables the internal widgets (polygon layers) based on the state of the radio button. + + Args: + enabled (bool): Whether to enable or disable the internal widgets. + """ + try: + self.info_label.setEnabled(enabled) + except Exception as e: + log_message( + f"Error in set_internal_widgets_enabled: {e}", + tag="Geest", + level=Qgis.Critical, + ) diff --git a/geest/gui/widgets/configuration_widgets/safety_polygon_configuration_widget.py b/geest/gui/widgets/configuration_widgets/safety_polygon_configuration_widget.py index c16a354..c53c26f 100644 --- a/geest/gui/widgets/configuration_widgets/safety_polygon_configuration_widget.py +++ b/geest/gui/widgets/configuration_widgets/safety_polygon_configuration_widget.py @@ -5,8 +5,7 @@ QTableWidgetItem, QSpinBox, ) -from qgis.PyQt.QtCore import QVariant -from qgis.PyQt.QtGui import QBrush, QColor +from qgis.PyQt.QtWidgets import QSizePolicy from qgis.core import Qgis from .base_configuration_widget import BaseConfigurationWidget from geest.utilities import log_message @@ -35,9 +34,14 @@ def add_internal_widgets(self) -> None: This method is called during the widget initialization and sets up the layout for the UI components. """ try: - self.info_label = QLabel("Classify polygons accoring to safety levels") + self.info_label = QLabel("Classify polygons according to safety levels") self.layout.addWidget(self.info_label) self.table_widget = QTableWidget() + self.table_widget.setSizePolicy( + QSizePolicy.Expanding, QSizePolicy.Expanding + ) + # Stop the label being editable + self.table_widget.setEditTriggers(QTableWidget.NoEditTriggers) self.layout.addWidget(self.table_widget) self.table_widget.setColumnCount(2) self.table_widget.setHorizontalHeaderLabels(["Name", "Value 0-100"]) @@ -48,7 +52,7 @@ def add_internal_widgets(self) -> None: ) safety_classes = self.attributes.get( - f"classify_poly_into_classes_unique_values", {} + f"classify_safety_polygon_into_classes_unique_values", {} ) if not isinstance(safety_classes, dict): safety_classes = {} @@ -82,7 +86,7 @@ def validate_value(value): def on_value_changed(value): # Color handling for current cell - if value is None or not (0 <= value <= 6): + if value is None or not (0 <= value <= 100): value_item.setStyleSheet("color: red;") value_item.setValue(0) else: @@ -145,10 +149,12 @@ def get_data(self) -> dict: if not self.isChecked(): return None - # Serialize the self.table_widget back into the classify_poly_into_classes_unique_values attribute + # Serialize the self.table_widget back into the classify_polygon_into_classes_unique_values attribute updated_attributes = self.table_to_dict() - self.attributes["classify_poly_into_classes_unique_values"] = updated_attributes + self.attributes["classify_safety_polygon_into_classes_unique_values"] = ( + updated_attributes + ) # log_message("------------------------------------") # log_message("------------------------------------") # log_message(f"Attributes: {self.attributes}") diff --git a/geest/gui/widgets/datasource_widgets/vector_and_field_datasource_widget.py b/geest/gui/widgets/datasource_widgets/vector_and_field_datasource_widget.py index f6bf033..f790194 100644 --- a/geest/gui/widgets/datasource_widgets/vector_and_field_datasource_widget.py +++ b/geest/gui/widgets/datasource_widgets/vector_and_field_datasource_widget.py @@ -36,7 +36,9 @@ def add_internal_widgets(self) -> None: try: self.layer_combo = QgsMapLayerComboBox() filter = None - if self.attributes.get("use_classify_poly_into_classes", 0): + if self.attributes.get("use_classify_polygon_into_classes", 0): + filter = QgsMapLayerProxyModel.PolygonLayer + elif self.attributes.get("use_classify_safety_polygon_into_classes", 0): filter = QgsMapLayerProxyModel.PolygonLayer else: filter = QgsMapLayerProxyModel.PointLayer diff --git a/geest/resources/README-DEVELOPERS.md b/geest/resources/README-DEVELOPERS.md index 4dbdf21..c0e86e9 100644 --- a/geest/resources/README-DEVELOPERS.md +++ b/geest/resources/README-DEVELOPERS.md @@ -36,9 +36,9 @@ graph TB D --> D19[use_mapilliary_downloader: integer] D --> D20[use_other_downloader: integer] D --> D21[use_add_layers_manually: integer] - D --> D22[use_classify_poly_into_classes: integer] + D --> D22[use_classify_polygon_into_classes: integer] D --> D23[use_csv_to_point_layer: integer] - D --> D24[use_poly_per_cell: integer] + D --> D24[use_polygon_per_cell: integer] D --> D25[use_polyline_per_cell: integer] D --> D26[use_point_per_cell: integer] D --> D27[use_nighttime_lights: integer] diff --git a/geest/resources/geest2.ods b/geest/resources/geest2.ods index f25df73222be981bdffe74bef2eb7210c7ceb2bf..2b968ad048f574205403b1880c09f0e38a6a5f34 100644 GIT binary patch delta 33735 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 literal 33131 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 diff --git a/geest/resources/model.json b/geest/resources/model.json index 5c4417b..5bf3a20 100644 --- a/geest/resources/model.json +++ b/geest/resources/model.json @@ -35,9 +35,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": "", "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -77,9 +78,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": "", "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -119,9 +121,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": "", "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -170,9 +173,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": "", "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -203,9 +207,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": "", "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -236,9 +241,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": "", "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -269,9 +275,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": "NDVI", "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": "", "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -302,9 +309,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": "", "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -344,9 +352,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": "", "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -386,9 +395,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": "", "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -428,9 +438,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": "", "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -470,9 +481,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -521,9 +533,10 @@ "use_mapilliary_downloader": 1, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 1, "use_nighttime_lights": 0, @@ -554,9 +567,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 1, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -587,9 +601,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 1, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -620,9 +635,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 1, + "use_polygon_per_cell": 1, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -662,9 +678,10 @@ "use_mapilliary_downloader": 1, "use_other_downloader": "Nightime lights", "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 1, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 1.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 1, @@ -704,9 +721,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": "ACLED", "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 1, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -746,9 +764,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": "Central Statistics Office/", "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 1, + "use_classify_polygon_into_classes": 1, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -788,9 +807,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": "World Bank", "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 1, + "use_classify_polygon_into_classes": 1, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -830,9 +850,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": "National Centers for Environmental Information", "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -863,9 +884,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": "National Centers for Environmental Information", "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -896,9 +918,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": "National Centers for Environmental Information", "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -929,9 +952,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": "National Centers for Environmental Information", "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -962,9 +986,10 @@ "use_mapilliary_downloader": 0, "use_other_downloader": "National Centers for Environmental Information", "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, @@ -1004,9 +1029,10 @@ "use_mapilliary_downloader": 1, "use_other_downloader": 0, "use_add_layers_manually": 0, - "use_classify_poly_into_classes": 0, + "use_classify_polygon_into_classes": 0, + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, - "use_poly_per_cell": 0, + "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, "use_point_per_cell": 0, "use_nighttime_lights": 0, diff --git a/geest/resources/schema.json b/geest/resources/schema.json index d40ca45..d17775d 100644 --- a/geest/resources/schema.json +++ b/geest/resources/schema.json @@ -107,13 +107,13 @@ "use_add_layers_manually": { "type": "integer" }, - "use_classify_poly_into_classes": { + "use_classify_polygon_into_classes": { "type": "integer" }, "use_csv_to_point_layer": { "type": "integer" }, - "use_poly_per_cell": { + "use_polygon_per_cell": { "type": "integer" }, "use_polyline_per_cell": { @@ -157,9 +157,9 @@ "use_mapilliary_downloader", "use_other_downloader", "use_add_layers_manually", - "use_classify_poly_into_classes", + "use_classify_polygon_into_classes", "use_csv_to_point_layer", - "use_poly_per_cell", + "use_polygon_per_cell", "use_polyline_per_cell", "use_point_per_cell", "use_nighttime_lights", From 709c2814edcf0efea1bc0047de04de303d1fa487 Mon Sep 17 00:00:00 2001 From: Tim Sutton Date: Tue, 19 Nov 2024 17:00:21 +0000 Subject: [PATCH 2/2] Fix some more issues with running all workflows in sequence --- geest/core/generate_model.py | 2 +- geest/core/workflows/classified_polygon_workflow.py | 6 +++--- geest/resources/model.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/geest/core/generate_model.py b/geest/core/generate_model.py index 86c4177..7da798d 100755 --- a/geest/core/generate_model.py +++ b/geest/core/generate_model.py @@ -75,7 +75,7 @@ def create_id(self, name): """ Helper method to create a lowercase, underscore-separated id from the name. """ - return name.lower().replace(" ", "_") + return name.lower().replace(" ", "_").replace("'", "_") def parse_to_json(self): """ diff --git a/geest/core/workflows/classified_polygon_workflow.py b/geest/core/workflows/classified_polygon_workflow.py index 436940d..b472b86 100644 --- a/geest/core/workflows/classified_polygon_workflow.py +++ b/geest/core/workflows/classified_polygon_workflow.py @@ -38,7 +38,7 @@ def __init__( ) # ⭐️ Item is a reference - whatever you change in this item will directly update the tree self.workflow_name = "use_classify_polygon_into_classes" layer_path = self.attributes.get( - "use_classify_polygon_into_classes_shapefile", None + "classify_polygon_into_classes_shapefile", None ) if not layer_path: @@ -48,11 +48,11 @@ def __init__( level=Qgis.Warning, ) layer_path = self.attributes.get( - "use_classify_polygon_into_classes_layer_source", None + "classify_polygon_into_classes_layer_source", None ) if not layer_path: log_message( - "No points layer found in use_classify_polygon_into_classes_layer_source.", + "No layer found in use_classify_polygon_into_classes_layer_source.", tag="Geest", level=Qgis.Warning, ) diff --git a/geest/resources/model.json b/geest/resources/model.json index 5bf3a20..321330f 100644 --- a/geest/resources/model.json +++ b/geest/resources/model.json @@ -146,7 +146,7 @@ "description": "The Accessibility Dimension evaluates women\u2019s daily mobility by examining their access to essential services. Levels of enablement for work access in this dimension are determined by service areas, which represent the geographic zones that facilities like childcare, supermarkets, universities, banks, and clinics can serve based on proximity. The nearer these facilities are to where women live, the more supportive and enabling the environment becomes for their participation in the workforce.", "factors": [ { - "id": "women's_travel_patterns", + "id": "women_s_travel_patterns", "name": "Women's Travel Patterns", "required": 0.0, "default_dimension_weighting": 0.2,