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))),

get_line_endings <- function(line){

  res <- lapply(line, function(x){ 
            pnts <- st_coordinates(x)
            pnts <- pnts[c(1, nrow(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(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
    Commented Aug 5, 2017 at 7:49

3 Answers 3


st_cast is what you are looking for.


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

ptns = st_cast(ls, "POINT")

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

You can also cast to MULTIPOINT if you wish.


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
    Commented Aug 5, 2017 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
    Commented Aug 5, 2017 at 11:16
  • Maybe they want to merge all their features first?
    – Spacedman
    Commented Aug 5, 2017 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
    Commented Aug 6, 2017 at 15:42
  • 3
    st_line_sample(ls,sample=c(0,1)) will get the first and last points, I think...
    – Spacedman
    Commented Aug 6, 2017 at 17:07

If still of interest (or for those having the same question):

The package lwgeom contains the st_startpoint and st_endpoint functions that return respectively the source and target vertices of a linestring.

  • Thanks for the addition, was not aware of these functions. Commented Jun 1, 2020 at 18:22

End point of each linestring:


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) {
}) %>% 
  st_sfc() %>%
  st_sf('geom' = .)

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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