diff --git a/ra2ce/analysis/indirect/multi_link_redundancy.py b/ra2ce/analysis/indirect/multi_link_redundancy.py index a8fd05e4f..170c39854 100644 --- a/ra2ce/analysis/indirect/multi_link_redundancy.py +++ b/ra2ce/analysis/indirect/multi_link_redundancy.py @@ -6,6 +6,7 @@ import osmnx import pandas as pd from geopandas import GeoDataFrame +import geopandas as gpd from ra2ce.analysis.analysis_config_data.analysis_config_data import ( AnalysisSectionIndirect, @@ -39,6 +40,35 @@ def __init__( self.output_path = analysis_input.output_path self.hazard_names = analysis_input.hazard_names + def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame): + """ + updates the time column with the calculated dataframe and updates the rest of the gdf_graph if time is None. + """ + if ( + WeighingEnum.TIME.config_value not in gdf_graph.columns + or WeighingEnum.TIME.config_value not in gdf_calculated.columns + ): + return gdf_graph + gdf_graph[WeighingEnum.TIME.config_value] = gdf_calculated[ + WeighingEnum.TIME.config_value + ] + for i, row in gdf_graph.iterrows(): + row_avgspeed = row.get("avgspeed", None) + row_length = row.get("length", None) + if ( + pd.isna(row[WeighingEnum.TIME.config_value]) + and row_avgspeed + and row_length + ): + gdf_graph.at[i, WeighingEnum.TIME.config_value] = ( + row_length * 1e-3 / row_avgspeed + ) + else: + gdf_graph.at[i, WeighingEnum.TIME.config_value] = row.get( + WeighingEnum.TIME.config_value, None + ) + return gdf_graph + def execute(self) -> GeoDataFrame: """Calculates the multi-link redundancy of a NetworkX graph. @@ -49,6 +79,15 @@ def execute(self) -> GeoDataFrame: Returns: aggregated_results (GeoDataFrame): The results of the analysis aggregated into a table. """ + + def _is_not_none(value): + return ( + value is not None + and value is not pd.NA + and not pd.isna(value) + and not np.isnan(value) + ) + results = [] master_graph = copy.deepcopy(self.graph_file_hazard.get_graph()) for hazard in self.hazard_names.names: @@ -72,7 +111,15 @@ def execute(self) -> GeoDataFrame: edges_remove = [ e for e in edges_remove - if (e[-1][hazard_name] > float(self.analysis.threshold)) + if (hazard_name in e[-1]) + and ( + _is_not_none(e[-1][hazard_name]) + and (e[-1][hazard_name] > float(self.analysis.threshold)) + and ( + ("bridge" not in e[-1]) + or ("bridge" in e[-1] and e[-1]["bridge"] != "yes") + ) + ) ] _graph.remove_edges_from(edges_remove) @@ -82,6 +129,7 @@ def execute(self) -> GeoDataFrame: "v", f"alt_{self.analysis.weighing.config_value}", "alt_nodes", + f"diff_{self.analysis.weighing.config_value}", "connected", ] @@ -109,11 +157,20 @@ def execute(self) -> GeoDataFrame: alt_value = _weighing_analyser.calculate_distance() alt_nodes, connected = np.NaN, 0 + diff = round( + alt_value + - _weighing_analyser.weighing_data[ + self.analysis.weighing.config_value + ], + 3, + ) + data = { "u": [u], "v": [v], f"alt_{self.analysis.weighing.config_value}": [alt_value], "alt_nodes": [alt_nodes], + f"diff_{self.analysis.weighing.config_value}": diff, "connected": [connected], } _weighing_analyser.extend_graph(data) @@ -135,15 +192,7 @@ def execute(self) -> GeoDataFrame: else: gdf = gdf.merge(df_calculated, how="left", on=["u", "v"]) - # calculate the differences in distance and time - # previously here you find if dist==dist which is a critical bug. Replaced by verifying dist is a value. - gdf[f"diff_{self.analysis.weighing.config_value}"] = [ - round(alt - base, 2) if alt else np.NaN - for (alt, base) in zip( - gdf[f"alt_{self.analysis.weighing.config_value}"], - gdf[f"{self.analysis.weighing.config_value}"], - ) - ] + gdf = self._update_time(df_calculated, gdf) gdf["hazard"] = hazard_name diff --git a/ra2ce/analysis/indirect/weighing_analysis/time_weighing_analysis.py b/ra2ce/analysis/indirect/weighing_analysis/time_weighing_analysis.py index 989d84e8c..2d633d8f4 100644 --- a/ra2ce/analysis/indirect/weighing_analysis/time_weighing_analysis.py +++ b/ra2ce/analysis/indirect/weighing_analysis/time_weighing_analysis.py @@ -1,4 +1,7 @@ import geopandas as gpd +import numpy as np +import pandas as pd + from ra2ce.analysis.analysis_config_data.enums.weighing_enum import WeighingEnum from ra2ce.analysis.indirect.weighing_analysis.weighing_analysis_protocol import ( WeighingAnalysisProtocol, @@ -13,21 +16,33 @@ def __init__(self) -> None: self.time_list = [] def _calculate_time(self) -> float: - _calculated_time = round( - (self.weighing_data["length"] * 1e-3) / self.weighing_data["avgspeed"], - 2, - ) # in hours and avg speed in km/h - self.weighing_data[WeighingEnum.TIME.config_value] = _calculated_time - return round(_calculated_time, 2) + length = self.weighing_data.get("length", None) + avgspeed = self.weighing_data.get("avgspeed", None) + if length and avgspeed: + _calculated_time = round( + (length * 1e-3) / avgspeed, + 3, + ) # in hours and avg speed in km/h + self.weighing_data[WeighingEnum.TIME.config_value] = _calculated_time + return round(_calculated_time, 3) + else: + return np.nan def calculate_distance(self) -> float: self.time_list.append(self._calculate_time()) return self.time_list[-1] def calculate_alternative_distance(self, alt_dist: float) -> float: - alt_time = (alt_dist * 1e-3) / self.weighing_data["avgspeed"] # in hours - self.time_list.append(self._calculate_time()) - return alt_time + avgspeed = self.weighing_data.get("avgspeed", None) + if avgspeed: + alt_time = (alt_dist * 1e-3) / avgspeed # in hours + self.time_list.append(self._calculate_time()) + return alt_time + else: + return np.nan def extend_graph(self, gdf_graph: gpd.GeoDataFrame | dict) -> None: - gdf_graph[WeighingEnum.TIME.config_value] = self.time_list + if isinstance(gdf_graph, gpd.GeoDataFrame): + gdf_graph[WeighingEnum.TIME.config_value] = self.time_list + elif isinstance(gdf_graph, dict): + gdf_graph[WeighingEnum.TIME.config_value] = [self.time_list[-1]]