Skip to content

Commit

Permalink
Add setup_acdc() function, as well as new datasets for the coastline …
Browse files Browse the repository at this point in the history
…and bathymetry to illustrate function with examples.
  • Loading branch information
edwardlavender committed Dec 21, 2020
1 parent 43fa5d5 commit 2695519
Show file tree
Hide file tree
Showing 10 changed files with 478 additions and 1 deletion.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export(false_detections_sf)
export(lcp_over_surface)
export(pythagoras_3d)
export(quality_check)
export(setup_acdc)
export(update_extent)
importFrom(lubridate,"%within%")
importFrom(magrittr,"%>%")
Expand Down
235 changes: 235 additions & 0 deletions R/acdc.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
######################################
######################################
#### setup_acdc()

#' @title Setup the ACDC algorithm
#' @description This function produces the acoustic contours required by the acoustic-centroid depth-contour (ACDC) algorithm.
#' @param rs A integer vector of receiver IDs.
#' @param xy A \code{\link[sp]{SpatialPoints}} object that defines the locations of each receiver. The order of points in this object should match the order of receivers defined in \code{rs}. The coordinate reference system should be the Universal Transverse Mercator system with distances in metres (to match \code{detection_range}, see below).
#' @param detection_range A number that defines the maximum detection range at which an individual could be detected from a receiver.
#' @param mobility A number that defines the distance that an individual could move in the time period between archival observations.
#' @param n_timesteps An integer that defines the the number of timesteps after a hypothetical detection for which centroids will be created, where the duration of each timestep is given by the duration between archival observations.
#' @param coastline (optional) A \code{\link[sp]{SpatialPolygonsDataFrame-class}} object that defines the coastline in an area. If provided, acoustic centroids are processed to remove any areas on land. Algorithm speed declines with the complexity of the coastline.
#' @param boundaries (optional) A \code{\link[raster]{extent}} object that defines the boundaries of an area within which individuals are assumed to have remained. If provided, acoustic centroids are processed to remain within this area.
#' @param plot A logical input that defines whether or not to produce a plot of the area, including receivers, the coastline and the area boundaries (if provided), and acoustic centroids. This is useful for checking purposes but it can reduce algorithm speed.
#' @param cl (optional) A cluster object created by \code{\link[parallel]{makeCluster}} to implement the algorithm in parallel. The connection to the cluster is closed within the function.
#' @param verbose A logical input that defines whether or not to print messages to the console to relay function progress.
#'
#' @return The function returns a list of \code{\link[sp]{SpatialPolygonsDataFrame-class}} objects, with one element for all numbers from 1 to the maximum receiver number (\code{rx}). Any list elements that do not correspond to receivers contain a \code{NULL} element. List elements that correspond to receivers contain a \code{\link[sp]{SpatialPolygonsDataFrame-class}} object containing all the centroids for that receiver.
#'
#' @examples
#' #### Define data for setup_acdc()
#' ## Define coordinates of receivers as SpatialPoints with UTM CRS
#' # CRS of receiver locations as recorded in dat_moorings
#' proj_wgs84 <- sp::CRS("+init=epsg:4326")
#' # CRS of receiver locations required
#' proj_utm <- sp::CRS(paste("+proj=utm +zone=29 +datum=WGS84",
#' "+units=m +no_defs +ellps=WGS84 +towgs84=0,0,0"))
#' # Define SpatialPoints object
#' xy_wgs84 <- sp::SpatialPoints(dat_moorings[, c("receiver_long", "receiver_lat")], proj_wgs84)
#' xy_utm <- sp::spTransform(xy_wgs84, proj_utm)
#'
#' #### Example (1): Define a list of centroids with specified parameters
#' # ... (Argument values are small to reduce computation time for examples)
#' centroids <- setup_acdc(rs = dat_moorings$receiver_id,
#' xy = xy_utm,
#' detection_range = 500,
#' mobility = 250,
#' n_timesteps = 3
#' )
#' # A list of SpatialPolygonsDataFrames is returned with elements from 1:max(rs)
#' # NULL elements correspond to numbers in this sequence that do not refer to receivers
#' # Otherwise a SpatialPolygonsDataFrame is returned with all the centroids for that receiver
#' centroids
#'
#' #### Example (2): Visualise the acoustic centroids produced via plot = TRUE
#' centroids <- setup_acdc(rs = dat_moorings$receiver_id,
#' xy = xy_utm,
#' detection_range = 500,
#' mobility = 250,
#' n_timesteps = 3,
#' plot = TRUE
#' )
#'
#' #### Example (3): Remove areas of the centroids that overlap with coastline
#' centroids <- setup_acdc(rs = dat_moorings$receiver_id,
#' xy = xy_utm,
#' detection_range = 500,
#' mobility = 250,
#' n_timesteps = 3,
#' plot = TRUE,
#' coastline = dat_coast
#' )
#'
#' #### Example (4): Remove areas of the centroids beyond a boundary
#' xy_utm_coords <- sp::coordinates(xy_utm)
#' boundaries <- raster::extent(min(xy_utm_coords[, 1]),
#' max(xy_utm_coords[, 1]),
#' min(xy_utm_coords[, 2]),
#' max(xy_utm_coords[, 2])
#' )
#' centroids <- setup_acdc(rs = dat_moorings$receiver_id,
#' xy = xy_utm,
#' detection_range = 500,
#' mobility = 250,
#' n_timesteps = 3,
#' plot = TRUE,
#' coastline = dat_coast,
#' boundaries = boundaries
#' )
#'
#' #### Example (5): Implement the algorithm in parallel
#' centroids <- setup_acdc(rs = dat_moorings$receiver_id,
#' xy = xy_utm,
#' detection_range = 500,
#' mobility = 250,
#' n_timesteps = 3,
#' plot = TRUE,
#' coastline = dat_coast,
#' boundaries = boundaries,
#' cl = parallel::makeCluster(2L)
#' )
#'
#' #### Example (6): Acoustic centroids can be saved to file using rlist::list.save()
#' # rlist::list.save(centroids, paste0(tempdir(), "/centroids.RData"))

#' @author Edward Lavender
#' @export
#'

setup_acdc <- function(
rs,
xy,
detection_range,
mobility,
n_timesteps = 250,
coastline = NULL,
boundaries = NULL,
plot = FALSE,
cl = NULL,
verbose = TRUE
){

#### Initiate function
cat_to_console <- function(..., show = verbose) if(show) cat(paste(..., "\n"))
cat_to_console("flapper::setup_acdc() called...")
if(is.numeric(rs)) rs <- as.integer(rs)
if(!is.integer(rs)) stop(paste("Argument 'rs' must be of class 'integer', not class(es):"), class(rs))
if(any(rs <= 0)) stop("Argument 'rs' cannot contain receiver IDs <= 0.")
if(any(duplicated(rs))){
message("Argument 'rs' contains duplicate elements. rs has been simplified to unique(rs).")
rs <- unique(rs)
}
if(!is.null(coastline) & !is.null(boundaries)) {
coastline <- raster::crop(coastline, boundaries)
if(is.null(coastline)) message("No coastline within defined boundaries. \n")
}
if(plot){
cat_to_console("... Plotting background map of area...")
if(!is.null(coastline)) {
raster::plot(coastline, col = "lightgreen", border = "darkgreen", lwd = 1.5)
graphics::points(xy, pch = 21, col = "royalblue", bg = "royalblue")
} else {
raster::plot(xy, pch = 21, col = "royalblue", bg = "royalblue")
}
if(!is.null(boundaries)) raster::lines(boundaries, col = "red", lty = 3)
}


#### Define a list of acoustic centroids for each receiver
cat_to_console("... Building a nested list of acoustic centroids. This is the slow step...")

## Define a sequence of centroid sizes
# Around each receiver, we'll create a polygon of this size
size_seq <- seq(detection_range, length.out = n_timesteps, by = mobility)

## Define a list of receivers, with a list of centroids for each receiver
bathy_ls <- pbapply::pblapply(1:length(rs), cl = cl, function(i){

centroids_ls <- lapply(size_seq, function(size){

# Define a buffer around the current receiver of appropriate size
bathy_poly <- rgeos::gBuffer(xy[i], width = size)

# Reduce the size of the polygon by overlapping to remove areas on land
# This keeps the polygons as small as possible which is important for ACDC/MP algorithm computation efficiency.
if(!is.null(coastline)) {
bathy_poly <- rgeos::gDifference(bathy_poly, coastline, byid = FALSE)
}

# Remove any areas beyond specified boundaries
# Again this keeps polygon size to a minimum
if(!is.null(boundaries)) {
bathy_poly <- raster::crop(bathy_poly, boundaries)
}

# Return acoustic centroid
return(bathy_poly)

})

# Define names of the rasters forn receiver, i, based on size
names(centroids_ls) <- paste0("s_", size_seq)
return(centroids_ls)

})
if(!is.null(cl)) parallel::stopCluster(cl)
names(bathy_ls) <- rs

#### Add NULL elements to the list for any receivers in the range 1:max(rs) that are not in rs
# This means we can use receiver numbers to go straight to the correct element in the list in ACDC/MP algorithms.
bathy_ls <- lapply(as.integer(1:max(rs)), function(i){
if(i %in% rs){
return(bathy_ls[[as.character(i)]])
} else{
return(NULL)
}
})

#### Convert nested list of polygons to a SpatialPolygonsDataFrame
# ... with one element for each receiver and each dataframe containing all the polygons for that receiver
cat_to_console("... Converting the nested list of acoustic centroids to a SpatialPolygonsDataFrame...")
if(is.null(bathy_ls)) {
stop("There are no acoustic centroids within defined spatial boundaries.")
}
spdf_ls <- pbapply::pblapply(bathy_ls, cl = NULL, function(element){
if(!is.null(element)){
# bind all the sub-elements in each element together into a single spatial polygon
sp <- raster::bind(element)
# convert this into a spatial polygons dataframe
spdf <- sp::SpatialPolygonsDataFrame(sp, data.frame(id = 1:length(sp)))
return(spdf)
}})

#### Visualise centroids
if(plot){
cat_to_console("... Plotting centroids on map...")
pbapply::pblapply(spdf_ls, function(spdf) if(!is.null(spdf)) raster::lines(spdf, col = "dimgrey", lwd = 0.75))
}

#### Return list of SpatialPolygonsDataFrame
return(spdf_ls)

}



######################################
######################################
#### .acdc()




######################################
######################################
#### acdc()




######################################
######################################
#### animate_acdc()


31 changes: 31 additions & 0 deletions R/dats.R
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,37 @@
#' @references Data collection and processing is described in Lavender (in prep). Fine-scale habitat use of the Critically Endangered flapper skate (\emph{Dipturus intermedius}). [Doctoral dissertation, University of St Andrews].
"dat_archival"

#####################################
#####################################
#### dat_coast

#' @title The coastline around the MEFS Firth of Lorn acoustic array
#' @description A SpatialPolygonsDataFrame delineating the coastline around a subset of acoustic receivers set up by the Movement Ecology of Flapper Skate (MEFS) project in the Firth of Lorn, off the west coast of Scotland.
#'
#' @format A SpatialPolygonsDataFrame (see \code{\link[sp]{SpatialPolygonsDataFrame-class}}).
#'
#' @source https://biogeo.ucdavis.edu/data/gadm3.6/Rsp/gadm36_GBR_0_sp.rds
"dat_coast"


#####################################
#####################################
#### dat_gebco

#' @title The bathymetry around the MEFS Firth of Lorn acoustic array
#' @description A dataset of the bathymetry (m) in an area around a subset of acoustic receivers set up by the Movement Ecology of Flapper Skate (MEFS) project in the Firth of Lorn, off the west coast of Scotland. Bathymetry data are provided by the General Bathymetric Chart of the Oceans (GEBCO).
#'
#' @format A \code{\link[raster]{raster}} with 36 rows, 36 columns and 1296 cells, with the following properties:
#' \describe{
#' \item{dimensions}{57, 74, 4218 (nrow, ncol, ncell)}
#' \item{resolution}{257, 463 (x, y)}
#' \item{extent}{695492.1, 714510.1, 6246657, 6273048 (xmin, xmax, ymin, ymax)}
#' \item{crs}{+proj=utm +zone=29 +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs}
#' }
#'
#' @source GEBCO Compilation Group (2019) GEBCO 2019 Grid (doi:10.5285/836f016a-33be-6ddc-e053-6c86abc0788e)
"dat_gebco"


#### End of code.
#####################################
Expand Down
Binary file not shown.
43 changes: 42 additions & 1 deletion data-raw/use_dats.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,51 @@ dat_acoustics <- readRDS("dat_acoustics.rds")
dat_sentinel <- readRDS("dat_sentinel.rds")
dat_archival <- readRDS("dat_archival.rds")

#### Obtain spatial data
## Open Source coastline data
dat_coast_fol <- readRDS(url("https://biogeo.ucdavis.edu/data/gadm3.6/Rsp/gadm36_GBR_0_sp.rds"))
## Open source bathymetry data
# Define a buffered region around the receivers for which to obtain bathymetry data
proj_wgs84 <- sp::CRS("+init=epsg:4326")
proj_utm <- sp::CRS(paste("+proj=utm +zone=29 +datum=WGS84",
"+units=m +no_defs +ellps=WGS84 +towgs84=0,0,0"))
rxy_wgs84 <- sp::SpatialPoints(dat_moorings[, c("receiver_long", "receiver_lat")], proj_wgs84)
rxy_utm <- sp::spTransform(rxy_wgs84, proj_utm)
rxy_utm_buf <- rgeos::gBuffer(rxy_utm, width = 1000)
bounds_utm <- raster::extent(rxy_utm_buf)
bounds_utm <- sp::SpatialPoints(sp::coordinates(bounds_utm), proj_utm)
bounds_wgs84 <- sp::spTransform(bounds_utm, proj_wgs84)
# Examine coordinates
# -5.786025, -5.562533, 56.34059, 56.53355
# Use these coordinates to download manually Open source GEBCO bathymetry data from https://download.gebco.net
# Data source: GEBCO Compilation Group (2019) GEBCO 2019 Grid (doi:10.5285/836f016a-33be-6ddc-e053-6c86abc0788e)
# Data saved in /data_raw/
dat_gebco_fol <- raster::raster("./data-raw/gebco_2020_n56.53355_s56.34059_w-5.786025_e-5.562533.tif")

#### Process spatial data
# Crop coastline to boundaries
area <- raster::extent(bounds_wgs84)
dat_coast_fol <- raster::crop(dat_coast_fol, area)
# Use spatial data with UTM coordinates
dat_coast <- sp::spTransform(dat_coast_fol, proj_utm)
dat_gebco <- raster::projectRaster(dat_gebco_fol, crs = proj_utm)
# Process bathymetry data to remove observations on land
dat_gebco[dat_gebco[] >= 0] <- NA
# Use absolute values
dat_gebco <- abs(dat_gebco)
# Visual checks
raster::plot(dat_coast)
raster::plot(dat_gebco, add = TRUE)
raster::lines(dat_coast)
points(rxy_utm, cex = 2)
axis(side = 1)
axis(side = 2)

#### Use data
usethis::use_data(dat_ids, overwrite = TRUE)
usethis::use_data(dat_moorings, overwrite = TRUE)
usethis::use_data(dat_acoustics, overwrite = TRUE)
usethis::use_data(dat_sentinel, overwrite = TRUE)
usethis::use_data(dat_archival, overwrite = TRUE)

usethis::use_data(dat_coast, overwrite = TRUE)
usethis::use_data(dat_gebco, overwrite = TRUE)
Binary file added data/dat_coast.rda
Binary file not shown.
Binary file added data/dat_gebco.rda
Binary file not shown.
19 changes: 19 additions & 0 deletions man/dat_coast.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions man/dat_gebco.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 2695519

Please sign in to comment.