rdplyrr-sf

Coalescing two `POINT` columns gives a list-column instead of another `POINT` column


I have two tibbles, each with a geometry column. I want to merge those and make a sort of coalesce() but if I combine ifelse() with st_is_empty() I end up with a list column instead of a POINT column:

library(dplyr, warn.conflicts = FALSE)
library(sf)
#> Linking to GEOS 3.12.1, GDAL 3.8.4, PROJ 9.3.1; sf_use_s2() is TRUE

df1 <- tibble(x = c("a", "b"), lat = c(0, NA), lon = c(0, NA)) |> 
    st_as_sf(coords = c("lon", "lat"), na.fail = FALSE, sf_column_name = "geom1", crs = 4326) |> 
    as_tibble()
df2 <- tibble(x = c("a", "b"), lat = c(1, 2), lon = c(3, 4)) |> 
    st_as_sf(coords = c("lon", "lat"), na.fail = FALSE, sf_column_name = "geom2", crs = 4326) |> 
    as_tibble()

df1
#> # A tibble: 2 × 2
#>   x           geom1
#>   <chr> <POINT [°]>
#> 1 a           (0 0)
#> 2 b           EMPTY
df2
#> # A tibble: 2 × 2
#>   x           geom2
#>   <chr> <POINT [°]>
#> 1 a           (3 1)
#> 2 b           (4 2)

df1 |> 
    left_join(df2, join_by(x)) |> 
    mutate(
        geom3 = ifelse(st_is_empty(geom1), geom2, geom1)
    )
#> # A tibble: 2 × 4
#>   x           geom1       geom2 geom3   
#>   <chr> <POINT [°]> <POINT [°]> <list>  
#> 1 a           (0 0)       (3 1) <XY [2]>
#> 2 b           EMPTY       (4 2) <XY [2]>

Created on 2024-07-19 with reprex v2.1.0

Is there a way to prevent this from happening? If not, how can I convert this XY list to a POINT object?

Expected output:

#> # A tibble: 2 × 3
#>   x     geom1          geom2          geom3
#>   <chr> <POINT[°]>   <POINT [°]>   <POINT [°]>
#> 1 a     (0 0)         (3 1)         (0 0)
#> 2 b     EMPTY         (4 2)         (4 2)

Solution

  • Looks like ifelse() drops the sf class but not dplyr::if_else() (thanks @SamR for the hint):

    library(dplyr, warn.conflicts = FALSE)
    library(sf)
    #> Linking to GEOS 3.12.1, GDAL 3.8.4, PROJ 9.3.1; sf_use_s2() is TRUE
    
    df1 <- tibble(x = c("a", "b"), lat = c(0, NA), lon = c(0, NA)) |> 
        st_as_sf(coords = c("lon", "lat"), na.fail = FALSE, sf_column_name = "geom1", crs = 4326) |> 
        as_tibble()
    df2 <- tibble(x = c("a", "b"), lat = c(1, 2), lon = c(3, 4)) |> 
        st_as_sf(coords = c("lon", "lat"), na.fail = FALSE, sf_column_name = "geom2", crs = 4326) |> 
        as_tibble()
    
    df1
    #> # A tibble: 2 × 2
    #>   x           geom1
    #>   <chr> <POINT [°]>
    #> 1 a           (0 0)
    #> 2 b           EMPTY
    df2
    #> # A tibble: 2 × 2
    #>   x           geom2
    #>   <chr> <POINT [°]>
    #> 1 a           (3 1)
    #> 2 b           (4 2)
    
    df1 |> 
        left_join(df2, join_by(x)) |> 
        mutate(
            geom3 = if_else(st_is_empty(geom1), geom2, geom1)
        )
    #> # A tibble: 2 × 4
    #>   x           geom1       geom2
    #>   <chr> <POINT [°]> <POINT [°]>
    #> 1 a           (0 0)       (3 1)
    #> 2 b           EMPTY       (4 2)
    #> # ℹ 1 more variable: geom3 <POINT [°]>