Skip to content

Commit 38b8d64

Browse files
committed
imporve the computational efficiency of calculate_paths_from_point_dist
1 parent 380b4f5 commit 38b8d64

1 file changed

Lines changed: 96 additions & 68 deletions

File tree

R/corenet.R

Lines changed: 96 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -487,74 +487,102 @@ prepare_network = function(network, key_attribute = "all_fastest_bicycle_go_dutc
487487
#' @return An sf object containing the paths that meet the criteria or NULL if no paths meet the criteria.
488488
#' @export
489489

490-
491-
calculate_paths_from_point_dist = function(network, point, minDistPts = 2, maxDistPts = 1500, centroids, path_type = "shortest", max_path_weight =10) {
492-
path_cache = list()
493-
494-
# Ensure the network's CRS is correctly set for distance measurement in meters
495-
if (is.na(sf::st_crs(network)) || sf::st_crs(network)$units != "m") {
496-
network = sf::st_transform(network, crs = 27700) # Example: UTM zone 32N for meters
497-
}
498-
499-
# Generate a unique key for the cache based on the point's coordinates
500-
point_key = paste(sort(as.character(point)), collapse = "_")
501-
if (exists("path_cache") && point_key %in% names(path_cache)) {
502-
return(path_cache[[point_key]])
503-
}
504-
505-
# Convert point and centroids to sfc if not already
506-
point_geom = sf::st_as_sfc(point)
507-
centroids_geom = sf::st_as_sfc(centroids)
508-
509-
# Calculate distances between point and centroids
510-
distances = sf::st_distance(point_geom, centroids_geom)
511-
distances_vector = as.vector(distances[1, ])
512-
distances_vector = units::set_units(distances_vector, "m")
513-
514-
valid_centroids = centroids[
515-
distances_vector >= units::set_units(minDistPts, "m") &
516-
distances_vector <= units::set_units(maxDistPts, "m"),
517-
]
518-
519-
if (nrow(valid_centroids) > 0) {
520-
# Define weights based on path_type
521-
weights_to_use = if (path_type == "all_simple") NA else "weight"
522-
523-
# Calculate paths based on specified path_type
524-
paths_from_point = sfnetworks::st_network_paths(
525-
network,
526-
from = point_geom,
527-
to = sf::st_as_sfc(valid_centroids),
528-
weights = "weight",
529-
type = path_type
530-
)
531-
532-
# edges_in_paths = paths_from_point |>
533-
# dplyr::pull(edge_paths) |>
534-
# base::unlist() |> base::unique()
535-
536-
# result = network |> dplyr::slice(unique(edges_in_paths)) |> sf::st_as_sf()
537-
paths_from_point$total_weight = purrr::map_dbl(
538-
paths_from_point$edge_paths,
539-
~ sum(network |> activate(edges) |> slice(.x) |> pull(weight))
540-
)
541-
542-
# Filter out paths exceeding weight threshold
543-
valid_paths = paths_from_point[paths_from_point$total_weight <= max_path_weight, ]
544-
545-
edges_in_paths = valid_paths |> # CHANGED: Use filtered paths
546-
dplyr::pull(edge_paths) |>
547-
base::unlist() |>
548-
base::unique()
549-
550-
result = network |> dplyr::slice(unique(edges_in_paths)) |> sf::st_as_sf()
551-
} else {
552-
result = NULL
553-
}
554-
555-
path_cache[[point_key]] = result
556-
557-
return(result)
490+
calculate_paths_from_point_dist = function(
491+
network,
492+
point,
493+
minDistPts = 2,
494+
maxDistPts = 1500,
495+
centroids,
496+
path_type = "shortest",
497+
max_path_weight = 10,
498+
crs_transform = 27700
499+
) {
500+
# 1) Ensure the network is in a meter-based CRS, transform if necessary
501+
net_crs = sf::st_crs(network)
502+
503+
# Check if units are in "m"; if missing or different, transform
504+
if (is.na(net_crs) || (!is.null(net_crs$units) && net_crs$units != "m")) {
505+
message("Transforming network to CRS: ", crs_transform)
506+
network = sf::st_transform(network, crs = crs_transform)
507+
}
508+
509+
# 2) Generate a unique key for caching based on the point's coordinates
510+
# You can modify how this key is generated if your 'point' is already sf-compatible
511+
point_key = paste(sort(as.character(point)), collapse = "_")
512+
513+
# 3) Check if result is already cached
514+
if (exists(point_key, envir = path_cache_env)) {
515+
return(get(point_key, envir = path_cache_env))
516+
}
517+
518+
# 4) Convert the point and centroids to sfc if not already
519+
point_geom = sf::st_as_sfc(point)
520+
centroids_geom = sf::st_as_sfc(centroids)
521+
522+
# 5) Filter centroids by distance to the point
523+
distances = sf::st_distance(point_geom, centroids_geom)
524+
# distances is a 1 x n matrix; convert to a numeric vector (in meters)
525+
distances_m = units::set_units(as.vector(distances[1, ]), "m")
526+
527+
valid_idx = which(
528+
distances_m >= units::set_units(minDistPts, "m") &
529+
distances_m <= units::set_units(maxDistPts, "m")
530+
)
531+
532+
# If no centroids qualify, cache NULL and return
533+
if (!length(valid_idx)) {
534+
assign(point_key, NULL, envir = path_cache_env)
535+
return(NULL)
536+
}
537+
538+
valid_centroids = centroids[valid_idx, , drop = FALSE]
539+
valid_centroids_geom = centroids_geom[valid_idx]
540+
541+
# 6) Determine which weight column to use
542+
# If path_type == "all_simple", typically no weights are used.
543+
# Adjust as needed if your network uses a different attribute for cost/distance.
544+
weights_to_use = if (path_type == "all_simple") NULL else "weight"
545+
546+
# 7) Compute paths from the single point to all valid centroids
547+
# This returns indices of nodes/edges for each path
548+
paths_from_point = sfnetworks::st_network_paths(
549+
network,
550+
from = point_geom,
551+
to = valid_centroids_geom,
552+
weights = weights_to_use,
553+
type = path_type
554+
)
555+
556+
# 8) Summation of edge weights for each path
557+
# Instead of re-activating edges for each path, extract them once
558+
net_edges = network |> tidygraph::activate("edges")
559+
edges_weight_vec = net_edges |> dplyr::pull(weight)
560+
561+
# Vectorized summation of weights for each path
562+
total_weights = sapply(paths_from_point$edge_paths, function(edge_ids) {
563+
sum(edges_weight_vec[edge_ids], na.rm = TRUE)
564+
})
565+
566+
paths_from_point$total_weight = total_weights
567+
568+
# 9) Filter out paths whose total weight exceeds threshold
569+
valid_paths = paths_from_point[total_weights <= max_path_weight, ]
570+
if (!nrow(valid_paths)) {
571+
assign(point_key, NULL, envir = path_cache_env)
572+
return(NULL)
573+
}
574+
575+
# 10) Collect the edges used by these filtered paths
576+
edges_in_paths = unique(unlist(valid_paths$edge_paths))
577+
578+
# 11) Slice the network edges to extract only those edges
579+
result = net_edges |>
580+
dplyr::slice(edges_in_paths) |>
581+
sf::st_as_sf()
582+
583+
# 12) Store in cache and return
584+
assign(point_key, result, envir = path_cache_env)
585+
return(result)
558586
}
559587

560588
#' Calculate the largest connected component of a network

0 commit comments

Comments
 (0)