Welcome to {tidyterra}
tidyterra is a package that adds common methods from
the tidyverse for SpatRaster
and SpatVectors objects created with the {terra} package. It
also adds specific geom_spat*()
functions for plotting
these kind of objects with {ggplot2}.
Why {tidyterra}?
Spat* objects are not like regular data frames. They are a different type of objects, implemented via the S4 object system, and have their own syntax and computation methods, implemented on the {terra} package.
By implementing tidyverse methods for these objects, and more specifically {dplyr} and {tidyr} methods, a useR can now work more easily with Spat*, just like (s)he would do with tabular data.
Note that in terms of performance, {terra} is much more optimized for working for this kind of objects, so it is recommended also to learn a bit of {terra} syntax. Each function of {tidyterra} refers (when possible) to the corresponding equivalent on {terra}.
A note for advanced {terra} users
As previously mentioned, {tidyterra} is not optimized in terms of
performance. Specially when working with filter()
and
mutate()
methods, it can be slow.
As a rule of thumb, {tidyterra} can handle objects with less than
10.000.000 slots of information(i.e.,
terra::ncell(a_rast) * terra::nlyr(a_rast) < 10e6
).
Get started with {tidyterra}
Load {tidyterra} with additional libraries of the {tidyverse}:
library(tidyterra)
#>
#> Attaching package: 'tidyterra'
#> The following object is masked from 'package:stats':
#>
#> filter
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(tidyr)
Currently, the following methods are available:
tidyverse method | SpatVector | SpatRaster |
---|---|---|
tibble::as_tibble() |
✔️ | ✔️ |
dplyr::select() |
✔️ | ✔️ Select layers |
dplyr::mutate() |
✔️ | ✔️ Create /modify layers |
dplyr::transmute() |
✔️ | ✔️ |
dplyr::filter() |
✔️ | ✔️ Modify cells values and (additionally) remove outer cells. |
dplyr::slice() |
✔️ | ✔️ Additional methods for slicing by row and column. |
dplyr::pull() |
✔️ | ✔️ |
dplyr::rename() |
✔️ | ✔️ |
dplyr::relocate() |
✔️ | ✔️ |
dplyr::distinct() |
✔️ | |
dplyr::arrange() |
✔️ | |
dplyr::glimpse() |
✔️ | ✔️ |
dplyr::inner_join() family |
✔️ | |
dplyr::summarise() |
✔️ | |
dplyr::group_by() family |
✔️ | |
dplyr::rowwise() |
✔️ | |
dplyr::count() , tally()
|
✔️ | |
dplyr::bind_cols() /
dplyr::bind_rows()
|
✔️ as bind_spat_cols() /
bind_spat_rows()
|
|
tidyr::drop_na() |
✔️ | ✔️ Remove cell values with NA on any layer.
Additionally, outer cells with NA are removed. |
tidyr::replace_na() |
✔️ | ✔️ |
ggplot2::autoplot() |
✔️ | ✔️ |
ggplot2::fortify() |
✔️ to sf via sf::st_as_sf()
|
To a tibble with coordinates. |
ggplot2::geom_*() |
✔️ geom_spatvector()
|
✔️ geom_spatraster() and
geom_spatraster_rgb() . |
Let’s see some of them in action:
SpatRasters
See an example with SpatRaster objects:
library(terra)
f <- system.file("extdata/cyl_temp.tif", package = "tidyterra")
temp <- rast(f)
temp
#> class : SpatRaster
#> dimensions : 87, 118, 3 (nrow, ncol, nlyr)
#> resolution : 3881.255, 3881.255 (x, y)
#> extent : -612335.4, -154347.3, 4283018, 4620687 (xmin, xmax, ymin, ymax)
#> coord. ref. : World_Robinson
#> source : cyl_temp.tif
#> names : tavg_04, tavg_05, tavg_06
#> min values : 1.885463, 5.817587, 10.46338
#> max values : 13.283829, 16.740898, 21.11378
mod <- temp %>%
select(-1) %>%
mutate(newcol = tavg_06 - tavg_05) %>%
relocate(newcol, .before = 1) %>%
replace_na(list(newcol = 3)) %>%
rename(difference = newcol)
mod
#> class : SpatRaster
#> dimensions : 87, 118, 3 (nrow, ncol, nlyr)
#> resolution : 3881.255, 3881.255 (x, y)
#> extent : -612335.4, -154347.3, 4283018, 4620687 (xmin, xmax, ymin, ymax)
#> coord. ref. : World_Robinson
#> source(s) : memory
#> names : difference, tavg_05, tavg_06
#> min values : 2.817647, 5.817587, 10.46338
#> max values : 5.307511, 16.740898, 21.11378
On the previous example, we had:
Eliminated the first layer of the raster
tavg_04
.Created a new layer
newcol
as the difference of the layerstavg_05
andtavg_06.
Relocated
newcol
as the first layer of the SpatRasterReplaced the
NA
cells onnewcol
with3
.Renamed
newcol
to difference.
In all the process, the essential properties of the SpatRaster
(number of cells, columns and rows, extent, resolution and coordinate
reference system) have not been modified. Other methods as
filter()
, slice()
or drop_na()
can modify these properties, as they would do when applied to a data
frame (number of rows would be modified on that case).
SpatVectors
tidyterra >= 0.4.0
provides support to SpatVectors
for most of the {dplyr} methods, so it is possible to arrange, group and
summarise information of SpatVectors
lux <- system.file("ex/lux.shp", package = "terra")
v_lux <- vect(lux)
v_lux %>%
# Create categories
mutate(gr = cut(POP / 1000, 5)) %>%
group_by(gr) %>%
# Summary
summarise(
n = n(),
tot_pop = sum(POP),
mean_area = mean(AREA)
) %>%
# Arrange
arrange(desc(gr))
#> class : SpatVector
#> geometry : polygons
#> dimensions : 3, 4 (geometries, attributes)
#> extent : 5.74414, 6.528252, 49.44781, 50.18162 (xmin, xmax, ymin, ymax)
#> coord. ref. : lon/lat WGS 84 (EPSG:4326)
#> names : gr n tot_pop mean_area
#> type : <fact> <int> <int> <num>
#> values : (147,183] 2 359427 244
#> (40.7,76.1] 1 48187 185
#> (4.99,40.7] 9 194391 209.8
As in the case of SpatRaster, basic properties as the geometry and the CRS are preserved.
Plotting with {ggplot2}
SpatRasters
{tidyterra} provides several geom_*
for SpatRasters.
When the SpatRaster has the CRS informed
(i.e. terra::crs(a_rast) != ""
), the geom uses
ggplot2::coord_sf()
, and may be also reprojected for
adjusting the coordinates to other spatial layers:
library(ggplot2)
# A faceted SpatRaster
ggplot() +
geom_spatraster(data = temp) +
facet_wrap(~lyr) +
scale_fill_whitebox_c(
palette = "muted",
na.value = "white"
)
# Contour lines for a specific layer
f_volcano <- system.file("extdata/volcano2.tif", package = "tidyterra")
volcano2 <- rast(f_volcano)
ggplot() +
geom_spatraster(data = volcano2) +
geom_spatraster_contour(data = volcano2, breaks = seq(80, 200, 5)) +
scale_fill_whitebox_c() +
coord_sf(expand = FALSE) +
labs(fill = "elevation")
# Contour filled
ggplot() +
geom_spatraster_contour_filled(data = volcano2) +
scale_fill_whitebox_d(palette = "atlas") +
labs(fill = "elevation")
With {tidyterra} you can also plot RGB SpatRasters to add imagery to your plots:
# Read a vector
f_v <- system.file("extdata/cyl.gpkg", package = "tidyterra")
v <- vect(f_v)
# Read a tile
f_rgb <- system.file("extdata/cyl_tile.tif", package = "tidyterra")
r_rgb <- rast(f_rgb)
rgb_plot <- ggplot(v) +
geom_spatraster_rgb(data = r_rgb) +
geom_spatvector(fill = NA, size = 1)
rgb_plot
# Change CRS automatically
rgb_plot +
coord_sf(crs = 3857)
{tidyterra} provides selected scales that are suitable for creating hypsometric and bathymetric maps:
asia <- rast(system.file("extdata/asia.tif", package = "tidyterra"))
asia
#> class : SpatRaster
#> dimensions : 232, 432, 1 (nrow, ncol, nlyr)
#> resolution : 22550.66, 22512.94 (x, y)
#> extent : 7619120, 17361007, -1304745, 3918256 (xmin, xmax, ymin, ymax)
#> coord. ref. : WGS 84 / Pseudo-Mercator (EPSG:3857)
#> source : asia.tif
#> name : file44bc291153f2
#> min value : -10071.50
#> max value : 6064.73
ggplot() +
geom_spatraster(data = asia) +
scale_fill_hypso_tint_c(
palette = "gmt_globe",
labels = scales::label_number(),
breaks = c(-10000, -5000, 0, 2500, 5000, 8000),
guide = guide_colorbar(
direction = "horizontal",
title.position = "top",
barwidth = 20
)
) +
labs(
fill = "elevation (m)",
title = "Hypsometric map of Asia"
) +
theme_minimal() +
theme(legend.position = "bottom")
SpatVectors
{tidyterra} allows you to plot SpatVectors with {ggplot2} using the
geom_spatvector()
functions:
lux <- system.file("ex/lux.shp", package = "terra")
v_lux <- terra::vect(lux)
ggplot(v_lux) +
geom_spatvector(aes(fill = POP), color = "white") +
geom_spatvector_text(aes(label = NAME_2), color = "grey90") +
scale_fill_binned(labels = scales::number_format()) +
coord_sf(crs = 3857)
The underlying implementation is to take advantage of the conversion
terra::vect()/sf::st_as_sf()
and use
ggplot2::geom_sf()
as an endpoint for creating the
layer.
With {tidyterra} we can also aggregate SpatRaster at our convenience:
# Dissolving
v_lux %>%
# Create categories
mutate(gr = cut(POP / 1000, 5)) %>%
group_by(gr) %>%
# Summary
summarise(
n = n(),
tot_pop = sum(POP),
mean_area = mean(AREA)
) %>%
ggplot() +
geom_spatvector(aes(fill = tot_pop), color = "black") +
geom_spatvector_label(aes(label = gr)) +
coord_sf(crs = 3857)
# Same but keeping internal boundaries
v_lux %>%
# Create categories
mutate(gr = cut(POP / 1000, 5)) %>%
group_by(gr) %>%
# Summary without dissolving
summarise(
n = n(),
tot_pop = sum(POP),
mean_area = mean(AREA),
.dissolve = FALSE
) %>%
ggplot() +
geom_spatvector(aes(fill = tot_pop), color = "black") +
geom_spatvector_label(aes(label = gr)) +
coord_sf(crs = 3857)