6

I am trying to return the vertices of a linestring using the sf package. So far I have:

ls  <- st_sfc(st_linestring(rbind(c(0,0),c(0,1))),
        st_linestring(rbind(c(0,0),c(10,0))))

get_line_endings <- function(line){

  res <- lapply(line, function(x){ 
            pnts <- st_coordinates(x)
            pnts <- pnts[c(1, nrow(pnts)),]
            data.frame(pnts)
          })

  res <- do.call("rbind", res)
  res <- res[!duplicated(res),]

  st_as_sf(res, coords = c("X", "Y"), crs = st_crs(line))
}

pnts <- get_line_endings(ls)

plot(ls)
plot(pnts, add = TRUE)

enter image description here

Is this error prone? Can I rely on st_coordinates being "in order"? Is there a more efficient way of doing this?

  • Your ls is a vector of two line strings. Within a single line string you can be sure that st_coordinates is in order. What is it you are trying to do? – Spacedman Aug 5 '17 at 7:49
7

st_cast is what you are looking for.

library(sf)
library(mapview)

ls  <- st_sfc(st_linestring(rbind(c(0,0),c(0,1))),
              st_linestring(rbind(c(0,0),c(10,0))))

ptns = st_cast(ls, "POINT")

mapview(ls, color = "red") + ptns

You can also cast to MULTIPOINT if you wish.

Edit:

As mentioned by @Spacedman in the comments, this returns 4 points, because 2 separate linestrings with 2 vertices each are cast. If we want only 3 points, we need to set up the line to be a single linestring with 3 vertices:

ls = st_sfc(st_linestring(matrix(c(c(0,1),c(0,0),c(10,0)), 
                                 ncol = 2, byrow = TRUE)))

Then pnts = st_cast(ls, "POINT") will result in 3 points.

Edit 2:

If you only want the endpoints, then st_line_sample is what you are looking for:

ptns = st_line_sample(ls, sample = 1)

where sample is a numeric value between 0 and 1 (0 being the start point and 1 the endpoint).

  • 1
    Your code returns four points, the OPs example returns three because of some deduplication of the line endings. We should ask the OP exactly what they want here... – Spacedman Aug 5 '17 at 7:43
  • @Spacedman Good observation! Given that it's 2 linestrings, returning 4 points is correct behaviour. I've edited the answer to set up the linestring to only consist of 3 points in which case only 3 points are returned by st_cast. As you mention in your comment to the question, it is a matter of intention. – TimSalabim Aug 5 '17 at 11:16
  • Maybe they want to merge all their features first? – Spacedman Aug 5 '17 at 12:47
  • Duplication is fine. The only thing thats not perfect about your solution is that I only want the ends not all vertices. pnts[c(1, nrow(pnts),] – jsta Aug 6 '17 at 15:42
  • 3
    st_line_sample(ls,sample=c(0,1)) will get the first and last points, I think... – Spacedman Aug 6 '17 at 17:07
0

End point of each linestring:

library(sf)
library(tidyverse)

s1 <- rbind(c(0,3),c(0,4),c(1,5),c(2,5))
s2 <- rbind(c(0.2,3), c(0.2,4), c(1,4.8), c(2,4.8))
s3 <- rbind(c(0,4.4), c(0.6,5))

# LINESTRING sf object
sfls <- sapply(list(s1,s2,s3), function(x) st_linestring(x)) %>%
  st_sfc() %>%
  st_sf('geom' = .)

# break it to points
sfpts <- st_geometry(sfls) %>% 
  lapply(., function(x) {
    st_sfc(x) %>% 
      st_cast(., 'POINT')})

# subset the last point from each geometry, make a POINT sf object
sfls_ends <- sapply(sfpts, function(p) {
  p[c(length(p))]
}) %>% 
  st_sfc() %>%
  st_sf('geom' = .)

# check with
plot(sfls)
plot(sfls_ends, add = TRUE, pch = 19, col = 'red')

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.