2.6 Geometry types

Do you remember then?

They can be in well-known binary (WKB) or well-known text (WKT) standard. (the first one is a bit hard to read!). st_as_binary provide a way to convert to binary and it provides intersesting examples regarding precision.

The standard look like that (more here):

POINT (5 2)
LINESTRING (1 5, 4 4, 4 1, 2 2, 3 2)
POLYGON ((1 5, 2 2, 4 1, 4 4, 1 5)) # w/o hole
POLYGON ((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2 2 0,1 2 0,1 1 0)) # with hole / interior boundaries
MULTIPOINT (5 2, 1 3, 3 4, 3 2)
MULTILINESTRING ((1 5, 4 4, 4 1, 2 2, 3 2), (1 2, 2 4))
MULTIPOLYGON (((1 5, 2 2, 4 1, 4 4, 1 5), (0 2, 1 2, 1 3, 0 3, 0 2)))
GEOMETRYCOLLECTION (MULTIPOINT (5 2, 1 3, 3 4, 3 2), LINESTRING (1 5, 4 4, 4 1, 2 2, 3 2))

2.6.1 In sf: the workflow:

knitr::include_graphics("https://raw.githubusercontent.com/geocompx/geocompr/main/figures/02-sfdiagram.png")

lnd_point = st_point(c(0.1, 51.5))                 # sfg object
lnd_geom = st_sfc(lnd_point, crs = "EPSG:4326")    # sfc object
lnd_attrib = data.frame(                           # data.frame object
  name = "London",
  temperature = 25,
  date = as.Date("2017-06-21")
  )
lnd_sf = st_sf(lnd_attrib, geometry = lnd_geom)    # sf object
lnd_sf
## Simple feature collection with 1 feature and 3 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 0.1 ymin: 51.5 xmax: 0.1 ymax: 51.5
## Geodetic CRS:  WGS 84
##     name temperature       date         geometry
## 1 London          25 2017-06-21 POINT (0.1 51.5)

2.6.2 First is building geometry (sfg)

st_point(c(5, 2, 3))              # XYZ point
## POINT Z (5 2 3)
st_point(c(5, 2, 3, 1))           # XYZM point
## POINT ZM (5 2 3 1)
# stop at the M diemsion
linestring_matrix = rbind(c(1, 5), c(4, 4), c(4, 1), c(2, 2), c(3, 2))
st_linestring(linestring_matrix)
## LINESTRING (1 5, 4 4, 4 1, 2 2, 3 2)
polygon_list = list(rbind(c(1, 5), c(2, 2), c(4, 1), c(4, 4), c(1, 5)))
st_polygon(polygon_list)
## POLYGON ((1 5, 2 2, 4 1, 4 4, 1 5))
## POLYGON with a hole
polygon_border = rbind(c(1, 5), c(2, 2), c(4, 1), c(4, 4), c(1, 5))
polygon_hole = rbind(c(2, 4), c(3, 4), c(3, 3), c(2, 3), c(2, 4))
polygon_with_hole_list = list(polygon_border, polygon_hole)
st_polygon(polygon_with_hole_list)
## POLYGON ((1 5, 2 2, 4 1, 4 4, 1 5), (2 4, 3 4, 3 3, 2 3, 2 4))

It can be a bit tedious but this is usefull when you want to build some small/shareable example.

2.6.3 Simple feature columns (sfc)

We move from one feature to more:

# sfc POINT
point1 = st_point(c(5, 2))
point2 = st_point(c(1, 3))
points_sfc = st_sfc(point1, point2)
points_sfc
## Geometry set for 2 features 
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 1 ymin: 2 xmax: 5 ymax: 3
## CRS:           NA
## POINT (5 2)
## POINT (1 3)
st_crs()
## Coordinate Reference System: NA

All geometries in sfc objects must have the same CRS

it can be provided when building sfc

# Set the CRS with an identifier referring to an 'EPSG' CRS code:
points_sfc_wgs = st_sfc(point1, point2, crs = "EPSG:4326")
st_crs(points_sfc_wgs) 
## Coordinate Reference System:
##   User input: EPSG:4326 
##   wkt:
## GEOGCRS["WGS 84",
##     ENSEMBLE["World Geodetic System 1984 ensemble",
##         MEMBER["World Geodetic System 1984 (Transit)"],
##         MEMBER["World Geodetic System 1984 (G730)"],
##         MEMBER["World Geodetic System 1984 (G873)"],
##         MEMBER["World Geodetic System 1984 (G1150)"],
##         MEMBER["World Geodetic System 1984 (G1674)"],
##         MEMBER["World Geodetic System 1984 (G1762)"],
##         MEMBER["World Geodetic System 1984 (G2139)"],
##         ELLIPSOID["WGS 84",6378137,298.257223563,
##             LENGTHUNIT["metre",1]],
##         ENSEMBLEACCURACY[2.0]],
##     PRIMEM["Greenwich",0,
##         ANGLEUNIT["degree",0.0174532925199433]],
##     CS[ellipsoidal,2],
##         AXIS["geodetic latitude (Lat)",north,
##             ORDER[1],
##             ANGLEUNIT["degree",0.0174532925199433]],
##         AXIS["geodetic longitude (Lon)",east,
##             ORDER[2],
##             ANGLEUNIT["degree",0.0174532925199433]],
##     USAGE[
##         SCOPE["Horizontal component of 3D system."],
##         AREA["World."],
##         BBOX[-90,-180,90,180]],
##     ID["EPSG",4326]]
# TODO erase EPSG and see what we have 

2.6.4 sfheaders package

  • fully compatible with sf

  • every function come with a c++ implementation (header -> hence the name) and can be linked in your package

  • it is also a bit less “verbose”

# matrices
m = matrix(1:8, ncol = 2)
sfheaders::sfg_linestring(obj = m)
## LINESTRING (1 5, 2 6, 3 7, 4 8)
#> LINESTRING (1 5, 2 6, 3 7, 4 8)
# data.frames
df = data.frame(x = 1:4, y = 4:1)
sfheaders::sfg_polygon(obj = df)
## POLYGON ((1 4, 2 3, 3 2, 4 1, 1 4))
#> POLYGON ((1 4, 2 3, 3 2, 4 1, 1 4))
  • sfheaders is also very good at “casting”: ie converting between types (cf later!)

2.6.5 Spherical geometry with s2

It is an implementation of a Discrete Global Grid System (other example is H3)

sf_use_s2()
## [1] TRUE
india_buffer_with_s2 = st_buffer(india, 1)
sf_use_s2(FALSE)
## Spherical geometry (s2) switched off
#> Spherical geometry (s2) switched off
india_buffer_without_s2 = st_buffer(india, 1)
## Warning in st_buffer.sfc(st_geometry(x), dist, nQuadSegs, endCapStyle =
## endCapStyle, : st_buffer does not correctly buffer longitude/latitude data
## dist is assumed to be in decimal degrees (arc_degrees).
#> Warning in st_buffer.sfc(st_geometry(x), dist, nQuadSegs, endCapStyle =
#> endCapStyle, : st_buffer does not correctly buffer longitude/latitude data
#> dist is assumed to be in decimal degrees (arc_degrees).