convert character linestring to geometry in sf

Viewed 1596

Basic question, how do I convert type character LINESTRING variable to a geometry type in sf and then separately, separate the two coordinates into different variables. E.g.

library(tidyverse)
library(sf)
df <- structure(list(geometry = c("LINESTRING (-85.76 38.34, -85.72 38.38)", 
                            "LINESTRING (-85.46 38.76, -85.42 38.76)", 
                            "LINESTRING (-85.89 38.31, -85.85 38.32)"
), var1 = c(4, 5, 6
), var2 = c(1, 2, 3
)), row.names = c(NA, -3L), class = c("tbl_df", "tbl", "data.frame"
))
df
# # A tibble: 3 x 3
#   geometry                                 var1  var2
#   <chr>                                   <dbl> <dbl>
# 1 LINESTRING (-85.76 38.34, -85.72 38.38)     4     1
# 2 LINESTRING (-85.46 38.76, -85.42 38.76)     5     2
# 3 LINESTRING (-85.89 38.31, -85.85 38.32)     6     3

Desired output:

final
#                     geometry   start_lon start_lat end_lon end_lat var1 var2
# -85.76, -85.72, 38.34, 38.38      -85.76     38.34  -85.72   38.38    4    1
# -85.46, -85.42, 38.76, 38.76      -85.46     38.76  -85.42   38.76    5    2
# -85.89, -85.85, 38.31, 38.32      -85.89     38.31  -85.85   38.32    6    3


#so I can quickly plot lines between points
plot(final[, "var1"])

I thought some combination like final <- st_as_sf(df) %>% st_geometry(geometry) would convert it but I cant figure it out. Perhaps sfheaders is useful here?

EDIT

I want an sf type solution and not to manually extract coordinates.

Any suggestions? thanks

2 Answers

library(sf) can read Well-known Text direclty, if you tell it to using the wkt = argument. This gives you your sf object.

sf <- sf::st_as_sf( df, wkt = "geometry" )
sf
# Simple feature collection with 3 features and 2 fields
# geometry type:  LINESTRING
# dimension:      XY
# bbox:           xmin: -85.89 ymin: 38.31 xmax: -85.42 ymax: 38.76
# CRS:            NA
#                         geometry var1 var2
# 1 LINESTRING (-85.76 38.34, -...    4    1
# 2 LINESTRING (-85.46 38.76, -...    5    2
# 3 LINESTRING (-85.89 38.31, -...    6    3

Then there are many ways you can get the coordinates out and reshape them. Here I'm using sfheaders to convert the sf object to a long data.frame, then using library(data.table) to reshape it.

library(sfheaders)
library(data.table)

dt <- sfheaders::sf_to_df( sf, fill = TRUE )
setDT( dt )

## In this example, every 2nd row is to be made into new columns

odds <- 1:nrow(dt) %% 2 == 1
evens <- !odds

dt <- dt[odds][
  dt[evens, .(end_lon = x, end_lat = y, linestring_id)]
  , on = "linestring_id"
]

setnames( dt, c("x","y"), c("start_lon", "start_lat"))

dt
#    var1 var2 sfg_id linestring_id start_lon start_lat end_lon end_lat
# 1:    4    1      1             1    -85.76     38.34  -85.72   38.38
# 2:    5    2      2             2    -85.46     38.76  -85.42   38.76
# 3:    6    3      3             3    -85.89     38.31  -85.85   38.32

I am not familiar with the mentioned package but using regex we can do something like

gsub("[^0-9\\.\\s-]", "", df$geometry, perl= TRUE)

For the desired output we can use

df <- cbind.data.frame(df, do.call("rbind.data.frame", lapply(strsplit(gsub("[^0-9\\.\\s-]", "", df$geometry, perl= TRUE), " "), function(i) as.numeric(i[2:length(i)]))))
df <- df[ , c(1, 4:7, 2, 3)]
names(df) <- c("geometry", "start_lon", "start_lat", "end_lon", "end_lat", "var1", "var2")

What it does: gsub extracts the characters you are interested in, strsplit splits the strings after every space, the part in lapply is to drop each first space and to convert to numeric and do.call binds all to a dataframe. Finally, we change column order and name the columns.

Related