The s2 R package provides bindings to Google’s S2Geometry library. The package exposes an API similar to Google’s BigQuery Geography API, whose functions also operate on spherical geometries. Package sf uses this package by default for nearly all its geometrical operations on objects with ellipsoidal (unprojected) coordinates; in cases where it doesn’t, such as st_relate(), it emits a warning.

This package is a complete rewrite of an earlier CRAN package s2 with versions up to 0.4-2, for which the sources are found here.

Installation

You can install the released version of s2 from CRAN with:

And the development version from GitHub with:

# install.packages("remotes")
remotes::install_github("r-spatial/s2")

Example

The s2 package provides geometry transformers and predicates similar to those found in GEOS, except instead of assuming a planar geometry, s2’s functions work in latitude and longitude and assume a spherical geometry:

library(s2)

s2_contains(
  # polygon containing much  of the northern hemisphere
  "POLYGON ((-63.5 44.6, -149.75 61.20, 116.4 40.2, 13.5 52.51, -63.5 44.6))",
  # ...should contain the north pole
  "POINT (0 90)"
)
#> [1] TRUE

The sf package uses s2 for geographic coordinates with sf::sf_use_s2(TRUE), and will become the default after sf version 1.0.0. The sf package also supports creating s2 vectors using as_s2_geography():

library(dplyr)
library(sf)

nc_s2 <- read_sf(system.file("shape/nc.shp", package = "sf")) %>% 
  mutate(geometry = as_s2_geography(geometry)) %>% 
  as_tibble() %>% 
  select(NAME, geometry)

nc_s2
#> # A tibble: 100 × 2
#>    NAME        geometry                                                         
#>    <chr>       <s2_geography>                                                   
#>  1 Ashe        <POLYGON ((-81.4528885 36.2395859, -81.4310379 36.2607193, -81.4…
#>  2 Alleghany   <POLYGON ((-81.1766739 36.4154434, -81.1533661 36.4247398, -81.1…
#>  3 Surry       <POLYGON ((-80.4530106 36.2570877, -80.4353104 36.5510445, -80.6…
#>  4 Currituck   <MULTIPOLYGON (((-75.9419327 36.2943382, -75.9575119 36.2594528,…
#>  5 Northampton <POLYGON ((-77.1419601 36.4170647, -77.1393204 36.4564781, -77.1…
#>  6 Hertford    <POLYGON ((-76.7074966 36.2661324, -76.7413483 36.3151665, -76.9…
#>  7 Camden      <POLYGON ((-76.0173492 36.3377304, -76.0328751 36.3359756, -76.0…
#>  8 Gates       <POLYGON ((-76.46035 36.3738976, -76.5024643 36.4522858, -76.498…
#>  9 Warren      <POLYGON ((-78.1347198 36.2365837, -78.1096268 36.2135086, -78.0…
#> 10 Stokes      <POLYGON ((-80.0240555 36.5450249, -80.0480957 36.5471344, -80.4…
#> # … with 90 more rows

Use accessors to extract information about geometries:

nc_s2 %>% 
  mutate(
    area = s2_area(geometry),
    perimeter = s2_perimeter(geometry)
  )
#> # A tibble: 100 × 4
#>    NAME        geometry                                           area perimeter
#>    <chr>       <s2_geography>                                    <dbl>     <dbl>
#>  1 Ashe        <POLYGON ((-81.4528885 36.2395859, -81.431037…   1.14e9   141627.
#>  2 Alleghany   <POLYGON ((-81.1766739 36.4154434, -81.153366…   6.11e8   119876.
#>  3 Surry       <POLYGON ((-80.4530106 36.2570877, -80.435310…   1.42e9   160458.
#>  4 Currituck   <MULTIPOLYGON (((-75.9419327 36.2943382, -75.…   6.94e8   301644.
#>  5 Northampton <POLYGON ((-77.1419601 36.4170647, -77.139320…   1.52e9   211794.
#>  6 Hertford    <POLYGON ((-76.7074966 36.2661324, -76.741348…   9.68e8   160780.
#>  7 Camden      <POLYGON ((-76.0173492 36.3377304, -76.032875…   6.16e8   150430.
#>  8 Gates       <POLYGON ((-76.46035 36.3738976, -76.5024643 …   9.03e8   123170.
#>  9 Warren      <POLYGON ((-78.1347198 36.2365837, -78.109626…   1.18e9   141073.
#> 10 Stokes      <POLYGON ((-80.0240555 36.5450249, -80.048095…   1.23e9   140583.
#> # … with 90 more rows

Use predicates to subset vectors:

nc_s2 %>% 
  filter(s2_contains(geometry, "POINT (-80.9313 35.6196)"))
#> # A tibble: 1 × 2
#>   NAME    geometry                                                              
#>   <chr>   <s2_geography>                                                        
#> 1 Catawba <POLYGON ((-80.9312744 35.6195908, -81.0035782 35.6970558, -81.054779…

Use transformers to create new geometries:

nc_s2 %>% 
  mutate(geometry = s2_boundary(geometry))
#> # A tibble: 100 × 2
#>    NAME        geometry                                                         
#>    <chr>       <s2_geography>                                                   
#>  1 Ashe        <LINESTRING (-81.4528885 36.2395859, -81.4310379 36.2607193, -81…
#>  2 Alleghany   <LINESTRING (-81.1766739 36.4154434, -81.1533661 36.4247398, -81…
#>  3 Surry       <LINESTRING (-80.4530106 36.2570877, -80.4353104 36.5510445, -80…
#>  4 Currituck   <MULTILINESTRING ((-75.9419327 36.2943382, -75.9575119 36.259452…
#>  5 Northampton <LINESTRING (-77.1419601 36.4170647, -77.1393204 36.4564781, -77…
#>  6 Hertford    <LINESTRING (-76.7074966 36.2661324, -76.7413483 36.3151665, -76…
#>  7 Camden      <LINESTRING (-76.0173492 36.3377304, -76.0328751 36.3359756, -76…
#>  8 Gates       <LINESTRING (-76.46035 36.3738976, -76.5024643 36.4522858, -76.4…
#>  9 Warren      <LINESTRING (-78.1347198 36.2365837, -78.1096268 36.2135086, -78…
#> 10 Stokes      <LINESTRING (-80.0240555 36.5450249, -80.0480957 36.5471344, -80…
#> # … with 90 more rows

Finally, use the WKB or WKT exporters to export to sf or some other package:

nc_s2 %>% 
  mutate(geometry = st_as_sfc(s2_as_binary(geometry))) %>% 
  st_as_sf()
#> Simple feature collection with 100 features and 1 field
#> Geometry type: GEOMETRY
#> Dimension:     XY
#> Bounding box:  xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
#> CRS:           NA
#> # A tibble: 100 × 2
#>    NAME                                                                 geometry
#>    <chr>                                                              <GEOMETRY>
#>  1 Ashe        POLYGON ((-81.45289 36.23959, -81.43104 36.26072, -81.41233 36.2…
#>  2 Alleghany   POLYGON ((-81.17667 36.41544, -81.15337 36.42474, -81.1384 36.41…
#>  3 Surry       POLYGON ((-80.45301 36.25709, -80.43531 36.55104, -80.61105 36.5…
#>  4 Currituck   MULTIPOLYGON (((-75.94193 36.29434, -75.95751 36.25945, -75.9137…
#>  5 Northampton POLYGON ((-77.14196 36.41706, -77.13932 36.45648, -77.12733 36.4…
#>  6 Hertford    POLYGON ((-76.7075 36.26613, -76.74135 36.31517, -76.92408 36.39…
#>  7 Camden      POLYGON ((-76.01735 36.33773, -76.03288 36.33598, -76.04395 36.3…
#>  8 Gates       POLYGON ((-76.46035 36.3739, -76.50246 36.45229, -76.49834 36.50…
#>  9 Warren      POLYGON ((-78.13472 36.23658, -78.10963 36.21351, -78.05835 36.2…
#> 10 Stokes      POLYGON ((-80.02406 36.54502, -80.0481 36.54713, -80.43531 36.55…
#> # … with 90 more rows

Acknowledgment

This project gratefully acknowledges financial support from the