I think that the sf
package need to know first how you want to create the lines from your points. I mean which pair of POINT
make every LINESTRING
. In my example that was defined inside the lapply
function. Follow the reproducible and commented code below, hope that helps:
# Load library
library(sf)
# Create points data
multipoints <- st_multipoint(matrix(c(10, 10, 15, 20, 30, 30), nrow = 3, byrow = TRUE), dim = "XY")
points <- st_cast(st_geometry(multipoints), "POINT")
# Number of total linestrings to be created
n <- length(points) - 1
# Build linestrings
linestrings <- lapply(X = 1:n, FUN = function(x) {
pair <- st_combine(c(points[x], points[x + 1]))
line <- st_cast(pair, "LINESTRING")
return(line)
})
# One MULTILINESTRING object with all the LINESTRINGS
multilinetring <- st_multilinestring(do.call("rbind", linestrings))
# Plot
plot(multipoints, pch = 19, cex = 2)
plot(multilinetring[[1]], col = "orange", lwd = 2, add = TRUE)
plot(multilinetring[[2]], col = "green", lwd = 2, add = TRUE)
Here is a tidyverse
method of doing it, starting with your table from before converting to sf
. The approach is to create a long-form table where each row is a start or end point, but include a lineid
so that you can group_by
on it and summarise
to union the right points together, and then st_cast
to LINESTRING
.
library(tidyverse)
library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.2.3, proj.4 4.9.3
table <- structure(list(NOMBRE = c("AL011900", "AL011900", "AL011900", "AL011900", "AL021900", "AL021900", "AL021900", "AL041905", "AL041905", "AL041905", "AL041905"), LAT = c(15, 15.2, 15.3, 15.4, 19, 19.5, 20, 36.3, 37.9, 39.6, 41), LONG = c(-42.1, -43.4, -44.7, -45.6, -59.3, -60, -60.6, -48.6, -47.9, -47.1, -46), INT = c(18.0054, 18.0054, 18.0054, 18.0054, 33.4386, 36.0108, 38.583, 46.2996, 43.7274, 41.1552, 41.1552)), row.names = c(NA, -11L), class = c("tbl_df", "tbl", "data.frame"), spec = structure(list(cols = list(NOMBRE = structure(list(), class = c("collector_character", "collector")), LAT = structure(list(), class = c("collector_double", "collector")), LONG = structure(list(), class = c("collector_double", "collector")), FECHA = structure(list(format = ""), class = c("collector_datetime", "collector")), INT = structure(list(), class = c("collector_double", "collector"))), default = structure(list(), class = c("collector_guess", "collector"))), class = "col_spec"))
table_sf <- table %>%
group_by(NOMBRE) %>%
mutate(
lineid = row_number(), # create a lineid
LONG_end = lead(LONG), # create the end point coords for each start point
LAT_end = lead(LAT)
) %>%
unite(start, LONG, LAT) %>% # collect coords into one column for reshaping
unite(end, LONG_end, LAT_end) %>%
filter(end != "NA_NA") %>% # remove nas (last points in a NOMBRE group don't start lines)
gather(start_end, coords, start, end) %>% # reshape to long
separate(coords, c("LONG", "LAT"), sep = "_") %>% # convert our text coordinates back to individual numeric columns
mutate_at(vars(LONG, LAT), as.numeric) %>%
st_as_sf(coords = c("LONG", "LAT")) %>% # create points
group_by(NOMBRE, INT, lineid) %>%
summarise() %>% # union points into lines using our created lineid
st_cast("LINESTRING")
plot(table_sf[, 1:2])
You can see in the plot that each line between two points has its own INT
as requested.
Best Answer
One alternative, to apply an
st_cast
to"LINESTRING"
over each row:cant really be much better than:
I assume that's what you mean in your last line, you don't give code. This is probably pretty close to optimal.
library(microbenchmark)
reckons the two-casts is about 10 times faster on your little example: