Skip to content

Arithmetic on layers

Layers can be manipulated as any other objects on which you can perform arithmetic. In other words, you can substract, add, multiply, and divide layers, either with other layers or with numbers. In this vignette, we will take a look at how this can facilitate the creation of a resistance map for functional connectivity analysis.

julia
using SpeciesDistributionToolkit
using CairoMakie

We will work on the twelve classes of landcover provided by the EarthEnv data:

julia
dataprovider = RasterData(EarthEnv, LandCover)
RasterData{EarthEnv, LandCover}(EarthEnv, LandCover)

In order to only read what is relevant to our illustration we will define a bounding box over Corsica.

julia
spatial_extent = (left = 8.412, bottom = 41.325, right = 9.662, top = 43.060)
(left = 8.412, bottom = 41.325, right = 9.662, top = 43.06)

As a good practice, we check the names of the layers again. Note that checking the name of the layers will not download the data, so this may be a good time to remove some layers you are not interested in (which of course would not be a good idea for this specific application).

julia
landcover_classes = SimpleSDMDatasets.layers(dataprovider)
12-element Vector{String}:
 "Evergreen/Deciduous Needleleaf Trees"
 "Evergreen Broadleaf Trees"
 "Deciduous Broadleaf Trees"
 "Mixed/Other Trees"
 "Shrubs"
 "Herbaceous Vegetation"
 "Cultivated and Managed Vegetation"
 "Regularly Flooded Vegetation"
 "Urban/Built-up"
 "Snow/Ice"
 "Barren"
 "Open Water"

To create a resistance map, we need to decide on a score for the resistance of each class of land use. For the sake of an hypothetical example, we will assume that the species we care about can easily traverse forested habitats, is less fond of shrubs, fields, etc., and is a poor swimmer who is afraid of cities. Think of it as your typical forestry graduate student.

julia
classes_resistance = [0.1, 0.1, 0.1, 0.2, 0.4, 0.5, 0.7, 0.9, 1.2, 0.8, 1.2, 0.95]
classes_resistance = classes_resistance ./ sum(classes_resistance)
12-element Vector{Float64}:
 0.013986013986013988
 0.013986013986013988
 0.013986013986013988
 0.027972027972027975
 0.05594405594405595
 0.06993006993006994
 0.0979020979020979
 0.1258741258741259
 0.16783216783216784
 0.1118881118881119
 0.16783216783216784
 0.13286713286713286

The next step is to download the layers – we do so with a list comprehension, in order to get a vector of layers:

julia
landuse = [
    SDMLayer(dataprovider; layer = class, full = true, spatial_extent...) for
    class in landcover_classes
];

The aggregation of the layers is simply ∑wᵢLᵢ, where wᵢ is the resistance of the i-th layer Lᵢ. In order to have the resistance layer expressed between 0 and 1, we finally call the rescale! method with new endpoints for the layer:

julia
resistance_layer =
    reduce(.+, [landuse[i] .* classes_resistance[i] for i in eachindex(landuse)])
rescale!(resistance_layer)
SDM Layer with 31559 Float64 cells
	Proj string: +proj=longlat +datum=WGS84 +no_defs
	Grid size: (209, 151)

The remaining step is to visualize this resistance map, and add a little colorbar to show which areas will be more difficult to cross:

Code for the figure
julia
resistance_map = heatmap(
    resistance_layer;
    colormap = Reverse(:linear_protanopic_deuteranopic_kbjyw_5_95_c25_n256),
    figure = (; size = (400, 350)),
    axis = (;
        aspect = DataAspect(),
        xlabel = "Latitude",
        ylabel = "Longitude",
        title = "Movement resistance",
    ),
)
Colorbar(resistance_map.figure[:, end + 1], resistance_map.plot; height = Relative(0.5))

This layer can then be used in landscape connectivity analyses using e.g. Omniscape.jl.