How to export SHP file after Spatial networks in R with sf and tidygraph

Hello, I have worked with centrality calculation from OSM database. I followed the instruction https://www.r-spatial.org/r/2019/09/26/spatial-networks.html .

However, I want to export the shapefile of the street network that carries centralities values. I am new to R. So I am not sure what part is used as an object to export.

I tried 2 method

  1. writeOGR
    writeOGR("edges", dsn="210217", layer="edges", driver = "ESRI Shapefile")

then it always show
SRI Shapefile") : inherits(obj, "Spatial") is not TRUE

  1. st_write
    st_write(graph, "bkk.shp")
    sf::st_write(graph, "bkk.shp", driver = "ESRI Shapefile")

Error in UseMethod("st_write") :
no applicable method for 'st_write' applied to an object of class "c('tbl_graph', 'igraph')"

-------------------------------- code I would start from this part ----

writeVECT(
SDF = bangkok_center,
vname = 'bangkok_center',
v.in.ogr_flags = 'overwrite'
)

execGRASS("g.proj", flags = c("c", "quiet"), proj4 = proj4)
execGRASS(
cmd = 'v.clean',
input = 'bangkok_center',
output = 'bangkok_cleaned',
tool = 'break',
flags = c('overwrite', 'c')
)

edges <-bangkok_center %>%
mutate(edgeID = c(1:n()))
edges

nodes <- edges %>%
st_coordinates() %>%
as_tibble() %>%
rename(edgeID = L1) %>%
group_by(edgeID) %>%
slice(c(1, n())) %>%
ungroup() %>%
mutate(start_end = rep(c('start', 'end'), times = n()/2))

nodes

nodes <- nodes %>%
mutate(xy = paste(.$X, .$Y)) %>%
mutate(nodeID = group_indices(., factor(xy, levels = unique(xy)))) %>%
select(-xy)
nodes

source_nodes <- nodes %>%
filter(start_end == 'start') %>%
pull(nodeID)

target_nodes <- nodes %>%
filter(start_end == 'end') %>%
pull(nodeID)

edges = edges %>%
mutate(from = source_nodes, to = target_nodes)

edges

nodes <- nodes %>%
distinct(nodeID, .keep_all = TRUE) %>%
select(-c(edgeID, start_end)) %>%
st_as_sf(coords = c('X', 'Y')) %>%
st_set_crs(st_crs(edges))

nodes

graph = tbl_graph(nodes = nodes, edges = as_tibble(edges), directed = FALSE)
graph

sf_to_tidygraph = function(x, directed = TRUE) {

edges <- x %>%
mutate(edgeID = c(1:n()))

nodes <- edges %>%
st_coordinates() %>%
as_tibble() %>%
rename(edgeID = L1) %>%
group_by(edgeID) %>%
slice(c(1, n())) %>%
ungroup() %>%
mutate(start_end = rep(c('start', 'end'), times = n()/2)) %>%
mutate(xy = paste(.$X, .$Y)) %>%
mutate(nodeID = group_indices(., factor(xy, levels = unique(xy)))) %>%
select(-xy)

source_nodes <- nodes %>%
filter(start_end == 'start') %>%
pull(nodeID)
target_nodes <- nodes %>%
filter(start_end == 'end') %>%
pull(nodeID)
edges = edges %>%
mutate(from = source_nodes, to = target_nodes)

nodes <- nodes %>%
distinct(nodeID, .keep_all = TRUE) %>%
select(-c(edgeID, start_end)) %>%
st_as_sf(coords = c('X', 'Y')) %>%
st_set_crs(st_crs(edges))

tbl_graph(nodes = nodes, edges = as_tibble(edges), directed = directed)

}
sf_to_tidygraph(bangkok_center, directed = FALSE)

graph <- graph %>%
activate(edges) %>%
mutate(length = st_length(geometry))
graph

graph %>%
activate(edges) %>%
as_tibble() %>%
st_as_sf() %>%
group_by(highway) %>%
summarise(length = sum(length))

ggplot() +
geom_sf(data = graph %>% activate(edges) %>% as_tibble() %>% st_as_sf()) +
geom_sf(data = graph %>% activate(nodes) %>% as_tibble() %>% st_as_sf(), size = 0.5)

graph <- graph %>%
activate(nodes) %>%
mutate(degree = centrality_degree()) %>%
mutate(betweenness = centrality_betweenness(weights = length)) %>%
activate(edges) %>%
mutate(betweenness = centrality_edge_betweenness(weights = length))

graph

st_write(graph, "bkk.shp")
sf::st_write(graph, "bkk.shp", driver = "ESRI Shapefile")

ggplot() +
geom_sf(data = graph %>% activate(edges) %>% as_tibble() %>% st_as_sf(), aes(col = betweenness, size = betweenness)) +
scale_colour_viridis_c(option = 'inferno') +
scale_size_continuous(range = c(0,4))

ggplot() +
geom_sf(data = graph %>% activate(edges) %>% as_tibble() %>% st_as_sf(), col = 'grey50') +
geom_sf(data = graph %>% activate(nodes) %>% as_tibble() %>% st_as_sf(), aes(col = degree, size = degree)) +
scale_colour_viridis_c(option = 'inferno') +
scale_size_continuous(range = c(0,4))

Thank you very much.

You need to convert the graph to a spatial object before saving it.

Consider this piece of code:

graph %>%
  activate(edges) %>%
  as_tibble() %>%
  st_as_sf() %>% # this is the important part!
  st_write("bangkok.shp")

Thank you so much for your quick suggestions!
I tried it and it works! I got the SHP file but it is not the part I want with centrality value.

So I move st_write down as in this code but it shows this error result.

Error in UseMethod("st_write") :
no applicable method for 'st_write' applied to an object of class "c('tbl_graph', 'igraph')"

I assume I have to export the node and edge shape file separately.

graph %>%
activate(edges) %>%
as_tibble() %>%
st_as_sf() %>%
st_write("bangkok.shp") #your previous suggestion
group_by(highway) %>%
summarise(length = sum(length))

ggplot() +
geom_sf(data = graph %>% activate(edges) %>% as_tibble() %>% st_as_sf()) +
geom_sf(data = graph %>% activate(nodes) %>% as_tibble() %>% st_as_sf(), size = 0.5)

graph <- graph %>%
activate(nodes) %>%
mutate(degree = centrality_degree()) %>%
mutate(betweenness = centrality_betweenness(weights = length)) %>%
activate(edges) %>%
mutate(betweenness = centrality_edge_betweenness(weights = length))

graph

graph <- graph %>%
activate(nodes) %>%
mutate(degree = centrality_degree()) %>%
mutate(betweenness = centrality_betweenness(weights = length)) %>%
st_write("bangkok_node.shp")
activate(edges) %>%
mutate(betweenness = centrality_edge_betweenness(weights = length))
st_write("bangkok_edge.shp")
graph

I am sorry but I don't understand the centrality part :frowning:

What you can do is extract not the edges but the nodes

graph %>%
  activate(nodes) %>%
  as_tibble() %>%
  st_as_sf() %>% # this is the important part!
  st_write("bangkok.shp")

Or you can calculate centroids of the lines (can't see what good that could be)

graph %>%
  activate(edges) %>%
  as_tibble() %>%
  st_as_sf() %>% # this is the important part!
  st_centroid() %>%
  st_write("bangkok.shp")

The key part is that you can call st_write() only after converting your object from igraph to a spatial data format, i.e. the st_as_sf() call.

Thank you for clarification on how to use st_write() :grinning:

For the centrality part, I also posted the question on stackoverflow

one recommendation is the object graph is not a spatial object but rather a network type of graph composed of two different sets of observations 1. nodes or points ; 2. edges or links.

Try to transform graph 's edges and nodes into sf format first, and then export them to a shapefile. i.e.:

sf_graph_nodes <- graph %>% activate(nodes) %>% as_tibble() %>% st_as_sf()
sf_graph_edges <- graph %>% activate(edges) %>% as_tibble() %>% st_as_sf()

st_write(sf_graph_nodes, "nodes_bkk.shp")
st_write(sf_graph_edges, "edges_bkk.shp")