Skip to content

Commit

Permalink
fix: 433 avg speed 0
Browse files Browse the repository at this point in the history
  • Loading branch information
ArdtK authored Jun 12, 2024
1 parent b9c18be commit ef96edf
Show file tree
Hide file tree
Showing 13 changed files with 242 additions and 205 deletions.
7 changes: 0 additions & 7 deletions ra2ce/analysis/losses/losses.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,6 @@ def _check_validity_df(self):
{self.link_id} is passed for feature ids of the graph"""
)

def _load_gdf(self, gdf_path: Path) -> gpd.GeoDataFrame:
"""This method reads the dataframe created from a .csv"""
if gdf_path.exists():
return gpd.read_file(gdf_path, index_col=f"{self.link_id}")
logging.warning("No `gdf` file found at {}.".format(gdf_path))
return gpd.GeoDataFrame()

def _get_vot_intensity_per_trip_purpose(self) -> dict[str, pd.DataFrame]:
"""
Generates a dictionary with all available `vot_purpose` with their intensity as a `pd.DataFrame`.
Expand Down
81 changes: 39 additions & 42 deletions ra2ce/analysis/losses/multi_link_redundancy.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,38 +45,34 @@ def _update_time(
"""
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 df_calculated.columns:
return df_calculated, gdf_graph

if (
WeighingEnum.TIME.config_value in gdf_graph.columns
and WeighingEnum.TIME.config_value in df_calculated.columns
):
df_calculated = df_calculated.drop(columns=[WeighingEnum.TIME.config_value])
return df_calculated, gdf_graph

if (
WeighingEnum.TIME.config_value not in gdf_graph.columns
and WeighingEnum.TIME.config_value not in df_calculated.columns
):
return df_calculated, gdf_graph

elif WeighingEnum.TIME.config_value in df_calculated.columns:
gdf_graph[WeighingEnum.TIME.config_value] = df_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
)
gdf_graph[WeighingEnum.TIME.config_value] = df_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 df_calculated, gdf_graph

def execute(self) -> gpd.GeoDataFrame:
Expand Down Expand Up @@ -148,48 +144,49 @@ def _is_not_none(value):

df_calculated = pd.DataFrame(columns=columns)
_weighing_analyser = WeighingAnalysisFactory.get_analysis(
self.analysis.weighing
self.analysis.weighing, gdf
)

for edges in edges_remove:
u, v, k, _weighing_analyser.weighing_data = edges
# Ensure each edge has a valid weighing attribute
_current_value_list = []
for edge in list(edges_remove):
_weighing_analyser.edge_data = edge[3]
_current_value_list.append(_weighing_analyser.get_current_value())

for e, edges in enumerate(edges_remove):
u, v, _, _weighing_analyser.edge_data = edges

if nx.has_path(_graph, u, v):
alt_dist = nx.dijkstra_path_length(
_graph, u, v, weight=WeighingEnum.LENGTH.config_value
_graph, u, v, weight=self.analysis.weighing.config_value
)
alt_nodes = nx.dijkstra_path(_graph, u, v)
connected = 1
alt_value = _weighing_analyser.calculate_alternative_distance(
alt_dist
)
alt_value = _weighing_analyser.calculate_alternative_value(alt_dist)

diff = round(alt_value - _current_value_list[e], 3)
else:
alt_value = _weighing_analyser.calculate_distance()
alt_value = _current_value_list[e]
alt_nodes, connected = np.NaN, 0

current_value = _weighing_analyser.weighing_data[
self.analysis.weighing.config_value
]
if not current_value: # if None
current_value = np.nan
diff = round(alt_value - current_value, 3)
diff = np.NaN

data = {
"u": [u],
"v": [v],
self.analysis.weighing.config_value: [_current_value_list[e]],
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)

if "rfid" in gdf:
data["rfid"] = [str(_weighing_analyser.weighing_data["rfid"])]
data["rfid"] = [str(_weighing_analyser.edge_data["rfid"])]

df_calculated = pd.concat(
[df_calculated, pd.DataFrame(data)], ignore_index=True
)

df_calculated[f"alt_{self.analysis.weighing.config_value}"] = pd.to_numeric(
df_calculated[f"alt_{self.analysis.weighing.config_value}"],
errors="coerce",
Expand Down
54 changes: 28 additions & 26 deletions ra2ce/analysis/losses/single_link_redundancy.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import math
from pathlib import Path

import networkx as nx
import numpy as np
import osmnx
from geopandas import GeoDataFrame

from ra2ce.analysis.analysis_config_data.analysis_config_data import (
AnalysisSectionLosses,
)
from ra2ce.analysis.analysis_config_data.enums.weighing_enum import WeighingEnum
from ra2ce.analysis.analysis_input_wrapper import AnalysisInputWrapper
from ra2ce.analysis.losses.analysis_losses_protocol import AnalysisLossesProtocol
from ra2ce.analysis.losses.weighing_analysis.weighing_analysis_factory import (
Expand Down Expand Up @@ -50,56 +49,59 @@ def execute(self) -> GeoDataFrame:
_gdf_graph = osmnx.graph_to_gdfs(self.graph_file.get_graph(), nodes=False)

# list for the length of the alternative routes
_current_value_list = []
_alt_value_list = []
_alt_nodes_list = []
_diff_value_list = []
_detour_exist_list = []

_weighing_analyser = WeighingAnalysisFactory.get_analysis(
self.analysis.weighing
self.analysis.weighing, _gdf_graph
)
for e_remove in list(self.graph_file.graph.edges.data(keys=True)):
u, v, k, _weighing_analyser.weighing_data = e_remove

# if data['highway'] in attr_list:
# Ensure each edge has a valid weighing attribute
for edge in list(self.graph_file.graph.edges.data(keys=True)):
_weighing_analyser.edge_data = edge[3]
_current_value_list.append(_weighing_analyser.get_current_value())

# Loop over all edges to temporarily remove them and calculate the alternative route
for e, e_remove in enumerate(list(self.graph_file.graph.edges.data(keys=True))):
u, v, k, _weighing_analyser.edge_data = e_remove

# remove the edge
self.graph_file.graph.remove_edge(u, v, k)

if nx.has_path(self.graph_file.graph, u, v):

# calculate the alternative distance if that edge is unavailable
alt_dist = nx.dijkstra_path_length(
self.graph_file.graph, u, v, weight=WeighingEnum.LENGTH.config_value
_alt_dist = nx.dijkstra_path_length(
self.graph_file.graph,
u,
v,
weight=self.analysis.weighing.config_value,
)
alt_nodes = nx.dijkstra_path(self.graph_file.graph, u, v)
alt_value = _weighing_analyser.calculate_alternative_distance(alt_dist)
_alt_nodes = nx.dijkstra_path(self.graph_file.graph, u, v)
_alt_value = _weighing_analyser.calculate_alternative_value(_alt_dist)

# append alternative route nodes
_alt_value_list.append(alt_value)
_alt_nodes_list.append(alt_nodes)
_alt_value_list.append(_alt_value)
_alt_nodes_list.append(_alt_nodes)

# calculate the difference in distance
_diff_value_list.append(
round(
alt_value
- _weighing_analyser.weighing_data[
self.analysis.weighing.config_value
],
7,
)
)
_diff_value_list.append(round(_alt_value - _current_value_list[e], 3))

_detour_exist_list.append(1)
else:
_alt_value_list.append(_weighing_analyser.calculate_distance())
_alt_nodes_list.append(np.NaN)
_diff_value_list.append(np.NaN)
_alt_value_list.append(_current_value_list[e])
_alt_nodes_list.append(math.nan)
_diff_value_list.append(math.nan)
_detour_exist_list.append(0)

# add edge again to the graph
self.graph_file.graph.add_edge(u, v, k, **_weighing_analyser.weighing_data)
self.graph_file.graph.add_edge(u, v, k, **_weighing_analyser.edge_data)

# Add the new columns to the geodataframe
_weighing_analyser.extend_graph(_gdf_graph)
_gdf_graph[self.analysis.weighing.config_value] = _current_value_list
_gdf_graph[f"alt_{self.analysis.weighing.config_value}"] = _alt_value_list
_gdf_graph["alt_nodes"] = _alt_nodes_list
_gdf_graph[f"diff_{self.analysis.weighing.config_value}"] = _diff_value_list
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
from typing import Any

import geopandas as gpd
import numpy as np

from ra2ce.analysis.analysis_config_data.enums.weighing_enum import WeighingEnum
from ra2ce.analysis.losses.weighing_analysis.weighing_analysis_protocol import (
WeighingAnalysisProtocol,
)
from ra2ce.network.network_config_data.enums.road_type_enum import RoadTypeEnum
from ra2ce.network.networks_utils import get_avgspeed_per_road_type


class LengthWeighingAnalysis(WeighingAnalysisProtocol):
weighing_data: dict
edge_data: dict[str, Any]
avgspeed_dict: dict[str, float]

def calculate_distance(self) -> float:
return np.nan
def __init__(self, gdf_graph: gpd.GeoDataFrame | None) -> None:
self.avgspeed_dict = {
_road_type.config_value: get_avgspeed_per_road_type(gdf_graph, _road_type)
for _road_type in RoadTypeEnum
}

def calculate_alternative_distance(self, alt_dist: float) -> float:
return alt_dist
def _calculate_distance(self, time: float) -> float:
_avgspeed = self.edge_data.get("avgspeed", None) # km/h
if not _avgspeed:
_avgspeed = self.avgspeed_dict[self.edge_data["highway"]]
return round(time * _avgspeed * 1e3, 1) # m

def extend_graph(self, gdf_graph: gpd.GeoDataFrame | dict) -> None:
return
def get_current_value(self) -> float:
_dist = self.edge_data.get(WeighingEnum.LENGTH.config_value, None) # h
if _dist:
return round(_dist, 1)
_time = self.edge_data.get(WeighingEnum.TIME.config_value, 0) # m
_dist = self._calculate_distance(_time)
self.edge_data[WeighingEnum.LENGTH.config_value] = _dist
return _dist

def calculate_alternative_value(self, alt_dist: float) -> float:
return alt_dist
68 changes: 30 additions & 38 deletions ra2ce/analysis/losses/weighing_analysis/time_weighing_analysis.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,39 @@
from typing import Any

import geopandas as gpd
import numpy as np

from ra2ce.analysis.analysis_config_data.enums.weighing_enum import WeighingEnum
from ra2ce.analysis.losses.weighing_analysis.weighing_analysis_protocol import (
WeighingAnalysisProtocol,
)
from ra2ce.network.network_config_data.enums.road_type_enum import RoadTypeEnum
from ra2ce.network.networks_utils import get_avgspeed_per_road_type


class TimeWeighingAnalysis(WeighingAnalysisProtocol):
time_list: list
weighing_data: dict

def __init__(self) -> None:
self.time_list = []

def _calculate_time(self) -> float:
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:
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:
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]]
edge_data: dict[str, Any]
avgspeed_dict: dict[str, float]

def __init__(self, gdf_graph: gpd.GeoDataFrame | None) -> None:
self.avgspeed_dict = {
_road_type.config_value: get_avgspeed_per_road_type(gdf_graph, _road_type)
for _road_type in RoadTypeEnum
}

def _calculate_time(self, dist: float) -> float:
_avgspeed = self.edge_data.get("avgspeed", None) # km/h
if not _avgspeed:
_avgspeed = self.avgspeed_dict[self.edge_data["highway"]]
return round(dist * 1e-3 / _avgspeed, 3) # h

def get_current_value(self) -> float:
_time = self.edge_data.get(WeighingEnum.TIME.config_value, None) # h
if _time:
return round(_time, 3)
_dist = self.edge_data.get(WeighingEnum.LENGTH.config_value, 0) # m
_time = self._calculate_time(_dist)
self.edge_data[WeighingEnum.TIME.config_value] = _time
return _time

def calculate_alternative_value(self, alt_dist: float) -> float:
return self._calculate_time(alt_dist)
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import geopandas as gpd

from ra2ce.analysis.analysis_config_data.enums.weighing_enum import WeighingEnum
from ra2ce.analysis.losses.weighing_analysis.length_weighing_analysis import (
LengthWeighingAnalysis,
Expand All @@ -12,11 +14,13 @@

class WeighingAnalysisFactory:
@staticmethod
def get_analysis(weighing_type: WeighingEnum) -> WeighingAnalysisProtocol:
def get_analysis(
weighing_type: WeighingEnum, gdf_graph: gpd.GeoDataFrame | None
) -> WeighingAnalysisProtocol:
if weighing_type == WeighingEnum.TIME:
return TimeWeighingAnalysis()
return TimeWeighingAnalysis(gdf_graph)
if weighing_type == WeighingEnum.LENGTH:
return LengthWeighingAnalysis()
return LengthWeighingAnalysis(gdf_graph)

raise NotImplementedError(
"Weighing type {} not yet supported.".format(weighing_type)
Expand Down
Loading

0 comments on commit ef96edf

Please sign in to comment.