Similarity between rangemaps can allow the detection of associations among species distributions, with important applications in macroecology, biogeography, environmental conservation and management. Commonly used similarity indices use counts of species occurrences and co-occurrences in discrete localities, regions or grid cells. However, gridding rangemaps distorts species’ distribution limits, generally overestimating their overlap and sometimes overriding important geographical barriers (Barbosa & Estrada, 2016). This tutorial shows how to calculate similarity between ungridded rangemaps with R. First, download, install and open R.
Functions to calculate the areas of pairwise intersection and union between rangemaps and to assess rangemap similarity are implemented in R package fuzzySim (Barbosa, 2015). Functions to operate with spatial objects are implemented e.g. in R package terra (Hijmans et al., 2026). Run the following code (without the #) in the R console to install updated versions of these packages:
# install.packages("fuzzySim")
# install.packages("terra")
Now load the required packages:
library(terra)
library(fuzzySim)
We’ll need some polygon rangemaps to compare. Here we’ll use the Alytes toad rangemaps taken from IUCN (2026) as an example.
rangemaps <- terra::vect("https://github.com/AMBarbosa/fuzzySim/raw/refs/heads/master/Alytes.gpkg")
plot(rangemaps, col = 1:length(rangemaps), border = 1:length(rangemaps), alpha = 0.5)
text(rangemaps, "SCI_NAME", col = 1:length(rangemaps), halo = TRUE, xpd = TRUE)
The following code will get you a matrix containing the areas of pair-wise intersection (in the lower triangle) and union (in the upper triangle) of the rangemaps in this folder (the diagonal contains the area of each individual rangemap). You can provide either a character vector of file names of the individual species’ range maps (e.g. in GeoPackage or in Shapefile format), or (as in the example below) a named list of SpatVector polygon objects containing the rangemaps to be compared. This can be useful especially if you previously want to process these polygons in R, e.g. to exclude extinct or introduced parts of the rangemaps (https://nc.iucnredlist.org/redlist/resources/files/1539614211-Mapping_attribute_codes_v1.16_2018.pdf).
rangemaps <- terra::split(rangemaps, "SCI_NAME")
rangemap_matrix <- fuzzySim::pairwiseRangemaps(rangemaps)
rangemap_matrix
## Alytes almogavarii Alytes cisternasii Alytes dickhilleni
## Alytes almogavarii 3.809704e+10 205043997556 62094614281
## Alytes cisternasii 0.000000e+00 166946954598 190944525921
## Alytes dickhilleni 0.000000e+00 0 23997571323
## Alytes maurus 0.000000e+00 0 0
## Alytes muletensis 0.000000e+00 0 0
## Alytes obstetricans 7.660213e+02 52204424992 531357543
## Alytes maurus Alytes muletensis Alytes obstetricans
## Alytes almogavarii 43360865445 38195657717 9.953714e+11
## Alytes cisternasii 172210777085 167045569357 1.072017e+12
## Alytes dickhilleni 29261393810 24096186082 9.807406e+11
## Alytes maurus 5263822486 5362437246 9.625382e+11
## Alytes muletensis 0 98614759 9.573730e+11
## Alytes obstetricans 0 0 9.572744e+11
Once you have your rangemap_matrix, you can use it to
calculate pairwise similarity between the rangemaps,
using a variety of indices. Jaccard is the one used by default:
rangemap_jac <- rangemapSim(rangemap_matrix)
rangemap_jac
## Alytes almogavarii Alytes cisternasii Alytes dickhilleni
## Alytes almogavarii 1.000000e+00 0.00000000 0.0000000000
## Alytes cisternasii 0.000000e+00 1.00000000 0.0000000000
## Alytes dickhilleni 0.000000e+00 0.00000000 1.0000000000
## Alytes maurus 0.000000e+00 0.00000000 0.0000000000
## Alytes muletensis 0.000000e+00 0.00000000 0.0000000000
## Alytes obstetricans 7.695834e-10 0.04869739 0.0005417922
## Alytes maurus Alytes muletensis Alytes obstetricans
## Alytes almogavarii 0 0 7.695834e-10
## Alytes cisternasii 0 0 4.869739e-02
## Alytes dickhilleni 0 0 5.417922e-04
## Alytes maurus 1 0 0.000000e+00
## Alytes muletensis 0 1 0.000000e+00
## Alytes obstetricans 0 0 1.000000e+00
You can plot a visual representation of the similarity matrix. Note that colour intensity below is based on quantiles and not proportional to the actual similarities, to allow visualizing the very small values. You can compare this figure, namely the most similar species, with the rangemaps shown above.
heatmap(rangemap_jac,
col = hcl.colors(100, "Lajolla"),
breaks = quantile(rangemap_jac, seq(0, 1, 0.01)),
symm = TRUE,
distfun = function(x) dist(apply(x, 2, rank)),
margins = c(8, 2),
cexRow = 0.9, cexCol = 0.9,
main = "Jaccard similarity")
For a more formal grouping of the rangemaps into (optionally fuzzy) chorotypes, you can try the RMacoqui package.
Check out the help files of these functions
(e.g. help(pairwiseRangemaps);
help(rangemapSim)) for additional options and information.
You can contact me
with any suggestions or concerns, but first remember to check for
updates to the package or to this tutorial at http://fuzzysim.r-forge.r-project.org. This tutorial was
built with RStudio + rmarkdown + knitr.
Thanks!
Barbosa A.M. (2015) fuzzySim:
applying fuzzy logic to binary similarity indices in ecology.
Methods in Ecology and Evolution, 6: 853-858
Barbosa A.M. & Estrada A. (2016) Calcular corotipos sin dividir el territorio en OGUs: una adaptación de los índices de similitud para su utilización directa sobre áreas de distribución. In: Gómez Zotano J., Arias García J., Olmedo Cobo J.A. & Serrano Montes J.L. (eds.), Avances en Biogeografía. Áreas de Distribución: Entre Puentes y Barreras, pp. 157-163. Editorial Universidad de Granada & Tundra Ediciones, Granada (Spain)
Hijmans R., Brown A., Barbosa A.M., Cordano E., Dyba K. (2026) ‘terra’: Spatial Data Analysis. R package version 1.9-40, https://rspatial.org/.
IUCN (2026) The IUCN Red List of Threatened Species. Version 2026-1. https://www.iucnredlist.org. Accessed on August 27th, 2026.