Nothing
#' @title Sample Polygons at a Fixed Density
#'
#' @description Perform constant-density sampling of polygons.
#'
#'
#' @param x either `SpatVector` or object that can be coerced into one, should contain polygons registered to a planar coordinate reference system
#' @param polygon.id character, name of column which contains a unique ID for each polygon
#' @param n.pts.per.ac numeric, sampling density in "points per acre"
#' @param min.samples integer, enforced minimum number of samples per polygon
#' @param sampling.type character, sampling type passed to [terra::spatSample()]
#'
#' @return `SpatVector` of sample points
#' @keywords manip
#' @author D.E. Beaudette
#'
#' @export
#'
constantDensitySampling <- function(x, polygon.id = 'pID', n.pts.per.ac = 1, min.samples = 5, sampling.type = 'regular') {
if (!requireNamespace("terra")) {
stop('please install the `terra` package', call. = FALSE)
}
# attempt conversion to terra object
if (!inherits(x, 'SpatVector')) {
x <- terra::vect(x)
}
# sanity check: this must be a projected CRS
if (terra::is.lonlat(x)) {
stop('input polygons must be in a projected coordinate system with units of meters', call. = FALSE)
}
## 2016-10-13: return NULL when there are no features
# must have > 0 features
if (length(x) == 0)
return(NULL)
# retain CRS information
p4s <- terra::crs(x)
# sequential processing
# sample and return a list, one element / valid polygon
res <- lapply(
1:nrow(x),
FUN = function(i) {
smp <- sample.by.poly(
x[i,],
n.pts.per.ac = n.pts.per.ac,
min.samples = min.samples,
sampling.type = sampling.type,
p4s = p4s
)
smp$pID <- i
smp
}
)
# check for NULL in this list:
# this happens when there aren't enough sample points based on min.samples
# * cases where it was too difficult to place a point
# * could be caused by invalid geometry / topological error
null.items <- which(sapply(res, is.null))
if (length(null.items) > 0) {
message('some polygons were too small for the requested number of samples')
res <- res[-null.items]
}
# if there are no polygons large enough to sample, return NULL
if (length(res) < 1) {
return(NULL)
} else if (length(res) > 1) {
res <- do.call('rbind', res)
} else {
res <- res[[1]]
}
# add polygon ID by intersection
res[[polygon.id]] <- x[[polygon.id]][[1]][apply(terra::relate(res, x, "intersects"), MARGIN = 1, which)]
return(res)
}
#' @title Sample a Single Polygon at a Target Density
#'
#' @description
#' Sample a single `SpatVector` polygon at a target density in "samples / acre".
#'
#'
#' @param p `SpatVector` containing a single polygon feature
#' @param n.pts.per.ac numeric, sampling density in "points per acre"
#' @param min.samples integer, enforced minimum number of samples per polygon
#' @param sampling.type sampling.type character, sampling type passed to [terra::spatSample()]
#' @param p4s output from [terra::crs()]
#'
#' @return `SpatVector` of sample points
#' @keywords manip
#' @author D.E. Beaudette
#'
#' @export
sample.by.poly <- function(p,
n.pts.per.ac = 1,
min.samples = 5,
sampling.type = 'regular',
p4s = NULL
) {
if (!requireNamespace("terra")) {
stop('please install the `terra` package', call. = FALSE)
}
# attempt conversion to terra object
if (!inherits(p, 'SpatVector')) {
p <- terra::vect(p)
}
# convert _projected_ units to acres
ac.i <- terra::expanse(p) * 2.47e-4
# determine number of points based on requested density
n.samples <- round(ac.i * n.pts.per.ac)
# if polygon is too small for given density, request the minimum number of samples
if (n.samples < min.samples) {
n.samples <- min.samples
}
if (n.samples > 0) {
# trap errors caused by bad geometry
s.i <- try(terra::spatSample(p, size = n.samples, method = sampling.type), silent = TRUE)
# errors: return NULL
# invalid geometry could be the cause
if (inherits(s.i, 'try-error')) {
# print error for debugging
message(paste0('sample.by.poly: ', attr(s.i, 'condition')), call. = FALSE)
# can't do much else, move on to the next feature
return(NULL)
}
# assign original CRS
if (!is.null(p4s) & !is.null(s.i))
terra::crs(s.i) <- p4s
return(s.i)
} else return(p[0,])
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.