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.
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* objects, 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.
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).
Load tidyterra with additional libraries of the tidyverse:
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() |
✔️ | ✔️ |
tidyr::fill() |
✔️ | |
tidyr::pivot_longer() |
✔️ | |
tidyr::pivot_wider() |
✔️ | |
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:
SpatRastersSee 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.11378On the previous example, we had:
Eliminated the first layer of the raster
tavg_04.
Created a new layer newcol as the difference of the
layers tavg_05 and tavg_06.
Relocated newcol as the first layer of the
SpatRaster.
Replaced the NA cells on newcol with
3.
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).
SpatVectorstidyterra >= 0.4.0 provides support to
SpatVectors for most of the dplyr and
tidyr 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.8As in the case of SpatRaster, basic properties as the
geometry and the CRS are preserved.
SpatRasterstidyterra 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"
)A faceted SpatRaster
# 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 lines plot for a SpatRaster
# Contour filled
ggplot() +
geom_spatraster_contour_filled(data = volcano2) +
scale_fill_whitebox_d(palette = "atlas") +
labs(fill = "elevation")Contour filled plot for a SpatRaster
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_plotPlotting a RGB SpatRaster
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 : 164, 306, 1 (nrow, ncol, nlyr)
#> resolution : 31836.23, 31847.57 (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 : -9558.468
#> max value : 5801.927
ggplot() +
geom_spatraster(data = asia) +
scale_fill_hypso_tint_c(
palette = "gmt_globe",
labels = scales::label_number(),
# Further refinements
breaks = c(-10000, -5000, 0, 2000, 5000, 8000),
guide = guide_colorbar(reverse = TRUE)
) +
labs(
fill = "elevation (m)",
title = "Hypsometric map of Asia"
) +
theme(
legend.position = "bottom",
legend.title.position = "top",
legend.key.width = rel(3),
legend.ticks = element_line(colour = "black", linewidth = 0.3),
legend.direction = "horizontal"
)Hypsometric tints
SpatVectorstidyterra 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)Plotting SpatVectors
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
SpatVectors 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)Union of SpatVectors
# 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)Union of SpatVector keeping the inner borders