-
Notifications
You must be signed in to change notification settings - Fork 299
Open
Description
st_cast() drops polygons when an earlier MULTIPOLYGON is empty
Summary
sf::st_cast(..., "POLYGON") loses parts and misaligns attributes when a data frame contains an empty MULTIPOLYGON followed by a multi-part MULTIPOLYGON. The second and third features are collapsed to a single polygon each and its attributes no longer line up with the expected rows.
Steps to Reproduce
Run the minimal example (no external data):
library(sf)
#> Linking to GEOS 3.12.1, GDAL 3.8.4, PROJ 9.4.0; sf_use_s2() is TRUE
library(sessioninfo)
square <- function(offset) {
rbind(
c(offset, 0), c(offset + 1, 0), c(offset + 1, 1),
c(offset, 1), c(offset, 0)
)
}
mp_empty <- st_multipolygon()
mp_two_a <- st_multipolygon(list(list(square(0)), list(square(10))))
mp_two_b <- st_multipolygon(list(list(square(20)), list(square(30))))
x <- st_sf(
name = c("empty", "first_multi", "second_multi"),
value = c("a", "b", "c"),
geometry = st_sfc(mp_empty, mp_two_a, mp_two_b, crs = 4326)
)
bbox_before <- do.call(
rbind,
lapply(x$geometry, function(g) sf::st_bbox(g)[c("xmin", "xmax")])
)
#> Before cast (per-row xmin/xmax):
print(data.frame(name = x$name, value = x$value, xmin = bbox_before[, "xmin"], xmax = bbox_before[, "xmax"]))
#> name value xmin xmax
#> 1 empty a NA NA
#> 2 first_multi b 0 11
#> 3 second_multi c 20 31
expected_names <- c("empty", "first_multi", "first_multi", "second_multi", "second_multi")
#> Expected names after cast: empty, first_multi, first_multi, second_multi, second_multi
cast <- st_cast(x, "POLYGON")
#> Warning in x[!e] <- ret: number of items to replace is not a multiple of
#> replacement length
print(cast)
#> Simple feature collection with 3 features and 2 fields (with 1 geometry empty)
#> Geometry type: POLYGON
#> Dimension: XY
#> Bounding box: xmin: 0 ymin: 0 xmax: 11 ymax: 1
#> Geodetic CRS: WGS 84
#> name value geometry
#> 1 empty a POLYGON EMPTY
#> 2 first_multi b POLYGON ((0 0, 1 0, 1 1, 0 ...
#> 3 second_multi c POLYGON ((10 0, 11 0, 11 1,...
print(out)
#> name value xmin xmax
#> 1 empty a NA NA
#> 2 first_multi b 0 1
#> 3 second_multi c 10 11 # polygon from first_multi
cast_corrected <- st_cast(x |> dplyr::filter(!sf::st_is_empty(x$geometry)), "POLYGON")
#> Warning in st_cast.sf(dplyr::filter(x, !sf::st_is_empty(x$geometry)),
#> "POLYGON"): repeating attributes for all sub-geometries for which they may not
#> be constant
# works
print(cast_corrected)
#> Simple feature collection with 4 features and 2 fields
#> Geometry type: POLYGON
#> Dimension: XY
#> Bounding box: xmin: 0 ymin: 0 xmax: 31 ymax: 1
#> Geodetic CRS: WGS 84
#> name value geometry
#> 1 first_multi b POLYGON ((0 0, 1 0, 1 1, 0 ...
#> 1.1 first_multi b POLYGON ((10 0, 11 0, 11 1,...
#> 2 second_multi c POLYGON ((20 0, 21 0, 21 1,...
#> 2.1 second_multi c POLYGON ((30 0, 31 0, 31 1,...Created on 2025-11-27 with reprex v2.1.1
Session info
sessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#> setting value
#> version R version 4.5.1 (2025-06-13)
#> os Ubuntu 24.04.3 LTS
#> system x86_64, linux-gnu
#> ui X11
#> language (EN)
#> collate C.UTF-8
#> ctype C.UTF-8
#> tz Pacific/Auckland
#> date 2025-11-27
#> pandoc 3.1.3 @ /usr/bin/ (via rmarkdown)
#> quarto 1.7.31 @ /opt/quarto/bin/quarto
#>
#> ─ Packages ───────────────────────────────────────────────────────────────────
#> package * version date (UTC) lib source
#> class 7.3-23 2025-01-01 [3] RSPM (R 4.4.0)
#> classInt 0.4-11 2025-01-08 [3] RSPM (R 4.4.0)
#> cli 3.6.5 2025-04-23 [3] RSPM (R 4.4.0)
#> DBI 1.2.3 2024-06-02 [3] RSPM (R 4.4.0)
#> digest 0.6.37 2024-08-19 [3] RSPM (R 4.4.2)
#> dplyr 1.1.4 2023-11-17 [3] RSPM (R 4.4.0)
#> e1071 1.7-16 2024-09-16 [3] RSPM (R 4.4.0)
#> evaluate 1.0.5 2025-08-27 [3] RSPM (R 4.5.0)
#> fastmap 1.2.0 2024-05-15 [3] RSPM (R 4.4.2)
#> fs 1.6.6 2025-04-12 [3] RSPM (R 4.4.0)
#> generics 0.1.4 2025-05-09 [3] RSPM (R 4.5.0)
#> glue 1.8.0 2024-09-30 [3] RSPM (R 4.4.2)
#> htmltools 0.5.8.1 2024-04-04 [3] RSPM (R 4.4.0)
#> KernSmooth 2.23-26 2025-01-01 [3] RSPM (R 4.4.0)
#> knitr 1.50 2025-03-16 [3] RSPM (R 4.4.0)
#> lifecycle 1.0.4 2023-11-07 [3] RSPM (R 4.4.0)
#> magrittr 2.0.4 2025-09-12 [3] RSPM (R 4.5.0)
#> pillar 1.11.1 2025-09-17 [3] RSPM (R 4.5.0)
#> pkgconfig 2.0.3 2019-09-22 [3] RSPM (R 4.4.0)
#> proxy 0.4-27 2022-06-09 [3] RSPM (R 4.4.2)
#> R6 2.6.1 2025-02-15 [3] RSPM (R 4.4.0)
#> Rcpp 1.1.0 2025-07-02 [3] RSPM (R 4.5.0)
#> reprex 2.1.1 2024-07-06 [3] RSPM (R 4.4.0)
#> rlang 1.1.6 2025-04-11 [3] RSPM (R 4.4.0)
#> rmarkdown 2.30 2025-09-28 [3] RSPM (R 4.5.0)
#> sessioninfo * 1.2.3 2025-02-05 [3] RSPM (R 4.4.0)
#> sf * 1.0-21 2025-05-15 [3] RSPM (R 4.5.0)
#> tibble 3.3.0 2025-06-08 [3] RSPM (R 4.5.0)
#> tidyselect 1.2.1 2024-03-11 [3] RSPM (R 4.4.0)
#> units 0.8-7 2025-03-11 [3] RSPM (R 4.4.0)
#> vctrs 0.6.5 2023-12-01 [3] RSPM (R 4.4.0)
#> withr 3.0.2 2024-10-28 [3] RSPM (R 4.4.0)
#> xfun 0.53 2025-08-19 [3] RSPM (R 4.5.0)
#> yaml 2.3.10 2024-07-26 [3] RSPM (R 4.4.2)
#>
#> [1] /home/kendonb/R/x86_64-pc-linux-gnu-library/4.5
#> [2] /usr/local/lib/R/site-library
#> [3] /usr/lib/R/site-library
#> [4] /usr/lib/R/library
#> * ── Packages attached to the search path.
#>
#> ──────────────────────────────────────────────────────────────────────────────Actual Behavior
Only two rows are returned: the empty polygon and the first polygon part of nonempty_second. The second polygon part is dropped and attribute-to-geometry alignment is wrong.
Environment
- R:
Rscript --vanilla - sf: 1.0.21
- GEOS: 3.12.1
- GDAL: 3.8.4
- PROJ: 9.4.0
- s2: enabled
Metadata
Metadata
Assignees
Labels
No labels