luukvdmeer / sfnetworks

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

Implement spatial binary predicates as node and edge query functions #60

Closed luukvdmeer closed 3 years ago

luukvdmeer commented 4 years ago

Is your feature request related to a problem? Please describe. This idea was brought up by @thomasp85 during the sfnetworks webinar today. As an alternative to using st_filter, you could apply spatial binary predicates as node and edge query functions that fit into the tidygraph style of node_* and edge_* functions. Also query functions based on geographical distance, like node_close_to could be implemented in a similar fashion.

See:

predicates

This would also allow smoother integration with ggraph.

luukvdmeer commented 3 years ago

A selection of predicates is now implemented as node and edge query function. Works smoothly within dplyr verbs. Reprex:

library(sfnetworks)
library(sf)
#> Linking to GEOS 3.7.1, GDAL 2.2.2, PROJ 4.9.2
library(tidygraph)
#> 
#> Attaching package: 'tidygraph'
#> The following object is masked from 'package:stats':
#> 
#>     filter

# Create a network and another geospatial feature.
net = as_sfnetwork(roxel)
p1 = st_point(c(7.53173, 51.95662))
p2 = st_point(c(7.53173, 51.95190))
p3 = st_point(c(7.53778, 51.95190))
p4 = st_point(c(7.53778, 51.95662))
rect = st_multipoint(c(p1, p2, p3, p4)) %>% 
  st_cast('POLYGON') %>% 
  st_sfc(crs = 4326)

# Use predicate queries in a mutate call.
net %>%
  mutate(within = node_is_within(rect)) %>%
  activate(edges) %>%
  mutate(crosses = edge_crosses(rect))
#> although coordinates are longitude/latitude, st_within assumes that they are planar
#> although coordinates are longitude/latitude, st_crosses assumes that they are planar
#> # An sfnetwork with 701 nodes and 851 edges
#> #
#> # CRS:  EPSG:4326 
#> #
#> # A directed multigraph with 14 components with spatially explicit edges
#> #
#> # Edge Data:     851 x 6 (active)
#> # Geometry type: LINESTRING
#> # Dimension:     XY
#> # Bounding box:  xmin: 7.522594 ymin: 51.94151 xmax: 7.546705 ymax: 51.9612
#>    from    to name       type                                   geometry crosses
#>   <int> <int> <fct>      <fct>                          <LINESTRING [°]> <lgl>  
#> 1     1     2 Havixbeck… reside…  (7.533722 51.95556, 7.533461 51.95576) FALSE  
#> 2     3     4 Pienersal… second… (7.532442 51.95422, 7.53236 51.95377, … FALSE  
#> 3     5     6 Schulte-B… reside… (7.532709 51.95209, 7.532823 51.95239,… FALSE  
#> 4     7     8 <NA>       path    (7.540063 51.94468, 7.539696 51.94479,… FALSE  
#> 5     9    10 Welsinghe… reside…   (7.537673 51.9475, 7.537614 51.94562) FALSE  
#> 6    11    12 <NA>       footway (7.543791 51.94733, 7.54369 51.94686, … FALSE  
#> # … with 845 more rows
#> #
#> # Node Data:     701 x 2
#> # Geometry type: POINT
#> # Dimension:     XY
#> # Bounding box:  xmin: 7.522622 ymin: 51.94151 xmax: 7.546705 ymax: 51.9612
#>              geometry within
#>           <POINT [°]> <lgl> 
#> 1 (7.533722 51.95556) TRUE  
#> 2 (7.533461 51.95576) TRUE  
#> 3 (7.532442 51.95422) TRUE  
#> # … with 698 more rows

# Use predicate queries in a filter call.
plot(net)
plot(
  net %>% filter(node_is_within(rect)),
  col = 'red',
  pch = 20,
  add = T
)
#> although coordinates are longitude/latitude, st_within assumes that they are planar
plot(
  net %>% filter(node_is_disjoint(rect)),
  col = 'blue',
  pch = 20,
  add = T
)
#> although coordinates are longitude/latitude, st_intersects assumes that they are planar
#> although coordinates are longitude/latitude, st_intersects assumes that they are planar

Created on 2020-11-02 by the reprex package (v0.3.0)

luukvdmeer commented 3 years ago

These are now part of the new version. See the join and filter vignette and the function reference.