luukvdmeer / sfnetworks

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

Standard creation functions for spatial graphs from only points #52

Open luukvdmeer opened 4 years ago

luukvdmeer commented 4 years ago

Is your feature request related to a problem? Please describe. You can now create an sfnetwork from only a set of spatial linestrings. The linestrings will be the edges, and the endpoints of the linestrings the nodes. Also, you can create an sfnetwork from only a set of spatial points. The points will be the nodes, and edges will be created in a sequential order (i.e. from node 1 to 2, node 2 to 3, etc). This was created as a solution for this StackOverflow issue.

However, there are much more ways to create a spatial graph from only a set of points. Dale (2017) lists the most common ones:

sn

Describe the solution you'd like More options to create an sfnetwork from only a set of spatial points. This could be implemented with an extra argument to as_sfnetwork.sf, specifying which creation algorithm to use, and also in the same style as tidygraphs create_* family of functions. That is: create_mnn, create_nn, create_mst, et cetera.

JosiahParry commented 2 years ago

regarding minimum spanning tree this can be accomplished using spdep and sfdep. here's an example of creating an sfnetwork using an mstree.

library(sf)
library(dplyr)
library(sfdep)
library(sfnetworks)

geo <- sfdep::guerry_nb |>
  select(geometry) |>
  mutate(nb = st_complete_nb(n()),
         wt = st_nb_dists(geometry, nb))
#> ! Polygon provided. Using point on surface.

listw <- recreate_listw(geo$nb, structure(geo$wt,  "B" = TRUE))

ms <- spdep::mstree(listw)

ms_df <- as.data.frame(ms) |>
  rlang::set_names(c("from", "to", "edge_length"))

geom <- sf::st_centroid(geo)[["geometry"]]
#> Warning in st_centroid.sf(geo): st_centroid assumes attributes are constant over
#> geometries of x
nodes <- geom

from <- geom[ms_df$from]
to <- geom[ms_df$to]

from <- as_tibble(st_coordinates(from)) |>
  mutate(id = row_number())
to <- as_tibble(st_coordinates(to)) |>
  mutate(id = row_number())

# need to find a way to arrange using base R
edges2 <- sfheaders::sf_linestring(
  arrange(bind_rows(from, to), id),
  x = "X",
  y = "Y",
  linestring_id = "id") |>
  bind_cols(ms_df)

plot(sfnetwork(nodes, edges2))
#> Checking if spatial network structure is valid...
#> Spatial network structure is valid

Created on 2022-08-23 by the reprex package (v2.0.1)

JosiahParry commented 2 years ago

Creating an sfnetwork using spdep + sfdep for delaunay triangulation and gabriel neighbors

# spdep -------------------------------------------------------------------
library(sf)
#> Linking to GEOS 3.9.1, GDAL 3.2.3, PROJ 7.2.1; sf_use_s2() is TRUE
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(spdep)
#> Loading required package: sp
#> Loading required package: spData
#> To access larger datasets in this package, install the spDataLarge
#> package with: `install.packages('spDataLarge',
#> repos='https://nowosad.github.io/drat/', type='source')`
library(sfnetworks)

nodes <- st_centroid(sfdep::guerry)
#> Warning in st_centroid.sf(sfdep::guerry): st_centroid assumes attributes are
#> constant over geometries of x

geo <- st_geometry(nodes)
nb <- tri2nb(geo)

plot(nb, geo)


wt <- sfdep::st_nb_dists(geo, nb)

sfdep::st_as_graph(geo, nb, wt) |> 
  plot()
#> Checking if spatial network structure is valid...
#> Spatial network structure is valid


# gabriel neighbors
# ensure that it is symmetric to make sure no missing links
nb <- graph2nb(gabrielneigh(geo), sym = TRUE)
wt <- sfdep::st_nb_dists(geo, nb)
sfdep::st_as_graph(geo, nb, wt) |> 
  plot()
#> Checking if spatial network structure is valid...
#> Spatial network structure is valid

Created on 2022-08-23 by the reprex package (v2.0.1)