knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "# ",
  echo = TRUE, 
  eval = TRUE
  )
library(sfheaders)
# library(sf) ## for nice printing (disabled on github actinos!)

R

The hierarchy of sf objects goes

  1. sfg - simple feature geometry
  2. sfc - simple feature collection (a collection of sfg)
  3. sf - simple feature object (sfc with data attributes)

sfg

The sfg_ group of functions assume the input is a single geometry.

sfg_point( 1:2 )
sfg_point( 1:3 )
sfg_point( 1:4 )

sfg_linestring( 1:4 )

df <- data.frame(
  x = c(1,1,4,4,1)
  , y = c(1,4,4,1,1)
)
sfg_linestring(df)

sfg_polygon(df)

sfc

The sfc_ group of functions let you specify an 'id' value to identify individual geometries. (Except for sfc_point(), in this case every row is an individual geometry).

df <- data.frame(
  id = c(1,1,2,2,3)
  , x = c(1,1,4,4,1)
  , y = c(1,4,4,1,1)
)

sfc_point( df )

sfc_multipoint( df, multipoint_id = "id" )

sfc_linestring( df, linestring_id = "id" )

## leaving the 'id' field blank
sfc_polygon( df )

sf

The sf_ functions also let you specify an 'id' value, but in this case the id is retained and kept on the object

df <- data.frame(
  id = c(1,1,2,2,3)
  , x = c(1,1,4,4,1)
  , y = c(1,4,4,1,1)
)

sf_point( df )

sf_multipoint( df, multipoint_id = "id" )

sf_linestring( df, linestring_id = "id" )

## leaving the 'id' field blank
sf_polygon( df )

In all these examples I haven't needed to specify the geometry columns, because other than the id field, all the other columns are used for the coordinates.

If your data.frame has other, non-geometry and non-id columns, you must specify at least the x and y paramters

df <- data.frame(
  id = c(1,1,2,2,3)
  , x = c(1,1,4,4,1)
  , y = c(1,4,4,1,1)
)

df$val <- letters[ df$id ]

sf_point( df, x = "x", y = "y" )

sf_linestring( df, x = "x", y = "y", linestring_id = "id" )

If you want to keep all the other columns, set keep = TRUE

sf_linestring( df, x = "x", y = "y", linestring_id = "id", keep = TRUE )

Converting to Data Frames

As of version 1.0 you can now convert from sfg, sfc and sf objects to data.frames

df <- data.frame(
  id = c(1,1,2,2,3)
  , x = c(1,1,4,4,1)
  , y = c(1,4,4,1,1)
)

df$val <- letters[ df$id ]

sf <- sf_linestring( df, x = "x", y = "y", linestring_id = "id", keep = TRUE )

sf_to_df( sf )

And if you want to keep all the other columns and fill them down each row of the data.frame, set fill = TRUE

sf_to_df( sf, fill = TRUE )

Performance

Here's a quick benchmark showing how well this library performs

n <- 1e5
df <- data.frame(
  id = rep(1:(n/5), each = 5)
  , x = rnorm(n)
  , y = rnorm(n)
)

library(data.table)
library(microbenchmark)

dt <- as.data.table( df )
microbenchmark(

  dt = {
    sf <- dt[
      , {
        geometry <- sf::st_linestring( x = matrix( c( x, y ), ncol = 2, byrow = T ))
        geometry <- sf::st_sf( geometry = sf::st_sfc( geometry ) )
      }
      , by = id
    ]
    sf <- sf::st_as_sf( sf )
  },

  sfheaders = {
    sfh <- sfheaders::sf_linestring(
      obj = df
      , linestring_id = "id"
    )
  },
  times = 5
)

# Unit: milliseconds
#      expr        min         lq       mean     median         uq        max neval
#        dt 6599.67479 6654.12357 6779.23543 6750.19807 6833.46262 7058.71809     5
# sfheaders   21.07775   21.30438   23.20592   23.21665   25.06429   25.36654     5


dcooley/sfheaders documentation built on July 31, 2024, 5:43 p.m.