Skip to content

Commit

Permalink
create tt matrix if doesn't exist
Browse files Browse the repository at this point in the history
  • Loading branch information
Hussein-Mahfouz committed Sep 18, 2024
1 parent dd54595 commit 771edff
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 54 deletions.
71 changes: 29 additions & 42 deletions scripts/3.1_assign_primary_feasible_zones.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,52 +72,39 @@

logger.info("Loading travel time matrix")

# TODO: check config file to see if we have a travel time matrix. If not, create one
travel_times = pd.read_parquet(
acbm.root_path / "data/external/travel_times/oa/travel_time_matrix_acbm.parquet"
acbm.root_path / "data/external/travel_times/oa/travel_time_matrix.parquet"
)

logger.info("Travel time matrix loaded")

logger.info("Merging travel time matrix with boundaries")

# convert from_id and to_id to int to match the boundaries data type
travel_times = travel_times.astype({"from_id": int, "to_id": int})

# merge travel_times with boundaries
travel_times = travel_times.merge(
boundaries[["OBJECTID", "OA21CD"]],
left_on="from_id",
right_on="OBJECTID",
how="left",
)
travel_times = travel_times.drop(columns="OBJECTID")

travel_times = travel_times.merge(
boundaries[["OBJECTID", "OA21CD"]],
left_on="to_id",
right_on="OBJECTID",
how="left",
suffixes=("_from", "_to"),
)
travel_times = travel_times.drop(columns="OBJECTID")

# #### Travel distance matrix
#
# Some areas aren't reachable by specific modes. We create a travel distance matrix
# to fall back on when the, inplace=Truere are no travel time calculations

logger.info("Creating travel time estimates")

travel_time_estimates = zones_to_time_matrix(
zones=boundaries, id_col="OA21CD", to_dict=True
)

with open(
acbm.root_path / "data/interim/assigning/travel_time_estimates.pkl", "wb"
) as f:
pkl.dump(travel_time_estimates, f)

logger.info("Travel time estimates created")
# # Load the configuration file
# config_file_path = 'config.toml'
# travel_time_matrix_path = acbm.root_path / "data/external/travel_times/oa/travel_time_matrix.parquet"

# with open(config_file_path, 'r') as config_file:
# config = toml.load(config_file)

# # Check if travel_times is set to true and try to load the travel time matrix
# if config.get('travel_times') == True:
# try:
# travel_times = pd.read_parquet(travel_time_matrix_path)
# print("Travel time matrix loaded successfully.")
# except Exception as e:
# logger.info(f"Failed to load travel time matrix: {e}")
# logger.info("Creating a new travel time matrix.")
# # Create a new travel time matrix based on distances between zones
# travel_times = zones_to_time_matrix(zones = boundaries, id_col = "OA21CD")
# # TODO: save the travel time matrix

# # If travel_times is not true or loading failed, create a new travel time matrix
# if config.get('travel_times') != True:
# logger.info("No travel time matrix found. Creating a new travel time matrix.")
# # Create a new travel time matrix based on distances between zones
# travel_times = zones_to_time_matrix(zones = boundaries, id_col = "OA21CD")

# logger.info("Travel time estimates created")

# --- Intrazonal trip times
#
Expand All @@ -132,7 +119,7 @@

logger.info("Creating intrazonal travel time estimates")

intrazone_times = intrazone_time(boundaries.set_index("OBJECTID"))
intrazone_times = intrazone_time(zones=boundaries, key_column="OA21CD")

logger.info("Intrazonal travel time estimates created")

Expand Down
39 changes: 27 additions & 12 deletions src/acbm/assigning/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ def filter_matrix_to_boundary(
return filtered_matrix


def intrazone_time(zones: gpd.GeoDataFrame) -> dict:
def intrazone_time(zones: gpd.GeoDataFrame, key_column: str) -> dict:
"""
Estimate the time taken to travel within each zone.
Expand All @@ -376,6 +376,8 @@ def intrazone_time(zones: gpd.GeoDataFrame) -> dict:
----------
zones : gpd.GeoDataFrame
The GeoDataFrame containing the zones with zone IDs as the GeoDataFrame index.
key_column : str
The column name to use as the key for the dictionary.
Returns
-------
Expand All @@ -385,13 +387,14 @@ def intrazone_time(zones: gpd.GeoDataFrame) -> dict:
{53506: {'car': 0.3, 'pt': 0.5, 'cycle': 0.5, 'walk': 1.4, 'average': 0.5},
"""

# convert zones to metric crs
# Convert zones to metric CRS
zones = zones.to_crs(epsg=27700)
# get the sqrt of the area of each zone
# Calculate the area of each zone
zones["area"] = zones["geometry"].area
# Calculate the average distance within each zone
zones["average_dist"] = np.sqrt(zones["area"]) / 2

# mode speeds in m/s
# Mode speeds in m/s
mode_speeds_mps = {
"car": 20 * 1000 / 3600,
"pt": 15 * 1000 / 3600,
Expand All @@ -400,13 +403,13 @@ def intrazone_time(zones: gpd.GeoDataFrame) -> dict:
"average": 15 * 1000 / 3600,
}

# Create (and return) a dictionary where key = zone and values = travel time in minutes
# Create and return a dictionary where key = specified column and values = travel time in minutes per mode
return {
zone: {
mode: round((dist / speed) / 60, 1)
row[key_column]: {
mode: round((row["average_dist"] / speed) / 60, 1)
for mode, speed in mode_speeds_mps.items()
}
for zone, dist in zones["average_dist"].items()
for _, row in zones.iterrows()
}


Expand Down Expand Up @@ -438,16 +441,28 @@ def replace_intrazonal_travel_time(
# Copy the DataFrame to avoid modifying the original one
travel_times_copy = travel_times.copy()

# Create a new column 'mode' by splitting the 'combination' column
travel_times_copy["mode"] = travel_times_copy["combination"].str.split("_").str[0]
# Dynamically identify the "from" and "to" columns
from_cols = travel_times_copy.columns[
travel_times_copy.columns.str.contains("from", case=False)
]
to_cols = travel_times_copy.columns[
travel_times_copy.columns.str.contains("to", case=False)
]

if len(from_cols) != 1 or len(to_cols) != 1:
error_message = "Expected exactly one 'from' column and one 'to' column, but found multiple."
raise ValueError(error_message)

from_col = from_cols[0]
to_col = to_cols[0]

# Create a mask for intrazonal trips
intrazonal_mask = travel_times_copy["from_id"] == travel_times_copy["to_id"]
intrazonal_mask = travel_times_copy[from_col] == travel_times_copy[to_col]

# Apply the intrazonal estimates using a vectorized operation
travel_times_copy.loc[intrazonal_mask, column_to_replace] = travel_times_copy[
intrazonal_mask
].apply(lambda row: intrazonal_estimates[row["from_id"]][row["mode"]], axis=1)
].apply(lambda row: intrazonal_estimates[row[from_col]][row["mode"]], axis=1)

# Return the modified DataFrame
return travel_times_copy

0 comments on commit 771edff

Please sign in to comment.