or more precisely union many spatial tables in R in a tidy way.
This post is a mess. See the updated version.
(it seems that as of 2020-12-27, it works) ;dplyr::bind_rows
doesn’t work on {sf} objectsbase::rbind
needs the objects to have the same structure and only works on two tables and so that’s not straightforward to use*.
→ So we’ll use purrr::map
and tidyr::unnest
.
NB : now in 2021, I advise to simply use :
fs::dir_ls("my_folder", regexp = ".*\\.shp$") %>% purrr::map_dfr(sf::read_sf)
or now in 2023, because map_dfr()
is being superseded :
fs::dir_ls("my_folder", regexp = ".*\\.shp$") %>% purrr::map(sf::read_sf) %>% dplyr::bind_rows()
dir_ls("my_folder", regexp = ".*\\.shp$") %>% purrr::map(sf:::read_sf) %>% purrr::list_rbind() %>% sf::st_sf()
As of October 2019 this method doesn’t work any longer, due to an update in the vctrs package.
https://github.com/r-spatial/sf/issues/1172.
So your best bet is to have the same structure in your shapefile and use :
dir_ls("shp", glob = "*.shp") %>% map(read_sf) %>% do.call(rbind, .)
It works again with vctrs 0.2.2 (February 2020)
First get some data, the communes of three french départements :
library(tidyverse) library(sf) library(fs) library(httr) library(leaflet) # https://fr.actualitix.com/blog/shapefiles-des-departements-de-france.html url <- c("https://fr.actualitix.com/blog/actgeoshap/01-Ain.zip", "https://fr.actualitix.com/blog/actgeoshap/73-savoie.zip", "https://fr.actualitix.com/blog/actgeoshap/74-haute-savoie.zip") dep <- str_extract(url, "\\d{2}.*$") list(url, dep) %>% pwalk(~ GET(.x, write_disk(.y))) walk(dep, unzip, junkpaths = TRUE, exdir = "shp")
We can then create a 3 rows data frame containing a list-column in which we store the sf object. Then we just unnest it. This operation erases the sf-class, we have to add it back.
res <- dir_ls("shp", glob = "*.shp") %>% tibble(fname = .) %>% mutate(data = map(fname, read_sf)) %>% unnest(data) %>% st_sf() %>% st_set_crs("EPSG:2154") write_sf(res, "shp/3dep.shp") res %>% st_transform(4326) %>% leaflet() %>% addPolygons() %>% addTiles()

Bonus : we have the source filename stored in the resulting shapefile.
* We could have used
dir_ls("shp", glob = "*.shp") %>% map(read_sf) %>% do.call(rbind, .)
but the column structure doesn’t match here…