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

Not all tidygraph functions work on sfnetwork objects #29

Open
6 tasks
luukvdmeer opened this issue Apr 3, 2020 · 6 comments
Open
6 tasks

Not all tidygraph functions work on sfnetwork objects #29

luukvdmeer opened this issue Apr 3, 2020 · 6 comments
Assignees
Labels
low priority 💤 Would be nice to have but not necessary

Comments

@luukvdmeer
Copy link
Owner

luukvdmeer commented Apr 3, 2020

Although sfnetwork subclasses tbl_graph, not all of tidygraph functions work on sfnetwork objects. This is caused by the geometry list column, and the nodes and edges being sf objects.

The problems occur because of two reasons:

1. Usage of igraph::add_vertices and/or igraph::add_edges inside functions

For some reason these functions break when there are list columns in the nodes and/or edges. The error given when using igraph::add_vertices is:

#> Error in UseMethod("st_bbox") : 
#>  no applicable method for 'st_bbox' applied to an object of class "logical"

It is a mystery for me why that function would call sf::st_bbox, but I did not look into it yet. When using igraph::add_edges RStudio even crashes completely (i.e. aborted because of fatal error).

The affected functions are:

  • bind_nodes
  • bind_edges
  • right_join
  • full_join

The latter two are generics and the issue could be solved by writing a sfnetwork method for them. The first two are not generics.

2. Recreating a tbl_graph object inside a function

Creating a tbl_graph with tidygraph::tbl_graph does not work when the edges are an sf object, because of the sticky geometry column.

The affected functions are:

  • graph_join
  • reroute

The first is not a generic. The latter is a generic so we can solve the issue by writing a sfnetwork method for it.

@luukvdmeer luukvdmeer added feature 🎁 Request a new feature high priority ⏰ Should be fixed soon labels Apr 3, 2020
@luukvdmeer luukvdmeer added this to the Milestone 1 milestone Apr 3, 2020
@luukvdmeer luukvdmeer self-assigned this Apr 3, 2020
@luukvdmeer

This comment has been minimized.

@luukvdmeer luukvdmeer modified the milestones: Milestone 1, Milestone 2 Apr 6, 2020
@luukvdmeer luukvdmeer removed the high priority ⏰ Should be fixed soon label Apr 6, 2020
@luukvdmeer luukvdmeer changed the title Tidygraph 'methods' for sfnetwork objects Tidygraph methods for sfnetwork objects Apr 7, 2020
@luukvdmeer

This comment has been minimized.

@luukvdmeer luukvdmeer reopened this Jun 16, 2020
@luukvdmeer luukvdmeer modified the milestones: Milestone 2, Milestone 3 Jun 16, 2020
@luukvdmeer luukvdmeer changed the title Tidygraph methods for sfnetwork objects Not all tidygraph functions work on sfnetwork objects Dec 16, 2020
@luukvdmeer luukvdmeer added bug 🐛 Something isn't working low priority 💤 Would be nice to have but not necessary and removed feature 🎁 Request a new feature labels Dec 16, 2020
@luukvdmeer luukvdmeer removed this from the v0.4.0 milestone Dec 16, 2020
@FzliangFrank
Copy link

FzliangFrank commented Jun 9, 2023

I run into this very same error when I try to add a new vertex to a graph which has sfc object as attribute.

Here is code to replace this error.

# Create Demo Graph
g <- igraph::make_tree(20, 3, mode = "out")
nV <- length(V(g))
nE <- length(E(g))
V(g)$label <- sample(letters, nV, replace = T) # fix this latter NAME needs to not identical
V(g)$attr1_int <- sample(seq(10), nV, replace = T)
V(g)$attr2_letter <- sample(LETTERS, nV, replace = T)
V(g)$attr3_numb <- runif(nV) * 100
sp_df = data.frame(x = runif(nV), y= runif(nV)) |> sf::st_as_sf(coords=c('x', 'y'))
V(g)$attr4_geom <- sp_df$geom
E(g)$attr1 <- runif(nE) * 100

# Below code cause bug
g = g + vertex(13)

I suspect something do with graph::add.vertex

@agila5
Copy link
Collaborator

agila5 commented Jun 10, 2023

A simpler example:

# packages
library(igraph)
library(sf)

g <- make_empty_graph()
(g <- add_vertices(g, 2))
#> IGRAPH 4de905f D--- 2 0 -- 
#> + edges from 4de905f:
V(g)$geom <- st_sfc(st_point(c(0, 0)), st_point(c(1, 1)))
add_vertices(g, 1)
#> Error in UseMethod("st_bbox"): no applicable method for 'st_bbox' applied to an object of class "logical"

Created on 2023-06-10 with reprex v2.0.2

Should we open an issue in the igraph repository?

@FzliangFrank
Copy link

FzliangFrank commented Jun 12, 2023

Work around. It turns out oversubscribe a sf object cause it to add a new empty geometry.

Say you have 10 points in object my_sf_points. If you do my_sf_points[11] this value will be NA.

Based on this I wrote my own add_vertex_sf function:

#' add_vertex_sf
#'
#' @description When there is sf object in attributes
#' sf comparable adding vertex function.
#'
#' @param g igraph object
#' @param id a chr or that is passing to igraph::vertex
#' @param ... other arg pass to igraph::vertex
#' @param geom a geometry that to add if any
#' @return new added igraph
#'
#' @noRd


add_vertex_sf = function(g, id, ..., geom=NULL) {
  stopifnot(is.null(geom) || sf::st_is_valid(geom))
  schema = purrr::imap(vertex_attr(g), ~ class(.x))
  sf_attr_i = purrr::detect_index(schema, ~'sfc' %in% .x)
  sf_attr_name = names(schema)[sf_attr_i]

  if(sf_attr_i != 0) {
    sf_attr = vertex_attr(g, sf_attr_name)
    if(!is.null(geom)) sf_attr = c(sf_attr, geom)
    g = delete_vertex_attr(g, sf_attr_name)
    g = g + vertex(id, ...)
    set_vertex_attr(g, name = sf_attr_name, value = sf_attr[seq(length(V(g)))])
  } else {
    g = g + vertex(id, ...)
  }
}

@luukvdmeer
Copy link
Owner Author

An update on this issue:

  • Since tidygraph v1.3.0, creation of tbl_graph objects can handle sticky columns, meaning that reason number 2 is not applicable anymore. Still, in sfnetworks v1.0 we added a specific sfnetwork method for tidygraph::reroute(), to update geometries after rerouting.
  • To work around the issues with tidygraph::bind_nodes() and tidygraph::bind_edges(), in sfnetworks v1.0 there are now the functions bind_spatial_nodes() and bind_spatial_edges(), that correctly handle geometry list columns and check if the spatial network structure is still valid after binding.

Some more (simplified) insight on the issue of reason number 1:

In igraph the attributes of binded vertices and edges are first added as NA values, and only later the true values are copied. E.g. if a network with 2 edges having values "a" and "b" for attribute foo, and we want to bind one extra edge having value "c" for attribute foo, attribute foo of the new network will be initialized as c("a", "b", NA), and only later be updated to form c("a", "b", "c"). However, if the case of the geometry list column, c(< geometry >, < geometry >, NA) does not work. It will complain the value NA does not have the same CRS as the geometries, and if the geometries have no CRS, it will complain instead that it cannot compute the bounding box of NA. As far as I could understand the igraph code, fixing this would require a change in the C core which I don't think will happen just to accommodate for sf-style geometry columns. From the sf side, it could be fixed by allowing to concat a vector of geometries with NA values, but not sure if that is conceptually defendable.

@luukvdmeer luukvdmeer removed the bug 🐛 Something isn't working label Dec 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
low priority 💤 Would be nice to have but not necessary
Projects
None yet
Development

No branches or pull requests

3 participants