library(plotbiomes)
library(sf)
library(terra)
library(ggplot2)
library(dplyr)
The plotbiomes package was created to plot Whittaker’s biome types in R via ggplot.
Recently I used a modified version of the biomes polygon dataset to infer the distribution of 48,129 tree species across the different biome types, and uploaded the results in a Zenodo archive. The 48,129 tree species were the same as documented in the Tree Globally Observed Environmental Ranges (TreeGOER) database, with also the same values used for mean annual temperature (MAT, BIO01) and annual precipitation (MAP, BIO12) as extracted from the WorldClim 2.1 database.
Here I show how the Whittaker biome type can be inferred from information on MAN and MAP. For combinations of MAN and MAP that occur outside the polygons, the location with respect to the polygons is also captured in a complementary zonation system.
# whitpol.file <- choose.files()
# Provide the location where the files for the shapefile
# (whittaker.shp, whittaker.shx, whittaker.dbf and whittaker.prj) were downloaded to
whitpol <- st_read(whitpol.file, quiet=TRUE)
st_crs(whitpol) <- NA
The polygon shows the distribution of nine biome types.
whitpol
## Simple feature collection with 9 features and 3 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: -15.63134 ymin: 0 xmax: 29.9846 ymax: 44.41541
## CRS: NA
## b_ID biome b_score geometry
## 1 6 Tundra 1 POLYGON ((-10.62857 0.16969...
## 2 7 Boreal forest 5 POLYGON ((-3.851808 11.0979...
## 3 8 Temperate grassland/desert 2 POLYGON ((-9.268834 0.33749...
## 4 5 Woodland/shrubland 4 POLYGON ((-9.758988 0.29510...
## 5 9 Temperate seasonal forest 6 POLYGON ((-0.3888281 3.8951...
## 6 3 Temperate rain forest 8 POLYGON ((3.242143 16.19025...
## 7 4 Tropical rain forest 9 POLYGON ((20.76227 34.52938...
## 8 1 Tropical seasonal forest/savanna 7 POLYGON ((28.55986 27.4391,...
## 9 2 Subtropical desert 3 POLYGON ((14.13556 0, 15.00...
In the plot, the b_score will be shown in the centroid of each polygon. This variable represents a score that is higher for biome types that are generally of higher temperature or precipitation in pairwise comparisons between between biome types.
whitcentr <- st_centroid(whitpol)
## Warning: st_centroid assumes attributes are constant over geometries
whitcoord <- st_coordinates(whitcentr)
whitpol <- cbind(whitpol, whitcoord)
The plotbiomes package includes a colour scheme. Here it is modified to include the score with the name of the biome type.
whitpol$label <- paste0(whitpol$biome, " (", whitpol$b_score, ")")
Ricklefs.df <- data.frame(Ricklefs_colors)
Ricklefs.df$biome <- rownames(Ricklefs.df)
Ricklefs.df <- left_join(Ricklefs.df,
whitpol[, c("biome", "label")],
by="biome")
Ricklefs.df
## Ricklefs_colors biome
## 1 #C1E1DD Tundra
## 2 #A5C790 Boreal forest
## 3 #97B669 Temperate seasonal forest
## 4 #75A95E Temperate rain forest
## 5 #317A22 Tropical rain forest
## 6 #A09700 Tropical seasonal forest/savanna
## 7 #DCBB50 Subtropical desert
## 8 #FCD57A Temperate grassland/desert
## 9 #D16E3F Woodland/shrubland
## label geometry
## 1 Tundra (1) POLYGON ((-10.62857 0.16969...
## 2 Boreal forest (5) POLYGON ((-3.851808 11.0979...
## 3 Temperate seasonal forest (6) POLYGON ((-0.3888281 3.8951...
## 4 Temperate rain forest (8) POLYGON ((3.242143 16.19025...
## 5 Tropical rain forest (9) POLYGON ((20.76227 34.52938...
## 6 Tropical seasonal forest/savanna (7) POLYGON ((28.55986 27.4391,...
## 7 Subtropical desert (3) POLYGON ((14.13556 0, 15.00...
## 8 Temperate grassland/desert (2) POLYGON ((-9.268834 0.33749...
## 9 Woodland/shrubland (4) POLYGON ((-9.758988 0.29510...
Now we can plot the figure, including the scores for each biome type. The same script was used to create the figure included in the Zenodo archive.
xlab <- "Average Annual Temperature (°C)"
plot_1 <- ggplot() +
# add biome polygons
geom_sf(data = whitpol,
aes(fill = label),
colour = "gray98",
size = 1) +
geom_text(data = whitpol,
aes(x=X,
y=Y,
label=b_score),
colour = "black",
size = 5) +
theme_bw() +
xlab(xlab) +
ylab("Annual Precipitation (dm)") +
scale_fill_manual(name = "Whittaker biomes",
breaks = Ricklefs.df$label,
labels = Ricklefs.df$label,
values = Ricklefs.df$Ricklefs_colors) +
# scale_y_continuous(limits =c(-1, 45)) +
coord_sf(clip = "off")
plot_1
The following function extracts the biome type (as its b_score) from the biome types polygons.
whittaker_extract <- function(
x, whitpol)
{
names(x) <- c("temp_c", "precp_dm")
x$ID <- seq_len(nrow(x))
x$inside <- as.logical(1)
biometerra <- terra::vect(whitpol)
biomedata <- terra::extract(biometerra, y=x[, c("temp_c", "precp_dm")])
biomedata <- slice_max(biomedata, order_by=b_score, by=id.y, n=1)
biomeout <- left_join(x,
biomedata,
by=c("ID"="id.y"))
biomeout[is.na(biomeout$b_score), "inside"] <- as.logical(0)
# outside
biomeout[biomeout$inside == FALSE & biomeout$temp_c < -4.08, "b_score"] <- 1.1 # outside Tundra
biomeout[is.na(biomeout$b_score) & biomeout$temp_c < 4.35, "b_score"] <- 5.1 # outside Boreal forest
biomeout[is.na(biomeout$b_score) & biomeout$temp_c < 20.65, "b_score"] <- 8.1 # outside Boreal forest
biomeout[is.na(biomeout$b_score) & biomeout$precp_dm > 28.131, "b_score"] <- 9.1 # outside Tropical rain forest
biomeout[is.na(biomeout$b_score) & biomeout$precp_dm > 9.991, "b_score"] <- 7.1 # outside Tropical seasonal forest
biomeout[is.na(biomeout$b_score), "b_score"] <- 3.1 # outside Subtropical desert
return(biomeout)
}
As one example, based on the MAT and MAP for Nairobi with data obtained from the CitiesGOER database, its biome type is: Tropical seasonal forest/savanna.
Be careful to specify the precipitation in dm, not mm or cm!
nairobi <- data.frame(MAT=19.61,
MAP=1036/100)
whittaker_extract(nairobi, whitpol)
## temp_c precp_dm ID inside b_ID biome b_score
## 1 19.61 10.36 1 TRUE 1 Tropical seasonal forest/savanna 7
## X Y label
## 1 24.06443 15.78098 Tropical seasonal forest/savanna (7)
plot_1 +
geom_point(data=nairobi,
aes(x=MAT,
y=MAP),
colour="red")
Observations on boundaries between polygons are given the biome type of the highest score.
For observations outside the polygons, a different score is given that reflects their proximity to the polygons. For example, a score of 1.1 shows that the observation has the same temperature range as Tundra (with score 1).
To demonstrate how boundary and outside observations are treated, here I am plotting observations on boundaries and outside the polygons.
data.in <- Whittaker_biomes
data.in$precp_dm <- data.in$precp_cm / 10
data.in <- data.in[, c("temp_c", "precp_dm")]
# data.in
data.outside <- data.frame(temp_c = c(-10, -15, 0, 10, 35, 35, 35, 21),
precp_dm = c(10, 5, 20, 30, 30, 5, 20, 45))
data.in <- rbind(data.in, data.outside)
data.out <- whittaker_extract(data.in, whitpol)
# data.out
Observations on the boundary between Temperate grassland/desert and Subtropical desert are classified as the latter. Observations on the upper boundaries of these biome types are classified as the biome types occurring above them.
score.focal1 <- c(1, 1.1)
score.focal2 <- c(2) # straight line at precipitation = 0
score.focal3 <- c(3, 3.1) # straight line at precipitation = 0
plot_1 +
geom_point(data=data.out[data.out$b_score %in% score.focal1, ],
aes(x=temp_c,
y=precp_dm),
colour="red", shape=1) +
geom_point(data=data.out[data.out$b_score %in% score.focal2, ],
aes(x=temp_c,
y=precp_dm),
colour="green", shape=3) +
geom_point(data=data.out[data.out$b_score %in% score.focal3, ],
aes(x=temp_c,
y=precp_dm),
colour="blue", shape=4)
Observations on the boundary between Temperate grassland/desert and Woodland/shrubland are classified as the latter. Observations on the boundary between Boreal forest and Temperate seasonal forest are classified as the latter.
score.focal4 <- c(4)
score.focal5 <- c(5, 5.1)
score.focal6 <- c(6)
plot_1 +
geom_point(data=data.out[data.out$b_score %in% score.focal4, ],
aes(x=temp_c,
y=precp_dm),
colour="red", shape=1) +
geom_point(data=data.out[data.out$b_score %in% score.focal5, ],
aes(x=temp_c,
y=precp_dm),
colour="green", shape=3) +
geom_point(data=data.out[data.out$b_score %in% score.focal6, ],
aes(x=temp_c,
y=precp_dm),
colour="blue", shape=4)
Observations on the boundary between Temperate rain forest and Tropical rain forest are classified as the latter. Observations outside the polygons are given a *.1 score that reflects the nearest polygon.
score.focal7 <- c(7, 7.1)
score.focal8 <- c(8, 8.1)
score.focal9 <- c(9, 9.1)
plot_1 +
geom_point(data=data.out[data.out$b_score %in% score.focal7, ],
aes(x=temp_c,
y=precp_dm),
colour="red", shape=1) +
geom_point(data=data.out[data.out$b_score %in% score.focal8, ],
aes(x=temp_c,
y=precp_dm),
colour="green", shape=3) +
geom_point(data=data.out[data.out$b_score %in% score.focal9, ],
aes(x=temp_c,
y=precp_dm),
colour="blue", shape=4)
This publication was initiated partially from ongoing work in a Darwin Initiative project (DAREX001) that develops a Global Biodiversity Standard for tree planting. The GlobalUsefulNativeTrees database and Tree Globally Observed Environmental Ranges database were realised through co-funding from this project. With scripts such as the ones shown here, when the Global Biodiversity Standard scheme becomes operational, tree planting projects can seek guidance on suitable species for planting.
sessionInfo()
## R version 4.2.1 (2022-06-23 ucrt)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19045)
##
## Matrix products: default
##
## locale:
## [1] LC_COLLATE=English_United Kingdom.utf8
## [2] LC_CTYPE=English_United Kingdom.utf8
## [3] LC_MONETARY=English_United Kingdom.utf8
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United Kingdom.utf8
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] dplyr_1.1.2 ggplot2_3.5.1 terra_1.7-46
## [4] sf_1.0-16 plotbiomes_0.0.0.9001
##
## loaded via a namespace (and not attached):
## [1] tidyselect_1.2.0 xfun_0.33 bslib_0.4.0 lattice_0.20-45
## [5] mapview_2.11.2 colorspace_2.0-3 vctrs_0.6.3 generics_0.1.3
## [9] htmltools_0.5.6 stats4_4.2.1 yaml_2.3.5 base64enc_0.1-3
## [13] utf8_1.2.2 rlang_1.1.1 e1071_1.7-11 jquerylib_0.1.4
## [17] pillar_1.9.0 withr_2.5.0 glue_1.6.2 DBI_1.1.3
## [21] sp_1.5-0 lifecycle_1.0.3 stringr_1.4.1 munsell_0.5.0
## [25] gtable_0.3.1 raster_3.6-23 htmlwidgets_1.5.4 codetools_0.2-18
## [29] evaluate_0.16 knitr_1.40 fastmap_1.1.1 crosstalk_1.2.0
## [33] class_7.3-20 fansi_1.0.3 highr_0.9 leafem_0.2.3
## [37] Rcpp_1.0.12 KernSmooth_2.23-20 scales_1.3.0 classInt_0.4-8
## [41] satellite_1.0.5 cachem_1.0.6 leaflet_2.2.0 jsonlite_1.8.0
## [45] farver_2.1.1 png_0.1-7 digest_0.6.29 stringi_1.7.8
## [49] grid_4.2.1 cli_3.4.1 tools_4.2.1 magrittr_2.0.3
## [53] sass_0.4.2 proxy_0.4-27 tibble_3.2.1 pkgconfig_2.0.3
## [57] data.table_1.14.2 rmarkdown_2.16 rstudioapi_0.14 R6_2.5.1
## [61] units_0.8-0 compiler_4.2.1