diff --git a/geest/core/generate_model.py b/geest/core/generate_model.py index 7da798d..01728a5 100755 --- a/geest/core/generate_model.py +++ b/geest/core/generate_model.py @@ -27,10 +27,8 @@ def load_spreadsheet(self): self.dataframe = self.dataframe[ [ "Dimension", - "Dimension Required", "Default Dimension Analysis Weighting", "Factor", - "Factor Required", "Default Factor Dimension Weighting", "Indicator", "Default Indicator Factor Weighting", @@ -62,8 +60,7 @@ def load_spreadsheet(self): "Use Nighttime Lights", "Use Environmental Hazards", "Use Street Lights", - "Analysis Mode", # New column - "Indicator Required", # New column + "Analysis Mode", ] ] @@ -81,7 +78,7 @@ def parse_to_json(self): """ Parse the dataframe into the hierarchical JSON structure. """ - dimension_map = {} + analysis_model = {} for _, row in self.dataframe.iterrows(): dimension = row["Dimension"] @@ -89,17 +86,12 @@ def parse_to_json(self): # Prepare dimension data dimension_id = self.create_id(dimension) - dimension_required = ( - row["Dimension Required"] - if not pd.isna(row["Dimension Required"]) - else "" - ) default_dimension_analysis_weighting = ( row["Default Dimension Analysis Weighting"] if not pd.isna(row["Default Dimension Analysis Weighting"]) else "" ) - if dimension_id not in dimension_map: + if dimension_id not in analysis_model: # Hardcoded descriptions for specific dimensions description = "" if dimension_id == "contextual": @@ -110,23 +102,21 @@ def parse_to_json(self): description = "The Place-Characterization Dimension refers to the social, environmental, and infrastructural attributes of geographical locations, such as walkability, safety, and vulnerability to natural hazards. Unlike the Accessibility Dimension, these factors do not involve mobility but focus on the inherent characteristics of a place that influence women’s ability to participate in the workforce." # If the Dimension doesn't exist yet, create it - if dimension not in dimension_map: + if dimension not in analysis_model: new_dimension = { "id": dimension_id, "name": dimension, - "required": dimension_required, "default_analysis_weighting": default_dimension_analysis_weighting, + # Initialise the weighting to the default value + "analysis_weighting": default_dimension_analysis_weighting, "description": description, "factors": [], } self.result["dimensions"].append(new_dimension) - dimension_map[dimension] = new_dimension + analysis_model[dimension] = new_dimension # Prepare factor data factor_id = self.create_id(factor) - factor_required = ( - row["Factor Required"] if not pd.isna(row["Factor Required"]) else "" - ) default_factor_dimension_weighting = ( row["Default Factor Dimension Weighting"] if not pd.isna(row["Default Factor Dimension Weighting"]) @@ -134,13 +124,14 @@ def parse_to_json(self): ) # If the Factor doesn't exist in the current dimension, add it - factor_map = {f["name"]: f for f in dimension_map[dimension]["factors"]} + factor_map = {f["name"]: f for f in analysis_model[dimension]["factors"]} if factor not in factor_map: new_factor = { "id": factor_id, "name": factor, - "required": factor_required, "default_dimension_weighting": default_factor_dimension_weighting, + # Initialise the weighting to the default value + "dimension_weighting": default_factor_dimension_weighting, "indicators": [], "description": ( row["Factor Description"] @@ -148,20 +139,23 @@ def parse_to_json(self): else "" ), } - dimension_map[dimension]["factors"].append(new_factor) + analysis_model[dimension]["factors"].append(new_factor) factor_map[factor] = new_factor - # Add layer data to the current Factor, including new columns - layer_data = { + # Add indicator data to the current Factor, including new columns + default_factor_weighting = ( + row["Default Indicator Factor Weighting"] + if not pd.isna(row["Default Indicator Factor Weighting"]) + else "" + ) + indicator_data = { # These are all parsed from the spreadsheet "indicator": row["Indicator"] if not pd.isna(row["Indicator"]) else "", "id": row["ID"] if not pd.isna(row["ID"]) else "", "description": "", - "default_indicator_factor_weighting": ( - row["Default Indicator Factor Weighting"] - if not pd.isna(row["Default Indicator Factor Weighting"]) - else "" - ), + "default_factor_weighting": default_factor_weighting, + # Initialise the weighting to the default value + "factor_weighting": default_factor_weighting, "default_index_score": ( row["Default Index Score"] if not pd.isna(row["Default Index Score"]) @@ -291,14 +285,9 @@ def parse_to_json(self): "analysis_mode": ( row["Analysis Mode"] if not pd.isna(row["Analysis Mode"]) else "" ), # New column - "indicator_required": ( - row["Indicator Required"] - if not pd.isna(row["Indicator Required"]) - else "" - ), # New column } - factor_map[factor]["indicators"].append(layer_data) + factor_map[factor]["indicators"].append(indicator_data) def get_json(self): """ diff --git a/geest/core/generate_schema.py b/geest/core/generate_schema.py index 1b5df70..75f1e77 100755 --- a/geest/core/generate_schema.py +++ b/geest/core/generate_schema.py @@ -15,7 +15,6 @@ def infer_schema(data): return { "type": "object", "properties": properties, - "required": required_keys, } elif isinstance(data, list): if len(data) > 0: diff --git a/geest/core/json_tree_item.py b/geest/core/json_tree_item.py index ce42957..100e13b 100644 --- a/geest/core/json_tree_item.py +++ b/geest/core/json_tree_item.py @@ -134,13 +134,10 @@ def disable(self): data["analysis_mode"] = "Do Not Use" if self.isDimension(): - data["required"] = False data["analysis_weighting"] = 0.0 if self.isFactor(): - data["required"] = False data["dimension_weighting"] = 0.0 if self.isIndicator(): - data["indicator_required"] = False data["factor_weighting"] = 0.0 def enable(self): @@ -150,18 +147,15 @@ def enable(self): data = self.attributes() data["analysis_mode"] = "" if self.isDimension(): - data["required"] = True data["analysis_weighting"] = data["default_analysis_weighting"] if self.isFactor(): - data["required"] = True data["dimension_weighting"] = data["default_dimension_weighting"] if self.parent().getStatus() == "Excluded from analysis": self.parent().attributes()[ "analysis_weighting" ] = self.parent().attribute("default_analysis_weighting") if self.isIndicator(): - data["indicator_required"] = True - data["factor_weighting"] = data["default_indicator_factor_weighting"] + data["factor_weighting"] = data["default_factor_weighting"] if self.parent().getStatus() == "Excluded from analysis": self.parent().attributes()[ "dimension_weighting" @@ -210,8 +204,7 @@ def getItemTooltip(self): def getStatusIcon(self): """Retrieve the appropriate icon for the item based on its role.""" status = self.getStatus() - if self.isAnalysis(): - return None + if status == "Excluded from analysis": return QIcon(resources_path("resources", "icons", "excluded.svg")) if status == "Completed successfully": @@ -246,7 +239,6 @@ def getStatus(self): # First check if the item weighting is 0, or its parent factor is zero # If so, return "Excluded from analysis" if self.isIndicator(): - # Required flag can be overridden by the factor so we dont check it right now required_by_parent = float( self.parentItem.attributes().get("dimension_weighting", 0.0) ) @@ -258,9 +250,14 @@ def getStatus(self): # log_message(f"Excluded from analysis: {data.get('id')}") return "Excluded from analysis" if self.isFactor(): - if not data.get("required", False): - if not float(data.get("dimension_weighting", 0.0)): - return "Excluded from analysis" + if not float(data.get("dimension_weighting", 0.0)): + return "Excluded from analysis" + # If the sum of the indicator weightings is zero, 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 self.isDimension(): # If the analysis weighting is zero, return "Excluded from analysis" if not float(data.get("analysis_weighting", 0.0)): @@ -268,7 +265,14 @@ def getStatus(self): # If the sum of the factor weightings is zero, return "Excluded from analysis" weight_sum = 0 for child in self.childItems: - weight_sum += float(child.attribute("factor_weighting", 0.0)) + weight_sum += float(child.attribute("dimension_weighting", 0.0)) + if not weight_sum: + return "Excluded from analysis" + if self.isAnalysis(): + # If the sum of the dimension weightings is zero, return "Excluded from analysis" + weight_sum = 0 + for child in self.childItems: + weight_sum += float(child.attribute("analysis_weighting", 0.0)) if not weight_sum: return "Excluded from analysis" @@ -287,13 +291,17 @@ def getStatus(self): ): return "Not configured (optional)" # Item required and not configured - if (data.get("analysis_mode", "") == "") and data.get( - "indicator_required", False + if ( + self.isIndicator() + and (data.get("analysis_mode", "") == "") + and data.get("indicator_required", False) ): return "Required and not configured" # Item not required but not configured - if (data.get("analysis_mode", "") == "") and not data.get( - "indicator_required", False + if ( + self.isIndicator() + and (data.get("analysis_mode", "") == "") + and not data.get("indicator_required", False) ): return "Not configured (optional)" if "Not Run" in data.get("result", "") and not data.get("result_file", ""): @@ -389,29 +397,6 @@ def getAnalysisDimensionGuids(self): return guids # attributes["analysis_mode"] = "dimension_aggregation" - def getAnalysisAttributes(self): - """Return the dict of dimensions under this analysis.""" - attributes = {} - if self.isAnalysis(): - attributes["analysis_name"] = self.attribute("analysis_name", "Not Set") - attributes["description"] = self.attribute( - "analysis_description", "Not Set" - ) - attributes["working_folder"] = self.attribute("working_folder", "Not Set") - attributes["cell_size_m"] = self.attribute("cell_size_m", 100) - - attributes["dimensions"] = [ - { - "dimension_no": i, - "dimension_id": child.attribute("id", ""), - "dimension_name": child.data(0), - "dimension_weighting": child.data(2), - "result_file": child.attribute(f"result_file", ""), - } - for i, child in enumerate(self.childItems) - ] - return attributes - def getItemByGuid(self, guid): """Return the item with the specified guid.""" if self.guid == guid: @@ -470,3 +455,26 @@ def updateFactorWeighting(self, factor_guid, new_weighting): except Exception as e: # Handle any exceptions and log the error log_message(f"Error updating weighting: {e}", level=Qgis.Warning) + + def updateDimensionWeighting(self, dimension_guid, new_weighting): + """Update the weighting of a specific dimension by its guid.""" + try: + # Search for the factor by name + dimension_item = self.getItemByGuid(dimension_guid) + # If found, update the weighting + if dimension_item: + dimension_item.setData(2, f"{new_weighting:.2f}") + # weighting references the level above (i.e. analysis) + dimension_item.attributes()["analysis_weighting"] = new_weighting + + else: + # Log if the factor name is not found + log_message( + f"Factor '{dimension_guid}' not found.", + tag="Geest", + level=Qgis.Warning, + ) + + except Exception as e: + # Handle any exceptions and log the error + log_message(f"Error updating weighting: {e}", level=Qgis.Warning) diff --git a/geest/core/workflow_factory.py b/geest/core/workflow_factory.py index 68ba565..ef64fea 100644 --- a/geest/core/workflow_factory.py +++ b/geest/core/workflow_factory.py @@ -79,7 +79,6 @@ def create_workflow( return PointPerCellWorkflow(item, cell_size_m, feedback, context) 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_polygon_per_cell": return PolygonPerCellWorkflow(item, cell_size_m, feedback, context) elif analysis_mode == "factor_aggregation": diff --git a/geest/core/workflows/aggregation_workflow_base.py b/geest/core/workflows/aggregation_workflow_base.py index 91fd2bf..56faef4 100644 --- a/geest/core/workflows/aggregation_workflow_base.py +++ b/geest/core/workflows/aggregation_workflow_base.py @@ -189,10 +189,11 @@ def get_raster_list(self, index) -> list: item = self.item.getItemByGuid(guid) status = item.getStatus() == "Completed successfully" mode = item.attributes().get("analysis_mode", "Do Not Use") == "Do Not Use" + excluded = item.getStatus() == "Excluded from analysis" id = item.attribute("id").lower() - if not status and not mode: + if not status and not mode and not excluded: raise ValueError( - f"{id} is not completed successfully and is not set to 'Do Not Use'" + f"{id} is not completed successfully and is not set to 'Do Not Use' or 'Excluded from analysis'" ) if mode: @@ -202,6 +203,13 @@ def get_raster_list(self, index) -> list: level=Qgis.Info, ) continue + if excluded: + log_message( + f"Skipping {item.attribute('id')} as it is excluded from analysis", + tag="Geest", + level=Qgis.Info, + ) + continue if not item.attribute("result_file", ""): log_message( f"Skipping {id} as it has no result file", diff --git a/geest/core/workflows/analysis_aggregation_workflow.py b/geest/core/workflows/analysis_aggregation_workflow.py index a554c03..7857c2c 100644 --- a/geest/core/workflows/analysis_aggregation_workflow.py +++ b/geest/core/workflows/analysis_aggregation_workflow.py @@ -30,8 +30,14 @@ 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.id = "geest_analysis" - self.aggregation_attributes = self.item.getAnalysisAttributes() - self.layers = self.aggregation_attributes.get(f"dimensions", []) + self.guids = ( + self.item.getAnalysisDimensionGuids() + ) # get a list of the items to aggregate + self.id = ( + self.item.attribute("analysis_name") + .lower() + .replace(" ", "_") + .replace("'", "") + ) # should not be needed any more self.weight_key = "dimension_weighting" self.workflow_name = "analysis_aggregation" diff --git a/geest/gui/dialogs/analysis_aggregation_dialog.py b/geest/gui/dialogs/analysis_aggregation_dialog.py index 7c0bbff..70a9fff 100644 --- a/geest/gui/dialogs/analysis_aggregation_dialog.py +++ b/geest/gui/dialogs/analysis_aggregation_dialog.py @@ -129,7 +129,6 @@ def __init__(self, analysis_item, editing=False, parent=None): dimension_id = attributes.get("name") analysis_weighting = float(attributes.get("analysis_weighting", 0.0)) default_analysis_weighting = attributes.get("default_analysis_weighting", 0) - dimension_required = attributes.get("required", 1) name_item = QTableWidgetItem(dimension_id) name_item.setFlags(Qt.ItemIsEnabled) @@ -148,8 +147,6 @@ def __init__(self, analysis_item, editing=False, parent=None): # Use checkboxes checkbox_widget = self.create_checkbox_widget(row, analysis_weighting) self.table.setCellWidget(row, 2, checkbox_widget) - if dimension_required == 1: - checkbox_widget.setEnabled(False) # Reset button reset_button = QPushButton("Reset") @@ -226,7 +223,7 @@ def create_checkbox_widget(self, row: int, analysis_weighting: float) -> QWidget checkbox.stateChanged.connect( lambda state, r=row: self.toggle_row_widgets(r, state) ) - + checkbox.setEnabled(True) # Enable by default # Create a container widget with a centered layout container = QWidget() layout = QHBoxLayout() diff --git a/geest/gui/dialogs/dimension_aggregation_dialog.py b/geest/gui/dialogs/dimension_aggregation_dialog.py index 60c5c80..0b300a5 100644 --- a/geest/gui/dialogs/dimension_aggregation_dialog.py +++ b/geest/gui/dialogs/dimension_aggregation_dialog.py @@ -150,8 +150,6 @@ def __init__( default_dimension_weighting = attributes.get( "default_dimension_weighting", 0 ) - factor_required = attributes.get("required", 1) - name_item = QTableWidgetItem(factor_id) name_item.setFlags(Qt.ItemIsEnabled) self.table.setItem(row, 0, name_item) @@ -169,8 +167,6 @@ def __init__( # Use checkboxes checkbox_widget = self.create_checkbox_widget(row, dimension_weighting) self.table.setCellWidget(row, 2, checkbox_widget) - if factor_required == 1: - checkbox_widget.setEnabled(False) # Reset button reset_button = QPushButton("Reset") diff --git a/geest/gui/dialogs/factor_aggregation_dialog.py b/geest/gui/dialogs/factor_aggregation_dialog.py index 857ee21..2ce1a06 100644 --- a/geest/gui/dialogs/factor_aggregation_dialog.py +++ b/geest/gui/dialogs/factor_aggregation_dialog.py @@ -238,9 +238,7 @@ def populate_table(self): data_source_widget = DataSourceWidgetFactory.create_widget( attributes["analysis_mode"], 1, attributes ) - default_indicator_factor_weighting = attributes.get( - "default_indicator_factor_weighting", 0 - ) + default_factor_weighting = attributes.get("default_factor_weighting", 0) self.table.setCellWidget(row, 0, data_source_widget) self.data_sources[guid] = data_source_widget @@ -273,7 +271,7 @@ def populate_table(self): # Reset Button reset_button = QPushButton("Reset") reset_button.clicked.connect( - lambda checked, item=weighting_item, value=default_indicator_factor_weighting: item.setValue( + lambda checked, item=weighting_item, value=default_factor_weighting: item.setValue( value ) ) diff --git a/geest/gui/panels/create_project_panel.py b/geest/gui/panels/create_project_panel.py index 544a679..cca8b13 100644 --- a/geest/gui/panels/create_project_panel.py +++ b/geest/gui/panels/create_project_panel.py @@ -64,6 +64,11 @@ def initUI(self): self.create_project_directory_button.clicked.connect( self.create_new_project_folder ) + project_crs = QgsProject.instance().crs().authid() + if project_crs == "EPSG:4326" or project_crs == "": + self.use_boundary_crs.setChecked(False) + self.use_boundary_crs.setEnabled(False) + self.next_button.clicked.connect(self.create_project) self.previous_button.clicked.connect(self.on_previous_button_clicked) diff --git a/geest/gui/panels/tree_panel.py b/geest/gui/panels/tree_panel.py index b431ebf..84dcc20 100644 --- a/geest/gui/panels/tree_panel.py +++ b/geest/gui/panels/tree_panel.py @@ -243,7 +243,7 @@ def on_item_double_clicked(self, index): elif item.role == "factor": self.edit_factor_aggregation(item) elif item.role == "analysis": - self.show_attributes(item) + self.edit_analysis_aggregation(item) def on_previous_button_clicked(self): self.switch_to_previous_tab.emit() @@ -285,8 +285,7 @@ def clear_workflows(self): self.save_json_to_working_directory() def clear_all_items(self, parent_item=None): - # Bool test for if it was triggered from a button or menu - if parent_item is None or isinstance(parent_item, bool): + if parent_item is None: parent_item = self.model.rootItem for i in range(parent_item.childCount()): child_item = parent_item.child(i) @@ -359,11 +358,7 @@ def working_directory_changed(self, new_directory): if os.path.exists(master_model_path): try: shutil.copy(master_model_path, model_path) - log_message( - f"Copied master model.json to {model_path}", - "Geest", - level=Qgis.Info, - ) + log_message(f"Copied master model.json to {model_path}") except Exception as e: log_message( f"Error copying master model.json: {str(e)}", @@ -482,7 +477,7 @@ def update_action_text(): update_action_text() clear_results_action = QAction("Clear Results", self) - clear_results_action.triggered.connect(self.clear_all_items) + clear_results_action.triggered.connect(self.clear_workflows) # Update when menu shows menu = QMenu(self) @@ -507,6 +502,9 @@ def update_action_text(): menu.addAction(clear_results_action) menu.addAction(run_item_action) menu.addAction(add_to_map_action) + add_study_area_layers_action = QAction("Add Study Area to Map", self) + add_study_area_layers_action.triggered.connect(self.add_study_area_to_map) + menu.addAction(add_study_area_layers_action) # Check the role of the item directly from the stored role if item.role == "dimension": @@ -764,38 +762,67 @@ def show_context_menu(self, table, pos): clipboard = QApplication.clipboard() clipboard.setText(item.text()) - def add_to_map(self, item): - """Add the item to the map.""" - # TODO refactor use of the term Layer everywhere to Indicator - # for now, some spaghetti code to get the layer_uri + def add_study_area_to_map(self): + """Add the study area layers to the map. + + Note that the area grid layer can be slow to draw!. + """ gpkg_path = os.path.join( self.working_directory, "study_area", "study_area.gpkg" ) + project = QgsProject.instance() + + # Check if 'Geest' group exists, otherwise create it + root = project.layerTreeRoot() + geest_group = root.findGroup("Geest Study Area") + if geest_group is None: + geest_group = root.insertGroup( + 0, "Geest Study Area" + ) # Insert at the top of the layers panel + + layers = [ + "study_area_bbox", + "study_area_bboxes", + "study_area_polygons", + "study_area_grid", + ] + for layer_name in layers: + gpkg_layer_path = f"{gpkg_path}|layername={layer_name}" + layer = QgsVectorLayer(gpkg_layer_path, layer_name, "ogr") - if item.role == "analysis": - layers = [ - "study_area_bbox", - "study_area_bboxes", - "study_area_polygons", - "study_area_grid", - ] - for layer_name in layers: - gpkg_layer_path = f"{gpkg_path}|layername={layer_name}" - layer = QgsVectorLayer(gpkg_layer_path, layer_name, "ogr") - - if layer.isValid(): - QgsProject.instance().addMapLayer(layer) - log_message( - f"Added '{layer_name}' layer to the map.", - tag="Geest", - level=Qgis.Info, - ) - else: - log_message( - f"Failed to add '{layer_name}' layer to the map.", - tag="Geest", - level=Qgis.Critical, - ) + if layer.isValid(): + QgsProject.instance().addMapLayer(layer) + log_message(f"Added '{layer_name}' layer to the map.") + else: + log_message( + f"Failed to add '{layer_name}' layer to the map.", + tag="Geest", + level=Qgis.Critical, + ) + + # Check if a layer with the same data source exists in the correct group + existing_layer = None + for child in geest_group.children(): + if isinstance(child, QgsLayerTreeGroup): + continue + if child.layer().source() == gpkg_layer_path: + existing_layer = child.layer() + break + + # If the layer exists, refresh it instead of removing and re-adding + if existing_layer is not None: + log_message(f"Refreshing existing layer: {existing_layer.name()}") + existing_layer.reload() + else: + # Add the new layer to the appropriate subgroup + QgsProject.instance().addMapLayer(layer, False) + layer_tree_layer = geest_group.addLayer(layer) + log_message( + f"Added layer: {layer.name()} to group: {geest_group.name()}" + ) + + def add_to_map(self, item): + """Add the item to the map.""" layer_uri = item.attribute(f"result_file") diff --git a/geest/gui/views/treeview.py b/geest/gui/views/treeview.py index 1020581..6b3d8d5 100644 --- a/geest/gui/views/treeview.py +++ b/geest/gui/views/treeview.py @@ -74,13 +74,24 @@ def loadJsonData(self, json_data): analysis_cell_size_m = json_data.get("analysis_cell_size_m", 100.0) working_folder = json_data.get("working_folder", "Not Set") guid = json_data.get("guid", str(uuid.uuid4())) # Deserialize UUID - + analysis_result = json_data.get("result", "") + analysis_execution_start_time = json_data.get("execution_start_time", "") + analysis_result_file = json_data.get("result_file", "") + analysis_execution_end_time = json_data.get("execution_end_time", "") + analysis_error = json_data.get("error", "") + analysis_error_file = json_data.get("error_file", "") # Store special properties in the attributes dictionary analysis_attributes = { "analysis_name": analysis_name, "description": analysis_description, "working_folder": working_folder, "analysis_cell_size_m": analysis_cell_size_m, + "result": analysis_result, + "result_file": analysis_result_file, + "execution_start_time": analysis_execution_start_time, + "execution_end_time": analysis_execution_end_time, + "error": analysis_error, + "error_file": analysis_error_file, } # Create the "Analysis" item @@ -134,10 +145,10 @@ def _create_dimension_item(self, dimension, parent_item): "id": dimension.get("id", ""), "name": dimension.get("name", ""), "description": dimension.get("description", ""), - "required": dimension.get("required", False), "default_analysis_weighting": dimension.get( "default_analysis_weighting", 0.0 ), + "analysis_weighting": dimension.get("analysis_weighting", 0.0), "analysis_mode": dimension.get("factor_aggregation", ""), "result": dimension.get("result", ""), "execution_start_time": dimension.get("execution_start_time", ""), @@ -177,7 +188,6 @@ def _create_factor_item(self, factor, parent_item): "id": factor.get("id", ""), "name": factor.get("name", ""), "description": factor.get("description", ""), - "required": factor.get("required", False), "default_dimension_weighting": factor.get( "default_dimension_weighting", 0.0 ), @@ -368,6 +378,7 @@ def recurse_tree(item): "guid": item.guid, # Serialize UUID "dimensions": [recurse_tree(child) for child in item.childItems], } + json_data.update(item.attributes()) return json_data elif item.role == "dimension": json_data = { diff --git a/geest/resources/README-DEVELOPERS.md b/geest/resources/README-DEVELOPERS.md index c0e86e9..168721c 100644 --- a/geest/resources/README-DEVELOPERS.md +++ b/geest/resources/README-DEVELOPERS.md @@ -18,7 +18,7 @@ graph TB D --> D1[indicator: string] D --> D2[id: string] D --> D3[description: string] - D --> D4[default_indicator_factor_weighting: number] + D --> D4[default_factor_weighting: number] D --> D5[default_index_score: integer] D --> D6[index_score: integer] D --> D7[use_default_index_score: integer] diff --git a/geest/resources/geest2.ods b/geest/resources/geest2.ods index 4a249e2..07db9c0 100644 Binary files a/geest/resources/geest2.ods and b/geest/resources/geest2.ods differ diff --git a/geest/resources/model.json b/geest/resources/model.json index 321330f..478fc7d 100644 --- a/geest/resources/model.json +++ b/geest/resources/model.json @@ -3,21 +3,22 @@ { "id": "contextual", "name": "Contextual", - "required": 0.0, "default_analysis_weighting": 0.333333, + "analysis_weighting": 0.333333, "description": "The Contextual Dimension refers to the laws and policies that shape workplace gender discrimination, financial autonomy, and overall gender empowerment. Although this dimension may vary between countries due to differences in legal frameworks, it remains consistent within a single country, as national policies and regulations are typically applied uniformly across countries.", "factors": [ { "id": "workplace_discrimination", "name": "Workplace Discrimination", - "required": 0.0, "default_dimension_weighting": 0.333333, + "dimension_weighting": 0.333333, "indicators": [ { "indicator": "WBL 2024 Workplace Index Score", "id": "Workplace_Index", "description": "", - "default_indicator_factor_weighting": 1.0, + "default_factor_weighting": 1.0, + "factor_weighting": 1.0, "default_index_score": 0, "index_score": 100, "use_default_index_score": 1, @@ -44,8 +45,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Workplace Discrimination involves laws that address gender biases and stereotypes that hinder women's career advancement, especially in male-dominated fields." @@ -53,14 +53,15 @@ { "id": "regulatory_frameworks", "name": "Regulatory Frameworks", - "required": 0.0, "default_dimension_weighting": 0.3333333, + "dimension_weighting": 0.3333333, "indicators": [ { "indicator": "WBL 2024 Pay+Parenthood Index Score", "id": "Pay_Parenthood_Index", "description": "", - "default_indicator_factor_weighting": 1.0, + "default_factor_weighting": 1.0, + "factor_weighting": 1.0, "default_index_score": 0, "index_score": 100, "use_default_index_score": 1, @@ -87,8 +88,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Regulatory Frameworks pertain to laws and policies that protect women\u2019s employment rights, such as childcare support and parental leave, influencing their workforce participation" @@ -96,14 +96,15 @@ { "id": "financial_inclusion", "name": "Financial Inclusion", - "required": 0.0, "default_dimension_weighting": 0.333333, + "dimension_weighting": 0.333333, "indicators": [ { "indicator": "WBL 2024 Entrepeneurship Index Score", "id": "Entrepeneurship_Index", "description": "", - "default_indicator_factor_weighting": 1.0, + "default_factor_weighting": 1.0, + "factor_weighting": 1.0, "default_index_score": 0, "index_score": 100, "use_default_index_score": 1, @@ -130,8 +131,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Financial Inclusion involves laws concerning women\u2019s access to financial resources like loans and credit, which is crucial for starting businesses and investing in economic opportunities." @@ -141,21 +141,22 @@ { "id": "accessibility", "name": "Accessibility", - "required": 0.0, "default_analysis_weighting": 0.33333, + "analysis_weighting": 0.33333, "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", "name": "Women's Travel Patterns", - "required": 0.0, "default_dimension_weighting": 0.2, + "dimension_weighting": 0.2, "indicators": [ { "indicator": "Location of kindergartens/childcare", "id": "Kindergartens_Location", "description": "", - "default_indicator_factor_weighting": 0.2, + "default_factor_weighting": 0.2, + "factor_weighting": 0.2, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -182,14 +183,14 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" }, { "indicator": "Location of primary schools", "id": "Primary_School_Location", "description": "", - "default_indicator_factor_weighting": 0.2, + "default_factor_weighting": 0.2, + "factor_weighting": 0.2, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -216,14 +217,14 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" }, { "indicator": "Location of groceries", "id": "Groceries_Location", "description": "", - "default_indicator_factor_weighting": 0.2, + "default_factor_weighting": 0.2, + "factor_weighting": 0.2, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -242,7 +243,7 @@ "use_other_downloader": 0, "use_add_layers_manually": 0, "use_classify_polygon_into_classes": 0, - "use_classify_safety_polygon_into_classes": "", + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, @@ -250,14 +251,14 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" }, { "indicator": "Location of pharmacies", "id": "Pharmacies_Location", "description": "", - "default_indicator_factor_weighting": 0.2, + "default_factor_weighting": 0.2, + "factor_weighting": 0.2, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -276,7 +277,7 @@ "use_other_downloader": "NDVI", "use_add_layers_manually": 0, "use_classify_polygon_into_classes": 0, - "use_classify_safety_polygon_into_classes": "", + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, @@ -284,14 +285,14 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" }, { "indicator": "Location of green spaces", "id": "Green_Space_location", "description": "", - "default_indicator_factor_weighting": 0.2, + "default_factor_weighting": 0.2, + "factor_weighting": 0.2, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -310,7 +311,7 @@ "use_other_downloader": 0, "use_add_layers_manually": 0, "use_classify_polygon_into_classes": 0, - "use_classify_safety_polygon_into_classes": "", + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, @@ -318,8 +319,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Women\u2019s Travel Patterns (WTP) refer to the unique travel behaviors of women, often involving multiple stops for household or caregiving tasks, making proximity to essential services like markets, supermarkets, childcare centers, primary schools, pharmacies, and green spaces crucial." @@ -327,14 +327,15 @@ { "id": "access_to_public_transport", "name": "Access to Public Transport", - "required": 0.0, "default_dimension_weighting": 0.2, + "dimension_weighting": 0.2, "indicators": [ { "indicator": "Location of public transportation stops, including maritime", "id": "Pulic_Transport_location", "description": "", - "default_indicator_factor_weighting": 1.0, + "default_factor_weighting": 1.0, + "factor_weighting": 1.0, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -353,7 +354,7 @@ "use_other_downloader": 0, "use_add_layers_manually": 0, "use_classify_polygon_into_classes": 0, - "use_classify_safety_polygon_into_classes": "", + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, @@ -361,8 +362,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Access to Public Transport focuses on the availability and proximity of public transportation stops, which is crucial for women, especially those who rely on buses, trains, or trams to access jobs, education, and essential services." @@ -370,14 +370,15 @@ { "id": "access_to_health_facilities", "name": "Access to Health Facilities", - "required": 0.0, "default_dimension_weighting": 0.2, + "dimension_weighting": 0.2, "indicators": [ { "indicator": "Location of hospitals and clinics", "id": "Hospital_Location", "description": "", - "default_indicator_factor_weighting": 1.0, + "default_factor_weighting": 1.0, + "factor_weighting": 1.0, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -396,7 +397,7 @@ "use_other_downloader": 0, "use_add_layers_manually": 0, "use_classify_polygon_into_classes": 0, - "use_classify_safety_polygon_into_classes": "", + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, @@ -404,8 +405,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Access to Health Facilities evaluates how easily women can reach healthcare services in terms of distance, impacting their well-being and ability to participate in the workforce." @@ -413,14 +413,15 @@ { "id": "access_to_education_and_training_facilities", "name": "Access to Education and Training Facilities", - "required": 0.0, "default_dimension_weighting": 0.2, + "dimension_weighting": 0.2, "indicators": [ { "indicator": "Location of universities and technical schools", "id": "Universities_Location", "description": "", - "default_indicator_factor_weighting": 1.0, + "default_factor_weighting": 1.0, + "factor_weighting": 1.0, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -439,7 +440,7 @@ "use_other_downloader": 0, "use_add_layers_manually": 0, "use_classify_polygon_into_classes": 0, - "use_classify_safety_polygon_into_classes": "", + "use_classify_safety_polygon_into_classes": 0.0, "use_csv_to_point_layer": 0, "use_polygon_per_cell": 0, "use_polyline_per_cell": 0, @@ -447,8 +448,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Access to Education and Training Facilities assesses the proximity to higher education institutions and training centers, influencing women's ability to gain necessary qualifications." @@ -456,14 +456,15 @@ { "id": "access_to_financial_facilities", "name": "Access to Financial Facilities", - "required": 0.0, "default_dimension_weighting": 0.2, + "dimension_weighting": 0.2, "indicators": [ { "indicator": "Location of Banks and other FF", "id": "Banks_Location", "description": "", - "default_indicator_factor_weighting": 1.0, + "default_factor_weighting": 1.0, + "factor_weighting": 1.0, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -490,8 +491,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Access to Financial Facilities focuses on the proximity of banks and financial institutions, which is essential for women\u2019s economic empowerment and ability to access credit." @@ -501,21 +501,22 @@ { "id": "place_characterization", "name": "Place Characterization", - "required": 0.0, "default_analysis_weighting": 0.333333, + "analysis_weighting": 0.333333, "description": "The Place-Characterization Dimension refers to the social, environmental, and infrastructural attributes of geographical locations, such as walkability, safety, and vulnerability to natural hazards. Unlike the Accessibility Dimension, these factors do not involve mobility but focus on the inherent characteristics of a place that influence women\u2019s ability to participate in the workforce.", "factors": [ { "id": "active_transport", "name": "Active Transport", - "required": 0.0, "default_dimension_weighting": 0.142857142857143, + "dimension_weighting": 0.142857142857143, "indicators": [ { "indicator": "Location of street crossings", "id": "Street_Crossing_Location", "description": "", - "default_indicator_factor_weighting": 0.25, + "default_factor_weighting": 0.25, + "factor_weighting": 0.25, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -542,14 +543,14 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" }, { "indicator": "Location of cycle paths", "id": "Cycle_Paths_Location", "description": "", - "default_indicator_factor_weighting": 0.25, + "default_factor_weighting": 0.25, + "factor_weighting": 0.25, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -576,14 +577,14 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" }, { "indicator": "Location of footpaths", "id": "Footpaths_Location", "description": "", - "default_indicator_factor_weighting": 0.25, + "default_factor_weighting": 0.25, + "factor_weighting": 0.25, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -610,14 +611,14 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" }, { "indicator": "Block Layout", "id": "Block_Layout", "description": "", - "default_indicator_factor_weighting": 0.25, + "default_factor_weighting": 0.25, + "factor_weighting": 0.25, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -644,8 +645,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Active Transport refers to the presence of walkable environments and cycling infrastructure, as women often rely on walking or cycling for their daily commutes and errands." @@ -653,14 +653,15 @@ { "id": "safety", "name": "Safety", - "required": 0.0, "default_dimension_weighting": 0.142857142857143, + "dimension_weighting": 0.142857142857143, "indicators": [ { "indicator": "Street lights/Nigthttime lights", "id": "Street_Lights", "description": "", - "default_indicator_factor_weighting": 1.0, + "default_factor_weighting": 1.0, + "factor_weighting": 1.0, "default_index_score": 0, "index_score": 100, "use_default_index_score": 1, @@ -687,8 +688,7 @@ "use_nighttime_lights": 1, "use_environmental_hazards": 0, "use_street_lights": 1, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Safety addresses the perceived security of public spaces, evaluated through the availability of adequate lighting, which affects women\u2019s ability to move freely, seek employment, and access essential services." @@ -696,14 +696,15 @@ { "id": "fcv", "name": "FCV", - "required": 0.0, "default_dimension_weighting": 0.142857142857143, + "dimension_weighting": 0.142857142857143, "indicators": [ { "indicator": "ACLED data (Violence Estimated Events)", "id": "FCV", "description": "", - "default_indicator_factor_weighting": 1.0, + "default_factor_weighting": 1.0, + "factor_weighting": 1.0, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -730,8 +731,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Fragility, Conflict, and Violence (FCV) considers the frequency of events related to political unrest, conflict, and violence in a region, which can increase women\u2019s vulnerability and limit their access to employment and essential services." @@ -739,14 +739,15 @@ { "id": "education", "name": "Education", - "required": 0.0, "default_dimension_weighting": 0.142857142857143, + "dimension_weighting": 0.142857142857143, "indicators": [ { "indicator": "percentage of the labor force comprising women with university degrees", "id": "Education", "description": "", - "default_indicator_factor_weighting": 1.0, + "default_factor_weighting": 1.0, + "factor_weighting": 1.0, "default_index_score": 0, "index_score": 100, "use_default_index_score": 1, @@ -773,8 +774,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Education refers to the proportion of women in a region who have attained higher education, particularly in the specific field of analysis, serving as an indicator of societal attitudes towards women working in that sector." @@ -782,14 +782,15 @@ { "id": "digital_inclusion", "name": "Digital Inclusion", - "required": 0.0, "default_dimension_weighting": 0.142857142857143, + "dimension_weighting": 0.142857142857143, "indicators": [ { "indicator": "Individuals using the Internet (% of population)", "id": "Digital_Inclusion", "description": "", - "default_indicator_factor_weighting": 1.0, + "default_factor_weighting": 1.0, + "factor_weighting": 1.0, "default_index_score": 0, "index_score": 100, "use_default_index_score": 1, @@ -816,8 +817,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Digital Inclusion assesses the presence of digital infrastructure in a specific location, which is essential for women to pursue job opportunities, access training band education opportunities, and use financial services." @@ -825,14 +825,15 @@ { "id": "environmental_hazards", "name": "Environmental Hazards", - "required": 0.0, "default_dimension_weighting": 0.142857142857143, + "dimension_weighting": 0.142857142857143, "indicators": [ { "indicator": "Fire Hazards", "id": "Fire", "description": "", - "default_indicator_factor_weighting": 0.2, + "default_factor_weighting": 0.2, + "factor_weighting": 0.2, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -859,14 +860,14 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 1, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" }, { "indicator": "Flood Hazards", "id": "Flood", "description": "", - "default_indicator_factor_weighting": 0.2, + "default_factor_weighting": 0.2, + "factor_weighting": 0.2, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -893,14 +894,14 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 1, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" }, { "indicator": "Landslide", "id": "Landslide", "description": "", - "default_indicator_factor_weighting": 0.2, + "default_factor_weighting": 0.2, + "factor_weighting": 0.2, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -927,14 +928,14 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 1, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" }, { "indicator": "Tropical Cyclone", "id": "Cyclone", "description": "", - "default_indicator_factor_weighting": 0.2, + "default_factor_weighting": 0.2, + "factor_weighting": 0.2, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -961,14 +962,14 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 1, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" }, { "indicator": "Drought", "id": "Drought", "description": "", - "default_indicator_factor_weighting": 0.2, + "default_factor_weighting": 0.2, + "factor_weighting": 0.2, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -995,8 +996,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 1, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Environmental Hazards relate to the impact of environmental risks, such as floods, droughts, landslides, fires, and extreme weather events, which can disrupt job stability, particularly for women in vulnerable sectors." @@ -1004,14 +1004,15 @@ { "id": "water_sanitation", "name": "Water sanitation", - "required": 0.0, "default_dimension_weighting": 0.142857142857143, + "dimension_weighting": 0.142857142857143, "indicators": [ { "indicator": "Water points (OSM), catch basins, water valves and fire hydrants", "id": "Water_Sanitation", "description": "", - "default_indicator_factor_weighting": 1.0, + "default_factor_weighting": 1.0, + "factor_weighting": 1.0, "default_index_score": 0, "index_score": 0, "use_default_index_score": 0, @@ -1038,8 +1039,7 @@ "use_nighttime_lights": 0, "use_environmental_hazards": 0, "use_street_lights": 0, - "analysis_mode": "Do Not Use", - "indicator_required": 1 + "analysis_mode": "Do Not Use" } ], "description": "Water and Sanitation concerns the availability of clean water and sanitation facilities, affecting women\u2019s time allocation and capacity to engage in employment." diff --git a/geest/resources/qml/analysis.qml b/geest/resources/qml/analysis.qml new file mode 100644 index 0000000..94258c4 --- /dev/null +++ b/geest/resources/qml/analysis.qml @@ -0,0 +1,176 @@ + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + + + + + + + + + + + + + + + + + resamplingFilter + + 0 + diff --git a/geest/resources/schema.json b/geest/resources/schema.json index d17775d..88f310b 100644 --- a/geest/resources/schema.json +++ b/geest/resources/schema.json @@ -53,7 +53,7 @@ "description": { "type": "string" }, - "default_indicator_factor_weighting": { + "default_factor_weighting": { "type": "number" }, "default_index_score": { @@ -139,7 +139,7 @@ "indicator", "id", "description", - "default_indicator_factor_weighting", + "default_factor_weighting", "default_index_score", "index_score", "use_default_index_score",