Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #133 file locations for distances matrix() #145

Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
eda3860
Updated news to reflect file pathway being added to distanceMatrix() …
dsmith-unbc Nov 13, 2024
a240cad
Updated documentation code for distanceMatrix() and added the ability…
dsmith-unbc Nov 13, 2024
e35192d
Updated documentation for distanceMatrix() function
dsmith-unbc Nov 13, 2024
50fd11b
Had to adjust distanceMatrix() test since compressing to 80c changed …
dsmith-unbc Nov 13, 2024
852f215
Minor updates to allow checks to pass
dsmith-unbc Nov 13, 2024
42325f8
added new checks to test `distanceMatrix()` 'file_path' addition.
dsmith-unbc Nov 13, 2024
9d987e7
Merge branch 'hugomflavio:master' into issue-#133-file-locations-for-…
dsmith-unbc Nov 19, 2024
14290a8
Updated news description based on changes made to 'distancesMatrix()'
dsmith-unbc Nov 19, 2024
ec35ecc
Updated help documentation for 'distancesMatrix()' where the output o…
dsmith-unbc Nov 19, 2024
46d5e78
Some minor check fixes to address new 'distancesMatrix()' changes. Re…
dsmith-unbc Nov 19, 2024
4b74870
Modified 'distancesMatrix()' function to allow starters and targets t…
dsmith-unbc Nov 19, 2024
e697513
Merge branch 'hugomflavio:master' into issue-#133-file-locations-for-…
dsmith-unbc Dec 5, 2024
127d326
Removed curly brackets from break. Condensed write.csv and check for …
dsmith-unbc Dec 5, 2024
549c306
Update R/distances.R
dsmith-unbc Dec 6, 2024
4ca4199
Update R/distances.R
dsmith-unbc Dec 6, 2024
e2e51e3
Update R/distances.R
dsmith-unbc Dec 6, 2024
7a9ef9b
Update R/distances.R
dsmith-unbc Dec 6, 2024
21a97c0
Update R/distances.R
dsmith-unbc Dec 6, 2024
a838e9b
Update R/distances.R
dsmith-unbc Dec 6, 2024
86ac9d5
Update R/distances.R
dsmith-unbc Dec 6, 2024
2db30e9
Update R/distances.R
dsmith-unbc Dec 6, 2024
4ec30af
Update R/distances.R
dsmith-unbc Dec 6, 2024
19e8731
Update R/distances.R
dsmith-unbc Dec 6, 2024
866c8ea
Update R/distances.R
dsmith-unbc Dec 6, 2024
e3fdca4
Update R/distances.R
dsmith-unbc Dec 6, 2024
1c63547
Update R/distances.R
dsmith-unbc Dec 6, 2024
6c5fd8a
Update R/distances.R
dsmith-unbc Dec 6, 2024
750815a
Update R/distances.R
dsmith-unbc Dec 6, 2024
6882a8b
Removed actel = TRUE from warnings, changes warnings to messages, and…
dsmith-unbc Dec 6, 2024
4ab7749
minor fix to if statement
dsmith-unbc Dec 6, 2024
0339f1f
Merge branch 'hugomflavio:master' into issue-#133-file-locations-for-…
dsmith-unbc Dec 6, 2024
4f022f9
fixed bracket issue causing checks to fail.
dsmith-unbc Dec 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Enhancements:
* New `convertLotekCDMAFile()` function to prepare lotek txt logs for further analyses (issue [#103](https://github.com/hugomflavio/actel/issues/103)).
* New `plotDot()` function to help users inspect their spatial.txt files (issue [#55](https://github.com/hugomflavio/actel/issues/55)).
* Improved internal mechanisms for message/warning/debug/stop reporting. new internal function `event()` replaces the old `appendTo()`, `stopAndReport()`, and `emergencyBreak()` (issue [#135](https://github.com/hugomflavio/actel/issues/135))
* Added the ability to use output of `loadSpatial()` as 'starters' for `distancesMatrix()` when 'actel' is 'TRUE'. This allows a user to input their spatial.csv if located in a separate working directory (issue [#133](https://github.com/hugomflavio/actel/issues/133))

## actel 1.3.0

Expand Down
266 changes: 206 additions & 60 deletions R/distances.R
Original file line number Diff line number Diff line change
Expand Up @@ -342,26 +342,35 @@ transitionLayer <- function(x, directions = c(16, 8, 4)){

#' Calculate Distances Matrix
#'
#' Using a previously created transition layer (see \code{\link{transitionLayer}}), calculates the distances
#' between spatial points. Adapted from Grant Adams' script "distance to closest mpa". if the argument 'actel'
#' is set to TRUE (default), an actel-compatible matrix is generated, and the user will be asked if they would
#' like to save the matrix as 'distances.csv' in the current directory.
#'
#' It is highly recommended to read the manual page regarding distances matrices before running this function.
#' You can find it here: \href{https://hugomflavio.github.io/actel-website/manual-distances.html}{https://hugomflavio.github.io/actel-website/manual-distances.html}
#'
#' @param t.layer A TransitionLayer object, generated by \code{\link{transitionLayer}}.
#' @param starters A data frame with the points from which to start measuring the distance. Ignored if actel = TRUE (default), as the 'spatial.csv' is loaded as starters.
#' @param targets A data frame with the points to which a way must be found. Ignored if actel = TRUE (default), as the 'spatial.csv' is loaded as targets.
#' @param coord.x,coord.y The names of the columns containing the x and y coordinates in the starters and targets. Must be identical in the starters and targets.
#' @param id.col The name of the column containing the IDs of the points to be used as starters and targets. Must be identical in both files. Ignored if actel = TRUE (default), as the stations' standard names are used.
#' @param actel Logical: Should the distance matrix be optimized for actel? Defaults to TRUE.
#' Using a previously created transition layer (see \code{\link{transitionLayer}}),
#' calculates the distances between spatial points. Adapted from Grant Adams'
#' script "distance to closest mpa". If the argument 'actel' is set to
#' TRUE (default), an actel-compatible matrix is generated, and the user will be
#' asked if they would like to save the matrix as 'distances.csv' in the current
#' directory.
#'
#' It is highly recommended to read the manual page regarding distances matrices
#' before running this function.
#' You can find it here:\href{https://hugomflavio.github.io/actel-website/manual-distances.html}{https://hugomflavio.github.io/actel-website/manual-distances.html}
#'
#' @param t.layer A TransitionLayer object, generated by
#' \code{\link{transitionLayer}}.
#' @param starters A data frame with the points from which to start measuring
#' the distance or the output of `loadSpatial()`.
#' @param targets A data frame with the points to which a way must be found.
#' @param coord.x,coord.y The names of the columns containing the x and y
#' coordinates in the starters and targets. Must be identical in the starters
#' and targets.
#' @param id.col The name of the column containing the IDs of the points to
#' be used as starters and targets. Must be identical in both files.
#' Ignored if actel = TRUE (default), as the stations' standard names are used.
#' @param actel Logical: Should the distance matrix be optimized for actel?
#' Defaults to TRUE.
#'
#' @examples
#' \donttest{
#' # check if R can run the distance functions
#' aux <- c(
#' length(suppressWarnings(packageDescription("raster"))),
#' aux <- c(length(suppressWarnings(packageDescription("raster"))),
#' length(suppressWarnings(packageDescription("gdistance"))),
#' length(suppressWarnings(packageDescription("sp"))),
#' length(suppressWarnings(packageDescription("terra"))))
Expand All @@ -370,8 +379,10 @@ transitionLayer <- function(x, directions = c(16, 8, 4)){
#'
#' if (any(missing.packages)) {
#' message("Sorry, this function requires packages '",
#' paste(c("raster", "gdistance", "sp", "terra")[missing.packages], collapse = "', '"),
#' "' to operate. Please install ", ifelse(sum(missing.packages) > 1, "them", "it"),
#' paste(c("raster", "gdistance", "sp", "terra")[missing.packages],
#' collapse = "', '"),
#' "' to operate. Please install ", ifelse(sum(missing.packages) > 1,
#' "them", "it"),
#' " before proceeding.")
#' } else {
#' # move to a temporary directory
Expand Down Expand Up @@ -409,7 +420,7 @@ transitionLayer <- function(x, directions = c(16, 8, 4)){
#'
#' @export
#'
distancesMatrix <- function(t.layer, starters = NULL, targets = starters,
distancesMatrix <- function(t.layer, starters = NULL, targets = NULL,
coord.x = "x", coord.y = "y", id.col = NULL, actel = TRUE){
# initial checks on package presence
aux <- c(
Expand All @@ -419,41 +430,103 @@ distancesMatrix <- function(t.layer, starters = NULL, targets = starters,
length(suppressWarnings(packageDescription("terra"))))
missing.packages <- sapply(aux, function(x) x == 1)
if (any(missing.packages)) {
stop(paste0("This function requires packages '", paste(c("raster", "gdistance", "sp", "terra")[missing.packages], collapse = "', '"),
"' to operate. Please install ", ifelse(sum(missing.packages) > 1, "them", "it"), " before proceeding.\n"), call. = FALSE)
stop("This function requires packages '",
paste(c("raster", "gdistance", "sp", "terra")[missing.packages],
collapse = "', '"),
"' to operate. Please install ",
ifelse(sum(missing.packages) > 1, "them", "it"),
" before proceeding.\n", call. = FALSE)
}

if (!inherits(t.layer, "TransitionLayer"))
stop("Could not recognise 't.layer' as a TransitionLayer object. Make sure to compile it using the function transitionLayer.\n", call. = FALSE)
stop("Could not recognise 't.layer' as a TransitionLayer object. Make",
" sure to compile it using the function transitionLayer.\n",
call. = FALSE)

if (!is.null(id.col) && length(id.col) > 1)
stop("Please provide only one column name in 'id.col'", call. = FALSE)
if (!is.null(id.col) && is.numeric(id.col))
stop("Please refer to the column name in 'id.col', rather than the column index.\n", call. = FALSE)
stop("Please refer to the column name in 'id.col', rather than the column",
" index.\n", call. = FALSE)

if (actel) {
message("M: Creating actel-compatible distances matrix."); flush.console()
if (!is.null(starters) | !is.null(targets))
warning("starters' or 'targets' were set but will be ignored because 'actel' is set to TRUE. Set 'actel' to FALSE to use the 'starters' and 'targets' arguments.", call. = FALSE, immediate. = TRUE)
starters <- targets <- loadSpatial()
if (!is.null(id.col))
warning("id.col' was set but will be ignored because 'actel' is set to TRUE. Set 'actel' to FALSE to use the 'id.col' argument.", call. = FALSE, immediate. = TRUE)

if(!is.null(starters) | !is.null(targets)) {
# distancesMatrix() crashes if actel = TRUE and starters/targets were
# supplied but do not contain a column called "Standard.name"
if(!is.null(starters) && !("Standard.name" %in% colnames(starters))) {
stop("'starters' is missing column called 'Standard.name'",
call. = FALSE)
}

if(!is.null(targets) & !any(grepl(c("Standard.name"),
colnames(targets)))) {
stop("'targets' is missing column called 'Standard.name'",
call. = FALSE)
}
}

if (!is.null(starters) & !is.null(targets)) {
warning("'starters' and 'targets' were specified while 'actel'",
" is set to TRUE. Using 'starters' and 'targets' for distances.",
call. = FALSE, immediate. = TRUE)
}

if (!is.null(starters) & is.null(targets)) {
warning("'starters' were specified but 'targets' were not while 'actel'",
" is set to TRUE. Using 'starters' for 'targets'.",
call. = FALSE, immediate. = TRUE)

targets <- starters
}

if (is.null(starters) & is.null(targets)) {
warning("'starters' and 'targets' were not set while 'actel' is set to",
" TRUE. Creating 'starters' and 'targets' from 'spatial.csv' ",
" in working directory." , call. = FALSE, immediate. = TRUE)
starters <- targets <- loadSpatial()
}
dsmith-unbc marked this conversation as resolved.
Show resolved Hide resolved

if (!is.null(id.col)) {
warning("id.col' was set but will be ignored because 'actel' is set to",
" TRUE. Set 'actel' to FALSE to use the 'id.col' argument.",
call. = FALSE, immediate. = TRUE)
}
id.col <- "Standard.name"
}

# Failsafe from old way code worked where targets used starters by default
# when actel == FALSE
if(!actel) {
if(is.null(targets)) {
targets <- starters
}
}

if (!inherits(starters, "data.frame"))
if (!inherits(starters, "data.frame")) {
stop("'starters' must be a data frame.\n", call. = FALSE)
if (!inherits(targets, "data.frame"))
}
if (!inherits(targets, "data.frame")) {
stop("'targets' must be a data frame.\n", call. = FALSE)
}

if (is.na(match(coord.x, colnames(starters))))
stop(paste0("Could not find a column '", coord.x, "' in 'starters'."), call. = FALSE)
if (is.na(match(coord.y, colnames(starters))))
stop(paste0("Could not find a column '", coord.y, "' in 'starters'."), call. = FALSE)
if (is.na(match(coord.x, colnames(targets))))
stop(paste0("Could not find a column '", coord.x, "' in 'targets'."), call. = FALSE)
if (is.na(match(coord.y, colnames(targets))))
stop(paste0("Could not find a column '", coord.y, "' in 'targets'."), call. = FALSE)
if (is.na(match(coord.x, colnames(starters)))) {
stop(paste0("Could not find a column '",
coord.x, "' in 'starters'."), call. = FALSE)
}
if (is.na(match(coord.y, colnames(starters)))) {
stop(paste0("Could not find a column '",
coord.y, "' in 'starters'."), call. = FALSE)
}
if (is.na(match(coord.x, colnames(targets)))) {
stop(paste0("Could not find a column '",
coord.x, "' in 'targets'."), call. = FALSE)
}
if (is.na(match(coord.y, colnames(targets)))) {
stop(paste0("Could not find a column '",
coord.y, "' in 'targets'."), call. = FALSE)
}

starters <- starters[, c(id.col, coord.x, coord.y)]
colnames(starters) <- c(id.col, "longitude", "latitude")
Expand All @@ -465,25 +538,33 @@ distancesMatrix <- function(t.layer, starters = NULL, targets = starters,
if (!is.na(match(id.col, colnames(starters)))) {
outputRows <- starters[, id.col]
if (any(duplicated(outputRows))) {
warning("The '", id.col, "' column in 'starters' contains duplicated values; skipping row naming.", immediate. = TRUE, call. = FALSE)
warning("The '", id.col,
"' column in 'starters' contains duplicated values;",
" skipping row naming.", immediate. = TRUE, call. = FALSE)
row.rename <- FALSE
} else {
row.rename <- TRUE
}
} else {
warning("Could not find a '", id.col, "' column in 'starters'; skipping row naming.", immediate. = TRUE, call. = FALSE)
warning("Could not find a '", id.col,
"' column in 'starters'; skipping row naming.",
immediate. = TRUE, call. = FALSE)
row.rename <- FALSE
}
if (!is.na(match(id.col, colnames(targets)))) {
outputCols <- targets[, id.col]
if (any(duplicated(outputCols))) {
warning("The '", id.col, "' column in 'targets' contains duplicated values; skipping column naming.", immediate. = TRUE, call. = FALSE)
warning("The '", id.col,
"' column in 'targets' contains duplicated values;",
" skipping column naming.", immediate. = TRUE, call. = FALSE)
col.rename <- FALSE
} else {
col.rename <- TRUE
}
} else {
warning("Could not find a '", id.col, "' column in 'targets'; skipping column naming.", immediate. = TRUE, call. = FALSE)
warning("Could not find a '", id.col,
"' column in 'targets'; skipping column naming.",
immediate. = TRUE, call. = FALSE)
col.rename <- FALSE
}
} else {
Expand All @@ -493,45 +574,110 @@ distancesMatrix <- function(t.layer, starters = NULL, targets = starters,


#### Create starters and targets spatial dataframes
sp::coordinates(starters) <- ~ longitude + latitude # converts the file to a spatialPoints object
raster::crs(starters) <- raster::crs(t.layer) # sets the crs

sp::coordinates(targets) <- ~ longitude + latitude # converts the file to a spatialPoints object
# converts the file to a spatialPoints object
sp::coordinates(starters) <- ~ longitude + latitude

# sets the crs
raster::crs(starters) <- raster::crs(t.layer)

# converts the file to a spatialPoints object
sp::coordinates(targets) <- ~ longitude + latitude
raster::crs(targets) <- raster::crs(t.layer)

# NOTE: THE LINES ABOVE COULD BE CHANGED ONCE gdistance'S
# FUNCTIONS START LIKING SF OBJECTS LAYER
# starters <- sf::st_as_sf(starters, coords = c("longitude","latitude"), crs = ...)
# targets <- sf::st_as_sf(targets, coords = c("longitude","latitude"), crs = ...)
# starters <- sf::st_as_sf(starters, coords = c("longitude","latitude"),
# crs = ...)
# targets <- sf::st_as_sf(targets, coords = c("longitude","latitude"),
# crs = ...)
# NOTE: currently, transition layer objects are not
# responding correctly to crs requests (e.g. sf::st_crs)

#### Calculate a matrix of distances to each object
dist.mat <- data.frame(gdistance::costDistance(t.layer, starters, targets))
if (any(dist.mat == Inf)) {
warning("At least one station is completely blocked off from the remaining stations by land. Filling
the respective fields with NA. If your animals were expected to travel around the areas present
in the shape file, consider applying a 'buffer' when calculating the transition layer. This
will artificially add water space around the shape file.", call. = FALSE)
warning("At least one station is completely blocked off from the remaining",
" stations by land. Filling the respective fields with NA.",
" If your animals were expected to travel around the areas present",
" in the shape file, consider applying a 'buffer' when calculating",
" the transition layer. This will artificially add water space",
" around the shape file.", call. = FALSE)
dist.mat[dist.mat == Inf] <- NA
}

if (row.rename)
if (row.rename) {
rownames(dist.mat) <- outputRows
}

if (col.rename)
if (col.rename) {
colnames(dist.mat) <- outputCols
}

if (interactive() & actel) { # nocov start
decision <- userInput("Would you like to save an actel-compatible distances matrix as 'distances.csv' in the current work directory?(y/n) ",
decision <- userInput(paste0("Would you like to save an actel-compatible",
" distances matrix as 'distances.csv'?(y/n)"),
choices = c("y", "n"))
if (decision == "y") {
if (file.exists('distances.csv')) {
warning("A file 'distances.csv' is already present in the current directory.", call. = FALSE, immediate. = TRUE)
decision <- userInput("Continuing will overwrite this file. Would you like to continue?(y/n) ", choices = c("y", "n"))
dir_decision <- userInput(paste0("Would you like 'distances.csv'",
" saved in the current working",
" directory?(y/n)"),
choices = c("y", "n"))
if (dir_decision == "n") {

target_dir <- ""

while (!dir.exists(target_dir)) {
target_dir <- readline(paste0("Specify the folder to save",
" 'distances.csv' to.",
" Hit enter to skip."))
if (nchar(target_dir) == 0) {
dir_decision <- "y"
break
}

if (!dir.exists(target_dir)) {
dir_decision <- userInput(paste0("Directory specified not found.",
" Do you want to continue?(y/n)"),
choices = c("y", "n"))
if (dir_decision == "n") {
dir_decision <- "y"
break
}
}
}
}
}

if (decision == "y") {

if(dir_decision == "n") {

# If a different directory has been specified, a new path is created
# where 'distances.csv' will be saved
path <- paste0(target_dir, "/distances.csv")

} else {
# If no 'path' specified, then 'distances.csv' is saved to the
# current working directory

path <- paste("distances.csv", sep = "/")

}
# Checks if 'distances.csv' is already in the file path specified
if (file.exists(path)) {

warning("A file 'distances.csv' is already present in the current",
" directory.", call. = FALSE, immediate. = TRUE)

decision <- userInput(paste0("Continuing will overwrite this file.",
" Would you like to continue?(y/n) "),
choices = c("y", "n"))
}
if (decision == "y") {

write.csv(round(dist.mat, 0), path, row.names = TRUE)

}
if (decision == "y")
write.csv(round(dist.mat, 0), "distances.csv", row.names = TRUE)
}
} # nocov end
return(round(dist.mat, 0))
Expand Down
Loading
Loading