Skip to contents

Detects corridor behaviour by finding segments where the animal moves quickly in a spatially consistent direction. Corridors are characterised by high segment speed combined with low circular variance of travel direction among spatially neighbouring segments.

Usage

mt_corridor(
  x,
  speed_threshold = NULL,
  circvar_threshold = NULL,
  min_segments = 2L,
  verbose = FALSE
)

Arguments

x

A move2 object. Multi-track input is handled track-by-track via move2::mt_segments(); the input may be in any CRS (geographic or projected). Rows with empty geometries are dropped from the computation with an informational notice and re-padded with NA in the output.

speed_threshold

Numeric. Per-segment speed (metres / second) at or above which a segment is "fast." If NULL (default), the 0.75 quantile of within-object segment speeds is used and a warning names the resolved value.

circvar_threshold

Numeric in [0, 1]. Circular variance of pseudo-azimuths within a segment's neighbourhood at or below which the segment is "directionally consistent." If NULL (default), the 0.25 quantile of valid within-object circular variances is used and a warning names the resolved value.

min_segments

Integer. Minimum number of qualifying neighbours required for a segment to be classified as a corridor (default 2).

verbose

Logical. If TRUE, prints a one-line summary of how many segments were flagged. Default FALSE. Independent of the default-threshold, irregular-sampling, and empty-geometry notices, which are shown regardless.

Value

The input move2 object with five added columns, each of length nrow(x):

corridor

factor with levels "corridor" and "not corridor".

corridor_speed

per-segment speed (m/s).

corridor_azimuth

per-segment travel azimuth (degrees).

corridor_circvar

circular variance of pseudo-azimuths within the neighbourhood.

corridor_n_neighbours

number of segments found within the search radius.

Track-final rows, rows with empty geometries, and segments whose neighbourhood circular variance cannot be computed all carry NA in the per-segment columns; their corridor factor level is "not corridor".

Details

The algorithm:

  1. Build per-segment linestrings with move2::mt_segments() and take their midpoints with sf::st_line_interpolate(). Both are track-aware; cross-track midpoints are never produced.

  2. For each segment, find all other midpoints within its own half-length search radius via an sf spatial index (R-tree). The search uses the input CRS if projected, or a local azimuthal-equidistant projection (move2::mt_aeqd_crs()) if the input is in longitude/latitude.

  3. Compute a pseudo-azimuth ((2 * azimuth) mod 2*pi, working in radians) so parallel opposite directions collapse onto the same value: animals walking north and south along the same corridor are treated as directionally consistent.

  4. Compute the circular variance of pseudo-azimuths within each neighbourhood.

  5. Flag a segment as corridor if its speed is at least speed_threshold, its circular variance is at most circvar_threshold, and the corridor-qualifying neighbours within its search radius outnumber the non-qualifying ones (at least min_segments qualifying neighbours required).

Thresholds are user-supplied. When either is left as NULL the function falls back to a within-object quantile (0.75 of segment speeds; 0.25 of valid circular variances) and emits a warning naming the resolved numeric. For comparable corridor maps across individuals or populations, supply explicit thresholds: e.g. speed_threshold = quantile(as.numeric(mt_speed(all_tracks, units = "m/s")), 0.75, na.rm = TRUE) computed once on the pooled cohort and reused per individual.

This is a port of the move::corridor() concept (LaPoint et al. 2013) to the move2 / sf stack, using modern spatial indexing.

References

LaPoint S, Gallery P, Wikelski M, Kays R (2013). Animal behavior, cost-based corridor models, and real corridors. Landscape Ecology, 28, 1615–1630. doi:10.1007/s10980-013-9910-0

Examples

if (FALSE) { # \dontrun{
library(move2)
fishers <- mt_read(mt_example())
out <- mt_corridor(fishers)
table(out$corridor)

## fixed thresholds for cross-individual comparability:
all_speeds <- as.numeric(mt_speed(fishers, units = "m/s"))
out2 <- mt_corridor(
  fishers,
  speed_threshold   = stats::quantile(all_speeds, 0.75, na.rm = TRUE),
  circvar_threshold = 0.2
)
} # }