@@ -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