Disaggregate GEOMETRYCOLLECTION, MULTIPOINT, MULTILINESTRING, MULTIPOLYGON into sub-geometries

st_disaggregate(
  x,
  only_geometrycollection = FALSE,
  keep_empty = FALSE,
  warn = TRUE
)

Arguments

x

object of class sfg, sfc or sf

only_geometrycollection

logical; if TRUE only GEOMETRYCOLLECTIONs are disaggregated into their components, which can include MULTI-part geometries. If FALSE (default) MULTI-part geometries originating from GEOMETRYCOLLECTIONs or included as such in x are further disaggregated into their sub-geometries.

keep_empty

logical; if TRUE empty geometries are kept, if FALSE (default) they are dropped.

warn

logical; if TRUE, warn if attributes are assigned to sub-geometries

Value

an object of class sf, if the input x itself is an sf-object, else a object of class sfc, containing sub-geometries of the disaggregated input geometries. The level of disaggregation is determined by the argument only_geometrycollection.

Details

When GEOMETRYCOLLECTIONs, MULTIPOINTs, MULTILINESTRINGs or MULTIPOLYGONs are combined in a geometry set, possibly together with single part geometries, then disaggregating them can be a cumbersome task. In most situations, some combination of filtering by geometry type, st_collection_extract and/or st_cast, will do the job. However, st_disaggregate achieves that much more smoothly (for comparison s. examples below).

In particulate, extracting single and multi-part geometries as such when contained in the very same GEOMETRYCOLLECTION (i.e., without disaggregating the multi-part geometries into their sub-geometries) can be done with st_disaggregate, but neither with st_collection_extract nor with st_cast. Furthermore, st_disaggregate can dissolve a sfg object consisting of a multi-part geometry into all its sub-geometries, while st_cast would only return the first of the sub-geometry.

Examples

library(sf)

pt1 <- st_point(c(-10, -10))
pt2 <- st_point(c(10, -10))
pt3 <- st_point(c(10, 10))
pt4 <- st_point(c(-10, 10))
pt5 <- st_point(c(6, -5))
mpt <- st_multipoint(c(pt1, pt2, pt5))
ls1 <- st_linestring(c(pt1, pt2, pt3)) * 0.7 + c(1, 0)
ls2 <- st_linestring(c(pt1, pt4)) * 0.9 - c(1, 0)
ls3 <- st_linestring(c(pt1, pt3, pt4)) * 0.2 + c(-5, 6)
mls <- st_multilinestring(list(ls2, ls3))
pl1 <- st_polygon(list(rbind(pt1, pt3, pt4, pt1))) * 0.2 + c(-2, 3)
pl2 <- st_polygon(list(rbind(pt1, pt2, pt3, pt4, pt1) * 0.3,
                       rbind(pt1, pt2, pt3, pt1) * 0.1)) - c(2, 3)
pl3 <- st_polygon(list(rbind(pt1, pt5, pt3, pt4, pt1))) * 0.2 + c(3, 3)
mpl <- st_multipolygon(list(pl2, pl3))
gc1 <- st_geometrycollection(list(mpt, ls1, mpl))
gc2 <- st_geometrycollection(list()) # empty geometry

# function plotting sfc with distinct color for each geometry
plot_sfc <- function(sfc) {
  sfc %>% plot(., col = seq_along(.), border = seq_along(.), lwd = 2, cex = 1.5)
}

plot(gc1)

st_disaggregate(gc1) %>% summary()
#> LINESTRING      POINT    POLYGON    epsg:NA 
#>          1          3          2          0 
st_disaggregate(gc1) %>% plot_sfc()

st_disaggregate(gc1, only_geometrycollection = TRUE) %>% summary()
#>   LINESTRING   MULTIPOINT MULTIPOLYGON      epsg:NA 
#>            1            1            1            0 
st_disaggregate(gc1, only_geometrycollection = TRUE) %>% plot_sfc()


mixed_bag <- st_sfc(pt3, pt4, mls, pl1, gc1, gc2)
mixed_bag %>% plot_sfc()

mixed_bag %>% summary()
#> GEOMETRYCOLLECTION    MULTILINESTRING              POINT            POLYGON 
#>                  2                  1                  2                  1 
#>            epsg:NA 
#>                  0 
st_disaggregate(mixed_bag) %>% summary()
#> LINESTRING      POINT    POLYGON    epsg:NA 
#>          3          5          3          0 
st_disaggregate(mixed_bag, only_geometrycollection = TRUE) %>% summary()
#>      LINESTRING MULTILINESTRING      MULTIPOINT    MULTIPOLYGON           POINT 
#>               1               1               1               1               2 
#>         POLYGON         epsg:NA 
#>               1               0 
st_disaggregate(mixed_bag, keep_empty = TRUE) %>% summary()
#> GEOMETRYCOLLECTION         LINESTRING              POINT            POLYGON 
#>                  1                  3                  5                  3 
#>            epsg:NA 
#>                  0 
st_disaggregate(mixed_bag, keep_empty = TRUE) %>% st_is_empty() %>% summary()
#>    Mode   FALSE    TRUE 
#> logical      11       1 

mixed_bag_without_gc <- st_sfc(pt3, pt4, mpt, ls1, mls, pl1, mpl)
mixed_bag_without_gc %>% plot_sfc()

mixed_bag_without_gc %>% summary()
#>      LINESTRING MULTILINESTRING      MULTIPOINT    MULTIPOLYGON           POINT 
#>               1               1               1               1               2 
#>         POLYGON         epsg:NA 
#>               1               0 
st_disaggregate(mixed_bag_without_gc) %>% summary()
#> LINESTRING      POINT    POLYGON    epsg:NA 
#>          3          5          3          0 
# if no geometry collection is involved and only_geometrycollection = TRUE ...
# ... then no multi-part geometry is disaggregated:
all.equal(
  mixed_bag_without_gc,
  st_disaggregate(mixed_bag_without_gc, only_geometrycollection = TRUE)
)
#> [1] TRUE

# compare st_disaggregate() to st_cast() on mixed sets of single and multi-part-geometries:
mixed_ls_mls <- st_sfc(ls1, mls)
mixed_ls_mls %>% summary()
#>      LINESTRING MULTILINESTRING         epsg:NA 
#>               1               1               0 
st_disaggregate(mixed_ls_mls) %>% summary()
#> LINESTRING    epsg:NA 
#>          3          0 
st_cast(mixed_ls_mls, "LINESTRING") %>% summary() # only 1st sub-geometry of each multi-part kept.
#> Warning: keeping first linestring only
#> LINESTRING    epsg:NA 
#>          2          0 
st_cast(mixed_ls_mls, "MULTILINESTRING") %>% st_cast("LINESTRING") %>% summary() # this trick works!
#> LINESTRING    epsg:NA 
#>          3          0 
# but st_disaggregate() is more elegant:
st_equals(
  st_disaggregate(mixed_ls_mls),
  st_cast(mixed_ls_mls, "MULTILINESTRING") %>% st_cast("LINESTRING")
)
#> Sparse geometry binary predicate list of length 3, where the predicate
#> was `equals'
#>  1: 1
#>  2: 2
#>  3: 3

# compare usage of st_disaggregate() and st_collection_extract() for extracting
# single geometries of the same dimension from a geometry collection containing
# single as well as multi-part geometries of that very dimension:
gc <- st_geometrycollection(list(pt1, pt2, ls1, mls))
st_disaggregate(gc) %>% summary()
#> LINESTRING      POINT    epsg:NA 
#>          3          2          0 
st_collection_extract(gc, "LINESTRING") %>% summary()
#> MULTILINESTRING         epsg:NA 
#>               2               0 
st_disaggregate(gc) %>% .[st_dimension(.) == 1] %>% summary()
#> LINESTRING    epsg:NA 
#>          3          0 
st_collection_extract(gc, "LINESTRING") %>% st_cast("LINESTRING") %>% summary()
#> LINESTRING    epsg:NA 
#>          3          0 
st_equals(
  st_disaggregate(gc) %>% .[st_dimension(.) == 1],
  st_collection_extract(gc, "LINESTRING") %>% st_cast("LINESTRING")
)
#> Sparse geometry binary predicate list of length 3, where the predicate
#> was `equals'
#>  1: 2
#>  2: 3
#>  3: 1

# extracting single and multi-part geometries (of the same dimension) as such
# from a geometry collection only works with st_disaggregate():
st_disaggregate(gc, only_geometrycollection = TRUE) %>% .[st_dimension(.) == 1] %>% summary()
#>      LINESTRING MULTILINESTRING         epsg:NA 
#>               1               1               0