Search code examples
rspatial

Find outer edge of polygon in R


I have a dataframe of longitude and latitude points which when plotted look like: polygon I want to define a seperate dataframe which contains all of the outer left edge. I have highlighted a few of the points here: highlighted points.

I have tried using the st_convex_hull() function as well as getBorders() which is part of the cartography package, but this gives me the whole outer edge, not just this left edge I want.


Solution

  • This solution uses the northermost and southermost points to split the border with lwgeom. The desired "edge" need to be selectionned manually (two lines among three). I used meuse as example. The border is obtained with st_concave_hull(ratio=0.1) and the points are tested on it. Adjustement might be needed depending of the concavity.

    library(tidyverse)
    library(sf)
    #> Linking to GEOS 3.11.2, GDAL 3.7.2, PROJ 9.3.0; sf_use_s2() is TRUE
    library(lwgeom) # for lwgeom::st_split
    #> Warning: le package 'lwgeom' a été compilé avec la version R 4.3.3
    #> Linking to liblwgeom 3.0.0beta1 r16016, GEOS 3.11.2, PROJ 9.3.1
    #> Warning in fun(libname, pkgname): PROJ versions differ: lwgeom has 9.3.1 sf has
    #> 9.3.0
    #> 
    #> Attachement du package : 'lwgeom'
    #> L'objet suivant est masqué depuis 'package:sf':
    #> 
    #>     st_perimeter
    library(sp) # for meuse
    
    data(meuse) 
    df_sf = meuse %>% st_as_sf(coords= c("x","y"),remove= FALSE) %>% 
      mutate(ID= 1:n())
    
    ggplot(data=df_sf)+geom_sf()
    

    
    # Boundary
    st_simplify(df_sf %>% summarise()  %>% st_concave_hull(ratio=0.1) %>% st_cast(to ="LINESTRING")) %>% 
      st_as_sfc() %>% ggplot(data=.)+geom_sf()
    

    
    # Getting the points
    border_point =   df_sf %>% summarise()  %>% st_concave_hull(ratio=0.1) %>% st_cast(to ="POINT") %>% 
      st_join(df_sf, .predicate = st_equals) 
    
    # Choosing start/end point. 
    MinMax =    bind_rows(border_point %>% slice_max(y), 
                          border_point %>% slice_min(y))
    
    # Splitting along those points
    boundary_split = 
    lwgeom::st_split(df_sf %>% summarise()  %>% st_concave_hull(ratio=0.1) %>% st_cast(to ="LINESTRING"),
                     MinMax ) %>% 
      st_collection_extract(., "LINESTRING") %>% 
      mutate(id_line = 1:n())
    
    # Three lines are generated, since the start/end of the linestring acts as a splitting point as well
    # Plotting to identify the releveants id_line (left shore), 
    boundary_split %>% 
      ggplot(data=.)+geom_sf( aes(col = id_line %>% as.factor()))
    

    
    # manually selecting 
    left_boundary = boundary_split %>% filter( id_line %in% c(1,2)) 
    
    left_points =  st_join(df_sf,left_boundary  ) %>%  
      # Removing unjoinned point (NA), there is certainly a better way
      filter( id_line %in% c(1,2)) 
    
    # Ploting elements
    
    ggplot()+
      geom_sf(data=df_sf %>% summarise()  %>% st_concave_hull(ratio=0.1),fill=NA)+
      geom_sf(data=df_sf,col ="grey")+
      geom_sf(data =MinMax, col ="green", size =5)+
      geom_sf(data = boundary_split,aes(col = id_line %>% as.factor()) )+
      geom_sf(data = left_points, col ="red")
    

    Created on 2024-04-16 with reprex v2.1.0