Fill gaps between suitable habitat patches in a stream network
Source:R/fill_habitat_gaps.R
fill_habitat_gaps.RdFor a given set of suitable stream segments (e.g. from a thresholded species distribution model), the function identifies short unsuitable reaches that interrupt otherwise continuous suitable habitat patches, and fills them in. This consolidates fragmented habitat predictions into ecologically meaningful patches, under the assumption that fish can move through short unsuitable reaches to access suitable habitat on either side.
A gap is defined as a sequence of consecutive unsuitable reaches between two suitable patches. A gap is filled if its total length is less than the specified threshold (sigma_mob, max_gap_m, or the minimum of both).
Arguments
- g
igraph object. A directed stream network graph. Edge direction should go from upstream to downstream (subc_id -> target).
- suitable_ids
numeric or character vector. The subc_ids of reaches classified as suitable habitat (above the suitability threshold).
- edge_length
named numeric vector. Reach lengths in metres, named by subc_id. Used to compute gap lengths.
- sigma_mob
numeric. Species-specific dispersal distance in metres (e.g. from fishmove). Gaps shorter than sigma_mob are filled. Optional if max_gap_m is provided.
- max_gap_m
numeric. Maximum gap length in metres to fill, regardless of species dispersal distance. Optional if sigma_mob is provided. If both sigma_mob and max_gap_m are provided, the minimum of the two is used as the gap threshold.
Value
A character vector of subc_ids of all suitable reaches after gap filling (original suitable reaches + filled gap reaches).
Details
The gap-filling algorithm works as follows:
Build a subgraph of all unsuitable reaches.
Find connected components of unsuitable reaches (potential gaps).
For each component, check if it is flanked by suitable reaches on both sides (upstream and downstream).
If the total length of the component is less than the gap threshold, fill it (reclassify as suitable).
Examples
if (FALSE) { # \dontrun{
# Load stream network as graph
network_g <- read_geopackage("stream_network.gpkg", import_as = "graph")
# Set edge weights to reach length
E(network_g)$weight <- reach_lengths[match(E(network_g)$subc_id,
names(reach_lengths))]
# Define suitable reaches (e.g. from thresholded SDM)
suitable <- network_dt %>%
filter(idw_Salmo_farioides >= 0.7) %>%
pull(subc_id)
# Fill gaps shorter than sigma_mob
suitable_filled <- fill_habitat_gaps(
g = network_g,
suitable_ids = suitable,
edge_length = setNames(E(network_g)$weight, V(network_g)$name),
sigma_mob = 12585,
max_gap_m = 5000
)
} # }