Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error when blending points into network #252

Open
xiaochi-liu opened this issue Aug 11, 2023 · 3 comments
Open

Error when blending points into network #252

xiaochi-liu opened this issue Aug 11, 2023 · 3 comments

Comments

@xiaochi-liu
Copy link

Describe the bug
Hi there. Thanks for this handy package!

I just got an minor error that couldn't blend certain points into the road network:
image

This is strange because the st_network_blend() method works perfectly fine for my other data points. Only the points shown above couldn't be blended. The test data can be accessed here.

Reproducible example

library(tidyverse)
library(sf)
#> Linking to GEOS 3.11.0, GDAL 3.5.3, PROJ 9.1.0; sf_use_s2() is TRUE
library(sfnetworks)

# import data
sf_road_test = read_rds("sf_road_test.rds")
sf_point_test = read_rds("sf_point_test.rds")

# turn road `sf` into `sfnetwork`
sf_road_test %>% 
  as_sfnetwork() -> sfnetwork_road_test

# blend points into network
sfnetwork_road_test %>% 
  st_network_blend(sf_point_test)
#> Warning: st_network_blend assumes attributes are constant over geometries
#> Error in `structure()`:
#> ! Assigned data `value` must be compatible with existing data.
#> ✖ Existing data has 0 rows.
#> ✖ Assigned data has 2 rows.
#> ℹ Only vectors of size 1 are recycled.
#> Caused by error in `vectbl_recycle_rhs_rows()`:
#> ! Can't recycle input of size 2 to size 0.
#> Backtrace:
#>      ▆
#>   1. ├─sfnetwork_road_test %>% st_network_blend(sf_point_test)
#>   2. ├─sfnetworks::st_network_blend(., sf_point_test)
#>   3. ├─sfnetworks:::st_network_blend.sfnetwork(., sf_point_test)
#>   4. │ └─sfnetworks:::blend_(x, y, tolerance)
#>   5. │   ├─base::`$<-`(`*tmp*`, ".sfnetwork_index", value = `<int>`)
#>   6. │   └─sf:::`$<-.sf`(`*tmp*`, ".sfnetwork_index", value = `<int>`)
#>   7. │     ├─base::`[[<-`(`*tmp*`, i, value = `<int>`)
#>   8. │     ├─sf:::`[[<-.sf`(`*tmp*`, i, value = `<int>`)
#>   9. │     │ └─base::structure(...)
#>  10. │     ├─base::NextMethod()
#>  11. │     └─tibble:::`[[<-.tbl_df`(`*tmp*`, i, value = `<int>`)
#>  12. │       └─tibble:::tbl_subassign(...)
#>  13. │         └─tibble:::vectbl_recycle_rhs_rows(value, fast_nrow(xo), i_arg = NULL, value_arg, call)
#>  14. │           ├─base::withCallingHandlers(...)
#>  15. │           └─vctrs::vec_recycle(value[[j]], nrow)
#>  16. └─vctrs:::stop_recycle_incompatible_size(...)
#>  17.   └─vctrs:::stop_vctrs(...)
#>  18.     └─rlang::abort(message, class = c(class, "vctrs_error"), ..., call = call)

Expected behavior
The sf_point_test can be blended into sfnetwork_road_test.

R Session Info

sessionInfo()
#> R version 4.3.0 (2023-04-21)
#> Platform: aarch64-apple-darwin20 (64-bit)
#> Running under: macOS Ventura 13.4.1
#> 
#> Matrix products: default
#> BLAS:   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib 
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0
#> 
#> locale:
#> [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
#> 
#> time zone: Australia/Melbourne
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> loaded via a namespace (and not attached):
#>  [1] digest_0.6.33   fastmap_1.1.1   xfun_0.39       glue_1.6.2     
#>  [5] knitr_1.43      htmltools_0.5.5 rmarkdown_2.23  lifecycle_1.0.3
#>  [9] cli_3.6.1       reprex_2.0.2    withr_2.5.0     compiler_4.3.0 
#> [13] rstudioapi_0.14 tools_4.3.0     evaluate_0.21   yaml_2.3.7     
#> [17] rlang_1.1.1     fs_1.6.3

Thank you very much for your kind guidance!

@Robinlovelace
Copy link
Collaborator

Maybe because multiple points are associated with a single point on the network?

@agila5
Copy link
Collaborator

agila5 commented Aug 11, 2023

I think this is an error that occurs when blending sf objects with attributes into the network and every external point is matched with an existing node. Simpler reprex:

#packages
library(sf)
#> Linking to GEOS 3.11.2, GDAL 3.6.2, PROJ 9.2.0; sf_use_s2() is TRUE
library(sfnetworks)

# toy-data
l <- st_linestring(rbind(c(0, 0), c(1, 1)))
sfc <- as_sfnetwork(st_sfc(l))
external_point <- st_sfc(st_point(c(-1, -1)))

# plot
# plot(sfc, axes = TRUE, xlim = c(-1.5, 2), ylim = c(-1.5, 2), cex = 2, lwd = 2)
# plot(external_point, col = "red", add = TRUE, pch = 20, cex = 2)

# blend sfc object
sfc2 <- st_network_blend(sfc, external_point)

# the output is identical to original net
igraph::identical_graphs(sfc, sfc2)
#> [1] TRUE

# since the closest point to "external_point" is the boundary point of l.
# Nevertheless, if I add an attribute to "external_point"
external_sf <- st_sf(
  data.frame(x = "A"), 
  geometry = external_point
)

st_network_blend(sfc, external_sf)
#> Error in `[[<-.data.frame`(`*tmp*`, i, value = 3:2): replacement has 2 rows, data has 0

Created on 2023-08-11 with reprex v2.0.2

I think the error is here

sfnetworks/R/blend.R

Lines 456 to 458 in 23a4125

y$.sfnetwork_index = NA_integer_
y[matched_feat_idxs, ]$.sfnetwork_index = matched_node_idxs
y[new_feat_idxs, ]$.sfnetwork_index = added_node_idxs

since new_feat_idxs is created as follows

sfnetworks/R/blend.R

Lines 444 to 445 in 23a4125

is_new = is_split
new_feat_idxs = edge_pts$feat_id[is_new]

and is_new might be a vector of just FALSE values (implying that new_feat_idxs is a vector with 0 elements).

@xiaochi-liu
Copy link
Author

Thank you very much @agila5 and @Robinlovelace!

I removed all attributes for the external points, which can then be blended into the network!

May I ask, is it possible to fix it? What I want to do after blending is to extract the network nodes that are generated by the points blended:

library(sf)
library(sfnetworks)

# toy-data
l = st_linestring(rbind(c(0, 0), c(1, 1)))
sfc = as_sfnetwork(st_sfc(l))
external_sf = st_sf(
  data.frame(x = "A"), 
  geometry = external_point
)

# blend external points into the network (if successful)
sfc2 = st_network_blend(sfc, external_sf)

# extract the nodes that are generated by blending points
sfc2 %>% 
  activate("nodes") %>% 
  st_as_sf() %>% 
  filter(!is.na(A)) -> sf_point_blended

Thus, it would be great if the external points had some attributes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants