Identify the pixels and coordinates that are at a (set of) buffer distance(s)
of the objects passed into `coords`

.
This is similar to `sf::st_buffer`

but much faster and without the georeferencing information.
In other words, it can be used for similar problems, but where speed is important.
This code is substantially adapted from `PlotRegionHighlighter::createCircle`

.

```
cir(
landscape,
coords,
loci,
maxRadius = ncol(landscape)/4,
minRadius = maxRadius,
allowOverlap = TRUE,
allowDuplicates = FALSE,
includeBehavior = "includePixels",
returnDistances = FALSE,
angles = NA_real_,
returnAngles = FALSE,
returnIndices = TRUE,
closest = FALSE,
simplify = TRUE
)
```

- landscape
Raster on which the circles are built.

- coords
Either a matrix with 2 (or 3) columns,

`x`

and`y`

(and`id`

), representing the coordinates (and an associated id, like cell index), or a`SpatialPoints*`

object around which to make circles. Must be same coordinate system as the`landscape`

argument. Default is missing, meaning it uses the default to`loci`

.- loci
Numeric. An alternative to

`coords`

. These are the indices on`landscape`

to initiate this function (see`coords`

). Default is one point in centre of`landscape`

.- maxRadius
Numeric vector of length 1 or same length as

`coords`

- minRadius
Numeric vector of length 1 or same length as

`coords`

. Default is`maxRadius`

, meaning return all cells that are touched by the narrow ring at that exact radius. If smaller than`maxRadius`

, then this will create a buffer or doughnut or ring.- allowOverlap
Logical. Should duplicates across id be removed or kept. Default TRUE.

- allowDuplicates
Logical. Should duplicates within id be removed or kept. Default FALSE. This is useful if the actual x, y coordinates are desired, rather than the cell indices. This will increase the size of the returned object.

- includeBehavior
Character string. Currently accepts only

`"includePixels"`

, the default, and`"excludePixels"`

. See details.- returnDistances
Logical. If

`TRUE`

, then a column will be added to the returned data.table that reports the distance from`coords`

to every point that was in the circle/doughnut surrounding`coords`

. Default`FALSE`

, which is faster.- angles
Numeric. Optional vector of angles, in radians, to use. This will create "spokes" outward from

`coords.`

Default is`NA`

, meaning, use internally derived angles that will "fill" the circle.- returnAngles
Logical. If

`TRUE`

, then a column will be added to the returned data.table that reports the angle from`coords`

to every point that was in the circle/doughnut surrounding`coords`

. Default`FALSE.`

- returnIndices
Logical or numeric. If

`1`

or`TRUE`

, will return a`data.table`

with indices and values of successful spread events. If`2`

, it will simply return a vector of pixel indices of all cells that were touched. This will be the fastest option. If`FALSE`

, then it will return a raster with values. See Details.- closest
Logical. When determining non-overlapping circles, should the function give preference to the closest

`loci`

or the first one (much faster). Default is`FALSE`

, meaning the faster, though maybe not desired behaviour.- simplify
logical. If

`TRUE`

, then all duplicate pixels are removed. This means that some`x`

,`y`

combinations will disappear.

A `matrix`

with 4 columns, `id`

, `indices`

,
`x`

, `y`

. The `x`

and `y`

indicate the exact coordinates of
the `indices`

(i.e., cell number) of the `landscape`

associated with the ring or circle being identified by this function.

This function identifies all the pixels as defined by a donut
with inner radius `minRadius`

and outer radius of `maxRadius`

.
The `includeBehavior`

defines whether the cells that intersect the radii
but whose centres are not inside the donut are included `includePixels`

or not `excludePixels`

in the returned pixels identified.
If this is `excludePixels`

, and if a `minRadius`

and
`maxRadius`

are equal, this will return no pixels.

`rings()`

which uses `spread`

internally.
`cir`

tends to be faster when there are few starting points, `rings`

tends to be faster
when there are many starting points. `cir`

scales with `maxRadius^2`

and `coords`

.
Another difference between the two functions is that `rings`

takes the centre of the pixel
as the centre of a circle, whereas `cir`

takes the exact coordinates.
See example. For the specific case of creating distance surfaces from specific
points, see `distanceFromEachPoint()`

, which is often faster.
For the more general GIS buffering, see `sf::st_buffer`

.

```
library(data.table)
#>
#> Attaching package: ‘data.table’
#> The following object is masked from ‘package:terra’:
#>
#> shift
library(terra)
origDTThreads <- data.table::setDTthreads(2L)
origNcpus <- options(Ncpus = 2L)
set.seed(1462)
# circle centred
ras <- rast(ext(0, 15, 0, 15), res = 1, val = 0)
middleCircle <- cir(ras)
ras[middleCircle[, "indices"]] <- 1
circlePoints <- vect(middleCircle[, c("x", "y")])
if (interactive()) {
# clearPlot()
terra::plot(ras)
terra::plot(circlePoints, add = TRUE)
}
# circles non centred
ras <- randomPolygons(ras, numTypes = 4)
n <- 2
agent <- vect(cbind(x = stats::runif(n, xmin(ras), xmax(ras)),
y = stats::runif(n, xmin(ras), xmax(ras))))
cirs <- cir(ras, agent, maxRadius = 15, simplify = TRUE) ## TODO: empty with some seeds! e.g. 1642
cirsSP <- vect(cirs[, c("x", "y")]) ## TODO: error with some seeds! e.g. 1642
cirsRas <- rast(ras)
cirsRas[] <- 0
cirsRas[cirs[, "indices"]] <- 1
if (interactive()) {
terra::plot(ras)
terra::plot(cirsRas, add = TRUE, col = c("transparent", "#00000055"))
terra::plot(agent, add = TRUE)
terra::plot(cirsSP, add = TRUE)
}
# Example comparing rings and cir
hab <- rast(system.file("extdata", "hab1.tif", package = "SpaDES.tools"))
radius <- 4
n <- 2
coords <- vect(cbind(x = stats::runif(n, xmin(hab), xmax(hab)),
y = stats::runif(n, xmin(hab), xmax(hab))))
# cirs
cirs <- cir(hab, coords, maxRadius = rep(radius, length(coords)), simplify = TRUE)
ras1 <- rast(hab)
ras1[] <- 0
ras1[cirs[, "indices"]] <- cirs[, "id"]
if (interactive()) {
terra::plot(ras1)
}
# rings
loci <- cellFromXY(hab, crds(coords))
cirs2 <- rings(hab, loci, maxRadius = radius, minRadius = radius - 1, returnIndices = TRUE)
ras2 <- rast(hab)
ras2[] <- 0
ras2[cirs2$indices] <- cirs2$id
if (interactive()) {
terra::plot(c(ras1, ras2))
}
hab <- rast(system.file("extdata", "hab2.tif", package = "SpaDES.tools"))
cirs <- cir(hab, coords, maxRadius = 44, minRadius = 0)
ras1 <- rast(hab)
ras1[] <- 0
cirsOverlap <- data.table::data.table(cirs)[, list(sumIDs = sum(id)), by = indices]
ras1[cirsOverlap$indices] <- cirsOverlap$sumIDs
if (interactive()) {
terra::plot(ras1)
}
# Provide a specific set of angles
ras <- rast(ext(0, 330, 0, 330), res = 1)
ras[] <- 0
n <- 2
coords <- cbind(x = stats::runif(n, xmin(ras), xmax(ras)),
y = stats::runif(n, xmin(ras), xmax(ras)))
circ <- cir(ras, coords, angles = seq(0, 2 * pi, length.out = 21),
maxRadius = 200, minRadius = 0, returnIndices = FALSE,
allowOverlap = TRUE, returnAngles = TRUE)
# clean up
data.table::setDTthreads(origDTThreads)
options(Ncpus = origNcpus)
```