Example 5: Bird Observations for 2015–2024 (5 km Resolution)

Author
Published

May 19, 2026

Note: This example is part of the Global Sampling Effort Dataset repository, which provides pre-computed, taxon-stratified rasters of spatial sampling effort derived from GBIF occurrence records. For an overview of all available examples, taxonomic groups, etc., see the main page. If you use these data or code, please cite:

El-Gabbas, A. (2026) A global, taxon-stratified, high-resolution sampling-effort dataset from GBIF for bias-aware ecological modelling. Diversity and Distributions 32, no. 5: e70205. https://doi.org/10.1111/ddi.70205..


Setup: Load required packages and define plot theme

require(ecokit)
require(dplyr)
require(terra)
require(rworldmap)
require(colorRamps)
require(sf)
require(ggplot2)
require(grid)
require(tidyterra)
global_map <- sf::st_as_sf(rworldmap::getMap(resolution = "high"))
common_theme <- ggplot2::theme_void() + 
  ggplot2::theme(
    plot.margin = grid::unit(c(0, 0, 0, 0), "lines"),
    legend.position = "right",
    legend.box.spacing = grid::unit(10, "pt"),
    legend.margin = ggplot2::margin(),
    legend.title = ggplot2::element_text(size = 7),
    legend.text = ggplot2::element_text(size = 7))

Bird observations globally (2015–2024; 5 km resolution)

This example retrieves the total number of bird observations (Aves) at 5 km resolution for each year from 2015 to 2024. Unlike previous examples that download a single aggregated raster, this call returns one raster per year. The files are saved to the effort_maps directory.

Compare with Example 2, which shows bird observations aggregated over the full period (1980–2025) at 10 km resolution.

effort_all <- ecokit::get_sampling_effort( 
  group = "aves", descendants = "all", metric = "n_obs", 
  years = 2015:2024, resolution = 5, out_dir = "effort_maps")

Inspect the returned tibble

The returned tibble now contains one row per year, each with its own OSF file identifier and local path:

dplyr::glimpse(effort_all)
Rows: 10
Columns: 9
$ group      <chr> "aves", "aves", "aves", "aves", "aves", "aves", "aves", "av…
$ descendant <chr> "all", "all", "all", "all", "all", "all", "all", "all", "al…
$ year       <chr> "2015", "2016", "2017", "2018", "2019", "2020", "2021", "20…
$ metric     <chr> "n_obs", "n_obs", "n_obs", "n_obs", "n_obs", "n_obs", "n_ob…
$ resolution <dbl> 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
$ name       <chr> "n_obs_Aves_2015_res_5.tif", "n_obs_Aves_2016_res_5.tif", "…
$ id         <chr> "69136d3d77fc36646dfdca3a", "69136d466d85e225bb0c1fa2", "69…
$ local_path <chr> "effort_maps/n_obs_Aves_2015_res_5.tif", "effort_maps/n_obs…
$ meta       <list> [[<NULL>, <NULL>, "n_obs_Aves_2015_res_5.tif", "file", "/69…

Visualise the raster

Load annual rasters into a multi-layer stack

Load all ten annual rasters into a single SpatRaster stack and assign descriptive layer names:

r_map <- terra::rast(effort_all$local_path) %>% 
  setNames(paste0("Aves_n_obs_", 2015:2024))
r_map
class       : SpatRaster
size        : 4320, 8640, 10  (nrow, ncol, nlyr)
resolution  : 0.04166667, 0.04166667  (x, y)
extent      : -180, 180, -90, 90  (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat WGS 84 (EPSG:4326)
sources     : n_obs_Aves_2015_res_5.tif
              n_obs_Aves_2016_res_5.tif
              n_obs_Aves_2017_res_5.tif
              ... and 7 more sources
names       : Aves_~_2015, Aves_~_2016, Aves_~_2017, Aves_~_2018, Aves_~_2019, Aves_~_2020, ...
min values  :           0,           0,           0,           0,           0,           0, ...
max values  :      172333,      179194,      185998,      220154,      260091,      252681, ...

Sum annual layers and transform

Sum the ten annual layers into a single cumulative raster, replace zeros with NA (to avoid -Inf after transformation), and apply a log10 transformation for visualisation:

r_map <- sum(r_map) %>%
  terra::classify(cbind(0, NA)) %>% 
  log10()

The maps below show the cumulative bird observations for 2015–2024. Compare with the full-period maps (1980–2025) in Example 2 to see how the spatial distribution of sampling effort has shifted in recent years.

Global map (log10 scale)

ggplot2::ggplot() +
  ggplot2::geom_sf(data = global_map, color = "black", size = 0.25, fill = "grey95") +
  tidyterra::geom_spatraster(data = r_map, maxcell = 2.3e6) +
  ggplot2::scale_fill_gradientn(colours = colorRamps::matlab.like2(100), na.value = "transparent") +
  ggplot2::geom_sf(data = global_map, color = "grey60", size = 0.125, fill = "transparent") +
  ggplot2::labs(fill = "# observations\n(log10)") +
  ggplot2::coord_sf(expand = FALSE) +
  common_theme

Europe (log10 scale)

r_map2 <- terra::crop(r_map, terra::ext(-11, 37.5, 35, 71))
ggplot2::ggplot() +
  tidyterra::geom_spatraster(data = r_map2, maxcell = 2.3e6) +
  ggplot2::scale_fill_gradientn(colours = colorRamps::matlab.like2(100), na.value = "transparent") +
  ggplot2::geom_sf(data = global_map, color = "grey30", size = 0.2, fill = "transparent") +
  ggplot2::labs(fill = "# observations\n(log10)") +
  ggplot2::coord_sf(expand = FALSE, xlim = c(-11, 37.5), ylim = c(35, 71)) +
  common_theme

USA (log10 scale)

r_map2 <- terra::crop(r_map, terra::ext(-125, -66.5, 24.5, 49.5))
ggplot2::ggplot() +
  tidyterra::geom_spatraster(data = r_map2, maxcell = 2.3e6) +
  ggplot2::scale_fill_gradientn(colours = colorRamps::matlab.like2(100), na.value = "transparent") +
  ggplot2::geom_sf(data = global_map, color = "grey30", size = 0.5, fill = "transparent") +
  ggplot2::labs(fill = "# observations\n(log10)") +
  ggplot2::coord_sf(expand = FALSE, xlim = c(-125, -66.5), ylim = c(24.5, 49.5)) +
  common_theme

India (log10 scale)

r_map2 <- terra::crop(r_map, terra::ext(68.1, 97.4, 6.7, 35.5))
ggplot2::ggplot() +
  tidyterra::geom_spatraster(data = r_map2, maxcell = 2.3e6) +
  ggplot2::scale_fill_gradientn(colours = colorRamps::matlab.like2(100), na.value = "transparent") +
  ggplot2::geom_sf(data = global_map, color = "grey30", size = 0.5, fill = "transparent") +
  ggplot2::labs(fill = "# observations\n(log10)") +
  ggplot2::coord_sf(expand = FALSE, xlim = c(68.1, 97.4), ylim = c(6.7, 35.5)) +
  common_theme

← Previous: Example 4 | Next: Example 6