R – Creating SF Points and Lines from Data Frame Columns in Dplyr

linepointrsf

So I have a data.frame like this:

df = data.frame(
  x1 = runif(10, 10,11), 
  y1 = runif(10, 47,48),
  y2 = runif(10, 11,12), 
  y2 = runif(10, 48,49),
  name = sample(letters, 10))

And I want to create a sf dataframe that containes the lines from the points (x1, y1) to (x2, y2).

I fail at two stages.

  • First I am not entirely sure how to use st_point() in e.g. a dplyr::mutate
  • And I also do not know how to then convert it to a linestring

My imaginary pseudo code would look something like this:

# does not work  
df = data.frame(
  x1 = runif(10, 10,11), 
  y1 = runif(10, 47,48),
  y2 = runif(10, 11,12), 
  y2 = runif(10, 48,49),
  name = sample(letters, 10)) %>% 
  mutate( # create the points
    pt1 = st_point(c(x1, y1)),
    pt2 = st_point(c(x2, y2))
  ) %>% # create the lines
  mutate(
    line = st_linestring(pt1, pt2)
  )

Of course this does not work. But maybe someone has an idea on how to make this work.

Best Answer

I'd skip the dplyr and write it as a function, making building blocks that I can test:

make_line <- function(xy2){
    st_linestring(matrix(xy2, nrow=2, byrow=TRUE))
}

make_lines <- function(df, names=c("x1","y1","x2","y2")){
    m = as.matrix(df[,names])
    lines = apply(m, 1, make_line, simplify=FALSE)
    st_sfc(lines)
}

sf_pts_to_lines <- function(df, names=c("x1","y1","x2","y2")){
    geom = make_lines(df, names)
    df = st_sf(df, geometry=geom)
    df
}

Then I do:

df = sf_pts_to_lines(df)

You could if you wanted put that in a pipeline:

df = df %>% sf_pts_to_lines %>% ....

But doing things a line at a time in small chunks that you can easily test by checking intermediate results can be a much easier way to solve problems, and is generally faster than when R has to work round your pipe syntax.

Note that if you do it on a data frame that already has geometry, you need to drop the geometry first:

df = sf_pts_to_lines(st_drop_geometry(df))
Related Question