R/distanceFromEachPoint.R
distanceFromEachPoint.Rd
This is a modification of terra::distance()
for the case of many points.
This version can often be faster for a single point because it does not return a RasterLayer
.
This is different than terra::distance()
because it does not take the
minimum distance from the set of points to all cells.
Rather this returns the every pair-wise point distance.
As a result, this can be used for doing inverse distance weightings, seed rain,
cumulative effects of distance-based processes etc.
If memory limitation is an issue, maxDistance
will keep memory use down,
but with the consequences that there will be a maximum distance returned.
This function has the potential to use a lot of memory if there are a lot of
from
and to
points.
distanceFromEachPoint(
from,
to = NULL,
landscape,
angles = NA_real_,
maxDistance = NA_real_,
cumulativeFn = NULL,
distFn = function(dist) 1/(1 + dist),
cl,
...
)
Numeric matrix with 2 or 3 or more columns. They must include x and y,
representing x and y coordinates of "from" cell. If there is a column
named "id", it will be "id" from to
, i.e,. specific pair distances.
All other columns will be included in the return value of the function.
Numeric matrix with 2 or 3 columns (or optionally more, all of which
will be returned),
x and y, representing x and y coordinates of "to" cells, and
optional "id" which will be matched with "id" from from
. Default is all cells.
RasterLayer
. optional. This is only used if to
is NULL, in which case
all cells are considered to
.
Logical. If TRUE
, then the function will return angles in radians,
as well as distances.
Numeric in units of number of cells. The algorithm will build
the whole surface (from from
to to
), but will
remove all distances that are above this distance.
Using this will keep memory use down.
A function that can be used to incrementally accumulate
values in each to
location, as the function iterates
through each from
. See Details.
A function. This can be a function of landscape
,
fromCell
(single integer value of a from pixel),
toCells
(integer vector value of all the to pixel indices),
and dist
.
If cumulativeFn
is supplied, this will be used to convert
the distances to some other set of units that will be accumulated
by the cumulativeFn
. See Details and examples.
A cluster object. Optional. This would generally be created using
parallel::makeCluster()
or equivalent. This is an alternative way, instead
of beginCluster()
, to use parallelism for this function, allowing for
more control over cluster use.
Any additional objects needed for distFn
.
A sorted matrix on id
with same number of rows as to
,
but with one extra column, "dists"
, indicating the distance
between from
and to
.
This function is cluster aware if the raster
package is available.
If there is a cluster running, it will use it.
To start a cluster use raster::beginCluster()
, with N
being
the number of cores to use. See examples in SpaDES.core::experiment
.
If the user requires an id (indicating the from
cell for each to
cell)
to be returned with the function, the user must add an identifier to the
from
matrix, such as "id"
.
Otherwise, the function will only return the coordinates and distances.
distanceFromEachPoint
calls .pointDistance
, which is not intended to be called
directly by the user.
This function has the potential to return a very large object, as it is doing pairwise
distances (and optionally directions) between from and to.
If there are memory limitations because there are many
from
and many to
points, then cumulativeFn
and distFn
can be used.
These two functions together will be used iteratively through the from
points. The
distFn
should be a transformation of distances to be used by the
cumulativeFn
function. For example, if distFn
is 1 / (1+x)
,
the default, and cumulativeFn
is +
, then it will do a sum of
inverse distance weights.
See examples.
rings()
, cir()
, terra::distance()
,
which can all be made to do the same thing, under specific combinations of arguments.
But each has different primary use cases. Each is also faster under different conditions.
For instance, if maxDistance
is relatively small compared to the number of cells
in the landscape
, then cir()
will likely be faster. If a minimum
distance from all cells in the landscape
to any cell in from
, then
distanceFromPoints
will be fastest. This function scales best when there are
many to
points or all cells are used to = NULL
(which is default).
library(terra)
origDTThreads <- data.table::setDTthreads(2L)
origNcpus <- options(Ncpus = 2L)
n <- 2
distRas <- rast(ext(0, 40, 0, 40), res = 1)
coords <- cbind(x = round(runif(n, xmin(distRas), xmax(distRas))) + 0.5,
y = round(runif(n, xmin(distRas), xmax(distRas))) + 0.5)
# inverse distance weights
dists1 <- distanceFromEachPoint(coords, landscape = distRas)
indices <- cellFromXY(distRas, dists1[, c("x", "y")])
invDist <- tapply(dists1[, "dists"], indices, function(x) sum(1 / (1 + x))) # idw function
distRas[] <- as.vector(invDist)
if (interactive()) {
# clearPlot()
terra::plot(distRas)
}
# With iterative summing via cumulativeFn to keep memory use low, with same result
dists1 <- distanceFromEachPoint(coords[, c("x", "y"), drop = FALSE],
landscape = distRas, cumulativeFn = `+`)
idwRaster <- rast(distRas)
idwRaster[] <- dists1[, "dists"]
if (interactive()) terra::plot(idwRaster)
all(idwRaster[] == distRas[]) # TRUE
#> [1] TRUE
# A more complex example of cumulative inverse distance sums, weighted by the value
# of the origin cell
ras <- rast(ext(0, 34, 0, 34), res = 1, val = 0)
rp <- randomPolygons(ras, numTypes = 10) ^ 2
n <- 15
cells <- sample(ncell(ras), n)
coords <- xyFromCell(ras, cells)
distFn <- function(landscape, fromCell, dist) landscape[fromCell] / (1 + dist)
#beginCluster(3) # can do parallel
dists1 <- distanceFromEachPoint(coords[, c("x", "y"), drop = FALSE],
landscape = rp, distFn = distFn, cumulativeFn = `+`)
#endCluster() # if beginCluster was run
idwRaster <- rast(ras)
idwRaster[] <- dists1[, "dists"]
if (interactive()) {
# clearPlot()
terra::plot(rp)
sp1 <- vect(coords)
terra::plot(sp1, add = TRUE)
terra::plot(idwRaster)
terra::plot(sp1, add = TRUE)
}
# On linux; can use numeric passed to cl; will use mclapply with mc.cores = cl
if (identical(Sys.info()["sysname"], "Linux")) {
dists1 <- distanceFromEachPoint(coords[, c("x", "y"), drop = FALSE],
landscape = rp, distFn = distFn,
cumulativeFn = `+`, cl = 2)
}
# clean up
data.table::setDTthreads(origDTThreads)
options(Ncpus = origNcpus)