Welcome to the Species Habitat Suitability Modelling Workshop! We will explore how to use biomod2 to fit, evaluate, and project multi-algorithm SDMs under climate change.
We’ll explore multi-algorithm modelling, model evaluation, variable importance, ensemble modelling, and future projections under climate change.
Learning Goals
Prepare occurrence & environmental data
Fit multiple algorithms (GLM, GAM, RF, GBM, CTA)
Evaluate models and interpret variable importance
Create ensemble predictions and project under future climates
Interactive map preserved as reproducible code: the archived page did not include its Leaflet JavaScript dependencies. The static occurrence map above and the complete R code remain available.
Interpretation: Green = presence, Red = absence. Interactive maps help visualize sampling bias or clustering.
Environmental Predictors
Code
Show codeHide codeCopyable source
plot(preds, main ="Environmental Predictors")
Interpretation: These environmental layers (precipitation, temperature, etc.) are predictors for species distribution.
The weighted mean ensemble (EMwmean) gives higher weight to better-performing algorithms.
Why it looks compressed - biomod2’s internal plotting code does not preserve the raster’s coordinate ratio. - It sets up a multi-panel layout (mfrow) to plot multiple ensemble models side by side. - Each panel is drawn with equal x/y units, not the spatial extent ratio, so the map looks “squished”.
Solution - Extract predictions and plot with terra::plot()
This gives you correct aspect ratio and prettier maps:
Show codeHide codeCopyable source
# Balanced aspect using terraens_rast <-get_predictions(myBiomodEnsembleProj)terra::plot(ens_rast, nc =2, main ="Ensemble Forecasts (Balanced Plot)")
Using plotRGB() — composite visualization
plotRGB() displays three raster layers as R–G–B channels (e.g., temperature in red, precipitation in blue, elevation in green). It’s ideal for visualizing covariate contrasts or model uncertainty (mean, sd, cv).
Optional Enhancement: View each layer separately with consistent color palette
Show codeHide codeCopyable source
cols <-hcl.colors(100, "YlOrRd", rev =TRUE)plot(ens_rast, col = cols)
Show codeHide codeCopyable source
library(terra)library(raster)library(RColorBrewer)library(sp)# Convert terra SpatRaster to raster (Raster* object)ens_raster <- raster::stack(ens_rast)# Inspect names to select the right layernames(ens_raster)# Example: "MySpecies_EMwmeanByTSS_mergedData_mergedRun_mergedAlgo"# Subset the TSS-weighted layerens_layer <- ens_raster[["MySpecies_EMwmeanByTSS_mergedData_mergedRun_mergedAlgo"]]# Check for valuessummary(ens_layer)# If it shows only NAs, projection/resampling step might have failed.# Optional: replace NAs with 0 or mask to study area# ens_layer[is.na(ens_layer[])] <- 0# Define a nice color palettecols <-colorRampPalette(RColorBrewer::brewer.pal(11, "BrBG"))(100)plot(ens_layer,col = cols,main ="TSS-weighted ensemble prediction",axes =TRUE, # shows coordinatesbox =TRUE)
You can also produces a color-tuned visualization of all three ensemble layers, scaled and plotted like an RGB composite (or single-color maps), with axes and proper legends using terra.
Show codeHide codeCopyable source
library(terra)library(RColorBrewer)# Extract ensemble raster stackens_rast <-get_predictions(myBiomodEnsembleProj)# Optional: check namesnames(ens_rast)# [1] "MySpecies_EMmeanByTSS_mergedData_mergedRun_mergedAlgo"# [2] "MySpecies_EMwmeanByTSS_mergedData_mergedRun_mergedAlgo"# [3] "MySpecies_EMcvByTSS_mergedData_mergedRun_mergedAlgo"# Select the three layerslayers <- ens_rast[[c(1,2,3)]]# Scale each layer to 0-255 (RGB scale)scale_to_255 <-function(x) { vals <- x[] vals_scaled <-round( (vals -min(vals, na.rm=TRUE)) / (max(vals, na.rm=TRUE) -min(vals, na.rm=TRUE)) *255 ) x[] <- vals_scaledreturn(x)}layers_scaled <-lapply(layers, scale_to_255)layers_scaled <-rast(layers_scaled)names(layers_scaled) <-c("EMmean","EMwmean","EMcv")# Plot single layers with custom color palettescols <-colorRampPalette(brewer.pal(11, "BrBG"))(100)for (i in1:3) {plot(layers_scaled[[i]],col = cols,main =paste0(names(layers_scaled)[i], " (scaled)"),axes =TRUE, box =TRUE)}
Show codeHide codeCopyable source
# Optional: RGB composite (assign layers to R, G, B channels)# Only works if all layers are scaled 0-255plotRGB(layers_scaled, r=1, g=2, b=3, scale=255, stretch="lin",main="Ensemble RGB composite (EMmean=R, EMwmean=G, EMcv=B)")
Think about:
How does the ensemble prediction compare to individual models?
What are the advantages of using an ensemble approach?
How does model uncertainty (e.g., prob.cv) inform your confidence in predictions?
Further Deepen Your Understanding:
Where are the highest predicted suitability values?
Are there areas of high uncertainty (compare with EMcv or EMci projections)?
How does the ensemble map compare to individual model projections?
Future Climate Projection
Show codeHide codeCopyable source
if (data_source =="sdm") {# 19 bioclim variables (place your file in the project folder)# Example file name; change if needed future_2070 <-try(rast("future_2070_bioclim.tif"), silent =TRUE)if (inherits(future_2070, "SpatRaster")) {if (is.na(crs(future_2070))) crs(future_2070) <-"EPSG:4326" temp_f <- future_2070[[1]] # bio1 precip_f <- future_2070[[12]] # bio12 elev_c <- preds[["elevation"]] veg_c <- preds[["vegetation"]]# Align current to WGS84 of future elev_wgs <-project(elev_c, temp_f) veg_wgs <-project(veg_c, temp_f, method ="near") ext_study <-ext(elev_wgs) temp_crop <-crop(temp_f, ext_study) precip_crop <-crop(precip_f, ext_study) elev_res <-resample(elev_wgs, temp_crop) veg_res <-resample(veg_wgs, temp_crop, method ="near")# Stack with same names & order as training preds_future_sel <-c(elev_res, precip_crop, temp_crop, veg_res)names(preds_future_sel) <-c("elevation","precipitation","temperature","vegetation")# Project myBiomodProjFuture <-BIOMOD_Projection(bm.mod = myBiomodModelOut,new.env = preds_future_sel[[c("elevation","precipitation","temperature","vegetation")]],proj.name ="future_2070_fix",selected.models ="all",binary.meth ="TSS",compress =FALSE )# Crop projection back to study area (preds footprint) r <-get_predictions(myBiomodProjFuture) r_zoom <-zoom_to_preds(r, preds) } else {message("future_2070_bioclim.tif not found; skipping future projection.") r_zoom <-NULL }} else {message("Using biomod2 fallback data; future-projection demo (elev/veg + bio) is skipped.") r_zoom <-NULL}
Think about: - Discussion: How do predicted suitable regions shift?
Are expansions or contractions consistent with ecological expectations?
How could this information be used in conservation planning?
Show codeHide codeCopyable source
if (!is.null(r_zoom)) { terra::plot(r_zoom, nc =3, mar =c(2,2,2,3), axes =FALSE)}
Now you can import your results into QGIS or ArcGIS for spatial overlay with land-use or protected areas.
Try It Yourself
Modify one of the following:
Use different algorithms (e.g., Maxent, XGBoost).
Add pseudo-absence generation.
Use k-fold spatial cross-validation.
Compare projections for two SSP scenarios (245 vs.585).
Summary
Prepared occurrence and environmental data Fitted multi-algorithm SDMs Evaluated models and explored variable importance Built ensembles and projected under climate change
Curious Questions:
How could you use these outputs in QGIS or ArcGIS?
What are the limitations of SDM outputs for real-world decision making?
Discussion and Reflection
Group Discussion Prompts:
What are the main sources of uncertainty in SDMs?
How would you improve the data or modeling process?
What ethical considerations arise when using SDMs for conservation or management?
Ideas for Student Mini-Projects
Try modeling a different species (change the resp.var). Add or remove environmental predictors and see how results change.
Compare results using different cross-validation strategies (e.g., k-fold, block).
Explore the effect of sample size by subsetting the data.
References
Thuiller W., Georges D., Engler R., Breiner F. (2023). biomod2: Ensemble platform for species distribution modeling. R package version 4.5.4. Hijmans, R. J. (2023). geodata: Download Geographic Data. R package version 0.6-3.