UrbanAnalyst / dodgr

Distances on Directed Graphs in R
https://urbananalyst.github.io/dodgr/
128 stars 16 forks source link

Behaviour of `dodgr_contract` and `dodgr_to_sf` #250

Open juanfonsecaLS1 opened 2 months ago

juanfonsecaLS1 commented 2 months ago

Hi @mpadge, Thanks for your work! I am creating this issue for an unexpected behaviour of the dodgr_contract which I believe is linked also to dodgr_to_sf. In the documentation, it is mentioned that:

Contraction will reduce these to the single path with the shortest weighted distance (or time), and uncontraction will restore only that single edge with shortest weighted distance

However, I have noticed that in some cases the contraction picks the edges a higher weighted distance. Here is an example

library(sf)
library(dplyr)
library(dodgr)

packageVersion("sf")
#> [1] '1.0.17'
packageVersion("dodgr")
#> [1] '0.4.1.26'

bogota_sf <- st_read("raw_sf.gpkg") |>
  st_transform(4326)
#> Reading layer `raw_sf' from data source 
#>   using driver `GPKG'
#> Simple feature collection with 3033 features and 15 fields
#> Geometry type: LINESTRING
#> Dimension:     XY
#> Bounding box:  xmin: -74.11581 ymin: 4.622787 xmax: -74.05244 ymax: 4.688989
#> Geodetic CRS:  MAGNA-SIRGAS

clear_dodgr_cache()

graph <- graph_bogota <- weight_streetnet(bogota_sf,
                                          left_side = F,
                                          wt_profile = "motorcar",
                                          keep_cols = c("oneway","lanes","surface","maxspeed"))

graph_cont <- graph |>
  dodgr_contract_graph()

sf_graph <- graph |>
  dodgr_to_sf()

# edge "541113078"

## Comparing distances full graph vs contracted
graph$d[graph$way_id == "541113078"] |> sum()
#> [1] 964.3081
graph_cont$d[graph_cont$way_id == "541113078"] |> sum()
#> [1] 914.9369

## original sf vs sf from dodgr
bogota_sf |> filter(osm_id == 541113078) |> st_length() |> as.numeric() |> sum()
#> [1] 966.0979
sf_graph |> filter(way_id == "541113078") |> st_length() |> as.numeric() |> sum()
#> [1] 916.6321

selected_link <- sf_graph |>
  filter(way_id =="541113078")

# Buffer for selection
buffer_selected_link <- selected_link |>
  st_union() |>
  st_buffer(dist = 300) 

# Filtering links within buffer of selected ling
bogota_sf_links_buffer <- bogota_sf[st_within(bogota_sf,buffer_selected_link,sparse = F),]

bogota_sf_link <- bogota_sf |> filter(osm_id == 541113078)

## Plotting differences 
plot(bogota_sf_links_buffer[,"geom"],col = "gray70",lwd = 4, reset=FALSE)
plot(bogota_sf_link[,"geom"],col = "blue",lwd = 2, add = T)
plot(selected_link[,"geometry"],col = "red",lwd = 2, add = T)

Created on 2024-09-25 with reprex v2.1.1

For some reason, the short section of link (in blue) is not kept after the contraction, but the loop is. Am I missing something?

(I am attaching the file with the network I am using raw_sf.zip)

Thanks again!

juanfonsecaLS1 commented 1 month ago

I think I found what is happening. The contraction do return the shortest weighted distance and assign it to one of the links, in this case the longest one. I was comparing the unweighted distance.

What was a bit confusing is that dodgr_to_sf seems to return in some cases the link with the shortest geometry and in others the longest one.

Is there a way to force dodgr_to_sf to return the 'shortest' geometry?

mpadge commented 1 month ago

@juanfonsecaLS1 Good that you resolved that, and that is indeed what i suspected when i read your issue. But your comments nevertheless made me realise that the docs really aren't clear about how this is handled, so i'll keep this issue open until that's been updated and improved. Thanks!