Dispersal mortality

Here you will learn

In this notebook, we demonstrate the possibility to use independent movement probablities and costs, similar to (Fletcher Jr et al. 2019) and (Marx et al. 2020).

Demonstration of independent likelihood and cost of movement

In this notebook, we demonstrate the possibility to use independent movement probablities and costs, similar to (Fletcher Jr et al. 2019) and (Marx et al. 2020).

Preamble

See notebook Getting Started for a ‘basic workflow’ to learn about the fundamentals on data import and the creation of a ConScape Grid:

#using Pkg
#Pkg.activate(joinpath(ENV["HOME"], ".julia", "dev", "ConScape"))

using ConScape
using SparseArrays
using Statistics
using Plots
datadir = joinpath(ENV["HOME"], "Downloads", "input_maps")
outdir = joinpath(ENV["TMPDIR"], "figures")
if !isdir(outdir)
    mkdir(outdir)
end
"C:/Users/bram.van.moorter/Documents/ConScape_website/site/notebooks/data/"

We use the data provided in (Marx et al. 2020):

mov_prob, meta_p = ConScape.readasc(joinpath(datadir, 
            "prob_panther_cropped.asc"))
hab_qual, meta_q = ConScape.readasc(joinpath(datadir, 
            "prob_panther_cropped.asc"))
([0.111111111938953 0.5 … 0.111111111938953 0.16666667163372; 0.125 0.125 … 0.111111111938953 0.16666667163372; … ; NaN NaN … 1.0 0.111111111938953; NaN NaN … 1.0 0.333333343267441], Dict{Any, Any}("cellsize" => 500.0, "nrows" => 99, "nodata_value" => -999, "ncols" => 174, "xllcorner" => 615138.0, "yllcorner" => 257108.0))

Note, we did not have quality data for each pixel, for this demonstration we simply assumed that the permeability adequately represents quality (see Notebook Getting Started for a better example with independent quality data).

We remove negative values:

mov_prob = max.(0, mov_prob)
hab_qual = max.(0, hab_qual);

Cost data

In addition to the data on the habitat quality and the likelihood of movement, we also have independent data on the cost of movement ((Marx et al. 2020)):

mov_cost, meta_c = ConScape.readasc(joinpath(datadir, 
            "mort_panther_cropped.asc"))
mov_cost = max.(0, mov_cost);

As discussed in vanmoorter2021defining, to use the mortality risk as a cost in the RSP framework, we need to define the cost as the negative logarithm of the survival probability between adjacent pixels:

mov_cost = -log.(1 .- mov_cost);

We create the Grid by using these cost data, instead of a transformation from the likelihood of movement:

g = ConScape.Grid(size(mov_prob)...,
    affinities=ConScape.graph_matrix_from_raster(mov_prob),
    qualities=hab_qual,
    costs=ConScape.graph_matrix_from_raster(mov_cost));
┌ Info: cost graph contains 15 strongly connected subgraphs
└ @ ConScape C:\Users\bram.van.moorter\.julia\packages\ConScape\spkWs\src\grid.jl:215
┌ Info: removing 14 nodes from affinity and cost graphs
└ @ ConScape C:\Users\bram.van.moorter\.julia\packages\ConScape\spkWs\src\grid.jl:225

To visualize the movement costs, we define the following function, similar to the plot_indegrees function we used earlier (see Notebook Getting Started):

function plot_incost(g; kwargs...)
    values = sum(g.costmatrix, dims=1)
    canvas = zeros(g.nrows, g.ncols)
    for (i,v) in enumerate(values)
        canvas[g.id_to_grid_coordinate_list[i]] = v
    end
    heatmap(canvas, yflip=true, axis=nothing, border=:none; kwargs...)
end
plot_incost (generic function with 1 method)
plot_incost(g, title="Mortality risk")

Computing the survival probability

Now we can compute the GridRSP:

h = ConScape.GridRSP(g, θ=1.0);

Note that to compute the survival probability from \(s\) to \(t\) corresponding to the mortality cost associated to edges \((i,j)\), we need to set \(\theta=1.0\) ((Moorter et al. 2021)).

The survival probability from all source pixels to a target pixel (e.g. 15000) is:

tmp = zeros(17212)
tmp[15000] = 1

display(ConScape.plot_values(g, tmp, title="Target"))

surv = ConScape.survival_probability(h);
display(ConScape.plot_values(g, map(t -> t==1 ? NaN : t,  surv[:,15000]), title="Survival probability"))

a

b

Survival probability to a target.

Finally, we can use these survival probabilities to compute for each pixel the amount of habitat it is functionally connected to:

func = ConScape.connected_habitat(h, 
            connectivity_function=ConScape.survival_probability);
ConScape.heatmap(Array(func), yflip=true, title="Functional habitat")

Amount of connected habitat based on survival connectivity.

Summary

The ConScape library allows users to define the cost of movement between adjacent pixels both as a transformation from the likelihood of movement, but also from independent cost data. This application is very useful to model dispersal with mortality ((Fletcher Jr et al. 2019)), but could also be applied in combination with ‘energy landscapes’ discussed in: (Moorter et al. 2021).

References

Fletcher Jr, Robert J, Jorge A Sefair, Chao Wang, Caroline L Poli, Thomas AH Smith, Emilio M Bruna, Robert D Holt, Michael Barfield, Andrew J Marx, and Miguel A Acevedo. 2019. “Towards a Unified Framework for Connectivity That Disentangles Movement and Mortality in Space and Time.” Ecology Letters 22 (10): 1680–89.
Marx, Andrew J, Chao Wang, Jorge A Sefair, Miguel A Acevedo, and Robert J Fletcher Jr. 2020. “Samc: An r Package for Connectivity Modeling with Spatial Absorbing Markov Chains.” Ecography.
Moorter, Bram van, Ilkka Kivimäki, Manuela Panzacchi, and Marco Saerens. 2021. “Defining and Quantifying Effective Connectivity of Landscapes for Species’ Movements.” Ecography 44 (6): 870–84.