Given a sequence of `starting' and `ending' locations (start and end), this function determines whether or not the Euclidean path (`segment') between each location pair crosses a barrier.

segments_cross_barrier(start, end, barrier, distance = NULL, mobility = NULL)

Arguments

start

A two-column matrix of coordinates that defines the `start' location of each segment.

end

A two-column matrix of coordinates that defines the `end' location of each segment.

barrier

A simple feature geometry that defines the barrier (see st_intersects).

distance

(optional) A raster that defines distances from the barrier. If supplied, mobility is required (see below).

mobility

(optional) If distance is supplied, mobility is a number that defines the distance threshold. Location pairs for which both locations are further than mobility from the barrier are assumed not to overlap with the barrier. If start and end are possible locations for an animal at a given pair of time steps, mobility is the distance that the individual could move between time steps (see pf).

Value

The function returns a one-column matrix, with each row corresponding to a row in start/end, with a logical value that defines whether or not the Euclidean path segment connecting those two locations crosses the barrier (TRUE) or not (FALSE).

Edward Lavender

Details

This function was motivated by the need to support internal routines in pf_simplify. Specifically, the function is used to minimise the number of shortest-distance calculations that are required by restricting calculations (if applicable) to particle pairs that require movement around a barrier, such as the coastline. (In these cases, Euclidean distances may be poor approximations of shortest distances that an aquatic animal must travel.)

The function implements a three-step approach to derive barrier overlaps:

  1. The function determines whether or not the minimum convex polygon (i.e., boundary box) around start/end intersects with barrier. If it does not, then no location segments can overlap with the barrier. This step is fast.

  2. If the locations' minimum convex polygon intersects with the barrier, and if distance and mobility have been supplied, the function extracts the distance of each location in start and end from the barrier. Location pairs for which both locations are further than mobility from the barrier are dropped. This step is also fast.

  3. For any remaining location pairs, the function links each start and end location and determines whether or not each linkage (`segment') intersects with the barrier using sf routines. This step can be slow for large numbers of locations (hence the first two filtering steps).

The following criteria apply to applications of this function:

  1. The number of observations in start and end must match.

  2. The coordinate reference system for start, end and barrier must match.

  3. If distance is supplied, mobility must also be supplied.

  4. The function requires the sf_linestring and st_intersects functions.

For speed in iterative applications, the function does not check whether or not these criteria are met.

Examples

#### Plot example area and define barrier
raster::plot(dat_gebco)
raster::lines(dat_coast)
barrier <- sf::st_as_sf(dat_coast)

#### Example (1): Implement function using barrier only

## Define example starting and ending locations
start <- matrix(c(
  701854.9, 6260399,
  709202.5, 6258892
), ncol = 2, byrow = TRUE)
end <- matrix(c(
  706753.3, 6264261,
  709673.5, 6257102
), ncol = 2, byrow = TRUE)

## Visualise segments
# ... The first segment crosses the coastline (our barrier)
# ... The second segment does not cross the coastline (our barrier)
graphics::arrows(
  x0 = start[1, 1], y0 = start[1, 2],
  x1 = end[1, 1], y1 = end[1, 2]
)
graphics::arrows(
  x0 = start[2, 1], y0 = start[2, 2],
  x1 = end[2, 1], y1 = end[2, 2]
)


## Implement function
segments_cross_barrier(start, end, barrier = barrier)
#>       [,1]
#> [1,]  TRUE
#> [2,] FALSE

#### Example (2): Implement function using barrier with distance and mobility

## Define distances from barrier
dat_dist <- raster::rasterize(dat_coast, dat_gebco)
dat_dist <- raster::distance(dat_dist)
#> Error in .local(x, y = y, ...): RasterLayer has no NA cells (for which to compute a distance)

## Implement function for a specified mobility parameter
segments_cross_barrier(start, end,
  barrier = barrier,
  distance = dat_dist, mobility = 500
)
#>       [,1]
#> [1,] FALSE
#> [2,] FALSE

#### Example (3): With many locations, supplying distance improves speed

## Sample a large number of random starting/ending locations
start <- raster::sampleRandom(dat_gebco, size = 3000, xy = TRUE)[, 1:2]
#> Error in .local(x, size, ...): size <= ncell(x) is not TRUE
end <- raster::sampleRandom(dat_gebco, size = 3000, xy = TRUE)[, 1:2]
#> Error in .local(x, size, ...): size <= ncell(x) is not TRUE

## Compare the duration of the function without/with distance included
# The first method without distance is much slower than the second method
# (~0.714 s versus 0.131 s for 3000 locations)
system.time(
  int_1 <- segments_cross_barrier(start, end, barrier = barrier)
)
#>    user  system elapsed 
#>   0.003   0.000   0.004 
system.time(
  int_2 <- segments_cross_barrier(start, end,
    barrier = barrier,
    distance = dat_dist, mobility = 500
  )
)
#>    user  system elapsed 
#>   0.002   0.000   0.003 

## The two approaches return identical solutions:
identical(int_1, int_2)
#> [1] FALSE