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.
You can install the released version of s2 from CRAN with:
install.packages("s2")
And the development version from GitHub with:
# install.packages("remotes")
remotes::install_github("r-spatial/s2")
The S2 package requires Abseil and OpenSSL. You can install these using a system package manager on most platforms:
- Windows: Both OpenSSL and Abseil are available from RTools since R 4.3
- MacOS:
brew install openssl abseil
- Debian/Ubuntu:
apt-get install libssl-dev libabsl-dev
- Fedora:
dnf install openssl-devel abseil-cpp-devel
- Alpine:
apk add abseil-cpp
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.41…
#> 2 Alleghany POLYGON ((-81.1766739 36.4154434, -81.1533661 36.4247398, -81.13…
#> 3 Surry POLYGON ((-80.4530106 36.2570877, -80.4353104 36.5510445, -80.61…
#> 4 Currituck MULTIPOLYGON (((-75.9419327 36.2943382, -75.9575119 36.2594528, …
#> 5 Northampton POLYGON ((-77.1419601 36.4170647, -77.1393204 36.4564781, -77.12…
#> 6 Hertford POLYGON ((-76.7074966 36.2661324, -76.7413483 36.3151665, -76.92…
#> 7 Camden POLYGON ((-76.0173492 36.3377304, -76.0328751 36.3359756, -76.04…
#> 8 Gates POLYGON ((-76.46035 36.3738976, -76.5024643 36.4522858, -76.4983…
#> 9 Warren POLYGON ((-78.1347198 36.2365837, -78.1096268 36.2135086, -78.05…
#> 10 Stokes POLYGON ((-80.0240555 36.5450249, -80.0480957 36.5471344, -80.43…
#> # ℹ 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.4310379 3… 1.14e9 141627.
#> 2 Alleghany POLYGON ((-81.1766739 36.4154434, -81.1533661 3… 6.11e8 119876.
#> 3 Surry POLYGON ((-80.4530106 36.2570877, -80.4353104 3… 1.42e9 160458.
#> 4 Currituck MULTIPOLYGON (((-75.9419327 36.2943382, -75.957… 6.94e8 301644.
#> 5 Northampton POLYGON ((-77.1419601 36.4170647, -77.1393204 3… 1.52e9 211794.
#> 6 Hertford POLYGON ((-76.7074966 36.2661324, -76.7413483 3… 9.68e8 160780.
#> 7 Camden POLYGON ((-76.0173492 36.3377304, -76.0328751 3… 6.16e8 150430.
#> 8 Gates POLYGON ((-76.46035 36.3738976, -76.5024643 36.… 9.03e8 123170.
#> 9 Warren POLYGON ((-78.1347198 36.2365837, -78.1096268 3… 1.18e9 141073.
#> 10 Stokes POLYGON ((-80.0240555 36.5450249, -80.0480957 3… 1.23e9 140583.
#> # ℹ 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.0547791…
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.2594528…
#> 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.49…
#> 9 Warren LINESTRING (-78.1347198 36.2365837, -78.1096268 36.2135086, -78.…
#> 10 Stokes LINESTRING (-80.0240555 36.5450249, -80.0480957 36.5471344, -80.…
#> # ℹ 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…
#> # ℹ 90 more rows
This project gratefully acknowledges financial support from the