SimpleSDMLayers.jl - JuliaCon 2020 demo
We present SimpleSDMLayers.jl, a package offering a basis for species distribution modelling (SDM) in Julia.
This brief demo is a companion to our JuliaCon 2020 poster, available here. We will quickly go over the features presented in the poster, and show how easy SimpleSDMLayers.jl is to use.
A script version of the demo is also available in this repository, which also contains all resources used for the poster.
For more details and advanced examples, check out our complete documentation.
Setup
Before getting started, we need to install the two core packages for the demo: SimpleSDMLayers.jl and GBIF.jl.
] add SimpleSDMLayers GBIFWe then have to load them, along with a few supporting packages (already installed in NextJournal). Note that this may take a while to precompile the first time we use the packages.
using SimpleSDMLayersusing GBIFusing Plotsusing StatsPlotsusing Statisticsusing StatsBase1 - Basic example
SimpleSDMLayers.jl makes it very easy to access climate and land cover data from sources that are frequently used in SDM studies.
For example, accessing average annual temperature data from the WorldClim 2.1 database can be done in just one call (note that it has to download and extract the data when first called):
# Get world temperaturetemperature = worldclim(1)The SimpleSDMPredictor type is the core of the package. The layer data is stored in a grid, easy to manipulate with any function working on Array elements:
temperature.gridAnd the layer is defined by it's bounding coordinates:
temperature.left, temperature.right, temperature.bottom, temperature.topAs displayed on the poster, SimpleSDMLayers.jl makes it incredibly simple to visualize the data:
# Map temperatureplot(temperature)# Add some labelsplot!(xguide = "Longitude", yguide = "Latitude", colorbar_title = "Average temperature (°C)")2 - Common manipulations
SimpleSDMLayers.jl provides support for common manipulations in SDM workflows.
For example, we can clip a layer to a region of interest simply by providing the bounding coordinates:
# Coordinates clippingtemperature_europe = temperature[left = -11.2, right = 30.6, bottom = 29.2, top = 71.0]This returns a smaller layer, which we can plot separately:
plot(temperature_europe, frame = :box, ticks = false, colorbar = false)Another useful feature we might want to try is to perform sliding window operations. For instance, we might want to get the average temperature value in a 100 km radius:
averaged = slidingwindow(temperature_europe, Statistics.mean, 100.0)plot(averaged, frame = :box, ticks = false, colorbar = false)Finally, we can also coarsen the layer resolution (i.e. aggregating grid cells) based on a given function:
coarsened = coarsen(temperature_europe, Statistics.mean, (4,4))plot(coarsened, frame = :box, ticks = false, colorbar = false)3 - Integration with GBIF.jl
What makes SimpleSDMLayers.jl even more interesting is its integration with another package from EcoJulia, GBIF.jl. GBIF is a database of species occurrences, and is a common source of data for SDM studies.
For example, let's say we are interested in the Belted Kingfisher. Getting occurrences in very simple:
# Specify taxonkingfisher = GBIF.taxon("Megaceryle alcyon", strict=true)# Get occurrenceskf_occurrences = occurrences(kingfisher)The GBIF API returns 20 occurrences at the time. Let's add a few more:
# Get some more occurrencesfor i in 1:9 occurrences!(kf_occurrences)endkf_occurrencesNow that we have the observations, we could want to plot them to visualize where the species was seen. But first, an useful trick will be to clip our world-scale temperature layer and to restrict it to the region where the species was actually seen.
temperature_clip = clip(temperature, kf_occurrences)Now we can start by plotting the temperature values as a background. Note that we can use more specific functions, such as contour():
contour(temperature_clip, fill=true, colorbar_title = "Average temperature(°C)", xguide = "Longitude", yguide = "Latitude")The longitude() and latitudes() allow us to extract the coordinates from the GBIF occurrences:
longitudes(kf_occurrences)Hence, the occurrences can be mapped simply by scattering them over the temperature layer:
scatter!(longitudes(kf_occurrences), latitudes(kf_occurrences), label = "Kingfisher occurrences", legend = :bottomleft, c=:white, msc=:orange)4 - Workflow example
SimpleSDMLayers.jl is a base on which you can easily build your own SDM workflows. Here is an example with our own implementation of the BIOCLIM model:
bioclim_path = tempname()download("https://raw.githubusercontent.com/gabrieldansereau/juliacon-2020-poster/master/bioclim.jl", bioclim_path)include(bioclim_path)First, we will get the 19 WorldClim variables (again, it might have to download and extract the data):
# Get all worldclim variablesvars = worldclim(1:19);Then we can apply our the model in a few steps:
# Get predictions for each variablevars_predictions = [bioclim(v, kf_occurrences) for v in vars];# Get minimum prediction per sitesdm_predictions = reduce(min, vars_predictions)# Set value to nothing if prediction is zeroreplace!(x -> isnothing(x) || iszero(x) ? nothing : x, sdm_predictions.grid);The result is also a layer, which can be visualized as before:
# Map plain backgroundplot(temperature_clip, c = :lightgrey, xguide = "Longitude", yguide = "Latitude")# Map predictionsplot!(clip(sdm_predictions, kf_occurrences), c = :viridis, clim = (minimum(sdm_predictions), maximum(sdm_predictions)), colorbar_title = "Predicted suitability score")Resources
For more information, have a look at these other resources: