Skip to contents

mt_corridor() identifies segments of a track where the animal was moving quickly in a spatially consistent direction. It is a port of the legacy move::corridor() function (LaPoint et al. 2013) to the modern move2 / sf stack — the concept is unchanged.

When to use corridor detection

The typical context is a migratory or commuting animal that travels between two (or several) regions via recognisable routes. Within those routes, successive segments point the same way and are traversed at speed; elsewhere on the track the animal forages or rests at slow, directionally inconsistent speeds. Corridor detection asks: which segments of the track belong to the route, and which belong to the foraging / resting stretches at each end?

The algorithm in one paragraph

For each segment i of the track, compute its speed and azimuth. Build a search circle centred at the segment midpoint with radius equal to half the segment length. Collect all other segment midpoints inside that circle. Compute the circular variance of pseudo-azimuths ((180 + azimuth) * 2 mod 360) within the neighbourhood — the doubling is what lets two animals walking the same corridor in opposite directions count as consistent. A segment is classified as corridor if (a) its speed exceeds the global speed_quantile, (b) its neighbourhood’s circular variance is below the global circ_quantile, and (c) the neighbourhood contains at least min_segments qualifying neighbours, which must outnumber the non-qualifying ones.

Load data

The function requires geographic coordinates (EPSG:4326) so that the search radius can be computed in metres on the sphere with geosphere. Re-project if necessary.

library(move2)
library(sf)
library(move2utils)

## fisher data (already in WGS84)
fishers <- mt_read(mt_example())
fishers <- fishers[!st_is_empty(fishers), ]
leroy <- filter_track_data(fishers, .track_id = "M1")
st_crs(leroy)$input
#> [1] "EPSG:4326"

Run corridor detection on a fisher

out <- mt_corridor(leroy)
#> Found 22 corridor segments (2.4% of 918 segments).
table(out$corridor)
#> 
#>     corridor not corridor 
#>           22          897

Five columns are added to the input move2:

  • corridor — factor with levels "corridor" and "not corridor".
  • corridor_speed — per-segment speed (m/s), last row NA.
  • corridor_azimuth — per-segment azimuth (degrees), last row NA.
  • corridor_circvar — circular variance of pseudo-azimuths within the segment’s neighbourhood.
  • corridor_n_neighbours — number of segments found within the search radius.

A quick map

corr   <- as.character(out$corridor)

out$segments <- mt_segments(out)
plot(out$segments, col = c("firebrick","grey80")[as.factor(out$corridor)], asp = 1,
     lwd=c(2,0.7)[as.factor(out$corridor)],
    main = sprintf("Leroy (fisher): %d corridor segments of %d",
                    sum(corr == "corridor"), length(corr)))
points(out, col = c("firebrick","grey40")[as.factor(out$corridor)], pch = 16, 
       cex = c(0.6,0.3)[as.factor(out$corridor)])
legend("topright", pch = 16, col = c("grey40", "firebrick"),
       legend = c("not corridor", "corridor"), bty = "n")
Leroy's track with the minority of segments that meet the default corridor criteria (fast, directionally consistent) highlighted in firebrick. Most of the track — slower, tightly looping foraging movement — stays grey. The corridor segments here are the sparse commuting segments between forage patches.

Leroy’s track with the minority of segments that meet the default corridor criteria (fast, directionally consistent) highlighted in firebrick. Most of the track — slower, tightly looping foraging movement — stays grey. The corridor segments here are the sparse commuting segments between forage patches.

Tightening speed_quantile and circ_quantile makes the detector more conservative — only the fastest, most directionally consistent portions qualify.

Tuning

Argument Default What to tune it for
speed_quantile 0.75 Raise for stricter speed cutoff (more conservative). Lower to catch slower but directionally consistent travel (e.g. a plodding long-distance walker).
circ_quantile 0.25 Lower for tighter directional consistency. Raise if the corridor genuinely winds.
min_segments 2 Minimum neighbourhood support. Raise if you want only well-populated corridor stretches.

Further reading