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 GBIF
We 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 SimpleSDMLayers
using GBIF
using Plots
using StatsPlots
using Statistics
using StatsBase
1 - 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 temperature
temperature = 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.grid
And the layer is defined by it's bounding coordinates:
temperature.left, temperature.right, temperature.bottom, temperature.top
As displayed on the poster, SimpleSDMLayers.jl
makes it incredibly simple to visualize the data:
# Map temperature
plot(temperature)
# Add some labels
plot!(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 clipping
temperature_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 taxon
kingfisher = GBIF.taxon("Megaceryle alcyon", strict=true)
# Get occurrences
kf_occurrences = occurrences(kingfisher)
The GBIF API returns 20 occurrences at the time. Let's add a few more:
# Get some more occurrences
for i in 1:9
occurrences!(kf_occurrences)
end
kf_occurrences
Now 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 variables
vars = worldclim(1:19);
Then we can apply our the model in a few steps:
# Get predictions for each variable
vars_predictions = [bioclim(v, kf_occurrences) for v in vars];
# Get minimum prediction per site
sdm_predictions = reduce(min, vars_predictions)
# Set value to nothing if prediction is zero
replace!(x -> isnothing(x) || iszero(x) ? nothing : x, sdm_predictions.grid);
The result is also a layer, which can be visualized as before:
# Map plain background
plot(temperature_clip, c = :lightgrey,
xguide = "Longitude", yguide = "Latitude")
# Map predictions
plot!(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: