2

I have a dataframe with two sets of coordinates X,Y and prevXal,prevYval and I would like to make linestrings column wise, and create an sf object of the same number of linestrings as rows in the dataframe.

Using this dataframe example, I would like to create 4 linestrings.

library(sf)
df 
            X       Y prevXval prevYval
1    -0.121  65.001    0.067   65.117
2   180.000 -50.039 -179.879  -50.156
3    -0.075  47.787    0.038   47.904
4   179.754 -73.960 -179.900  -73.979

I started with st_linestring(), but this function works down the rows.

So I tried a function to break up the points to the right input format for st_linestring().

# Create a function that creates a linestring - that extracts vectors (using the as.numeric fn) of the X,Y and prevXval, prevYval points from nx4 dataframe, then bind to a matrix 
make_line <- function(x) {rbind(as.numeric(df[x,1:2]),
                              as.numeric(df[x,3:4]))%>%
            # Convert to a linestring object
            st_linestring()

make_line(1)
LINESTRING (-0.121 65.001, 0.067 65.117)

This works for one row, but I can't seem to find an efficient way to iterate over all the rows, having also tried purrr. Any suggestions?

2

split-apply-combine is the general pattern you're looking for, e.g.

library(sf)
df <-
tibble::tribble(~ID,        ~X,      ~Y, ~prevXval, ~prevYval,
                  1,    -0.121,  65.001,     0.067,    65.117,
                  2,   180.000, -50.039,  -179.879,   -50.156,
                  3,    -0.075,  47.787,     0.038,    47.904,
                  4,   179.754, -73.960,  -179.900,   -73.979)

rows <- split(df, seq(nrow(df)))
lines <- lapply(rows, function(row) {
    lmat <- matrix(unlist(row[2:5]), ncol = 2, byrow = TRUE)
    st_linestring(lmat)
  })
lines <- st_sfc(lines)
lines_sf <- st_sf('ID' = df$ID, 'geometry' = lines)

purrr::map can be used in place of lapply.

2

Here's my version of your function to create a line string from a single row:

st_segment = function(r){st_linestring(t(matrix(unlist(r), 2, 2)))}

then with my test dataset:

df = data.frame(X=runif(10),Y=runif(10), PX=runif(10), PY=runif(10))

I can create a geometry column by using sapply on that data frame:

df$geom = st_sfc(sapply(1:nrow(df), 
    function(i){st_segment(df[i,])},simplify=FALSE))

giving:

head(df)
           X          Y         PX        PY                           geom
1 0.40675125 0.18953097 0.31176190 0.8423786 LINESTRING (0.4067513 0.189...
2 0.76997740 0.05169539 0.04452896 0.6224400 LINESTRING (0.7699774 0.051...
3 0.01624562 0.55156073 0.27115739 0.2459570 LINESTRING (0.01624562 0.55...
4 0.73451115 0.74298509 0.89374478 0.4820546 LINESTRING (0.7345111 0.742...
5 0.28522902 0.93939136 0.15557674 0.7243534 LINESTRING (0.285229 0.9393...
6 0.79610820 0.59341010 0.36035310 0.9634315 LINESTRING (0.7961082 0.593...

Which I can then turn into a spatial data frame:

> df = st_sf(df)

and if I plot that I see the geometry...

0

Here's a solution using purrr

library(tidyverse)
library(sf)
df <- tibble::tribble(~ID,        ~X,      ~Y, ~prevXval, ~prevYval,
                      1,    -0.121,  65.001,     0.067,    65.117,
                      2,   180.000, -50.039,  -179.879,   -50.156,
                      3,    -0.075,  47.787,     0.038,    47.904,
                      4,   179.754, -73.960,  -179.900,   -73.979)

make_line <- function(X, Y, prevXval, prevYval) {
    st_linestring(matrix(c(X, prevXval, Y, prevYval), 2, 2))
}


df %>%
    select(X, Y, prevXval, prevYval) %>% 
    pmap(make_line) %>% 
    st_as_sfc(crs = 4326) 

Output:

Geometry set for 4 features 
geometry type:  LINESTRING
dimension:      XY
bbox:           xmin: -179.9 ymin: -73.979 xmax: 180 ymax: 65.117
epsg (SRID):    4326
proj4string:    +proj=longlat +datum=WGS84 +no_defs
LINESTRING (-0.121 65.001, 0.067 65.117)
LINESTRING (180 -50.039, -179.879 -50.156)
LINESTRING (-0.075 47.787, 0.038 47.904)
LINESTRING (179.754 -73.96, -179.9 -73.979)

If you want to obtain a spatial dataframe, just add a couple more transformations to the chain:

df %>%
    select(X, Y, prevXval, prevYval) %>% 
    pmap(make_line) %>% 
    st_as_sfc(crs = 4326) %>% 
    {cbind(df, geom = .)} %>% 
    st_sf() 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.