Introducing: the POLYGON FULL
Summary
This post introduces the POLYGON FULL
geometry, new in sf
1.0-18
which appeared on CRAN Today.
Why?
Polygons delineate a two-dimensional bound area. The OGC simple
feature access
standard defines
besides the regular POLYGON
and MULTIPOLYGON
(a set of polygons)
also the POLYGON EMPTY
and MULTIPOLYGON EMPTY
, which can both be
thought of as “no points”. There is however nothing polygonal about “no
points”, you can read more about this in Even Rouault’s blog
entry.
Although the simple feature standard does not explicitly state this, it
was clearly developed for flat, planar
geometries. The Earth
however is round, and doing geometrical operations e.g. on the
two-dimensional surface of a sphere changes many things. One of them is
that the total, entire surface is also a bound area. To denote that
area, since sf
1.0-18 we can now use POLYGON FULL
.
How?
As of 2020, R package sf
uses the R packgae s2
and the s2geometry
library for computations on
geometries with geodetic (unprojected) coordinates, unless it is told
not to do so (and assume a flat Earth) by
library(sf)
## Linking to GEOS 3.12.1, GDAL 3.8.4, PROJ 9.4.0; sf_use_s2() is TRUE
sf_use_s2(FALSE)
## Spherical geometry (s2) switched off
The s2geomety
library uses the concept of a POLYGON FULL
, and
represents it internally by POLYGON((0 -90, 0 -90))
. Package sf
also
does this:
sf_use_s2(TRUE)
## Spherical geometry (s2) switched on
(p = st_as_sfc("POLYGON FULL", crs = 'OGC:CRS84'))
## Geometry set for 1 feature
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: -180 ymin: -90 xmax: 180 ymax: 90
## Geodetic CRS: WGS 84 (CRS84)
## POLYGON FULL
If s2
is switched off, this geometry is not recognized as a
POLYGON FULL
but instead is printed as the POLYGON((0 -90, 0 -90))
:
sf_use_s2(FALSE)
## Spherical geometry (s2) switched off
p
## Geometry set for 1 feature
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: -180 ymin: -90 xmax: 180 ymax: 90
## Geodetic CRS: WGS 84 (CRS84)
## POLYGON ((0 -90, 0 -90))
which obviously is not a valid polygon
st_is_valid(p)
## [1] NA
and will lead to errors when used, e.g. in
st_make_valid(p)
## Error in scan(text = lst[[length(lst)]], quiet = TRUE): scan() expected 'a real', got 'IllegalArgumentException:'
## Error in (function (msg) : IllegalArgumentException: Invalid number of points in LinearRing found 2 - must be 0 or >= 3
When using s2
, it works nicely:
sf_use_s2(TRUE)
## Spherical geometry (s2) switched on
st_is_full(p)
## [1] TRUE
st_is_empty(p)
## [1] FALSE
st_is_valid(p)
## [1] TRUE
pt = st_as_sfc("POINT(7 52)", crs = 'OGC:CRS84')
st_intersects(p, pt)
## Sparse geometry binary predicate list of length 1, where the predicate
## was `intersects'
## 1: 1
st_intersection(p, pt)
## Geometry set for 1 feature
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: 7 ymin: 52 xmax: 7 ymax: 52
## Geodetic CRS: WGS 84 (CRS84)
## POINT (7 52)
st_distance(p, pt)
## Units: [m]
## [,1]
## [1,] 0
st_area(p) |> units::set_units(km^2) # spherical approximation
## 510066073 [km^2]
st_bbox(p)
## xmin ymin xmax ymax
## -180 -90 180 90
Examples
A more full example is described in this vignette, yielding this figure:
options(s2_oriented = TRUE) # don't change orientation from here on
co = st_as_sf(s2::s2_data_countries())
g = st_as_sfc("POLYGON FULL", crs = 'EPSG:4326')
oc = st_difference(g, st_union(co)) # oceans
b = st_buffer(st_as_sfc("POINT(-30 52)", crs = 'EPSG:4326'), 9800000) # visible half
i = st_intersection(b, oc) # visible ocean
plot(st_transform(i, "+proj=ortho +lat_0=52 +lon_0=-30"), col = 'blue')
Some more discussion leading to the current implementation is found in this sf issue. Comments are welcome in this issue!