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

Fix/rm sp build polys #52

Merged
merged 20 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
export(build_lines)
export(build_lines_sp)
export(build_polys)
export(build_polys_sp)
export(dyad_id)
export(edge_dist)
export(edge_nn)
Expand Down
138 changes: 75 additions & 63 deletions R/build_polys.R
Original file line number Diff line number Diff line change
@@ -1,63 +1,77 @@
#' Build Polygons
#'
#' \code{build_polys} creates a \code{SpatialPolygons} object from a
#' \code{data.table}. The function accepts a \code{data.table} with
#' relocation data, individual identifiers, a \code{projection},
#' \code{hrType} and \code{hrParams}. The relocation data is transformed
#' into \code{SpatialPolygons} for each individual and optionally, each
#' \code{splitBy}. Relocation data should be in two columns representing
#' the X and Y coordinates.
#'
#' The \code{DT} must be a \code{data.table}. If your data is a
#' \code{data.frame}, you can convert it by reference using
#' \code{\link[data.table:setDT]{data.table::setDT}}.
#'
#' The \code{id}, \code{coords} (and optional \code{splitBy}) arguments
#' expect the names of respective columns in \code{DT} which correspond
#' `build_polys` generates a simple feature collection with POLYGONs from a
#' `data.table`. The function accepts a `data.table` with
#' relocation data, individual identifiers, a projection,
#' home range type and parameters. The relocation
#' data is transformed into POLYGONs using either [adehabitatHR::mcp] or
#' [adehabitatHR::kernelUD] for each individual and, optionally,
#' combination of columns listed in `splitBy`. Relocation data should be in two
#' columns representing the X and Y coordinates.
#'
#' [group_polys] uses `build_polys` for grouping overlapping
#' polygons created from relocations.
#'
#' ## R-spatial evolution
#'
#' Please note, spatsoc has followed updates from R spatial, GDAL and PROJ for
#' handling projections, see more below and details at
#' \url{https://r-spatial.org/r/2020/03/17/wkt.html}.
#'
#' In addition, `build_polys` previously used [sp::SpatialPoints] but has been
#' updated to use [sf::st_as_sf] according to the R-spatial evolution, see more
#' at \url{https://r-spatial.org/r/2022/04/12/evolution.html}. A deprecated
#' version of this function using [sp::SpatialPoints] is retained as
#' [build_polys_sp] temporarily but users are urged to transition as soon as
#' possible.
#'
#' ## Notes on arguments
#'
#' The `DT` must be a `data.table`. If your data is a `data.frame`, you can
#' convert it by reference using [data.table::setDT].
#'
#' The `id`, `coords` (and optional `splitBy`) arguments
#' expect the names of respective columns in `DT` which correspond
#' to the individual identifier, X and Y coordinates, and additional
#' grouping columns.
#'
#' The \code{projection} argument expects a character string defining
#' the EPSG code. For example, for UTM zone 36N (EPSG 32736), the projection
#' argument is "EPSG:32736". See \url{https://spatialreference.org}
#' for a list of EPSG codes. Please note, R spatial has followed updates
#' to GDAL and PROJ for handling projections, see more at
#' \url{https://r-spatial.org/r/2020/03/17/wkt.html}. It is likely
#' that \code{build_polys} will return "Warning in proj4string(xy) :
#' CRS object has comment, which is lost in output" due to these changes.
#'
#' The \code{hrType} must be either one of "kernel" or "mcp". The
#' \code{hrParams} must be a named list of arguments matching those
#' of \code{adehabitatHR::kernelUD} and \code{adehabitatHR::getverticeshr}
#' or \code{adehabitatHR::mcp}.
#'
#' The \code{splitBy} argument offers further control building
#' \code{SpatialPolygons}. If in your \code{DT}, you have multiple
#' The `projection` argument expects a character string or numeric
#' defining the coordinate reference system to be passed to [sf::st_crs].
#' For example, for UTM zone 36S (EPSG 32736), the projection
#' argument is `projection = "EPSG:32736"` or `projection = 32736`.
#' See \url{https://spatialreference.org}
#' for a list of EPSG codes.
#'
#' The `hrType` must be either one of "kernel" or "mcp". The
#' `hrParams` must be a named list of arguments matching those
#' of [adehabitatHR::kernelUD] and [adehabitatHR::getverticeshr]
#' or [adehabitatHR::mcp].
#'
#' The `splitBy` argument offers further control building
#' POLYGONs. If in your `DT`, you have multiple
#' temporal groups (e.g.: years) for example, you can provide the
#' name of the column which identifies them and build \code{SpatialPolygons}
#' name of the column which identifies them and build POLYGONs
#' for each individual in each year.
#'
#' \code{group_polys} uses \code{build_polys} for grouping overlapping
#' polygons created from relocations.
#'
#' @return \code{build_polys} returns a \code{SpatialPolygons} object
#' with a polyon for each individual (and optionally \code{splitBy}
#' combination).
#' @return `build_polys` returns a simple feature collection with POLYGONs
#' for each individual (and optionally `splitBy` combination).
#'
#' An error is returned when \code{hrParams} do not match the arguments
#' of the \code{hrType} \code{adehabitatHR} function.
#' An error is returned when `hrParams` do not match the arguments
#' of the respective `hrType` `adehabitatHR` function.
#'
#'
#' @inheritParams group_polys
#' @param spPts alternatively, provide solely a SpatialPointsDataFrame with one
#' column representing the ID of each point.
#' column representing the ID of each point, as specified by [adehabitatHR::mcp]
#' or [adehabitatHR::kernelUD]
#' @param projection numeric or character defining the coordinate reference
#' system to be passed to [sf::st_crs()]. For example, either
#' system to be passed to [sf::st_crs]. For example, either
#' `projection = "EPSG:32736"` or `projection = 32736`.
#' @export
#'
#' @family Build functions
#' @seealso \code{\link{group_polys}}
#' @seealso [group_polys]
#'
#' @examples
#' # Load data.table
Expand All @@ -70,7 +84,7 @@
#' DT[, datetime := as.POSIXct(datetime, tz = 'UTC')]
#'
#' # EPSG code for example data
#' utm <- 'EPSG:32736'
#' utm <- 32736
#'
#' # Build polygons for each individual using kernelUD and getverticeshr
#' build_polys(DT, projection = utm, hrType = 'kernel',
Expand All @@ -79,18 +93,9 @@
#'
#' # Build polygons for each individual by year
#' DT[, yr := year(datetime)]
#' build_polys(DT, projection = utm, hrType = 'mcp', hrParams = list(percent = 95),
#' build_polys(DT, projection = utm, hrType = 'mcp',
#' hrParams = list(percent = 95),
#' id = 'ID', coords = c('X', 'Y'), splitBy = 'yr')
#'
#' # Build polygons from SpatialPointsDataFrame
#' library(sp)
#' pts <- SpatialPointsDataFrame(coords = DT[, .(X, Y)],
#' proj4string = CRS(utm),
#' data = DT[, .(ID)]
#' )
#'
#' build_polys(spPts = pts, hrType = 'mcp', hrParams = list(percent = 95))
#'
build_polys <- function(DT = NULL,
projection = NULL,
hrType = NULL,
Expand Down Expand Up @@ -153,7 +158,7 @@ build_polys <- function(DT = NULL,
FUN = function(x) {
is.numeric(x) | is.character(x) | is.integer(x)
}
), .SDcols = splitBy]))) {
), .SDcols = c(splitBy)]))) {
stop(
strwrap(
prefix = " ",
Expand All @@ -176,12 +181,15 @@ build_polys <- function(DT = NULL,
}

if (is.null(spPts)) {
spPts <- sp::SpatialPointsDataFrame(
DT[, .SD, .SDcols = eval.parent(coords, n = 1)],
proj4string = sp::CRS(projection),
data = DT[, .(ID = do.call(paste,
c(.SD, sep = '-'))),
.SDcols = splitBy])
DT[, ade_id := do.call(function(...) paste(..., sep = '-'), .SD),
.SDcols = c(splitBy)]
spPts <- sf::as_Spatial(
sf::st_as_sf(
DT[, .SD, .SDcols = c(coords, 'ade_id')],
coords = coords,
crs = sf::st_crs(projection)),
IDs = DT$ade_id
)
}

hrParams$xy <- spPts
Expand All @@ -192,7 +200,7 @@ build_polys <- function(DT = NULL,
if (!('unout' %in% names(hrParams))) {
hrParams$unout <- 'm2'
}
return(do.call(adehabitatHR::mcp, hrParams))
out <- do.call(adehabitatHR::mcp, hrParams)
} else {
stop(
strwrap(
Expand All @@ -213,10 +221,10 @@ build_polys <- function(DT = NULL,
}
kern <- do.call(adehabitatHR::kernelUD,
hrParams[intersect(names(hrParams), names(kernelParam))])
return(do.call(adehabitatHR::getverticeshr,
out <- do.call(adehabitatHR::getverticeshr,
c(x = list(kern),
hrParams[intersect(names(hrParams),
names(verticesParam))])))
names(verticesParam))]))
} else {
stop(
strwrap(
Expand All @@ -228,7 +236,11 @@ build_polys <- function(DT = NULL,
)
)
}
} else {
stop('hrType not one of "kernel" or "mcp"')
}

return(sf::st_as_sf(out))
}


150 changes: 150 additions & 0 deletions R/build_polys_sp.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#' Build Polygons (deprecated version of function with retired spatial packages)
#'
#' @inheritParams build_polys
#' @export
#'
#' @family Build functions
#' @seealso \code{\link{group_polys}}
build_polys_sp <- function(DT = NULL,
projection = NULL,
hrType = NULL,
hrParams = NULL,
id = NULL,
coords = NULL,
splitBy = NULL,
spPts = NULL) {
.Deprecated(msg = 'build_polys has been updated to use modern spatial R packages, removing dependencies on rgdal, rgeos, maptools in favor of sf. This version will be preserved until September 2023 for testing and user transition.')

# due to NSE notes in R CMD check
. <- NULL

if (is.null(DT) && is.null(spPts)) {
stop('input DT or spPts required')
}

if (!is.null(DT) && !is.null(spPts)) {
stop('cannot provide both DT and spPts')
}

if (!is.null(DT) && is.null(spPts)) {
if (is.null(coords)) {
stop('coords must be provided')
}

if (is.null(id)) {
stop('id must be provided')
}

if (is.null(projection)) {
stop('projection must be provided')
}

if (length(coords) != 2) {
stop('coords requires a vector of column names for coordinates X and Y')
}

if (is.null(splitBy)) {
splitBy <- id
} else {
splitBy <- c(id, splitBy)
}

if (any(!(c(splitBy, coords) %in% colnames(DT)))) {
stop(paste0(
as.character(paste(setdiff(
c(id, coords), colnames(DT)
),
collapse = ', ')),
' field(s) provided are not present in input DT'
))
}

if (any(!(DT[, vapply(.SD, is.numeric, TRUE),
.SDcols = coords]))) {
stop('coords must be numeric')
}

if (any(!(DT[, lapply(
.SD,
FUN = function(x) {
is.numeric(x) | is.character(x) | is.integer(x)
}
), .SDcols = splitBy]))) {
stop(
strwrap(
prefix = " ",
initial = "",
x = 'id (and splitBy when provided)
must be character, numeric or integer type'
)
)
}

}


if (is.null(hrType)) {
stop('hrType must be provided')
}

if (is.null(hrParams)) {
message('hrParams is not provided, using defaults')
}

if (is.null(spPts)) {
spPts <- sp::SpatialPointsDataFrame(
DT[, .SD, .SDcols = eval.parent(coords, n = 1)],
proj4string = sp::CRS(projection),
data = DT[, .(ID = do.call(paste,
c(.SD, sep = '-'))),
.SDcols = splitBy])
}

hrParams$xy <- spPts

if (hrType == 'mcp') {
functionParams <- formals(adehabitatHR::mcp)
if (all(names(hrParams) %in% names(functionParams))) {
if (!('unout' %in% names(hrParams))) {
hrParams$unout <- 'm2'
}
return(do.call(adehabitatHR::mcp, hrParams))
} else {
stop(
strwrap(
prefix = " ",
initial = "",
x = 'hrParams provided do not match function parameters,
see ?adehabitatHR::mcp'
)
)
}
} else if (hrType == 'kernel') {
kernelParam <- formals(adehabitatHR::kernelUD)
verticesParam <- formals(adehabitatHR::getverticeshr.estUD)

if (all(names(hrParams) %in% c(names(kernelParam), names(verticesParam)))) {
if (!('unout' %in% names(hrParams))) {
hrParams$unout <- 'm2'
}
kern <- do.call(adehabitatHR::kernelUD,
hrParams[intersect(names(hrParams), names(kernelParam))])
return(do.call(adehabitatHR::getverticeshr,
c(x = list(kern),
hrParams[intersect(names(hrParams),
names(verticesParam))])))
} else {
stop(
strwrap(
prefix = " ",
initial = "",
x = 'hrParams provided do not match
function parameters, see ?adehabitatHR::kernelUD
and ?adehabitatHR::getverticeshr'
)
)
}
}
}


4 changes: 1 addition & 3 deletions R/group_lines.R
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@
#' argument is "EPSG:32736". See \url{https://spatialreference.org}
#' for a list of EPSG codes. Please note, R spatial has followed updates
#' to GDAL and PROJ for handling projections, see more at
#' \url{https://r-spatial.org/r/2020/03/17/wkt.html}. It is likely
#' that \code{build_polys} will return "Warning in proj4string(xy) :
#' CRS object has comment, which is lost in output" due to these changes.
#' \url{https://r-spatial.org/r/2020/03/17/wkt.html}.
#'
#' The \code{sortBy} is used to order the input \code{data.table} when creating
#' \code{SpatialLines}. It must a \code{POSIXct} to ensure the rows are sorted
Expand Down
4 changes: 1 addition & 3 deletions R/group_polys.R
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@
#' 'EPSG:32736'. See \url{https://spatialreference.org} for a list of EPSG
#' codes. Please note, R spatial has followed updates to GDAL and PROJ for
#' handling projections, see more at
#' \url{https://r-spatial.org/r/2020/03/17/wkt.html}. It is likely
#' that \code{build_polys} will return "Warning in proj4string(xy) :
#' CRS object has comment, which is lost in output" due to these changes.
#' \url{https://r-spatial.org/r/2020/03/17/wkt.html}.
#'
#' The \code{hrType} must be either one of "kernel" or "mcp". The
#' \code{hrParams} must be a named list of arguments matching those of
Expand Down
Loading