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 lines #53

Merged
merged 19 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
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Suggests:
asnipe,
markdown
SystemRequirements: GEOS (>= 3.2.0)
RoxygenNote: 7.2.2
RoxygenNote: 7.2.3
VignetteBuilder: knitr
Roxygen: list(markdown = TRUE)
BugReports: https://github.com/ropensci/spatsoc/issues
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by roxygen2: do not edit by hand

export(build_lines)
export(build_lines_sp)
export(build_polys)
export(dyad_id)
export(edge_dist)
Expand Down
94 changes: 51 additions & 43 deletions R/build_lines.R
Original file line number Diff line number Diff line change
@@ -1,37 +1,54 @@
#' Build Lines
#'
#'
#' \code{build_lines} creates a \code{SpatialLines} object from a \code{data.table}.
#' The function accepts a \code{data.table} with relocation data, individual
#' identifiers a sorting column and a \code{projection}. The relocation data
#' is transformed into \code{SpatialLines} for each individual and optionally,
#' each \code{splitBy}. Relocation data should be in two columns representing
#' the X and Y coordinates.
#'
#' 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
#' `build_lines` generates a simple feature collection with LINESTRINGs from a
#' `data.table`. The function accepts a `data.table` with relocation data,
#' individual identifiers, a sorting column and a `projection`. The relocation
#' data is transformed into LINESTRINGs for each individual and, optionally,
#' combination of columns listed in `splitBy`. Relocation data should be in two
#' columns representing the X and Y coordinates.
#'
#' ## R-spatial evolution
#'
#' Please note, spatsoc has followed updates from R spatial, GDAL and PROJ
#' for handling projections, see more at
#' \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
#' by date time.
#'
#' The \code{splitBy} argument offers further control building \code{SpatialLines}.
#' If in your \code{DT}, you have multiple temporal groups (e.g.: years) for
#' In addition, `build_lines` previously used [sp::SpatialLines] but has been
#' updated to use [sf::st_as_sf] and [sf::st_linestring] 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::SpatialLines] is retained as
#' [build_lines_sp] temporarily but users are urged to transition as soon as
#' possible.
#'
#' ## Notes on arguments
#'
#' The `projection` argument expects a numeric or character defining the
#' coordinate reference system.
#' For example, for UTM zone 36N (EPSG 32736), the projection argument is either
#' `projection = 'EPSG:32736'` or `projection = 32736`.
#' See details in [`sf::st_crs()`] and \url{https://spatialreference.org}
#' for a list of EPSG codes.
#'
#' The `sortBy` argument is used to order the input `DT` when creating
#' sf LINESTRINGs. It must a column in the input `DT` of type
#' POSIXct to ensure the rows are sorted by date time.
#'
#' The `splitBy` argument offers further control building LINESTRINGs.
#' If in your input `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{SpatialLines} for each individual in each year.
#' build LINESTRINGs for each individual in each year.
#'
#' \code{build_lines} is used by \code{group_lines} for grouping overlapping
#' lines created from relocations.
#' `build_lines` is used by `group_lines` for grouping overlapping
#' lines generated from relocations.
#'
#' @return \code{build_lines} returns a \code{SpatialLines} object with a line
#' for each individual (and optionally \code{splitBy} combination).
#' @return `build_lines` returns an sf LINESTRING object with a line
#' for each individual (and optionally `splitBy` combination).
#'
#' An error is returned when an individual has less than 2 relocations, making
#' it impossible to build a line.
#' Individuals (or combinations of individuals and `splitBy`) with less than two
#' relocations are dropped since it requires at least two relocations to
#' build a line.
#'
#' @inheritParams group_lines
#' @inheritParams build_polys
Expand All @@ -54,7 +71,7 @@
#' DT[, datetime := as.POSIXct(datetime, tz = 'UTC')]
#'
#' # EPSG code for example data
#' utm <- 'EPSG:32736'
#' utm <- 32736
#'
#' # Build lines for each individual
#' lines <- build_lines(DT, projection = utm, id = 'ID', coords = c('X', 'Y'),
Expand All @@ -64,7 +81,6 @@
#' DT[, yr := year(datetime)]
#' lines <- build_lines(DT, projection = utm, id = 'ID', coords = c('X', 'Y'),
#' sortBy = 'datetime', splitBy = 'yr')
#'
build_lines <-
function(DT = NULL,
projection = NULL,
Expand Down Expand Up @@ -132,7 +148,7 @@ build_lines <-
}

if (!('POSIXct' %in%
unlist(lapply(DT[, .SD, .SDcols = sortBy], class)))) {
unlist(lapply(DT[, .SD, .SDcols = c(sortBy)], class)))) {
stop('sortBy provided must be 1 column of type POSIXct')
}

Expand All @@ -143,21 +159,13 @@ build_lines <-
warning('some rows dropped, cannot build lines with less than two points')
}

lst <- split(DT[dropRows, on = splitBy][!(dropped)][order(get(sortBy))],
by = c(splitBy), sorted = TRUE)
wo_drop <- DT[dropRows, on = splitBy][!(dropped)]

if (length(lst) == 0) {
return(NULL)
} else {
proj4string <- sp::CRS(projection)
l <- lapply(seq_along(lst), function(i) {
sp::SpatialLines(list(sp::Lines(sp::Line(
cbind(lst[[i]][[coords[1]]],
lst[[i]][[coords[2]]])
),
names(lst)[[i]])),
proj4string = proj4string)
})
return(do.call(sp::rbind.SpatialLines, l))
}
data.table::setorderv(wo_drop, sortBy)

lines <- sf::st_as_sf(
wo_drop[, .(geometry = sf::st_sfc(sf::st_linestring(as.matrix(.SD)))),
by = c(splitBy), .SDcols = coords],
crs = sf::st_crs(projection)
)
}
108 changes: 108 additions & 0 deletions R/build_lines_sp.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#' Build Lines (deprecated version of function with retired spatial packages)
#'
#'
#' @inheritParams build_lines
#'
#' @export
#'
#' @family Build functions
#' @seealso \code{\link{group_lines}}
#'
#' @import data.table
#'
build_lines_sp <-
function(DT = NULL,
projection = NULL,
id = NULL,
coords = NULL,
sortBy = NULL,
splitBy = NULL) {
.Deprecated(msg = 'build_lines 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
dropped <- . <- NULL

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

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 (is.null(sortBy)) {
stop('sortBy must be provided')
}

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

if (any(!(c(id, coords, splitBy, sortBy) %in% colnames(DT)))) {
stop(paste0(
as.character(paste(setdiff(
c(id, coords, splitBy, sortBy), 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 (is.null(splitBy)) {
splitBy <- id
} else {
splitBy <- c(id, splitBy)
}
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 (!('POSIXct' %in%
unlist(lapply(DT[, .SD, .SDcols = sortBy], class)))) {
stop('sortBy provided must be 1 column of type POSIXct')
}


dropRows <- DT[, .(dropped = .N < 2), by = c(splitBy)]

if (dropRows[(dropped), .N] > 0) {
warning('some rows dropped, cannot build lines with less than two points')
}

lst <- split(DT[dropRows, on = splitBy][!(dropped)][order(get(sortBy))],
by = c(splitBy), sorted = TRUE)

if (length(lst) == 0) {
return(NULL)
} else {
proj4string <- sp::CRS(projection)
l <- lapply(seq_along(lst), function(i) {
sp::SpatialLines(list(sp::Lines(sp::Line(
cbind(lst[[i]][[coords[1]]],
lst[[i]][[coords[2]]])
),
names(lst)[[i]])),
proj4string = proj4string)
})
return(do.call(sp::rbind.SpatialLines, l))
}
}
6 changes: 3 additions & 3 deletions R/build_polys.R
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@
#' @inheritParams group_polys
#' @param spPts alternatively, provide solely a SpatialPointsDataFrame with one
#' column representing the ID of each point.
#' @param projection character string defining the projection to be passed to
#' \code{sp::CRS}. For example, for UTM zone 36S (EPSG 32736),
#' the projection argument is 'EPSG:32736'. See details.
#' @param projection numeric or character defining the coordinate reference
#' system to be passed to [sf::st_crs()]. For example, either
#' `projection = "EPSG:32736"` or `projection = 32736`.
#' @export
#'
#' @family Build functions
Expand Down
65 changes: 39 additions & 26 deletions man/build_lines.Rd

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

Loading