luukvdmeer / sfnetworks

Tidy Geospatial Networks in R
https://luukvdmeer.github.io/sfnetworks/
Other
345 stars 20 forks source link

Weird behaviour of to_spatial_subdivision with duplicated internal points #212

Open agila5 opened 2 years ago

agila5 commented 2 years ago

Describe the bug While working on https://github.com/luukvdmeer/sfnetworks/discussions/211 I noticed that to_spatial_subdivision morpher behaves weirdly when there are identical internal points for a given LINESTRING.

Reproducible example

# packages
library(sf)
#> Linking to GEOS 3.9.1, GDAL 3.2.1, PROJ 7.2.1; sf_use_s2() is TRUE
library(tidygraph)
#> 
#> Attaching package: 'tidygraph'
#> The following object is masked from 'package:stats':
#> 
#>     filter
library(sfnetworks)

# simulate one segment with an internal duplicated point
seg = st_sfc(st_linestring(rbind(c(0, 0), c(1, 0), c(1, 0), c(2, 0))))
sfn = as_sfnetwork(seg)
convert(sfn, to_spatial_subdivision, .clean = TRUE)
#> # A sfnetwork with 3 nodes and 3 edges
#> #
#> # CRS:  NA 
#> #
#> # A directed multigraph with 1 component with spatially explicit edges
#> #
#> # Node Data:     3 x 1 (active)
#> # Geometry type: POINT
#> # Dimension:     XY
#> # Bounding box:  xmin: 0 ymin: 0 xmax: 2 ymax: 0
#>         x
#>   <POINT>
#> 1   (0 0)
#> 2   (1 0)
#> 3   (2 0)
#> #
#> # Edge Data:     3 x 3
#> # Geometry type: LINESTRING
#> # Dimension:     XY
#> # Bounding box:  xmin: 0 ymin: 0 xmax: 2 ymax: 0
#>    from    to            x
#>   <int> <int> <LINESTRING>
#> 1     1     2   (0 0, 1 0)
#> 2     2     2   (1 0, 1 0)
#> 3     2     3   (1 0, 2 0)

Created on 2022-06-22 by the reprex package (v2.0.1)

Expected behavior I would say that the morpher should not modify the input edges in this case. What do you think?

R Session Info

Session info ``` r sessioninfo::session_info() #> - Session info --------------------------------------------------------------- #> setting value #> version R version 4.1.3 (2022-03-10) #> os Windows 10 x64 (build 19044) #> system x86_64, mingw32 #> ui RTerm #> language (EN) #> collate English_United Kingdom.1252 #> ctype English_United Kingdom.1252 #> tz Europe/Berlin #> date 2022-06-22 #> pandoc 2.17.1.1 @ C:/Program Files/RStudio/bin/quarto/bin/ (via rmarkdown) #> #> - Packages ------------------------------------------------------------------- #> package * version date (UTC) lib source #> abind 1.4-5 2016-07-21 [1] CRAN (R 4.1.3) #> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.1.3) #> class 7.3-20 2022-01-16 [2] CRAN (R 4.1.3) #> classInt 0.4-3 2020-04-07 [1] CRAN (R 4.1.3) #> cli 3.3.0 2022-04-25 [1] CRAN (R 4.1.3) #> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.1.3) #> crayon 1.5.1 2022-03-26 [1] CRAN (R 4.1.3) #> DBI 1.1.2 2021-12-20 [1] CRAN (R 4.1.3) #> deldir 1.0-6 2021-10-23 [1] CRAN (R 4.1.3) #> digest 0.6.29 2021-12-01 [1] CRAN (R 4.1.3) #> dplyr 1.0.9 2022-04-28 [1] CRAN (R 4.1.3) #> e1071 1.7-9 2021-09-16 [1] CRAN (R 4.1.3) #> ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.1.3) #> evaluate 0.15 2022-02-18 [1] CRAN (R 4.1.3) #> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.1.3) #> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.1.3) #> fs 1.5.2 2021-12-08 [1] CRAN (R 4.1.3) #> generics 0.1.2 2022-01-31 [1] CRAN (R 4.1.3) #> ggplot2 3.3.6 2022-05-03 [1] CRAN (R 4.1.3) #> glue 1.6.2 2022-02-24 [1] CRAN (R 4.1.3) #> goftest 1.2-3 2021-10-07 [1] CRAN (R 4.1.3) #> gtable 0.3.0 2019-03-25 [1] CRAN (R 4.1.3) #> highr 0.9 2021-04-16 [1] CRAN (R 4.1.3) #> htmltools 0.5.2 2021-08-25 [1] CRAN (R 4.1.3) #> igraph 1.3.1 2022-04-20 [1] CRAN (R 4.1.3) #> KernSmooth 2.23-20 2021-05-03 [2] CRAN (R 4.1.3) #> knitr 1.39 2022-04-26 [1] CRAN (R 4.1.3) #> lattice 0.20-45 2021-09-22 [2] CRAN (R 4.1.3) #> lifecycle 1.0.1 2021-09-24 [1] CRAN (R 4.1.3) #> lwgeom 0.2-8 2021-10-06 [1] CRAN (R 4.1.3) #> magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.1.3) #> Matrix 1.4-0 2021-12-08 [2] CRAN (R 4.1.3) #> mgcv 1.8-39 2022-02-24 [2] CRAN (R 4.1.3) #> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.1.3) #> nlme 3.1-155 2022-01-16 [2] CRAN (R 4.1.3) #> pillar 1.7.0 2022-02-01 [1] CRAN (R 4.1.3) #> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.1.3) #> polyclip 1.10-0 2019-03-14 [1] CRAN (R 4.1.3) #> proxy 0.4-26 2021-06-07 [1] CRAN (R 4.1.3) #> purrr 0.3.4 2020-04-17 [1] CRAN (R 4.1.3) #> R.cache 0.15.0 2021-04-30 [1] CRAN (R 4.1.3) #> R.methodsS3 1.8.1 2020-08-26 [1] CRAN (R 4.1.1) #> R.oo 1.24.0 2020-08-26 [1] CRAN (R 4.1.1) #> R.utils 2.11.0 2021-09-26 [1] CRAN (R 4.1.3) #> R6 2.5.1 2021-08-19 [1] CRAN (R 4.1.3) #> Rcpp 1.0.8.3 2022-03-17 [1] CRAN (R 4.1.3) #> reprex 2.0.1 2021-08-05 [1] CRAN (R 4.1.3) #> rlang 1.0.2 2022-03-04 [1] CRAN (R 4.1.3) #> rmarkdown 2.14 2022-04-25 [1] CRAN (R 4.1.3) #> rpart 4.1.16 2022-01-24 [2] CRAN (R 4.1.3) #> rstudioapi 0.13 2020-11-12 [1] CRAN (R 4.1.3) #> scales 1.2.0 2022-04-13 [1] CRAN (R 4.1.3) #> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.1.3) #> sf * 1.0-7 2022-03-07 [1] CRAN (R 4.1.3) #> sfheaders 0.4.0 2020-12-01 [1] CRAN (R 4.1.3) #> sfnetworks * 0.5.5.900 2022-06-22 [1] local #> spatstat 2.3-4 2022-04-01 [1] CRAN (R 4.1.3) #> spatstat.core 2.4-4 2022-05-18 [1] CRAN (R 4.1.3) #> spatstat.data 2.2-0 2022-04-18 [1] CRAN (R 4.1.3) #> spatstat.geom 2.4-0 2022-03-29 [1] CRAN (R 4.1.3) #> spatstat.linnet 2.3-2 2022-02-16 [1] CRAN (R 4.1.3) #> spatstat.random 2.2-0 2022-03-30 [1] CRAN (R 4.1.3) #> spatstat.sparse 2.1-1 2022-04-18 [1] CRAN (R 4.1.3) #> spatstat.utils 2.3-1 2022-05-06 [1] CRAN (R 4.1.3) #> stringi 1.7.6 2021-11-29 [1] CRAN (R 4.1.3) #> stringr 1.4.0 2019-02-10 [1] CRAN (R 4.1.3) #> styler 1.7.0 2022-03-13 [1] CRAN (R 4.1.3) #> tensor 1.5 2012-05-05 [1] CRAN (R 4.1.3) #> tibble 3.1.7 2022-05-03 [1] CRAN (R 4.1.3) #> tidygraph * 1.2.1 2022-04-05 [1] CRAN (R 4.1.3) #> tidyr 1.2.0 2022-02-01 [1] CRAN (R 4.1.3) #> tidyselect 1.1.2 2022-02-21 [1] CRAN (R 4.1.3) #> units 0.8-0 2022-02-05 [1] CRAN (R 4.1.3) #> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.1.3) #> vctrs 0.4.1 2022-04-13 [1] CRAN (R 4.1.3) #> withr 2.5.0 2022-03-03 [1] CRAN (R 4.1.3) #> xfun 0.31 2022-05-10 [1] CRAN (R 4.1.3) #> yaml 2.3.5 2022-02-21 [1] CRAN (R 4.1.3) #> #> [1] C:/Users/Andrea Gilardi/Documents/R/win-library/4.1 #> [2] C:/Program Files/R/R-4.1.3/library #> #> ------------------------------------------------------------------------------ ```
luukvdmeer commented 2 years ago

It is a weird result, but I am not sure if I find it unexpected. The question is mainly: what should the morpher do when an edge crosses itself, which is essentially what is happening here. I would say it should create a node there. Of course in this specific case the result is weird because the duplicated internal points come directly after each other, and a zero-length loop edge is created.

agila5 commented 1 year ago

Another (probably unavoidable) consequence of the current implementation behind to_spatial_subdivision is that the algorithm splits the duplicated segments (that may occur in road transport systems with dual carriageway and oneway roads as showcased here: https://luukvdmeer.github.io/sfnetworks/articles/sfn02_preprocess_clean.html#dealing-with-one-way-edges) at each internal point. For example:

# packages
library(sf)
#> Linking to GEOS 3.10.2, GDAL 3.4.1, PROJ 7.2.1; sf_use_s2() is TRUE
library(tidygraph)
#> 
#> Attaching package: 'tidygraph'
#> The following object is masked from 'package:stats':
#> 
#>     filter
library(sfnetworks)
options(sfn_max_print_active = Inf, sfn_max_print_inactive = Inf)

# define a toy set of segments
my_segments <- st_sfc(
  st_linestring(rbind(c(0, 0), c(0.5, 0.5), c(1, 1))), # this is a non one-way segment
  st_linestring(rbind(c(1, 1), c(2, 2))) # this is a oneway segment
)

# duplicate the one-way segment + create directed sfn
my_segments <- c(my_segments, st_reverse(my_segments[1])) # duplicate the non-one-way
my_sfn <- as_sfnetwork(my_segments)

# apply the to_spatial_subdivision morpher
convert(my_sfn, to_spatial_subdivision, .clean = TRUE)
#> # A sfnetwork with 4 nodes and 5 edges
#> #
#> # CRS:  NA 
#> #
#> # A directed simple graph with 1 component with spatially explicit edges
#> #
#> # Node Data:     4 x 1 (active)
#> # Geometry type: POINT
#> # Dimension:     XY
#> # Bounding box:  xmin: 0 ymin: 0 xmax: 2 ymax: 2
#>           x
#>     <POINT>
#> 1     (0 0)
#> 2 (0.5 0.5)
#> 3     (1 1)
#> 4     (2 2)
#> #
#> # Edge Data:     5 x 3
#> # Geometry type: LINESTRING
#> # Dimension:     XY
#> # Bounding box:  xmin: 0 ymin: 0 xmax: 2 ymax: 2
#>    from    to              x
#>   <int> <int>   <LINESTRING>
#> 1     1     2 (0 0, 0.5 0.5)
#> 2     2     3 (0.5 0.5, 1 1)
#> 3     3     4     (1 1, 2 2)
#> 4     3     2 (1 1, 0.5 0.5)
#> 5     2     1 (0.5 0.5, 0 0)

Created on 2023-04-19 with reprex v2.0.2

Do you think that it is worth exploring this problem more precisely and think about a solution? Moreover (but this is probably the topic for a separate issue), to_spatial_smooth doesn't help here.