The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Gap analysis workflow for multiple species


This workflow demonstrates how to import your own point data (occurrences) and SDMs (rasters), check all inputs, and run the full gap analysis for multiple species using the GapAnalysis R package. We provide a general approach using a for-loop for processing and storing results for multiple species. Example Cucurbita data is provided within the package library.

Note: This vignette recommends R version 4.5 or above. Windows users are also recommended to have Rtools 4.5 or above installed.

For each species, the workflow:

  1. Runs SRSex on all records.
  2. Checks inputs (occurrences, rasters, protected areas, ecoregions).
  3. Runs gap analysis metrics.
  4. Stores results as a table to an output excel file.
  5. Creates a conservation priority summary figure as an output png file.
  6. Generates interactive leaflet maps for both ex-situ and in-situ conservation as output HTML files.

At script end, the user is prompted to save the workflow by knitting to output HTML.


Download GapAnalysis repository (ALTERNATIVE)

Copy the GapAnalysis GitHub repository locally as an alternative method to source functions and load example data if the GapAnalysis R package does not install properly in the step above. See the GitHub resource page on copying a repository.

Easiest method: Download and extract the GapAnalysis repository. Select the green [Code] button on the main page to download as a ZIP.

You can also clone the repository if you have a GitHub account.


Data preparation

If you downloaded the repository locally, source functions and load data by removing the hashes below. Your specific path will differ based on your working directory and the relative path to the downloaded repository.

# ── Load libraries ─────────────────────────────────────────────────────────────
library(GapAnalysis)
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)
library(stringr)
library(terra)
## terra 1.9.11
## 
## Attaching package: 'terra'
## The following object is masked from 'package:tidyr':
## 
##     extract
library(leaflet)
library(openxlsx)
library(ggplot2)
library(ggtext)
library(cowplot)
library(htmltools)
library(htmlwidgets)
library(kableExtra)
## 
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
# ── Set outputs folder ────────────────────────────────────────────────────────
# NOTE: This path must match the path set in the knit: field in the YAML header
# All files will be saved here. Update both locations if changing the path, e.g.:
#outputs_folder <- "C:/Users/you/Desktop/GapAnalysis/outputs"
outputs_folder <- "outputs"
if (!dir.exists(outputs_folder)) dir.create(outputs_folder)

# ── Source functions from GapAnalysis repo (alternative method) ───────────────
#files <- list.files("R", pattern = "\\.[rR]$", full.names = TRUE)
#for (f in files) source(f)

# ── Load data from GapAnalysis repo (alternative method) ─────────────────────
#load("data/CucurbitaData.rda")      # Occurrence data
#load("data/CucurbitaRasts.rda")     # Raster list
#load("data/ProtectedAreas.rda")     # Protected areas raster
#load("data/ecoregions.rda")         # Ecoregions data

# ── Load your own data ────────────────────────────────────────────────────────
# NOTE: To use your own occurrence data and SDM rasters, remove the hashes
# below and update the file paths.
#occData <- read.csv("path/to/your_occurrences.csv") # Local occurrence data
#sdms <- list(                                  # Local rasters read in as list
#   rast("path/to/species1_sdm.tif"),
#   rast("path/to/species2_sdm.tif"))
# NOTE: To download protected areas and ecoregions data locally, run getDatasets(). 
# This requires an active internet connection.
#getDatasets()          # Local download of protected areas and ecoregions data

# ── Load example Cucurbita data (GapAnalysis library) ────────────────────────
# NOTE: To use your own data, hash out the two lines below.
occData <- CucurbitaData                  
sdms    <- terra::unwrap(CucurbitaRasts)

# ── Prepare spatial layers ────────────────────────────────────────────────────
ecos     <- terra::vect(ecoregions)
proAreas <- terra::unwrap(ProtectedAreas)

# Standardize CRS across all spatial layers to prevent mismatch warnings
target_crs <- "+proj=longlat +datum=WGS84"
ecos     <- terra::project(ecos, target_crs)
proAreas <- terra::project(proAreas, target_crs)

# ── Prepare species list and SDM rasters ──────────────────────────────────────
taxa    <- unique(occData$species)
sdmList <- lapply(seq_along(taxa), function(i) {
  r <- sdms[[i]]
  if (terra::crs(r, proj = TRUE) != terra::crs(ecos, proj = TRUE)) {
    r <- terra::project(r, terra::crs(ecos))
  }
  r <- terra::subst(r, 0, NA)
  r[!(is.na(r) | r == 1)] <- NA
  r
})
names(sdmList) <- taxa

# Confirm species names
message("\nConfirm species for gap analysis:\n", paste(taxa, collapse = "\n"))
## 
## Confirm species for gap analysis:
## Cucurbita_cordata
## Cucurbita_digitata
## Cucurbita_palmata

Run gap analysis

Example gap analysis workflow using a named list and for-loop to process and store results for all species.

For details on each metric calculation, review the function documentation in the GapAnalysis R folder.

results_list <- list()

for (i in seq_along(taxa)) {
  taxon          <- taxa[i]
  occurrenceData <- occData[occData$species == taxon, ]
  sdm            <- sdms[[i]]

  if (terra::crs(sdm, proj = TRUE) != terra::crs(ecos, proj = TRUE)) {
    sdm <- terra::project(sdm, terra::crs(ecos))
  }
  sdm <- terra::subst(sdm, 0, NA)
  sdm[!(is.na(sdm) | sdm == 1)] <- NA

  # 1. Run SRSex first (on all records)
  srsex <- SRSex(taxon = taxon, occurrenceData = occurrenceData)

  # 2. Check inputs
  occurrences     <- checkOccurrences(csv = occurrenceData, taxon = taxon)
  if (!inherits(sdm, "SpatRaster")) stop("sdm is not a SpatRaster")
  sdm_checked     <- checksdm(sdm)
  proArea_cropped <- terra::crop(proAreas, terra::ext(sdm_checked))
  proArea_checked <- checkProtectedAreas(protectedArea = proArea_cropped,
                                         sdm           = sdm_checked)

  # Skip species if SDM raster has no values
  if (all(is.na(terra::values(sdm_checked)))) {
    message("Skipping ", taxon, ": SDM raster has no values.")
    next
  }

  # 3. Generate G buffers
  gBuffer <- generateGBuffers(
    taxon          = taxon,
    occurrenceData = occurrences$data,
    bufferDistM    = 50000
  )

  # 4. Ex-situ analysis
  grsex <- GRSex(taxon = taxon, sdm = sdm_checked, gBuffer = gBuffer)
  ersex <- ERSex(
    taxon          = taxon,
    sdm            = sdm_checked,
    occurrenceData = occurrences$data,
    gBuffer        = gBuffer,
    ecoregions     = ecos,
    idColumn       = "ECO_NAME"
  )
  fcsex <- FCSex(taxon = taxon, srsex = srsex, grsex = grsex, ersex = ersex)

  # 5. In-situ analysis
  srsin <- SRSin(
    taxon          = taxon,
    sdm            = sdm_checked,
    occurrenceData = occurrences$data,
    protectedAreas = proArea_checked
  )
  grsin <- GRSin(taxon = taxon, sdm = sdm_checked, protectedAreas = proArea_checked)
  ersin <- ERSin(
    taxon          = taxon,
    sdm            = sdm_checked,
    occurrenceData = occurrences$data,
    protectedAreas = proArea_checked,
    ecoregions     = ecos,
    idColumn       = "ECO_NAME"
  )
  fcsin   <- FCSin(taxon = taxon, srsin = srsin, grsin = grsin, ersin = ersin)
  fcsmean <- FCSc_mean(taxon = taxon, fcsin = fcsin, fcsex = fcsex)

  # 6. Store results
  results_list[[taxon]] <- list(
    srsex           = srsex,
    occurrences     = occurrences,
    sdm_checked     = sdm_checked,
    proArea_checked = proArea_checked,
    eco_checked     = ecoregions,
    grsex           = grsex,
    ersex           = ersex,
    fcsex           = fcsex,
    srsin           = srsin,
    grsin           = grsin,
    ersin           = ersin,
    fcsin           = fcsin,
    fcsmean         = fcsmean
  )
}
## A total of  139  out of the  139 contained records for  Cucurbita_cordata
## Removed   0  records because lat values were outside the range of -90 to 90 or lonitude values were outside the range of -180-180 or there is a no value present for latitude or longitude for a record.
## All checks completed
## All checks completed
## All checks completed
## A total of  253  out of the  253 contained records for  Cucurbita_digitata
## Removed   0  records because lat values were outside the range of -90 to 90 or lonitude values were outside the range of -180-180 or there is a no value present for latitude or longitude for a record.
## All checks completed
## All checks completed
## All checks completed
## A total of  811  out of the  811 contained records for  Cucurbita_palmata
## Removed   0  records because lat values were outside the range of -90 to 90 or lonitude values were outside the range of -180-180 or there is a no value present for latitude or longitude for a record.
## All checks completed
## All checks completed
## All checks completed

Summary metrics tables

Results are organized as a named list, allowing review of individual species metrics. The tables below summarize the main metrics for all species.

# ── Ex-situ metrics ───────────────────────────────────────────────────────────
exsitu_table <- do.call(rbind, lapply(names(results_list), function(taxon) {
  r <- results_list[[taxon]]
  data.frame(
    Species          = taxon,
    SRS_exsitu       = r$srsex$`SRS exsitu`,
    GRS_exsitu       = r$fcsex$`GRS exsitu`,
    ERS_exsitu       = r$fcsex$`ERS exsitu`,
    FCS_exsitu       = r$fcsex$`FCS exsitu`,
    # NOTE: "FCS existu score" is a known typo in the GapAnalysis package.
    # Update to "FCS exsitu score" when fixed upstream.
    FCS_exsitu_score = r$fcsex$`FCS existu score`
  )
}))

# ── In-situ metrics ───────────────────────────────────────────────────────────
insitu_table <- do.call(rbind, lapply(names(results_list), function(taxon) {
  r <- results_list[[taxon]]
  data.frame(
    Species          = taxon,
    SRS_insitu       = r$fcsin$`SRS insitu`,
    GRS_insitu       = r$fcsin$`GRS insitu`,
    ERS_insitu       = r$fcsin$`ERS insitu`,
    FCS_insitu       = r$fcsin$`FCS insitu`,
    FCS_insitu_score = r$fcsin$`FCS insitu score`
  )
}))

# ── Combined metrics ──────────────────────────────────────────────────────────
combined_table <- do.call(rbind, lapply(names(results_list), function(taxon) {
  r <- results_list[[taxon]]
  data.frame(
    Species         = taxon,
    FCSc_min        = r$fcsmean$FCSc_min,
    FCSc_max        = r$fcsmean$FCSc_max,
    FCSc_mean       = r$fcsmean$FCSc_mean,
    FCSc_min_class  = r$fcsmean$FCSc_min_class,
    FCSc_max_class  = r$fcsmean$FCSc_max_class,
    FCSc_mean_class = r$fcsmean$FCSc_mean_class
  )
}))

knitr::kable(exsitu_table,   digits = 2, caption = "<span style='text-align:left; display:block;'>Ex-situ gap analysis metrics</span>",  format = "html") %>% kableExtra::kable_styling()
Ex-situ gap analysis metrics
Species SRS_exsitu GRS_exsitu ERS_exsitu FCS_exsitu FCS_exsitu_score
Cucurbita_cordata 0.72 9.75 25 11.82 UP
Cucurbita_digitata 8.12 25.95 40 24.69 UP
Cucurbita_palmata 4.11 30.52 50 28.21 HP
knitr::kable(insitu_table,   digits = 2, caption = "<span style='text-align:left; display:block;'>In-situ gap analysis metrics</span>",   format = "html") %>% kableExtra::kable_styling()
In-situ gap analysis metrics
Species SRS_insitu GRS_insitu ERS_insitu FCS_insitu FCS_insitu_score
Cucurbita_cordata 79.13 62.33 75.00 72.15 MP
Cucurbita_digitata 12.56 20.86 100.00 44.47 HP
Cucurbita_palmata 68.64 49.23 85.71 67.86 MP
knitr::kable(combined_table, digits = 2, caption = "<span style='text-align:left; display:block;'>Combined gap analysis metrics</span>",  format = "html") %>% kableExtra::kable_styling()
Combined gap analysis metrics
Species FCSc_min FCSc_max FCSc_mean FCSc_min_class FCSc_max_class FCSc_mean_class
Cucurbita_cordata 11.82 72.15 41.99 UP MP HP
Cucurbita_digitata 24.69 44.47 34.58 UP HP HP
Cucurbita_palmata 28.21 67.86 48.04 HP MP HP

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.