From b47a294ba5509235cd41ace2dc0c7b8e24b7ae87 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 08:30:41 -0500 Subject: [PATCH 001/125] Update documentation --- NEWS.md | 22 +++++++++++++++++++ R/00c_GRegion_class.r | 2 +- R/03_options.r | 6 ++--- R/bioclims_GRaster.r | 13 +++++------ R/fasterRaster.r | 30 ++++++++++++------------- R/fillHoles.r | 2 +- R/fillNAs.r | 2 +- R/focal.r | 2 +- R/grassInfo.r | 2 +- R/grassStarted.r | 2 +- R/is.lonlat.r | 2 +- R/predict.r | 2 +- R/replace_single_square_bracket.r | 2 +- R/simplifyGeom.r | 13 +++++++---- R/vAttachDatabase.r | 17 +++++++++----- R/vDetachDatabase.r | 2 +- R/xor.r | 2 +- man/aggregate.Rd | 12 ++++------ man/bioclims.Rd | 5 ++--- man/disagg.Rd | 8 +++---- man/dot-addLocationProject.Rd | 20 +++++++++++++++++ man/examples/ex_aggregate_disagg.r | 8 +++---- man/examples/ex_extract.r | 9 ++++---- man/extract.Rd | 14 +++++++----- man/faster.Rd | 8 +++---- man/fasterRaster.Rd | 33 +++++++++++++--------------- man/fillHoles.Rd | 2 +- man/fillNAs.Rd | 2 +- man/focal.Rd | 2 +- man/grassInfo.Rd | 2 +- man/grassStarted.Rd | 2 +- man/is.lonlat.Rd | 2 +- man/predict.Rd | 2 +- man/replace_single_square_bracket.Rd | 2 +- man/vAttachDatabase.Rd | 8 +++---- man/vDetachDatabase.Rd | 2 +- man/xor.Rd | 2 +- 37 files changed, 154 insertions(+), 114 deletions(-) create mode 100644 man/dot-addLocationProject.Rd diff --git a/NEWS.md b/NEWS.md index 0b1aa192..156c71b5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,25 @@ +# fasterRaster 8.4.0.7027 (2024-09-XX) + +### Main task for this version +o Run all examples with **GRASS 8.4**! + +### Potentially code-breaking changes +o `aggregate()` no longer has the `dissolve` argument for `GVector`s (polygons will always be dissolved). + +### Updates for **GRASS 8.4** +o `addLocationProject()` adds either a `project` or `location` argument to a `list` to be passed to `rgrass::execGRASS()`. +o `project()` work with **GRASS** 8.4. +o `.vAttachDatabase()` no longer has the `"o"` flag when calling `v.db.connect` when running **GRASS >=8.4**. + +### Enhanced functionality and new functions +o `extract()` now automatically projects a `GVector` to match the CRS of a `GRaster` from which extraction is being made. + +### Bug fixes +o `categories()` correctly assigns active category column. +o `simplifyGeom()` works for 2-dimensional `GVector`s. +o `rasterize()` works when `by` is not `NULL`. +o `.layerIndex()` (called by `catergories()` and other functions related to catrgorical `GRaster`s) does not fail. + # fasterRaster 8.3.0.7026 (2024-09-22) o Recompile `pkgdown` diff --git a/R/00c_GRegion_class.r b/R/00c_GRegion_class.r index 04675757..80cb8562 100644 --- a/R/00c_GRegion_class.r +++ b/R/00c_GRegion_class.r @@ -1,4 +1,4 @@ -#' @title Classes for "fasterRaster" locations, rasters, and vectors#' +#' @title Classes for "fasterRaster" locations, rasters, and vectors #' #' @aliases GRegion #' @rdname GLocation diff --git a/R/03_options.r b/R/03_options.r index 12cfab58..218c36fa 100644 --- a/R/03_options.r +++ b/R/03_options.r @@ -18,8 +18,8 @@ #' Options include: #' #' * `grassDir` (character): The folder in which **GRASS** is installed on your computer. Typically, this option is set when you run [faster()]. Depending on your operating system, your install directory will look something like this: -#' * Windows: `"C:/Program Files/GRASS GIS 8.3"` -#' * Mac OS: `"/Applications/GRASS-8.3.app/Contents/Resources"` +#' * Windows: `"C:/Program Files/GRASS GIS 8.4"` +#' * Mac OS: `"/Applications/GRASS-8.4.app/Contents/Resources"` #' * Linux: `"/usr/local/grass"` #' #' * `addonsDir` (character): Folder in which **GRASS** addons are stored. If `NA` and `grassDir` is not `NA`, this will be assumed to be `file.path(grassDir, "addons")`. The default values is `NA`. @@ -28,7 +28,7 @@ #' #' * `memory` (integer/numeric): The amount of memory to allocate to a task, in GB, for **GRASS**. The default is 2048 MB (i.e., 2 GB). Some **GRASS** modules can take advantage of more memory. #' -#' * `clean` (logical): If `TRUE` (default), remove temporary files created internally by functions. If not deleted, they can eventually fill up hard drive space, but deleting them takes a little bit of time (usually <1 second for each function). +#' * `clean` (logical): If `TRUE` (default), remove temporary files created internally by functions. If not deleted, they can eventually fill up hard drive space, but deleting them takes a little bit of time (usually <1 second for each function). See also [mow()]. #' #' * `useDataTable` (logical): If `FALSE` (default), functions that return tabular output produce `data.frame`s. If `TRUE`, output will be `data.table`s from the **data.table** package. This can be much faster, but it might require you to know how to use `data.table`s if you want to manipulate them in **R**. You can always convert them to `data.frame`s using [base::as.data.frame()]. #' diff --git a/R/bioclims_GRaster.r b/R/bioclims_GRaster.r index 26013340..7b866ac7 100644 --- a/R/bioclims_GRaster.r +++ b/R/bioclims_GRaster.r @@ -2,6 +2,7 @@ #' #' @description The BIOCLIM set of bioclimatic variables were created for modeling species' geographic distributions (Booth et al. 2014). This function can create the "standard" 19 set of variables, plus several more from an "extended" set. #' +#' "Classic" set of BIOCLIM variables (Booth et al. 2014): #' The units reported below assume that input rasters are in mm (precipitation) and deg C (temperature), and that each raster represents a month (but other time units are allowed, with corresponding changes to the temporal units assumed below). #' #' * BIO1: Mean annual temperature, calculated using monthly means (deg C) @@ -29,22 +30,22 @@ #' * BIO41: Temperature of the quarter following the coldest quarter (based on mean temperature; deg C) #' * BIO42: Temperature of the quarter following the warmest quarter (based on mean temperature; deg C) #' * BIO43: Precipitation of the quarter following the coldest quarter (based on mean temperature; mm) -#' * BIO44: Precipitation of the quarter following the warmest quarter (based on mean temperature; mm) +#' * BIO44: Precipitation of the quarter following the warmest quarter (based on mean temperature; mm) #' #' * BIO45: Temperature of the quarter following the driest quarter (based on mean temperature; deg C) #' * BIO46: Temperature of the quarter following the wettest quarter (based on mean temperature; deg C) #' * BIO47: Precipitation of the quarter following the driest quarter (based on mean temperature; mm) -#' * BIO48: Precipitation of the quarter following the wettest quarter (based on mean temperature; mm) +#' * BIO48: Precipitation of the quarter following the wettest quarter (based on mean temperature; mm) #' #' * BIO49: Hottest month (based on maximum temperature) #' * BIO50: Coldest month (based on minimum temperature) #' * BIO51: Wettest month -#' * BIO52: Driest month +#' * BIO52: Driest month #' #' * BIO53: First month of the warmest quarter (based on mean temperature) #' * BIO54: First month of the coldest quarter (based on mean temperature) #' * BIO55: First month of the wettest quarter -#' * BIO56: First month of the driest quarter +#' * BIO56: First month of the driest quarter #' #' * BIO57: The greatest decrease in temperature from one month to the next (deg C; always >= 0) #' * BIO58: The greatest increase in temperature from one month to the next (deg C; always >= 0) @@ -59,8 +60,6 @@ #' #' BIOCLIMs 49 through 60 are not bioclimatic variables per se, but useful for assessing the properties of the variables that are defined based on the "-est" month or quarter. #' -#' The numbering of the new BIOCLIMs was begun at 41 because BIOCLIMs 20 through 40 are taken (Kriticos et al. 2014). -#' #' @param ppt A multi-layered `GRaster` or `SpatRaster`, representing monthly/weekly/daily precipitation. #' #' @param tmin,tmax A multi-layered `GRaster` or `SpatRaster`, representing monthly/weekly/daily minimum and maximum temperature. @@ -72,7 +71,7 @@ #' * `NULL` (default): Calculate BIOCLIMs 1 through 19 #' * `"*"`: Calculate all BIOCLIMs this function can calculate. #' * `"+"`: Calculate BIOCLIMs 41 onward. -#' * Any combination of the above (e.g., `c(1, 12, "+")`). +#' * Any combination of the above except `NULL` (e.g., `c(1, 12, "+")`). #' #' @param sample Logical: If `TRUE` (default), BIO4 and 15 are calculated with the sample standard deviation. If `FALSE`, then the population standard deviation is used. #' diff --git a/R/fasterRaster.r b/R/fasterRaster.r index 59b593d1..ef17eea7 100644 --- a/R/fasterRaster.r +++ b/R/fasterRaster.r @@ -134,7 +134,7 @@ #' * [wetness()]: Topographic wetness index #' #' ## Functions operating on categorical (factor) rasters -#' \code{\link[fasterRaster]{%in%}}, and \code{\link[fasterRaster]{%notin%}}: Mask cells that match or do not match a given category +#' * \code{\link[fasterRaster]{%in%}}, and \code{\link[fasterRaster]{%notin%}}: Mask cells that match or do not match a given category #' * [activeCat()] and [activeCats()]: Column(s) that defines category labels #' \code{\link[fasterRaster]{activeCat<-}}: Set column that defines category labels #' * [addCats()]: Add new columns to a "levels" table @@ -148,8 +148,8 @@ #' * [droplevels()]: Remove one or more levels #' * [freq()]: Frequency of each category across cells of a raster #' * [is.factor()]: Is a raster categorical? -#' * [levels()]: "Levels" table of a categorical raster -#' \code{\link[fasterRaster]{levels<-}}: Set "levels" table of a categorical raster +#' * [levels()]: "Levels" table of a categorical raster +#' * \code{\link[fasterRaster]{levels<-}}: Set "levels" table of a categorical raster #' * [match()], \code{\link[fasterRaster]{%in%}}, and \code{\link[fasterRaster]{%notin%}}: Find which cells of a `GRaster` match or do not match certain category labels #' * [minmax()]: "Lowest" and "highest" category values of categorical rasters (when argument `levels = TRUE`) #' * [missing.cases()]: Find rows of a categorical `GRaster`'s "levels" table that have at least one `NA` in them @@ -163,7 +163,7 @@ #' * [plotRGB()]: Display a multispectral `GRaster` using red, blue, green, and alpha channels #' * [vegIndex()]: Vegetation indices from surface reflectance #' -#' ### Functions that operate on **terra** `SpatRaster`s +#' ## Functions that operate on **terra** `SpatRaster`s #' * [bioclims()]: BIOCLIM rasters (classic set and extended set) #' * [fragmentation()]: Landscape fragmentation class from Riitters et al. (2020) #' @@ -187,10 +187,10 @@ #' * [zext()]: Vertical extent #' #' ## Functions that operate on or create `GVector`s -#' The \code{\link[fasterRaster]{[}} operator can be used to subset geometries of a `GVector`. -#' The [$] and \code{\link[fasterRaster]{[[}} operators can be used to get columns of a `GVector`'s data table. -#' The \code{\link[fasterRaster]{$<-}} operator can be used to replace specific columns of a `GVector`'s data table or to add columns. -#' \code{\link[fasterRaster]{addTable<-}}: Add a data table to a `GVector` +#' * The \code{\link[fasterRaster]{[}} operator can be used to subset geometries of a `GVector`. +#' * The [$] and \code{\link[fasterRaster]{[[}} operators can be used to get columns of a `GVector`'s data table. +#' * The \code{\link[fasterRaster]{$<-}} operator can be used to replace specific columns of a `GVector`'s data table or to add columns. +#' * \code{\link[fasterRaster]{addTable<-}}: Add a data table to a `GVector` #' * [aggregate()]: Combine `GVector` geometries #' * [as.data.frame()]: Convert a `GVector`'s attribute table to a `data.frame` #' * [as.data.table()]: Convert a `GVector`'s attribute table to a `data.table` @@ -229,10 +229,10 @@ #' * [tail()]: Last rows of a `GVector`'s data table #' * [thinPoints()]: Reduce number of points in same raster cell #' * [union()] or `+`: Combine two `GVector`s -#' * [xor()] or `/``: Select parts of polygons not shared by two `GVector`s +#' * [xor()] or `/`: Select parts of polygons not shared by two `GVector`s #' #' ## Functions for fixing issues with `GVector`s -#' (See also tools that can be used during `GVector` creation/loading in [fast()].) +#' (See also *Details* [fast()].) #' * [breakPolys()]: Break topologically clean areas #' * [fillHoles()]: Fill "holes" of a `GVector` #' * [fixBridges()]: Change "bridges" to "islands" @@ -256,7 +256,7 @@ #' * [as.int()]: Convert a `GRaster` to an integer raster (**GRASS** data type `CELL`) #' * [as.points()], [as.lines()], and [as.polygons()]: Convert a `GRaster` to a `GVector` #' * [fast()]: Convert a `SpatRaster` to a `GRaster`; a `SpatVector`, `sf` vector, numeric vector, `matrix`, `data.frame`, or `data.table` to a `GVector`; or load a vector or raster from a file -#' * [categories()] and [levels<-]: Convert an integer raster to a categorical ("factor") raster. +#' * [categories()] and \code{\link[fasterRaster]{levels<-}}: Convert an integer raster to a categorical ("factor") raster. #' * [rast()]: Convert a `GRaster` to a `SpatRaster` #' * [rasterize()]: Convert a `GVector` to a `GRaster` #' * [st_as_sf()]: Convert a `GVector` to a `sf` vector @@ -287,10 +287,10 @@ #' * [vegIndices][vegIndices]: Vegetation indices that can be calculated using [vegIndex()]. #' #' ## Esoteric tutorials and arcane notes -#' Comparisons between `GRegion`s can be performed using the `==` and `!=` operators. -#' Vignette on **GRASS** "projects/locations" and "mapsets": `vignette("projects_mapsets", package = "fasterRaster"`) -#' Vignette on **GRASS** "regions": `vignette("regions", package = "fasterRaster")` -#' Vignette on **fasteRaster** hidden functions: `vignette("hidden_functions", package = "fasterRaster")` +#' * Comparisons between `GRegion`s can be performed using the `==` and `!=` operators. +#' * Vignette on **GRASS** "projects/locations" and "mapsets": `vignette("projects_mapsets", package = "fasterRaster")` +#' * Vignette on **GRASS** "regions": `vignette("regions", package = "fasterRaster")` +#' * Vignette on **fasteRaster** hidden functions: `vignette("hidden_functions", package = "fasterRaster")` #' * [grassStarted()]: Has a connection **GRASS** been made within the current **R** session? #' #' @author Adam B. Smith diff --git a/R/fillHoles.r b/R/fillHoles.r index 293326ab..0029fd16 100644 --- a/R/fillHoles.r +++ b/R/fillHoles.r @@ -4,7 +4,7 @@ #' #' @param x A `GVector`. #' -#' @param fail Logical: If `TRUE` (default), and **GRASS 8.4** or higher is not installed, cause an error. If `FALSE`, a warning will be displayed and a `NULL` value will be returned. This function requires **GRASS 8.4** or higher to be installed. +#' @param fail Logical: If `TRUE` (default), and **GRASS 8.3** or higher is not installed, cause an error. If `FALSE`, a warning will be displayed and a `NULL` value will be returned. This function requires **GRASS 8.3** or higher to be installed. #' #' @returns A `GVector`. #' diff --git a/R/fillNAs.r b/R/fillNAs.r index 564d988a..78487590 100644 --- a/R/fillNAs.r +++ b/R/fillNAs.r @@ -8,7 +8,7 @@ #' #' @param method Character: Type of spline, either "`bilinear`" (default), "`bicubic`", or "`RST`" (regularized splines with tension). Partial matching is used and case is ignored. #' -#' **Note**: The RST method will often display warnings, but thesecan be ignored. +#' **Note**: The RST method will often display warnings, but these can be ignored. #' #' @param min,max Numeric: Lowest and highest values allowed in the interpolated values. Values outside these bounds will be truncated to the minimum/maximum value(s) allowed. The default imposes no constraints. For multi-layered rasters, you can supply a single value for `min` and/or `max`, or multiple values (one per layer). Values will be recycled if there are fewer than one or them per layer in the raster. #' diff --git a/R/focal.r b/R/focal.r index 8fb17dce..6cde292f 100644 --- a/R/focal.r +++ b/R/focal.r @@ -13,7 +13,7 @@ #' * "`mean`" (default) #' * "`median`" #' * "`mode`" -#' * "`min`" or "`max`": Minumum or maximum. Should not use a weights matrix. +#' * "`min`" or "`max`": Minimum or maximum. Should not use a weights matrix. #' * "`range`": Difference between the maximum and minimum. Should not use a weights matrix. #' * "`sd`": Sample standard deviation. NB: This is the same as the [stats::sd()] function. #' * "`sdpop`": Population standard deviation. NB: This is the same as the function "stddev" in the **GRASS** module `r.neighbors`. diff --git a/R/grassInfo.r b/R/grassInfo.r index ebad2b3a..144f0953 100644 --- a/R/grassInfo.r +++ b/R/grassInfo.r @@ -6,7 +6,7 @@ #' * `"citation"` (default) #' * `"copyright"`: Copyright information #' * `"version"`: Version number and release year -#' * `"versionNumber"`: Version number as numeric, major and minor only (e.g., 8.3) +#' * `"versionNumber"`: Version number as numeric, major and minor only (e.g., 8.4) #' #' Partial matching is used and case is ignored. #' diff --git a/R/grassStarted.r b/R/grassStarted.r index a3e78f4b..d05d7b37 100644 --- a/R/grassStarted.r +++ b/R/grassStarted.r @@ -1,6 +1,6 @@ #' Has "GRASS" been started or not? #' -#' @description Returns `TRUE` or `FALSE`, depending on whether a **GRASS** connection has been made or not within the current **R** session. Usually used only by developers. +#' @description Returns `TRUE` or `FALSE`, depending on whether a **GRASS** connection has been made or not within the current **R** session. Usually used only by developers. **GRASS** is started the first time [fast()] is used. #' #' @returns Logical. #' diff --git a/R/is.lonlat.r b/R/is.lonlat.r index ba3f98e1..8fa0b96b 100644 --- a/R/is.lonlat.r +++ b/R/is.lonlat.r @@ -1,6 +1,6 @@ #' Test if a coordinate reference system is unprojected #' -#' @description `is.lonlat()` attempst to determine if a coordinate reference system is unprojected (e.g., WGS84, NAD83, NAD27, etc.). For `GRaster`s and `GVector`s, the function should always be correct. For WKT character strings and `sf` vectors, it does this by looking for the "CONVERSION[" tag in the WKT string (or the object's WKT string), and if it finds one, returns `FALSE`. This may not be truthful in all cases. +#' @description `is.lonlat()` attempts to determine if a coordinate reference system is unprojected (e.g., WGS84, NAD83, NAD27, etc.). For `GRaster`s and `GVector`s, the function should always be correct. For WKT character strings and `sf` vectors, it does this by looking for the "CONVERSION[" tag in the WKT string (or the object's WKT string), and if it finds one, returns `FALSE`. This may not be truthful in all cases. #' #' @param x A WKT coordinate reference string or an object from which on can be obtained (e.g., a `GRaster`, `GVector`, `GRegion`, `GLocation`, `SpatRaster`, `SpatVector`, or `sf` object). #' diff --git a/R/predict.r b/R/predict.r index 07b9f93a..6726eba1 100644 --- a/R/predict.r +++ b/R/predict.r @@ -6,7 +6,7 @@ #' #' This `predict()` function can handle: #' * Linear predictors and intercepts like `y ~ 1 + x`; -#' * Quadratic terms like `y ~ x^2` (or, in **R** formular notation, `y ~ I(x^2)`); +#' * Quadratic terms like `y ~ x^2` (or, in **R** formula notation, `y ~ I(x^2)`); #' * Two-way interaction terms between scalars like `y ~ x1:x2` and `y ~ x1 * x2`; #' * Categorical predictors (i.e., categorical `GRaster`s; see `vignette("GRasters", package = "fasterRaster")`)); #' * Two-way interactions between a categorical predictor and a scalar predictor; and diff --git a/R/replace_single_square_bracket.r b/R/replace_single_square_bracket.r index 4faac3a3..0e1b04c8 100644 --- a/R/replace_single_square_bracket.r +++ b/R/replace_single_square_bracket.r @@ -1,6 +1,6 @@ #' Replace values of a GRaster #' -#' @description The `[<-` operator can be used to replace all of the values of a `GRaster`, or specific values depending on the expression in `i`. For example, you could use `rast[] <- 10` to assign 10 to all cells, or `rast[rast > 0] <- 10` to assign all cells with values >0 to 10. +#' @description The `[<-` operator can be used to replace all of the values of a `GRaster`, or specific values depending on the expression in `i`. For example, you could use `rast[] <- 10` to assign 10 to all cells, or `rast[rast > 0] <- 10` to assign all cells with values >0 to 10. You can also use one raster to set values in another, as in `rast1[rast2 > 0] <- 10`. #' #' @param x A `GRaster`. #' @param i Either missing or a conditional statement that resolves to a `GRaster`. diff --git a/R/simplifyGeom.r b/R/simplifyGeom.r index 55b0077f..9a6c062e 100644 --- a/R/simplifyGeom.r +++ b/R/simplifyGeom.r @@ -31,13 +31,18 @@ methods::setMethod( # automatic distance if (is.null(tolerance)) { - extent <- ext(x, vector=TRUE) + extent <- ext(x, vector = TRUE) xext <- extent[2L] - extent[1L] yext <- extent[4L] - extent[3L] - zext <- diff(zext) - tolerance <- 0.02 * min(xext, yext, zext, na.rm=TRUE) + if (is.3d(x)) { + zext <- zext(x) + zext <- diff(zext) + } else { + zext <- NULL + } + tolerance <- 0.02 * min(xext, yext, zext, na.rm = TRUE) } else { - if (tolerance < 0) stop("Argument ", sQuote("tolerance"), " must be > 0.") + if (tolerance < 0) stop("Argument `tolerance` must be > 0.") } diff --git a/R/vAttachDatabase.r b/R/vAttachDatabase.r index 04b0b855..d37af060 100644 --- a/R/vAttachDatabase.r +++ b/R/vAttachDatabase.r @@ -1,13 +1,13 @@ #' Add a database table to a GRASS attribute table #' -#' @description `.vAttachDatabase()` adds a table to a **GRASS** vector. This table is meant to be "invisible" to most users--they should use interact with attribute tables using the `GVector` slot `@table``. Some functions require tables (e.g., [extract()] and [spatSample()]). **This function is mostly of use to developers.** +#' @description `.vAttachDatabase()` adds a table to a **GRASS** vector. This table is meant to be "invisible" to most users--they should use interact with attribute tables using the `GVector` slot `@table`. Some functions require tables (e.g., [extract()] and [spatSample()]). **This function is mostly of use to developers.** #' #' @param x A `GVector` or the name of a vector in **GRASS**. #' #' @param table Either `NULL` (default), or a `data.frame` or `data.table`, or a numeric or integer vector: -#' * If `NULL`, then a bare minimal table will be created with a column named `frid`, holding sequential integer values. -#' * If a `data.frame` or `data.table` and no column is named `frid`, one will be created with sequential integer values. If the table does have a column named `frid`, then it should have integer (not just numeric) values. -#' * If a `vector`, then these are coerced to type `integer` and used to define the `frid` column. +#' * If `NULL`, then a bare minimal table will be created with a column named `cat`, holding sequential integer values. +#' * If a `data.frame` or `data.table` and no column is named `cat`, one will be created with sequential integer values. If the table does have a column named `cat`, then it should have integer (not just numeric) values. +#' * If a `vector`, then these are coerced to type `integer` and used to define the `cat` column. #' #' There should be one row/value per geometry in `x`. #' @@ -107,16 +107,21 @@ } # connect database to vector - rgrass::execGRASS( + args <- list( cmd = "v.db.connect", map = sources(x), table = srcTable, layer = "1", # key = "frid", key = "cat_", # adds an underscore, for some reason - flags = c(.quiet(), "overwrite", "o") + # flags = c(.quiet(), "overwrite", "o") + flags = c(.quiet(), "overwrite") ) + if (grassInfo("versionNumber") <= 8.3) args$flags <- c(arges$flags, "o") + + do.call(rgrass::execGRASS, args = args) + # args <- list( # cmd = "v.db.addtable", # map = src, diff --git a/R/vDetachDatabase.r b/R/vDetachDatabase.r index f1924aff..fdeda1ef 100644 --- a/R/vDetachDatabase.r +++ b/R/vDetachDatabase.r @@ -1,6 +1,6 @@ #' Add a database table to a GRASS attribute table #' -#' @description `.vDetachDatabase()` detaches the database from a **GRASS** vector and deletes it. This table is meant to be "invisible" to most users--they should use interact with attribute tables using the `GVector` slot `@table``. Some functions do require tables (e.g., [extract()] and [spatSample()]). **This function is mostly of use to developers.** +#' @description `.vDetachDatabase()` detaches the database from a **GRASS** vector and deletes it. This table is meant to be "invisible" to most users--they should use interact with attribute tables using the `GVector` slot `@table`. Some functions do require tables (e.g., [extract()] and [spatSample()]). **This function is mostly of use to developers.** #' #' @param x A `GVector` or the name of a vector in **GRASS**. #' diff --git a/R/xor.r b/R/xor.r index b8d8a2ab..390304a3 100644 --- a/R/xor.r +++ b/R/xor.r @@ -1,6 +1,6 @@ #' Select parts of polygons not shared between two GVectors #' -#' @description The `xor()` function selects the area that does not overlap between two "polygon" `GVector`s. You can also use the `/` operator, as in `vect 1 / vect2`. +#' @description The `xor()` function selects the area that does not overlap between two "polygon" `GVector`s. You can also use the `/` operator, as in `vect1 / vect2`. #' #' @param x,y `GVector`s. #' diff --git a/man/aggregate.Rd b/man/aggregate.Rd index 8feb72d2..6e59b423 100644 --- a/man/aggregate.Rd +++ b/man/aggregate.Rd @@ -15,7 +15,7 @@ na.rm = FALSE ) -\S4method{aggregate}{GVector}(x, dissolve = TRUE) +\S4method{aggregate}{GVector}(x) } \arguments{ \item{x}{A \code{GRaster} or \code{GVector}.} @@ -45,8 +45,6 @@ Note that unlike \code{terra::aggregate()} and \code{\link[terra:disaggregate]{t \item{prob}{Numeric (rasters only): Quantile at which to calculate \code{quantile}.} \item{na.rm}{Logical (rasters only): If \code{FALSE} (default), propagate \code{NA} cells or \code{NA} values.} - -\item{dissolve}{Logical (vectors only): If \code{TRUE} (default), then aggregated geometries will have their borders dissolved. This is ignored if the input \code{GVector} is not a "polygons" vector.} } \value{ A \code{GRaster} or \code{GVector}. @@ -77,15 +75,13 @@ elev <- fast(madElev) agg2 <- aggregate(elev, 2, "mean") agg2 -# terra -agg2terra <- aggregate(madElev, 2, "mean") -agg2terra - # Compare rasters aggregated by fasterRaster and terra. # These should be the same. +agg2terra <- aggregate(madElev, 2) + agg2 <- rast(agg2) agg2 <- extend(agg2, agg2terra) -agg2 - agg2terra +agg2 - agg2terra # value is ~0 ### Aggregate GRaster by a non-integer factor in 2 dimensions # fasterRaster diff --git a/man/bioclims.Rd b/man/bioclims.Rd index 9e54bec3..a98e1fec 100644 --- a/man/bioclims.Rd +++ b/man/bioclims.Rd @@ -43,7 +43,7 @@ \item \code{NULL} (default): Calculate BIOCLIMs 1 through 19 \item \code{"*"}: Calculate all BIOCLIMs this function can calculate. \item \code{"+"}: Calculate BIOCLIMs 41 onward. -\item Any combination of the above (e.g., \code{c(1, 12, "+")}). +\item Any combination of the above except \code{NULL} (e.g., \code{c(1, 12, "+")}). }} \item{sample}{Logical: If \code{TRUE} (default), BIO4 and 15 are calculated with the sample standard deviation. If \code{FALSE}, then the population standard deviation is used.} @@ -60,6 +60,7 @@ A \code{GRaster} with one or more layers. \description{ The BIOCLIM set of bioclimatic variables were created for modeling species' geographic distributions (Booth et al. 2014). This function can create the "standard" 19 set of variables, plus several more from an "extended" set. +"Classic" set of BIOCLIM variables (Booth et al. 2014): The units reported below assume that input rasters are in mm (precipitation) and deg C (temperature), and that each raster represents a month (but other time units are allowed, with corresponding changes to the temporal units assumed below). \itemize{ \item BIO1: Mean annual temperature, calculated using monthly means (deg C) @@ -114,8 +115,6 @@ The variables are defined assuming that the input rasters represent monthly valu BIOCLIMs 41 through 44 are added here to capture the "shoulder" seasons (spring and autumn) important in temperature regions. BIOCLIMs 45 through 48 are also included for consistency. BIOCLIMs 49 through 60 are not bioclimatic variables per se, but useful for assessing the properties of the variables that are defined based on the "-est" month or quarter. - -The numbering of the new BIOCLIMs was begun at 41 because BIOCLIMs 20 through 40 are taken (Kriticos et al. 2014). } \examples{ if (grassStarted()) { diff --git a/man/disagg.Rd b/man/disagg.Rd index ddfe8beb..bb066bde 100644 --- a/man/disagg.Rd +++ b/man/disagg.Rd @@ -39,15 +39,13 @@ elev <- fast(madElev) agg2 <- aggregate(elev, 2, "mean") agg2 -# terra -agg2terra <- aggregate(madElev, 2, "mean") -agg2terra - # Compare rasters aggregated by fasterRaster and terra. # These should be the same. +agg2terra <- aggregate(madElev, 2) + agg2 <- rast(agg2) agg2 <- extend(agg2, agg2terra) -agg2 - agg2terra +agg2 - agg2terra # value is ~0 ### Aggregate GRaster by a non-integer factor in 2 dimensions # fasterRaster diff --git a/man/dot-addLocationProject.Rd b/man/dot-addLocationProject.Rd new file mode 100644 index 00000000..7fad2810 --- /dev/null +++ b/man/dot-addLocationProject.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/addLocationProject.r +\name{.addLocationProject} +\alias{.addLocationProject} +\title{Add a "project" or "location" argument to an "args" list} +\usage{ +.addLocationProject(args, locProj) +} +\arguments{ +\item{args}{A \code{list} of arguments to be passed to a \strong{GRASS} module.} + +\item{locProj}{Character: The name of the location or project.} +} +\value{ +A \code{list}. +} +\description{ +Unhelpfully, starting with \strong{GRASS} 8.4, what were previously called "locations" and now called "projects." These were supposed to be back-compatible, but are not. This function takes a list of arguments that will be passed to \code{\link[rgrass:execGRASS]{rgrass::execGRASS()}} and adds either a "location" or "project" argument, depending on the version of \strong{GRASS} being used. +} +\keyword{external} diff --git a/man/examples/ex_aggregate_disagg.r b/man/examples/ex_aggregate_disagg.r index 45243641..ec458dc5 100644 --- a/man/examples/ex_aggregate_disagg.r +++ b/man/examples/ex_aggregate_disagg.r @@ -18,15 +18,13 @@ elev <- fast(madElev) agg2 <- aggregate(elev, 2, "mean") agg2 -# terra -agg2terra <- aggregate(madElev, 2, "mean") -agg2terra - # Compare rasters aggregated by fasterRaster and terra. # These should be the same. +agg2terra <- aggregate(madElev, 2) + agg2 <- rast(agg2) agg2 <- extend(agg2, agg2terra) -agg2 - agg2terra +agg2 - agg2terra # value is ~0 ### Aggregate GRaster by a non-integer factor in 2 dimensions # fasterRaster diff --git a/man/examples/ex_extract.r b/man/examples/ex_extract.r index 41dcec4e..657c5444 100644 --- a/man/examples/ex_extract.r +++ b/man/examples/ex_extract.r @@ -22,9 +22,8 @@ coast <- fast(madCoast4) # polygons vector extract(elev, dypsis, xy = TRUE) # Extract from categorical raster at points: -dypsisWGS84 <- project(dypsis, cover) # Convert to same CRS -categories <- extract(cover, dypsisWGS84) -categoryValues <- extract(cover, dypsisWGS84, cats = FALSE) +categories <- extract(cover, dypsis) +categoryValues <- extract(cover, dypsis, cats = FALSE) categories categoryValues @@ -35,7 +34,7 @@ extract(elev, coast, fun = c("sum", "mean", "countNonNA"), overlap = FALSE) extract(elev, rivers, fun = c("sum", "mean", "countNonNA"), overlap = FALSE) # Extract from a polygons vector at a points vector: -table <- extract(coast, dypsis, xy = TRUE) -head(table) # first 3 are outside polygons vector, next 3 are inside +polysFromPoints <- extract(coast, dypsis, xy = TRUE) +head(polysFromPoints) # first 3 are outside polygons vector, next 3 are inside } diff --git a/man/extract.Rd b/man/extract.Rd index 238dada2..08517e70 100644 --- a/man/extract.Rd +++ b/man/extract.Rd @@ -22,7 +22,8 @@ overlap = TRUE, xy = FALSE, cats = TRUE, - verbose = FALSE + verbose = FALSE, + ... ) \S4method{extract}{GRaster,data.frame}(x, y, xy = FALSE, cats = TRUE) @@ -76,6 +77,8 @@ \item{cats}{Logical (extracting from a raster): If \code{TRUE} (default) and \code{x} is a categorical \code{GRaster}, then return the category labels instead of the values.} \item{verbose}{Logical: If \code{TRUE}, display progress (will only function when extracting from points on a \code{GRaster} when the number of \code{GRaster}s is large).} + +\item{...}{Arguments to pass to \code{\link[=project]{project()}}. This is used only if extracting from a \code{GRaster} at locations specified by a \code{GVector}, and they have a different coordinate reference system. In this case, users should specify the \code{wrap} argument to \code{\link[=project]{project()}}.} } \value{ A \code{data.frame} or \code{data.table}. @@ -115,9 +118,8 @@ coast <- fast(madCoast4) # polygons vector extract(elev, dypsis, xy = TRUE) # Extract from categorical raster at points: -dypsisWGS84 <- project(dypsis, cover) # Convert to same CRS -categories <- extract(cover, dypsisWGS84) -categoryValues <- extract(cover, dypsisWGS84, cats = FALSE) +categories <- extract(cover, dypsis) +categoryValues <- extract(cover, dypsis, cats = FALSE) categories categoryValues @@ -128,8 +130,8 @@ extract(elev, coast, fun = c("sum", "mean", "countNonNA"), overlap = FALSE) extract(elev, rivers, fun = c("sum", "mean", "countNonNA"), overlap = FALSE) # Extract from a polygons vector at a points vector: -table <- extract(coast, dypsis, xy = TRUE) -head(table) # first 3 are outside polygons vector, next 3 are inside +polysFromPoints <- extract(coast, dypsis, xy = TRUE) +head(polysFromPoints) # first 3 are outside polygons vector, next 3 are inside } } diff --git a/man/faster.Rd b/man/faster.Rd index fdfdad2b..142fc927 100644 --- a/man/faster.Rd +++ b/man/faster.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/03_options.r \name{faster} \alias{faster} -\title{Set or get options shared across "fasterRaster" functions} +\title{Set or get options shared across fasterRaster functions} \usage{ faster(..., default = FALSE, restore = FALSE) } @@ -18,14 +18,14 @@ Options include: \itemize{ \item \code{grassDir} (character): The folder in which \strong{GRASS} is installed on your computer. Typically, this option is set when you run \code{\link[=faster]{faster()}}. Depending on your operating system, your install directory will look something like this: \itemize{ -\item Windows: \code{"C:/Program Files/GRASS GIS 8.3"} -\item Mac OS: \code{"/Applications/GRASS-8.3.app/Contents/Resources"} +\item Windows: \code{"C:/Program Files/GRASS GIS 8.4"} +\item Mac OS: \code{"/Applications/GRASS-8.4.app/Contents/Resources"} \item Linux: \code{"/usr/local/grass"} } \item \code{addonsDir} (character): Folder in which \strong{GRASS} addons are stored. If \code{NA} and \code{grassDir} is not \code{NA}, this will be assumed to be \code{file.path(grassDir, "addons")}. The default values is \code{NA}. \item \code{cores} (integer/numeric integer): Number of processor cores to use on a task. The default is 2. Some \strong{GRASS} modules are parallelized. \item \code{memory} (integer/numeric): The amount of memory to allocate to a task, in GB, for \strong{GRASS}. The default is 2048 MB (i.e., 2 GB). Some \strong{GRASS} modules can take advantage of more memory. -\item \code{clean} (logical): If \code{TRUE} (default), remove temporary files created internally by functions. If not deleted, they can eventually fill up hard drive space, but deleting them takes a little bit of time (usually <1 second for each function). +\item \code{clean} (logical): If \code{TRUE} (default), remove temporary files created internally by functions. If not deleted, they can eventually fill up hard drive space, but deleting them takes a little bit of time (usually <1 second for each function). See also \code{\link[=mow]{mow()}}. \item \code{useDataTable} (logical): If \code{FALSE} (default), functions that return tabular output produce \code{data.frame}s. If \code{TRUE}, output will be \code{data.table}s from the \strong{data.table} package. This can be much faster, but it might require you to know how to use \code{data.table}s if you want to manipulate them in \strong{R}. You can always convert them to \code{data.frame}s using \code{\link[base:as.data.frame]{base::as.data.frame()}}. \item \code{verbose} (logical): If \code{TRUE}, show \strong{GRASS} messages and otherwise hidden slots in classes. This is mainly used for debugging, so most users will want to keep this at its default, \code{FALSE}. \item \code{workDir} (character): The folder in which \strong{GRASS} rasters, vectors, and other objects are created and manipulated. By default, this is given by \code{\link[=tempdir]{tempdir()}}. diff --git a/man/fasterRaster.Rd b/man/fasterRaster.Rd index 5ff23df7..a1b9b153 100644 --- a/man/fasterRaster.Rd +++ b/man/fasterRaster.Rd @@ -160,9 +160,8 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] } \subsection{Functions operating on categorical (factor) rasters}{ - -\code{\link[fasterRaster]{\%in\%}}, and \code{\link[fasterRaster]{\%notin\%}}: Mask cells that match or do not match a given category \itemize{ +\item \code{\link[fasterRaster]{\%in\%}}, and \code{\link[fasterRaster]{\%notin\%}}: Mask cells that match or do not match a given category \item \code{\link[=activeCat]{activeCat()}} and \code{\link[=activeCats]{activeCats()}}: Column(s) that defines category labels \code{\link[fasterRaster]{activeCat<-}}: Set column that defines category labels \item \code{\link[=addCats]{addCats()}}: Add new columns to a "levels" table @@ -177,7 +176,7 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] \item \code{\link[=freq]{freq()}}: Frequency of each category across cells of a raster \item \code{\link[=is.factor]{is.factor()}}: Is a raster categorical? \item \code{\link[=levels]{levels()}}: "Levels" table of a categorical raster -\code{\link[fasterRaster]{levels<-}}: Set "levels" table of a categorical raster +\item \code{\link[fasterRaster]{levels<-}}: Set "levels" table of a categorical raster \item \code{\link[=match]{match()}}, \code{\link[fasterRaster]{\%in\%}}, and \code{\link[fasterRaster]{\%notin\%}}: Find which cells of a \code{GRaster} match or do not match certain category labels \item \code{\link[=minmax]{minmax()}}: "Lowest" and "highest" category values of categorical rasters (when argument \code{levels = TRUE}) \item \code{\link[=missing.cases]{missing.cases()}}: Find rows of a categorical \code{GRaster}'s "levels" table that have at least one \code{NA} in them @@ -194,6 +193,8 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] \item \code{\link[=plotRGB]{plotRGB()}}: Display a multispectral \code{GRaster} using red, blue, green, and alpha channels \item \code{\link[=vegIndex]{vegIndex()}}: Vegetation indices from surface reflectance } +} + \subsection{Functions that operate on \strong{terra} \code{SpatRaster}s}{ \itemize{ \item \code{\link[=bioclims]{bioclims()}}: BIOCLIM rasters (classic set and extended set) @@ -201,8 +202,6 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] } } -} - \subsection{Properties of \code{GVector}s}{ \itemize{ \item \code{\link[=crs]{crs()}}: Coordinate reference system @@ -226,12 +225,11 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] } \subsection{Functions that operate on or create \code{GVector}s}{ - -The \code{\link[fasterRaster]{[}} operator can be used to subset geometries of a \code{GVector}. -The \link{$} and \code{\link[fasterRaster]{[[}} operators can be used to get columns of a \code{GVector}'s data table. -The \code{\link[fasterRaster]{$<-}} operator can be used to replace specific columns of a \code{GVector}'s data table or to add columns. -\code{\link[fasterRaster]{addTable<-}}: Add a data table to a \code{GVector} \itemize{ +\item The \code{\link[fasterRaster]{[}} operator can be used to subset geometries of a \code{GVector}. +\item The \link{$} and \code{\link[fasterRaster]{[[}} operators can be used to get columns of a \code{GVector}'s data table. +\item The \code{\link[fasterRaster]{$<-}} operator can be used to replace specific columns of a \code{GVector}'s data table or to add columns. +\item \code{\link[fasterRaster]{addTable<-}}: Add a data table to a \code{GVector} \item \code{\link[=aggregate]{aggregate()}}: Combine \code{GVector} geometries \item \code{\link[=as.data.frame]{as.data.frame()}}: Convert a \code{GVector}'s attribute table to a \code{data.frame} \item \code{\link[=as.data.table]{as.data.table()}}: Convert a \code{GVector}'s attribute table to a \code{data.table} @@ -270,13 +268,13 @@ The \code{\link[fasterRaster]{$<-}} operator can be used to replace specific col \item \code{\link[=tail]{tail()}}: Last rows of a \code{GVector}'s data table \item \code{\link[=thinPoints]{thinPoints()}}: Reduce number of points in same raster cell \item \code{\link[=union]{union()}} or \code{+}: Combine two \code{GVector}s -\item \code{\link[=xor]{xor()}} or \verb{/``: Select parts of polygons not shared by two }GVector`s +\item \code{\link[=xor]{xor()}} or \code{/}: Select parts of polygons not shared by two \code{GVector}s } } \subsection{Functions for fixing issues with \code{GVector}s}{ -(See also tools that can be used during \code{GVector} creation/loading in \code{\link[=fast]{fast()}}.) +(See also \emph{Details} \code{\link[=fast]{fast()}}.) \itemize{ \item \code{\link[=breakPolys]{breakPolys()}}: Break topologically clean areas \item \code{\link[=fillHoles]{fillHoles()}}: Fill "holes" of a \code{GVector} @@ -304,7 +302,7 @@ The \code{\link[fasterRaster]{$<-}} operator can be used to replace specific col \item \code{\link[=as.int]{as.int()}}: Convert a \code{GRaster} to an integer raster (\strong{GRASS} data type \code{CELL}) \item \code{\link[=as.points]{as.points()}}, \code{\link[=as.lines]{as.lines()}}, and \code{\link[=as.polygons]{as.polygons()}}: Convert a \code{GRaster} to a \code{GVector} \item \code{\link[=fast]{fast()}}: Convert a \code{SpatRaster} to a \code{GRaster}; a \code{SpatVector}, \code{sf} vector, numeric vector, \code{matrix}, \code{data.frame}, or \code{data.table} to a \code{GVector}; or load a vector or raster from a file -\item \code{\link[=categories]{categories()}} and \link{levels<-}: Convert an integer raster to a categorical ("factor") raster. +\item \code{\link[=categories]{categories()}} and \code{\link[fasterRaster]{levels<-}}: Convert an integer raster to a categorical ("factor") raster. \item \code{\link[=rast]{rast()}}: Convert a \code{GRaster} to a \code{SpatRaster} \item \code{\link[=rasterize]{rasterize()}}: Convert a \code{GVector} to a \code{GRaster} \item \code{\link[=st_as_sf]{st_as_sf()}}: Convert a \code{GVector} to a \code{sf} vector @@ -343,12 +341,11 @@ The \code{\link[fasterRaster]{$<-}} operator can be used to replace specific col } \subsection{Esoteric tutorials and arcane notes}{ - -Comparisons between \code{GRegion}s can be performed using the \code{==} and \code{!=} operators. -Vignette on \strong{GRASS} "projects/locations" and "mapsets": \verb{vignette("projects_mapsets", package = "fasterRaster"}) -Vignette on \strong{GRASS} "regions": \code{vignette("regions", package = "fasterRaster")} -Vignette on \strong{fasteRaster} hidden functions: \code{vignette("hidden_functions", package = "fasterRaster")} \itemize{ +\item Comparisons between \code{GRegion}s can be performed using the \code{==} and \code{!=} operators. +\item Vignette on \strong{GRASS} "projects/locations" and "mapsets": \code{vignette("projects_mapsets", package = "fasterRaster")} +\item Vignette on \strong{GRASS} "regions": \code{vignette("regions", package = "fasterRaster")} +\item Vignette on \strong{fasteRaster} hidden functions: \code{vignette("hidden_functions", package = "fasterRaster")} \item \code{\link[=grassStarted]{grassStarted()}}: Has a connection \strong{GRASS} been made within the current \strong{R} session? } } diff --git a/man/fillHoles.Rd b/man/fillHoles.Rd index 25cf5919..70ccbe5a 100644 --- a/man/fillHoles.Rd +++ b/man/fillHoles.Rd @@ -10,7 +10,7 @@ \arguments{ \item{x}{A \code{GVector}.} -\item{fail}{Logical: If \code{TRUE} (default), and \strong{GRASS 8.4} or higher is not installed, cause an error. If \code{FALSE}, a warning will be displayed and a \code{NULL} value will be returned. This function requires \strong{GRASS 8.4} or higher to be installed.} +\item{fail}{Logical: If \code{TRUE} (default), and \strong{GRASS 8.3} or higher is not installed, cause an error. If \code{FALSE}, a warning will be displayed and a \code{NULL} value will be returned. This function requires \strong{GRASS 8.3} or higher to be installed.} } \value{ A \code{GVector}. diff --git a/man/fillNAs.Rd b/man/fillNAs.Rd index f4d88a83..045552fa 100644 --- a/man/fillNAs.Rd +++ b/man/fillNAs.Rd @@ -21,7 +21,7 @@ \item{method}{Character: Type of spline, either "\code{bilinear}" (default), "\code{bicubic}", or "\code{RST}" (regularized splines with tension). Partial matching is used and case is ignored. -\strong{Note}: The RST method will often display warnings, but thesecan be ignored.} +\strong{Note}: The RST method will often display warnings, but these can be ignored.} \item{min, max}{Numeric: Lowest and highest values allowed in the interpolated values. Values outside these bounds will be truncated to the minimum/maximum value(s) allowed. The default imposes no constraints. For multi-layered rasters, you can supply a single value for \code{min} and/or \code{max}, or multiple values (one per layer). Values will be recycled if there are fewer than one or them per layer in the raster.} diff --git a/man/focal.Rd b/man/focal.Rd index 42cd97c6..f1724b96 100644 --- a/man/focal.Rd +++ b/man/focal.Rd @@ -22,7 +22,7 @@ \item "\code{mean}" (default) \item "\code{median}" \item "\code{mode}" -\item "\code{min}" or "\code{max}": Minumum or maximum. Should not use a weights matrix. +\item "\code{min}" or "\code{max}": Minimum or maximum. Should not use a weights matrix. \item "\code{range}": Difference between the maximum and minimum. Should not use a weights matrix. \item "\code{sd}": Sample standard deviation. NB: This is the same as the \code{\link[stats:sd]{stats::sd()}} function. \item "\code{sdpop}": Population standard deviation. NB: This is the same as the function "stddev" in the \strong{GRASS} module \code{r.neighbors}. diff --git a/man/grassInfo.Rd b/man/grassInfo.Rd index f99f424a..c15dd565 100644 --- a/man/grassInfo.Rd +++ b/man/grassInfo.Rd @@ -12,7 +12,7 @@ grassInfo(x = "citation") \item \code{"citation"} (default) \item \code{"copyright"}: Copyright information \item \code{"version"}: Version number and release year -\item \code{"versionNumber"}: Version number as numeric, major and minor only (e.g., 8.3) +\item \code{"versionNumber"}: Version number as numeric, major and minor only (e.g., 8.4) } Partial matching is used and case is ignored.} diff --git a/man/grassStarted.Rd b/man/grassStarted.Rd index fdbdb86b..b7568650 100644 --- a/man/grassStarted.Rd +++ b/man/grassStarted.Rd @@ -10,7 +10,7 @@ grassStarted() Logical. } \description{ -Returns \code{TRUE} or \code{FALSE}, depending on whether a \strong{GRASS} connection has been made or not within the current \strong{R} session. Usually used only by developers. +Returns \code{TRUE} or \code{FALSE}, depending on whether a \strong{GRASS} connection has been made or not within the current \strong{R} session. Usually used only by developers. \strong{GRASS} is started the first time \code{\link[=fast]{fast()}} is used. } \examples{ diff --git a/man/is.lonlat.Rd b/man/is.lonlat.Rd index 1362a125..7cf5d366 100644 --- a/man/is.lonlat.Rd +++ b/man/is.lonlat.Rd @@ -20,7 +20,7 @@ Logical (\code{TRUE} if unprojected, \code{FALSE} otherwise). } \description{ -\code{is.lonlat()} attempst to determine if a coordinate reference system is unprojected (e.g., WGS84, NAD83, NAD27, etc.). For \code{GRaster}s and \code{GVector}s, the function should always be correct. For WKT character strings and \code{sf} vectors, it does this by looking for the "CONVERSION[" tag in the WKT string (or the object's WKT string), and if it finds one, returns \code{FALSE}. This may not be truthful in all cases. +\code{is.lonlat()} attempts to determine if a coordinate reference system is unprojected (e.g., WGS84, NAD83, NAD27, etc.). For \code{GRaster}s and \code{GVector}s, the function should always be correct. For WKT character strings and \code{sf} vectors, it does this by looking for the "CONVERSION[" tag in the WKT string (or the object's WKT string), and if it finds one, returns \code{FALSE}. This may not be truthful in all cases. } \seealso{ \code{\link[terra:is.lonlat]{terra::is.lonlat()}} diff --git a/man/predict.Rd b/man/predict.Rd index 54750d94..5c5719ad 100644 --- a/man/predict.Rd +++ b/man/predict.Rd @@ -25,7 +25,7 @@ The model must be either a linear model, which is of class \code{lm} and typical This \code{predict()} function can handle: \itemize{ \item Linear predictors and intercepts like \code{y ~ 1 + x}; -\item Quadratic terms like \code{y ~ x^2} (or, in \strong{R} formular notation, \code{y ~ I(x^2)}); +\item Quadratic terms like \code{y ~ x^2} (or, in \strong{R} formula notation, \code{y ~ I(x^2)}); \item Two-way interaction terms between scalars like \code{y ~ x1:x2} and \code{y ~ x1 * x2}; \item Categorical predictors (i.e., categorical \code{GRaster}s; see \code{vignette("GRasters", package = "fasterRaster")})); \item Two-way interactions between a categorical predictor and a scalar predictor; and diff --git a/man/replace_single_square_bracket.Rd b/man/replace_single_square_bracket.Rd index 3ee66edd..7dc98170 100644 --- a/man/replace_single_square_bracket.Rd +++ b/man/replace_single_square_bracket.Rd @@ -24,7 +24,7 @@ A \code{GRaster}. } \description{ -The \verb{[<-} operator can be used to replace all of the values of a \code{GRaster}, or specific values depending on the expression in \code{i}. For example, you could use \code{rast[] <- 10} to assign 10 to all cells, or \code{rast[rast > 0] <- 10} to assign all cells with values >0 to 10. +The \verb{[<-} operator can be used to replace all of the values of a \code{GRaster}, or specific values depending on the expression in \code{i}. For example, you could use \code{rast[] <- 10} to assign 10 to all cells, or \code{rast[rast > 0] <- 10} to assign all cells with values >0 to 10. You can also use one raster to set values in another, as in \code{rast1[rast2 > 0] <- 10}. } \examples{ if (grassStarted()) { diff --git a/man/vAttachDatabase.Rd b/man/vAttachDatabase.Rd index f5f27842..ed655a8a 100644 --- a/man/vAttachDatabase.Rd +++ b/man/vAttachDatabase.Rd @@ -11,9 +11,9 @@ \item{table}{Either \code{NULL} (default), or a \code{data.frame} or \code{data.table}, or a numeric or integer vector: \itemize{ -\item If \code{NULL}, then a bare minimal table will be created with a column named \code{frid}, holding sequential integer values. -\item If a \code{data.frame} or \code{data.table} and no column is named \code{frid}, one will be created with sequential integer values. If the table does have a column named \code{frid}, then it should have integer (not just numeric) values. -\item If a \code{vector}, then these are coerced to type \code{integer} and used to define the \code{frid} column. +\item If \code{NULL}, then a bare minimal table will be created with a column named \code{cat}, holding sequential integer values. +\item If a \code{data.frame} or \code{data.table} and no column is named \code{cat}, one will be created with sequential integer values. If the table does have a column named \code{cat}, then it should have integer (not just numeric) values. +\item If a \code{vector}, then these are coerced to type \code{integer} and used to define the \code{cat} column. } There should be one row/value per geometry in \code{x}.} @@ -26,6 +26,6 @@ There should be one row/value per geometry in \code{x}.} Invisibly returns the \code{\link[=sources]{sources()}} name of a vector in \strong{GRASS}. } \description{ -\code{.vAttachDatabase()} adds a table to a \strong{GRASS} vector. This table is meant to be "invisible" to most users--they should use interact with attribute tables using the \code{GVector} slot `@table``. Some functions require tables (e.g., \code{\link[=extract]{extract()}} and \code{\link[=spatSample]{spatSample()}}). \strong{This function is mostly of use to developers.} +\code{.vAttachDatabase()} adds a table to a \strong{GRASS} vector. This table is meant to be "invisible" to most users--they should use interact with attribute tables using the \code{GVector} slot \verb{@table}. Some functions require tables (e.g., \code{\link[=extract]{extract()}} and \code{\link[=spatSample]{spatSample()}}). \strong{This function is mostly of use to developers.} } \keyword{internal} diff --git a/man/vDetachDatabase.Rd b/man/vDetachDatabase.Rd index 0385ff0a..f0b5893e 100644 --- a/man/vDetachDatabase.Rd +++ b/man/vDetachDatabase.Rd @@ -13,6 +13,6 @@ Invisibly returns the \code{\link[=sources]{sources()}} name of a vector in \strong{GRASS}. } \description{ -\code{.vDetachDatabase()} detaches the database from a \strong{GRASS} vector and deletes it. This table is meant to be "invisible" to most users--they should use interact with attribute tables using the \code{GVector} slot `@table``. Some functions do require tables (e.g., \code{\link[=extract]{extract()}} and \code{\link[=spatSample]{spatSample()}}). \strong{This function is mostly of use to developers.} +\code{.vDetachDatabase()} detaches the database from a \strong{GRASS} vector and deletes it. This table is meant to be "invisible" to most users--they should use interact with attribute tables using the \code{GVector} slot \verb{@table}. Some functions do require tables (e.g., \code{\link[=extract]{extract()}} and \code{\link[=spatSample]{spatSample()}}). \strong{This function is mostly of use to developers.} } \keyword{internal} diff --git a/man/xor.Rd b/man/xor.Rd index 5c1da819..64240bb5 100644 --- a/man/xor.Rd +++ b/man/xor.Rd @@ -14,7 +14,7 @@ A \code{GVector}. } \description{ -The \code{xor()} function selects the area that does not overlap between two "polygon" \code{GVector}s. You can also use the \code{/} operator, as in \verb{vect 1 / vect2}. +The \code{xor()} function selects the area that does not overlap between two "polygon" \code{GVector}s. You can also use the \code{/} operator, as in \code{vect1 / vect2}. } \examples{ if (grassStarted()) { From 348f00be85c3213f9536a3ada433563068a2b0a9 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:36:30 -0500 Subject: [PATCH 002/125] Update fasterRaster_workspace.code-workspace --- fasterRaster_workspace.code-workspace | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fasterRaster_workspace.code-workspace b/fasterRaster_workspace.code-workspace index 355f765a..b0fa78d9 100644 --- a/fasterRaster_workspace.code-workspace +++ b/fasterRaster_workspace.code-workspace @@ -15,7 +15,14 @@ "statusBarItem.hoverBackground": "#2f7c47", "statusBarItem.remoteBackground": "#215732", "titleBar.activeBackground": "#215732", - "titleBar.inactiveBackground": "#21573299" + "titleBar.inactiveBackground": "#21573299", + "activityBar.foreground": "#e7e7e7", + "activityBar.inactiveForeground": "#e7e7e799", + "commandCenter.border": "#e7e7e799", + "statusBar.foreground": "#e7e7e7", + "statusBarItem.remoteForeground": "#e7e7e7", + "titleBar.activeForeground": "#e7e7e7", + "titleBar.inactiveForeground": "#e7e7e799" }, "peacock.color": "#215732" } From 71b25378fb8bf5dfa2a85862becc4a8cf40351fe Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:36:54 -0500 Subject: [PATCH 003/125] Add `grass()` --- NAMESPACE | 1 + NEWS.md | 1 + R/grass.r | 20 ++++++++++++++++++++ man/examples/ex_grass.r | 7 +++++++ man/fasterRaster.Rd | 1 + man/grass.Rd | 29 +++++++++++++++++++++++++++++ man/mow.Rd | 2 +- 7 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 R/grass.r create mode 100644 man/examples/ex_grass.r create mode 100644 man/grass.Rd diff --git a/NAMESPACE b/NAMESPACE index ca98b9d3..306662d4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -117,6 +117,7 @@ exportMethods(freq) exportMethods(geomorphons) exportMethods(geomtype) exportMethods(global) +exportMethods(grass) exportMethods(grid) exportMethods(head) exportMethods(hexagons) diff --git a/NEWS.md b/NEWS.md index 156c71b5..49be2efb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -13,6 +13,7 @@ o `.vAttachDatabase()` no longer has the `"o"` flag when calling `v.db.connect` ### Enhanced functionality and new functions o `extract()` now automatically projects a `GVector` to match the CRS of a `GRaster` from which extraction is being made. +o `grass()` allows users to start the **GRASS** GUI. ### Bug fixes o `categories()` correctly assigns active category column. diff --git a/R/grass.r b/R/grass.r new file mode 100644 index 00000000..c0db9e5c --- /dev/null +++ b/R/grass.r @@ -0,0 +1,20 @@ +#' Start the GRASS GUI (potentially dangerous!) +#' +#' @description This function starts the **GRASS** GUI. It is provided merely as a utility... in most cases, it should *not* be used if you are doing any kind of analysis of rasters or vectors using **fasterRaster**. The reason for this prohibition is that **fasterRaster** objects, like `GRaster`s and `GVector`s, are really "pointers" to objects in **GRASS**. If **fasterRaster** points to a **GRASS** object that is changed in **GRASS** but not **R**, then **fasterRaster** will not "know" about it, so changed won't be reflected in the **fasterRaster** object. +#' +#' One aspect of the GUI that is useful but will not change objects is to use it to plot rasters and vectors. However, the a **fasterRaster** object in **R** will have a different name in **GRASS**. The name in **GRASS** of a `GVector` or `GRaster` is given by [sources()]. +#' +#' @returns Nothing (starts the **GRASS** GUI). +#' +#' @seealso [mow()] +#' +#' @example man/examples/ex_grass.r +#' +#' @aliases grass +#' @rdname grass +#' @exportMethod grass +methods::setMethod( + f = "grass", + signature = c(x = "missing"), + function() rgrass::execGRASS("g.gui", ui = "wxpython", flags = c(.quiet(), "f")) +) diff --git a/man/examples/ex_grass.r b/man/examples/ex_grass.r new file mode 100644 index 00000000..c6d56948 --- /dev/null +++ b/man/examples/ex_grass.r @@ -0,0 +1,7 @@ +if (grassStarted()) { + +# Starting the GRASS GUI and making changes to rasters or vectors can "break" +# the GRasters and GVectors they are associated with in R. +if (FALSE) grass() + +} diff --git a/man/fasterRaster.Rd b/man/fasterRaster.Rd index a1b9b153..bdb5c81a 100644 --- a/man/fasterRaster.Rd +++ b/man/fasterRaster.Rd @@ -315,6 +315,7 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] \item \code{\link[=compareGeom]{compareGeom()}}: Determine if geographic metadata is same between \code{GRaster}s and/or \code{GVector}s \item \code{\link[=dropRows]{dropRows()}}: Remove rows from a \code{data.frame} or \code{data.table} \item \code{\link[=grassInfo]{grassInfo()}}: \strong{GRASS} version and citation +\item \code{\link[=grass]{grass()}}: Start the \strong{GRASS} GUI (not recommended for most users!!!) \item \code{\link[=mow]{mow()}}: Remove unused rasters and vectors from the \strong{GRASS} cache \item \code{\link[=reorient]{reorient()}}: Convert degrees between 'north-orientation' and 'east orientation' \item \code{\link[=replaceNAs]{replaceNAs()}}: Replace \code{NA}s in columns of a \code{data.table} or \code{data.frame}, or in a vector diff --git a/man/grass.Rd b/man/grass.Rd new file mode 100644 index 00000000..9aaf2db3 --- /dev/null +++ b/man/grass.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/grass.r +\name{grass,missing-method} +\alias{grass,missing-method} +\alias{grass} +\title{Start the GRASS GUI (potentially dangerous!)} +\usage{ +\S4method{grass}{missing}() +} +\value{ +Nothing (starts the \strong{GRASS} GUI). +} +\description{ +This function starts the \strong{GRASS} GUI. It is provided merely as a utility... in most cases, it should \emph{not} be used if you are doing any kind of analysis of rasters or vectors using \strong{fasterRaster}. The reason for this prohibition is that \strong{fasterRaster} objects, like \code{GRaster}s and \code{GVector}s, are really "pointers" to objects in \strong{GRASS}. If \strong{fasterRaster} points to a \strong{GRASS} object that is changed in \strong{GRASS} but not \strong{R}, then \strong{fasterRaster} will not "know" about it, so changed won't be reflected in the \strong{fasterRaster} object. + +One aspect of the GUI that is useful but will not change objects is to use it to plot rasters and vectors. However, the a \strong{fasterRaster} object in \strong{R} will have a different name in \strong{GRASS}. The name in \strong{GRASS} of a \code{GVector} or \code{GRaster} is given by \code{\link[=sources]{sources()}}. +} +\examples{ +if (grassStarted()) { + +# Starting the GRASS GUI and making changes to rasters or vectors can "break" +# the GRasters and GVectors they are associated with in R. +if (FALSE) grass() + +} +} +\seealso{ +\code{\link[=mow]{mow()}} +} diff --git a/man/mow.Rd b/man/mow.Rd index a6b73110..9ced6edd 100644 --- a/man/mow.Rd +++ b/man/mow.Rd @@ -33,5 +33,5 @@ if (FALSE) mow() } } \seealso{ -Option \code{clean} in \code{\link[=faster]{faster()}} +Option \code{clean} in \code{\link[=faster]{faster()}}; \code{\link[=grass]{grass()}} } From bc89ae8e2a1015a800c9b7fb87f42523b3babf88 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:37:01 -0500 Subject: [PATCH 004/125] Update pkgdown.yml --- inst/pkgdown.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/inst/pkgdown.yml b/inst/pkgdown.yml index ea3ea398..204f2ae5 100644 --- a/inst/pkgdown.yml +++ b/inst/pkgdown.yml @@ -1,5 +1,5 @@ -pandoc: 3.1.11 -pkgdown: 2.0.9 +pandoc: 3.1.1 +pkgdown: 2.1.1 pkgdown_sha: ~ articles: faster_fasterRaster: faster_fasterRaster.html @@ -8,8 +8,7 @@ articles: hidden_functions: hidden_functions.html projects_mapsets: projects_mapsets.html regions: regions.html -last_built: 2024-09-20T03:06Z +last_built: 2024-09-25T15:32Z urls: reference: https://github.com/adamlilith/fasterRaster/reference article: https://github.com/adamlilith/fasterRaster/articles - From 48b6eb79823adb93241420fd722a6f509a5b1af8 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:37:12 -0500 Subject: [PATCH 005/125] Add `grass()` --- R/01_generics.r | 1 + 1 file changed, 1 insertion(+) diff --git a/R/01_generics.r b/R/01_generics.r index c94efd8d..6fae3b53 100644 --- a/R/01_generics.r +++ b/R/01_generics.r @@ -147,6 +147,7 @@ methods::setGeneric(name = "fragmentation", def = function(x, ...) standardGener methods::setGeneric(name = "geomtype", package = "terra") methods::setGeneric(name = "geomorphons", def = function(x, ...) standardGeneric("geomorphons")) methods::setGeneric(name = "global", package = "terra") +methods::setGeneric(name = "grass", def = function(x, ...) standardGeneric("grass")) methods::setGeneric(name = "grid", def = function(x, ...) standardGeneric("grid")) methods::setGeneric(name = "head", package = "utils") From 9cbbb3b68fe41f7938b72f5390440afc0538896c Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:37:15 -0500 Subject: [PATCH 006/125] Create addLocationProject.r --- R/addLocationProject.r | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 R/addLocationProject.r diff --git a/R/addLocationProject.r b/R/addLocationProject.r new file mode 100644 index 00000000..bb76c824 --- /dev/null +++ b/R/addLocationProject.r @@ -0,0 +1,21 @@ +#' Add a "project" or "location" argument to an "args" list +#' +#' @description Unhelpfully, starting with **GRASS** 8.4, what were previously called "locations" and now called "projects." These were supposed to be back-compatible, but are not. This function takes a list of arguments that will be passed to [rgrass::execGRASS()] and adds either a "location" or "project" argument, depending on the version of **GRASS** being used. +#' +#' @param args A `list` of arguments to be passed to a **GRASS** module. +#' @param locProj Character: The name of the location or project. +#' +#' @returns A `list`. +#' +#' @keywords external +.addLocationProject <- function(args, locProj) { + + ver <- grassInfo("versionNumber") + if (ver <= 8.3) { + args$location <- locProj + } else { + args$project <- locProj + } + args + +} From 87f369b2669c9eda31eb78b9a47e8e9bc207a37d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:37:29 -0500 Subject: [PATCH 007/125] Remove `dissolve` argument --- R/aggregate.r | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/R/aggregate.r b/R/aggregate.r index 7e517de9..957ca1fd 100644 --- a/R/aggregate.r +++ b/R/aggregate.r @@ -30,8 +30,6 @@ #' #' @param weight Logical (rasters only): If `FALSE`, each source cell that has its center in the destination cell will be counted equally. If `TRUE`, the value of each source will be weighted the proportion of the destination cell the source cell covers. #' -#' @param dissolve Logical (vectors only): If `TRUE` (default), then aggregated geometries will have their borders dissolved. This is ignored if the input `GVector` is not a "polygons" vector. -#' #' @returns A `GRaster` or `GVector`. #' #' @seealso [stats::aggregate()], [terra::aggregate()], [disagg()], [terra::disagg()] @@ -157,11 +155,12 @@ methods::setMethod( methods::setMethod( f = "aggregate", signature = c(x = "GVector"), - function(x, dissolve = TRUE) { + function(x) { .locationRestore(x) gtype <- geomtype(x) - src <- .aggregate(x, dissolve = dissolve, gtype = gtype, copy = TRUE) + # src <- .aggregate(x, dissolve = dissolve, gtype = gtype, copy = TRUE) + src <- .aggregate(x, dissolve = TRUE, gtype = gtype, copy = TRUE) # aggregate data table if (nrow(x) == 0L) { @@ -221,7 +220,7 @@ methods::setMethod( #' @param dissolve Logical #' @param copy Logical: If TRUE, make copy of vector before operations #' @noRd -.aggregate <- function(x, gtype, dissolve, copy) { +.aggregate <- function(x, dissolve, gtype, copy) { if (inherits(x, "GVector")) { .locationRestore(x) @@ -235,8 +234,8 @@ methods::setMethod( oldcats <- .vCats(src, db = FALSE) if (length(oldcats) > 1L) { - table <- data.table::data.table(fr = rep(1L, length(oldcats))) - .vAttachDatabase(src, table = table, replace = TRUE) + table <- data.table::data.table(frid = rep(1L, length(oldcats))) + .vAttachDatabase(src, table = table, replace = TRUE, cats = oldcats) # newcats <- data.frame(oldfr = oldcats, fr = rep(1L, length(oldcats))) From b274747ebe8bd9660caa534db550e3652ce276dc Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:42:01 -0500 Subject: [PATCH 008/125] Update for **GRASS 8.4** --- R/backdoor.r | 2 +- R/global.r | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R/backdoor.r b/R/backdoor.r index 9463da03..c03b0d5a 100644 --- a/R/backdoor.r +++ b/R/backdoor.r @@ -3,4 +3,4 @@ #' This is a secret function for ABS's machine to be used for faster development of **fasterRaster**. #' #' @noRd -.backdoor <- function() faster(grassDir = "C:/Program Files/GRASS GIS 8.3", memory = 1024 * 8, cores = 2, verbose = TRUE) +.backdoor <- function() faster(grassDir = "C:/Program Files/GRASS GIS 8.4", memory = 1024 * 8, cores = 2, verbose = TRUE) diff --git a/R/global.r b/R/global.r index bd215b67..d208d5df 100644 --- a/R/global.r +++ b/R/global.r @@ -122,7 +122,7 @@ methods::setMethod( intern = TRUE ) - if (versionNumber >= 8.3) args$nprocs <- faster("cores") + if (versionNumber >= 8.4) args$nprocs <- faster("cores") if (any(fun == "median")) args$flags <- c(args$flags, "e") info <- do.call(rgrass::execGRASS, args) @@ -280,7 +280,7 @@ methods::setMethod( intern = TRUE ) - if (versionNumber >= 8.3) args$nprocs <- faster("cores") + if (versionNumber >= 8.4) args$nprocs <- faster("cores") thisInfo <- do.call(rgrass::execGRASS, args = args) if (faster("clean")) on.exit(.rm(srcSS, type = "raster", warn = FALSE), add = TRUE) @@ -331,7 +331,7 @@ methods::setMethod( intern = TRUE ) - if (grassInfo("versionNumber") >= 8.3) args$nprocs <- faster("cores") + if (grassInfo("versionNumber") >= 8.4) args$nprocs <- faster("cores") thisInfo <- do.call(rgrass::execGRASS, args = args) if (faster("clean")) on.exit(.rm(srcSS, type = "raster", warn = FALSE), add = TRUE) From 2f8378130f4b4f54fdcf4ab201aed682c2fd669d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:42:15 -0500 Subject: [PATCH 009/125] Update buffer.r --- R/buffer.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/buffer.r b/R/buffer.r index 638d0868..bfef663e 100644 --- a/R/buffer.r +++ b/R/buffer.r @@ -145,7 +145,7 @@ methods::setMethod( dissolve = TRUE ) { - # .message(msg = "buffer", message = "As of GRASS 8.4, terra's buffer() function is much faster, even for very large vectors.") + # .message(msg = "buffer", message = "As of GRASS 8.3, terra's buffer() function is much faster, even for very large vectors.") .locationRestore(x) From 1652e0f82010c870120e8fc91c852e0ac502fcc9 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:42:29 -0500 Subject: [PATCH 010/125] Specify `type` when calling `.ls()` --- R/exists.r | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/exists.r b/R/exists.r index e9ad5283..cd20a2aa 100644 --- a/R/exists.r +++ b/R/exists.r @@ -18,7 +18,7 @@ methods::setMethod( .locationRestore(x) src <- sources(x) - src %in% .ls() + src %in% .ls(type = "vector") } # EOF ) @@ -33,7 +33,7 @@ methods::setMethod( .locationRestore(x) src <- sources(x) - src %in% .ls() + src %in% .ls(type = "raster") } # EOF ) From e2b4c0be2709fb642ce82111053f1595a167905b Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:42:46 -0500 Subject: [PATCH 011/125] Add `...` to sig `GR` & `GV` --- R/extract.r | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/R/extract.r b/R/extract.r index 575d4a8c..e2d61c19 100644 --- a/R/extract.r +++ b/R/extract.r @@ -38,6 +38,8 @@ #' #' @param verbose Logical: If `TRUE`, display progress (will only function when extracting from points on a `GRaster` when the number of `GRaster`s is large). #' +#' @param ... Arguments to pass to [project()]. This is used only if extracting from a `GRaster` at locations specified by a `GVector`, and they have a different coordinate reference system. In this case, users should specify the `wrap` argument to [project()]. +#' #' @returns A `data.frame` or `data.table`. #' #' @example man/examples/ex_extract.r @@ -58,10 +60,27 @@ methods::setMethod( overlap = TRUE, xy = FALSE, cats = TRUE, - verbose = FALSE + verbose = FALSE, + ... ) { .locationRestore(x) + + sameCRS <- compareGeom(x, y, stopOnError = FALSE, messages = FALSE) + if (!sameCRS) { + + warning("The GVector has a different coordinate reference system than the GRaster.\n The GVector was projected to the CRS of the GRaster. If the GVector spans\n the international date line, you may need to specify `wrap = TRUE` when\n using this function.", immediate. = TRUE) + + dots <- list(...) + if (any(names(dots) == "wrap")) { + wrap <- dots$wrap + } else { + wrap = FALSE + } + y <- project(y, x, wrap = wrap) + + } + compareGeom(x, y) nLayers <- nlyr(x) From 6599191ed4e76f29164778db3124929981cfb0fb Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:42:53 -0500 Subject: [PATCH 012/125] Add `grass()` --- R/fasterRaster.r | 1 + 1 file changed, 1 insertion(+) diff --git a/R/fasterRaster.r b/R/fasterRaster.r index ef17eea7..d40ec646 100644 --- a/R/fasterRaster.r +++ b/R/fasterRaster.r @@ -266,6 +266,7 @@ #' * [compareGeom()]: Determine if geographic metadata is same between `GRaster`s and/or `GVector`s #' * [dropRows()]: Remove rows from a `data.frame` or `data.table` #' * [grassInfo()]: **GRASS** version and citation +#' * [grass()]: Start the **GRASS** GUI (not recommended for most users!!!) #' * [mow()]: Remove unused rasters and vectors from the **GRASS** cache #' * [reorient()]: Convert degrees between 'north-orientation' and 'east orientation' #' * [replaceNAs()]: Replace `NA`s in columns of a `data.table` or `data.frame`, or in a vector From 36eba1a8cb48cee0abaac3affc550e0cad530183 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:43:15 -0500 Subject: [PATCH 013/125] Fix `levels` bug (user value was overwritten as `NULL`) --- R/levels.r | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/levels.r b/R/levels.r index e7b3dbd4..6228f231 100644 --- a/R/levels.r +++ b/R/levels.r @@ -112,8 +112,8 @@ methods::setMethod( signature = c(x = "GRaster"), function(x, layer = 1, value, active = 1) { - layer <- NULL # obviates check(): "no visible binding for global variable `layer`" - layer <- .layerIndex(layer, x, recycle = TRUE) + # layer <- NULL # obviates check(): "no visible binding for global variable `layer`" + layer <- .layerIndex(layer = layer, x, recycle = TRUE) if (!inherits(value, "list")) value <- list(value) @@ -150,7 +150,7 @@ methods::setMethod( for (i in layer) { x@levels[[i]] <- value[[i]] - x@activeCat[i] <- as.integer(active) + x@activeCat[i] <- as.integer(active + 1L) } methods::validObject(x) x From a2c1cc8e545d83b5af77dc56c585cb39378a842d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:43:23 -0500 Subject: [PATCH 014/125] Update ls.r --- R/ls.r | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R/ls.r b/R/ls.r index 386c1697..be21dd6b 100644 --- a/R/ls.r +++ b/R/ls.r @@ -14,6 +14,7 @@ .ls <- function(type = c("rasters", "vectors", "rasters3d", "groups")) { rov <- c("rasters", "vectors", "rasters3d", "groups") + type <- omnibus::pmatchSafe(type, rov, useFirst = TRUE, n = length(rov)) if (is.null(type)) type <- rov # find rasters and vector @@ -23,7 +24,7 @@ # failure to match if (anyNA(match)) { - warning("No matches.", immediate.=TRUE) + warning("No matches.", immediate. = TRUE) out <- NULL # specific match @@ -31,7 +32,7 @@ # rasters if (any(match == 1L)) { - rasts <- rgrass::execGRASS("g.list", flags=.quiet(), type="raster", intern=TRUE, echoCmd=FALSE) + rasts <- rgrass::execGRASS("g.list", flags = .quiet(), type = "raster", intern = TRUE, echoCmd = FALSE) if (length(rasts) > 0L) names(rasts) <- rep("raster", length(rasts)) rasts <- sort(rasts) } From 3d2053199c167460afc9ff3081d7b5ce15b93459 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:43:29 -0500 Subject: [PATCH 015/125] Add `grass()` --- R/mow.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/mow.r b/R/mow.r index ae666043..5577302f 100644 --- a/R/mow.r +++ b/R/mow.r @@ -16,7 +16,7 @@ #' #' @returns Invisibly returns a list with the number of rasters and vectors deleted. #' -#' @seealso Option `clean` in [faster()] +#' @seealso Option `clean` in [faster()]; [grass()] #' #' @example man/examples/ex_mow.r #' From a22ccb360426a249ceaf3170ba84779c5ede4265 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:44:05 -0500 Subject: [PATCH 016/125] Revert to using `nprocs` when GRASS >= 8.3 --- R/nacell.r | 1 + 1 file changed, 1 insertion(+) diff --git a/R/nacell.r b/R/nacell.r index 6b2bf68f..99acb037 100644 --- a/R/nacell.r +++ b/R/nacell.r @@ -75,6 +75,7 @@ methods::setMethod( intern = TRUE ) + # Should be GRASS eight point three! if (grassInfo("versionNumber") >= 8.3) args$nprocs <- faster("cores") info <- do.call(rgrass::execGRASS, args = args) From 0cbc6781090e1ed241a8a52469f3b83df08152c8 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:44:23 -0500 Subject: [PATCH 017/125] Better catch of unprojected CRSs and extent thereof --- R/project.r | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/R/project.r b/R/project.r index 18862531..06b13d78 100644 --- a/R/project.r +++ b/R/project.r @@ -120,7 +120,9 @@ methods::setMethod( # .message(msg = "project_raster", message = "This function can produce erroneous results if the raster crosses a pole or the international date line.") # imperfect catch for cases where `wrap` should be `TRUE` but is not - if (.projection(x) %in% c("WGS84", "NAD83", "NAD27") & !wrap) { + unproj <- .projection(x) %in% + c("Latitude-Longitude", "WGS84", "WGS 84", "NAD83", "NAD 83", "NAD27", "NAD 27") + if (unproj & !wrap) { extent <- ext(x, vector = TRUE) if ((extent[1L] == -180 & extent[2L] == 180) | (extent[3L] == -90 & extent[4L] == 90)) warning("This GRaster seems to wrap around the globe to meet at the international\n date line and/or the poles. Should `wrap` be `TRUE`?") @@ -339,10 +341,8 @@ methods::setMethod( # .regionRespectsDims(x = x, y = y, align = align) # "center" method of determining cell resolution - } else if (res == "center") { - + } else if (res == "center") { .regionResCenterMethod(x = x, y = y, align = align) - } } @@ -362,7 +362,6 @@ methods::setMethod( args <- list( cmd = "r.proj", - location = .location(x), mapset = .mapset(x), input = sources(x)[i], output = srcs[i], @@ -370,6 +369,7 @@ methods::setMethod( memory = faster("memory"), flags = c(.quiet(), "overwrite") ) + args <- .addLocationProject(args, .location(x)) if (wrap) args$flags <- c(args$flags, "n") @@ -409,14 +409,16 @@ methods::setMethod( ) { # imperfect catch for cases where `wrap` should be `TRUE` but is not - if (.projection(x) %in% c("WGS84", "NAD83", "NAD27") & !wrap) { + unproj <- .projection(x) %in% + c("Latitude-Longitude", "WGS84", "WGS 84", "NAD83", "NAD 83", "NAD27", "NAD 27") + if (unproj & !wrap) { extent <- ext(x, vector = TRUE) if ( (extent[1L] == -180 & extent[2L] == 180) | (extent[3L] == -90 & extent[4L] == 90) | (extent[1L] > 0 & extent[2L] < 0) | - (extent[3L] > extent[4L]) + (extent[3L] > extent[4L]) | (extent[4L] < extent[3L]) ) warning("This GVector seems to span the international date line and/or poles. Should `wrap` be `TRUE`?") @@ -443,15 +445,16 @@ methods::setMethod( args <- list( cmd = "v.proj", - location = .location(x), dbase = faster("workDir"), mapset = .mapset(x), input = sources(x), output = src, flags = c(.quiet(), "overwrite") - ) + ) + args <- .addLocationProject(args, .location(x)) + if (wrap) args$flags <- c(args$flags, "w") # if crosses international date line - if (geomtype(x, grass = TRUE) == "point") args$flags <- c(args$flags, "b") # disable topology build for points + # if (geomtype(x, grass = TRUE) == "point") args$flags <- c(args$flags, "b") # disable topology build for points do.call(rgrass::execGRASS, args = args) out <- .makeGVector(src, table = x@table) From 3c6862ee60e92c6d63d4ee61530eaa533b746ca5 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:44:42 -0500 Subject: [PATCH 018/125] Fix bug in call of `.rasterize()` --- R/rasterize.r | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/rasterize.r b/R/rasterize.r index 80ea9ce0..f8d62fb4 100644 --- a/R/rasterize.r +++ b/R/rasterize.r @@ -66,7 +66,7 @@ methods::setMethod( nBys <- length(bys) src <- rep(NA_character_, nBys) levels <- list() - if (verbose & nBys > 1L) pb <- utils::txtProgressBar(min = 0, max = nBys, initial = 0, style = 3) + if (verbose & nBys > 1L) pb <- utils::txtProgressBar(min = 0, max = nBys, initial = 0, style = 3, width = 30) for (i in seq_len(nBys)) { @@ -78,7 +78,7 @@ methods::setMethod( index <- which(x@table[[by]] == bys[i]) xx <- x[index] - thisOut <- .rasterize(xx, y, field = field, background = background, by = NULL, verbose = FALSE) + thisOut <- .rasterize(xx, y, gtype = gtype, field = field, background = background, by = NULL, verbose = FALSE) src[i] <- thisOut$src levels[[i]] <- thisOut$levels From 73186d6faf5326a94590b73d284453e2d7909600 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:45:14 -0500 Subject: [PATCH 019/125] Revert to using `nprocs` when ver >=8.3 --- R/resample.r | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/R/resample.r b/R/resample.r index b9260e9e..dab35c9f 100644 --- a/R/resample.r +++ b/R/resample.r @@ -165,7 +165,7 @@ methods::setMethod( memory = faster("memory"), flags = c(.quiet(), "overwrite") ) - if (versionNumber >= 8.3) args$nprocs <- faster("cores") + if (versionNumber >= 8.4) args$nprocs <- faster("cores") do.call(rgrass::execGRASS, args = args) } @@ -181,6 +181,7 @@ methods::setMethod( memory = faster("memory"), flags = c(.quiet(), "overwrite") ) + # nprocs became available with GRASS eight point three if (versionNumber >= 8.3) args$nprocs <- faster("cores") do.call(rgrass::execGRASS, args = args) @@ -197,6 +198,7 @@ methods::setMethod( memory = faster("memory"), flags = c(.quiet(), "overwrite") ) + # nprocs became available with GRASS eight point three if (versionNumber > 8.3) args$nprocs <- faster("cores") do.call(rgrass::execGRASS, args = args) From 817aeb9756157092b0a6d1fb05943cfdcec662e8 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:45:28 -0500 Subject: [PATCH 020/125] Revert to using `nprocs` when ver >=8.3 --- R/stretch.r | 2 ++ 1 file changed, 2 insertions(+) diff --git a/R/stretch.r b/R/stretch.r index fdad6054..c6ff129c 100644 --- a/R/stretch.r +++ b/R/stretch.r @@ -67,6 +67,7 @@ methods::setMethod( percentile = minq * 100 ) + # nprocs became available with GRASS eight point three if (versionNumber >= 8.3) args$nprocs <- faster("cores") info <- do.call(rgrass::execGRASS, args) @@ -98,6 +99,7 @@ methods::setMethod( percentile = maxq * 100 ) + # nprocs became available with GRASS eight point three if (versionNumber >= 8.3) args$nprocs <- faster("cores") info <- do.call(rgrass::execGRASS, args) From b63da89a17b9b683dd429df4dd7848ece6aef32a Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 16:45:39 -0500 Subject: [PATCH 021/125] Update --- vignettes/GRasters.Rmd | 11 ++++--- vignettes/fasterRaster.Rmd | 1 + vignettes/faster_fasterRaster.Rmd | 5 +-- vignettes/hidden_functions.Rmd | 54 +++++++++++++++++++------------ vignettes/projects_mapsets.Rmd | 1 + vignettes/regions.Rmd | 1 + 6 files changed, 45 insertions(+), 28 deletions(-) diff --git a/vignettes/GRasters.Rmd b/vignettes/GRasters.Rmd index d5cfb776..59f7892c 100644 --- a/vignettes/GRasters.Rmd +++ b/vignettes/GRasters.Rmd @@ -14,20 +14,21 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) +fig.path = 'man/figures/' ``` -**fasterRaster** `GRaster`s can represent double-floating point numeric values, integers, or categorical data. +**fasterRaster** `GRaster`s can represent double-floating point numeric values, integers, or categorical/factor data. -## Double-floating point values +## Double-floating point value Double-floating point values are accurate to about the 15th to 17th decimal place. These are called "double" rasters in **fasterRaster** and `DCELL` rasters in **GRASS**. These rasters typically take the most memory. All `numeric` values in **R** are double-floating point values. -## Floating point values +## Floating point value Less common that double-floating point rasters, floating point rasters are accurate to about the 7th decimal place. These are called "float" rasters in **fasterRaster** and `FCELL` rasters in **GRASS**. These rasters typically take less memory than double-floating point rasters. -## Integers +## Integer Rasters that represent integers are called "integer" rasters in **fasterRaster** and `CELL` rasters in **GRASS**. You can force a raster to be an integer using [`as.int()`](https://adamlilith.github.io/fasterRaster/reference/as.int.html). Some of the functions in [`app()`](https://adamlilith.github.io/fasterRaster/reference/app.html) function will also return integer-type rasters. Integer rasters typically take the least memory. -## Categories +## Categorical/factor Categorical rasters (also called "factor" rasters) are actually integer rasters, but have an associated attribute table that maps each integer value to a category label, such as "wetland" or "forest". The table has at least two columns. The first is integer values, and (by default) the second is category names. This second column is the "active" category column that is used for plotting and in some functions. The active column can be changed using [`activeCat<-`](https://adamlilith.github.io/fasterRaster/reference/activeCat.html). ## Functions relevant to raster data types diff --git a/vignettes/fasterRaster.Rmd b/vignettes/fasterRaster.Rmd index 150aa45a..17b6a9a9 100644 --- a/vignettes/fasterRaster.Rmd +++ b/vignettes/fasterRaster.Rmd @@ -17,6 +17,7 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) +fig.path = 'man/figures/' ``` **fasterRaster** interfaces with **GRASS GIS** to process rasters and spatial vector data. It is intended as an add-on to the **terra** and **sf** packages, and relies heavily upon them. For most rasters and vectors that are small or medium-sized in memory/disk, those packages will almost always be faster. They may also be faster for very large objects. But when they aren't, **fasterRaster** can step in. diff --git a/vignettes/faster_fasterRaster.Rmd b/vignettes/faster_fasterRaster.Rmd index 0e2421c2..f665c94e 100644 --- a/vignettes/faster_fasterRaster.Rmd +++ b/vignettes/faster_fasterRaster.Rmd @@ -14,6 +14,7 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) +fig.path = 'man/figures/' ``` There are several ways to speed up **fasterRaster** functions. These are listed below in order of their most likely gains, with the first few being potentially the largest. @@ -24,8 +25,8 @@ There are several ways to speed up **fasterRaster** functions. These are listed 3. **Increase memory and the number of cores usable by GRASS:** By default, `fasterRaster` use 2 cores and 2048 MB (2 GB) of memory for `GRASS` modules that allow users to specify these values. You can set these to higher values using [`faster()`](https://adamlilith.github.io/fasterRaster/reference/faster.html) and thus potentially speed up some calculations. Functions in newer versions of `GRASS` have more capacity to use these options, so updating `GRASS` to the latest version can help, too. -4. **Turn off automatic removal of temporary files in the disk cache:** To obviate problems with disk space filling up, by default most **fasterRaster** functions delete intermediate files. However, if you are not creating a lot of very big `GRaster`s or `GVector`s, you can skip this time-taking step by setting the `clean` option to `FALSE` using `faster(clean = FALSE)`. By default, this setting is `TRUE`. +4. **Turn off automatic removal of temporary files in the disk cache:** To obviate problems with disk space filling up, by default most **fasterRaster** functions delete intermediate files. However, if you are not creating a lot of very big `GRaster`s or `GVector`s, you can skip this time-taking step by setting the `clean` option to `FALSE` using `faster(clean = FALSE)`. By default, this setting is `TRUE`. This can save a few seconds of runtime for functions that create temporary files. -5. **Do operation on `GRaster`s and `GVector`s in the same coordinate reference system together:** Every time you switch between using a `GRaster` or `GVector` with a different coordinate reference system (CRS), `GRASS` has to spend a few second changing to that CRS. So, you can save some time by doing as much work as possible with objects in one CRS, then switching to work on objects in another CRS. +5. **Do operations on `GRaster`s and `GVector`s in the same coordinate reference system together:** Every time you switch between using a `GRaster` or `GVector` with a different coordinate reference system (CRS), `GRASS` has to spend a few seconds changing to that CRS. You can save some time by doing as much work as possible with objects in one CRS, then switching to work on objects in another CRS. ~ FINIS ~ diff --git a/vignettes/hidden_functions.Rmd b/vignettes/hidden_functions.Rmd index 5a7b3f42..abab79ab 100644 --- a/vignettes/hidden_functions.Rmd +++ b/vignettes/hidden_functions.Rmd @@ -14,39 +14,32 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) +fig.path = 'man/figures/' ``` **fasterRaster** contains a set of "private" functions that users can access using `fasterRaster:::functionName`. These functions are useful for power users and developers. Not all hidden functions are listed here. Often, a method will have a hidden function of the same name that starts with a period (e.g., `.plot()`). This "period" function is intended to be supplied the [`sources()`](https://adamlilith.github.io/fasterRaster/reference/sources.html) name of a `GRaster` or `GVector` from other functions so that the calling function does not need to spend the time creating the `GRaster` or `GVector` pointer before calling the function. "Period" functions will, though, often work on `GRaster`s or `GVector`s, though some error-checking and region re-definition is not conducted. -* `.aggDisaggVect()`: Aggregate or disaggregate a vector using its [`sources()`](https://adamlilith.github.io/fasterRaster/reference/sources.html) name. -* `.copyGSpatial()`: Make a copy of the **GRASS** file pointed to by a `GRaster` or `GVector` +## General +* `.addLocationProject()`: Add a "location" or "project" argument to a list to be passed to [rgrass::execGRASS()] * `.fileExt()`: Get file extension +* `.ls()`: Lists the `sources` of all objects in the active **GRASS** "project/location" +* `.message()`: Display a warning or message if the given warning has not been displayed since **fasterRaster** was attached or if a given number or hours has passed since then +* `.quiet()`: Returns "quiet" if `faster("verbose")` is `TRUE` + +## Rasters and vectors +* `.copyGSpatial()`: Make a copy of the **GRASS** file pointed to by a `GRaster` or `GVector` * `.exists()`: Does the **GRASS** file of a `GRaster` or `GVector` exist? * `.ext()`: Extent from the [`sources()`](https://adamlilith.github.io/fasterRaster/reference/sources.html) name of a `GRaster` or `GVector` -* `.geomtype()`: Geometry type ("point", "line", or "area") from the [`sources()`](https://adamlilith.github.io/fasterRaster/reference/sources.html) name of a `GVector` -* `.layerIndex()`: Gets the index of `GRaster` layers from a numeric, integer, character, or logical vector -* `.locationCreate()` Make a connection to **GRASS** (i.e., start **GRASS** from within **R**) and create a location -* `.locationDelete()` Deletes all files associated with a **GRASS** "location" and mapset -* `.locationFind()`: Find a specific **GRASS** "location" that already exists -* `.locationRestore()` Reconnect **GRASS** to a previously-created **GRASS** "location" -* `.locations()`: List of all available "locations" -* `.ls()`: Lists the `sources` of all objects in the active **GRASS** "location" -* `.makeGRaster()` and `.makeGVector()`: Make `GRaster`s or `GVector`s from a vector of `sources`, which are pointers to files in **GRASS** * `.makeSourceNames()`: Makes one or more statistically unique strings that can be used as file names to represent rasters or vectors in **GRASS** -* `.mapset()`: **GRASS** "mapset" of an object or the active session -* `.message()`: Display a warning or message if the given warning has not been displayed since **fasterRaster** was attached or if a given number or hours has passed since then -* `.minVal()` and `.maxVal()`: Values in the `@minVal` and `@maxVal` slots in a `GRaster` -* `.nlevels()`: Number of levels in a `SpatVector`, `data.frame`, `data.table`, empty string, or a list of `data.frame`s, `data.table`s, and/or empty strings. * `.plot()`: Plot using the [`sources()`](https://adamlilith.github.io/fasterRaster/reference/sources.html) name of a `GRaster` or `GVector` * `.projection()`: Value of the `@projection` slot in a `GRaster` or `GVector` -* `.quiet()`: Returns "quiet" if `faster("verbose")` is `TRUE` * `.rastInfo()` and `.vectInfo()`: Metadata for a **GRASS** raster or vector -* `.region()`: Change or report the active region's extent and resolution -* `.regionDim()]`: Change or report the active region's resolution (also [`dim()`](https://adamlilith.github.io/fasterRaster/reference/dim.html) and related functions, with no arguments) -* `.regionExt()`: Change or report the active region's extent (also [`ext()`](https://adamlilith.github.io/fasterRaster/reference/ext.html) and related functions, with no arguments) -* `.regionRes()`: Change or report the active region's dimensions (also [`res()`](https://adamlilith.github.io/fasterRaster/reference/res.html) and related functions, with no arguments) * `.rename()`: Rename a **GRASS** raster or vector * `.rm()`: Delete rasters or vectors in **GRASS** + +## Vectors +* `.aggDisaggVect()`: Aggregate or disaggregate a vector using its [`sources()`](https://adamlilith.github.io/fasterRaster/reference/sources.html) name. +* `.geomtype()`: Geometry type ("point", "line", or "area") from the [`sources()`](https://adamlilith.github.io/fasterRaster/reference/sources.html) name of a `GVector` * `.validVector()`: Test if a `GVector` is valid. * `.vAsDataTable()`: Convert the attribute table linked to a vector in **GRASS** to a `data.table`. This table is distinct from the attribute table attached to a `GVector` * `.vAttachDatabase()`: Add a database table to the **GRASS** representation of a `GVector` @@ -58,5 +51,24 @@ knitr::opts_chunk$set( * `.vRecat()`: Change **GRASS** category indices of a **GRASS** vector * `.vValidCats()`: Are category values of a vector valid? -~ FINIS ~ +## Rasters +* `.layerIndex()`: Gets the index of `GRaster` layers from a numeric, integer, character, or logical vector +* `.makeGRaster()` and `.makeGVector()`: Make `GRaster`s or `GVector`s from a vector of `sources`, which are pointers to files in **GRASS** +* `.minVal()` and `.maxVal()`: Values in the `@minVal` and `@maxVal` slots in a `GRaster` +* `.nlevels()`: Number of levels in a `SpatVector`, `data.frame`, `data.table`, empty string, or a list of `data.frame`s, `data.table`s, and/or empty strings. + +## **GRAS** "Projects" ("locations") and "mapsets" +* `.locationCreate()` Make a connection to **GRASS** (i.e., start **GRASS** from within **R**) and create a location +* `.locationDelete()` Deletes all files associated with a **GRASS** "location" and mapset +* `.locationFind()`: Find a specific **GRASS** "location" that already exists +* `.locationRestore()` Reconnect **GRASS** to a previously-created **GRASS** "location" +* `.locations()`: List of all available "locations" +* `.mapset()`: **GRASS** "mapset" of an object or the active session +## **GRASS** "regions" +* `.region()`: Change or report the active region's extent and resolution +* `.regionDim()]`: Change or report the active region's resolution (also [`dim()`](https://adamlilith.github.io/fasterRaster/reference/dim.html) and related functions, with no arguments) +* `.regionExt()`: Change or report the active region's extent (also [`ext()`](https://adamlilith.github.io/fasterRaster/reference/ext.html) and related functions, with no arguments) +* `.regionRes()`: Change or report the active region's dimensions (also [`res()`](https://adamlilith.github.io/fasterRaster/reference/res.html) and related functions, with no arguments) + +~ FINIS ~ diff --git a/vignettes/projects_mapsets.Rmd b/vignettes/projects_mapsets.Rmd index 910f7ed7..02bfb51f 100644 --- a/vignettes/projects_mapsets.Rmd +++ b/vignettes/projects_mapsets.Rmd @@ -14,6 +14,7 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) +fig.path = 'man/figures/' ``` **GRASS GIS** uses "projects" (which used to be called "locations" before **GRASS** 8.4), and "mapsets" to store files (rasters, vectors, etc.). **fasterRaster** uses projects and mapsets, too, but in a manner that is invisibly to most users. Thus, this tutorial is mostly of interest to developers and other curious people. diff --git a/vignettes/regions.Rmd b/vignettes/regions.Rmd index b3acd752..03df0fbf 100644 --- a/vignettes/regions.Rmd +++ b/vignettes/regions.Rmd @@ -14,6 +14,7 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) +fig.path = 'man/figures/' ``` A **GRASS** *region* is a data structure like a raster in that it is composed of a grid, but different in that "cells" in this grid do not contain values. Rather, their resolution and the extent of the region influence how rasters are imported, created, processed, and exported. In most cases, whenever a raster undergoes one of these processes using a **GRASS** module, the raster will be resampled and/or crop/extend it so that matches the region"s extent and resolution. If ignored, this can cause unintended side effects if the region's geometry doesn't match the raster being processed. From 49c12633d7925c1a7bd26da21c0d8329ebb38190 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 23:30:11 -0500 Subject: [PATCH 022/125] Add `reference` section --- _pkgdown.yml | 377 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 377 insertions(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index f0d5ca92..5ef48754 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -26,3 +26,380 @@ articles: - projects_mapsets - regions - hidden_functions + +reference: +- title: Where to start +- desc: The most commonly-used functions. +- contents: + - faster + - fast + - rast + - vect + - st_as_sf + - writeRaster + - writeVector +- title: GRaster properties +- contents: + - crs + - datatype + - dim + - dim3d + - E + - ext + - freq + - is.2d + - is.3d + - is.cell + - is.doub + - is.factor + - is.float + - is.int + - is.lonlat + - levels + - minmax + - 'N' + - names + - ncol + - nacell + - ncell + - ncell3d + - ndepth + - nlyr + - nonnacell + - nrow + - nlevels + - res + - res3d + - S + - sources + - topology + - xres + - yres + - zext + - zres +- title: Math, comparison, and logic +- contents: + - Arith + - Compare-methods + - Logic-methods +- title: Math layer-by-layer on GRasters +- contents: + - abs + - acos + - asin + - atan + - atan2 + - ceiling + - cos + - exp + - floor + - is.na + - log + - ln + - log1p + - log2 + - log10 + - maskNA + - not.na + - round + - sin + - sqrt + - tan + - trunc +- title: Math across GRaster layers +- contents: + - count + - kurtosis + - max + - mean + - median + - min + - mmode + - nunique + - quantile + - range + - skewness + - stdev + - sum + - var + - varpop + - which.max + - which.min +- title: Subsetting and replacing GRasters +- contents: + - "`$`" + - "`$<-`" + - "`[`" + - "`[<-`" + - "`[[<-`" + - add<- +- title: Operations on GRasters +- contents: + - "`%in%`" + - "`%notin%`" + - as.doub + - as.int + - as.float + - as.lines + - as.points + - as.polygons + - aggregate + - bioclims + - buffer + - app + - c + - cellSize + - classify + - clump + - combineCats + - combineLevels + - crop + - denoise + - distance + - extend + - extract + - fillNAs + - focal + - fragmentation + - global + - hist + - interpIDW + - kernel + - layerCor + - longlat + - mask + - maskNA + - match + - merge + - names<- + - noise + - pairs + - pca + - pcs + - plot + - plotRGB + - project + - predict + - resample + - reorient + - sampleRast + - stretch + - scale + - scalepop + - selectRange + - spatSample + - subst + - thinLines + - tiles + - trim + - unscale + - zonal + - zonalGeog +- title: Creating GRasters *de novo* +- contents: + - fractalRast + - rnormRast + - rSpatialDepRast + - runifRast + - sineRast +- title: Terrain and hydrology +- contents: + - as.contour + - flow + - flowPath + - geomorphons + - hillshade + - horizonHeight + - sun + - ruggedness + - streams + - terrain + - wetness +- title: Categorical GRasters +- contents: + - "`%in%`" + - "`%notin%`" + - activeCat + - activeCat<- + - activeCats + - addCats + - addCats<- + - categories + - catNames + - cats + - combineCats + - combineLevels + - complete.cases + - droplevels + - freq + - is.factor + - levels + - levels<- + - match + - minmax + - missing.cases + - missingCats + - nlevels + - subst + - zonalGeog +- title: Remote sensing GRasters +- contents: + - compositeRGB + - plotRGB + - vegIndex +- titles: SpatRasters +- contents: + - bioclims + - fragmentation +- title: GVectors Properties +- contents: + - bottom + - crs + - datatype + - dim + - E + - expanse + - ext + - geomtype + - is.2d + - is.3d + - is.lines + - is.lonlat + - is.points + - is.polygons + - 'N' + - names + - ncol + - ngeom + - nrow + - nsubgeom + - S + - sources + - top + - topology + - W + - zext +- title: Subsetting and assignment of GVectors +- contents: + - "`$`" + - "`$<-`" + - "`[`" + - "`[[`" + - addTable<- + - dropTable +- title: Operations on GVectors +- contents: + - aggregate + - as.data.frame + - as.data.table + - as.points + - buffer + - clusterPoints + - colbind + - complete.cases + - connectors + - convHull + - crds + - crop + - delaunay + - disagg + - distance + - dropTable + - erase + - expanse + - extract + - grid + - head + - hexagons + - interpIDW + - interpSplines + - intersect + - kernel + - missing.cases + - names<- + - project + - rasterize + - rbind + - simplifyGeom + - smoothGeom + - st_as_sf + - st_buffer + - tail + - thinPoints + - union + - voronoi + - xor +- title: Creating GVectors *de novo* +- contents: + - rvoronoi +- title: Fixing GVector issues +- contents: + - breakPolys + - fillHoles + - fixBridges + - fixDangles + - fixLines + - remove0 + - removeAngles + - removeBridges + - removeDangles + - removeDupCentroids + - removeDups + - removeSmallPolys + - snap +- title: Converting between data types +- contents: + - as.contour + - as.doub + - as.data.frame + - as.data.table + - as.float + - as.int + - as.lines + - as.points + - as.polygons + - categories + - fast + - levels<- + - rast + - rasterize + - st_as_sf + - vect +- title: General functions +- contents: + - compareGeom + - dropRows + - grass + - grassInfo + - grassStarted + - mow + - replaceNAs + - seqToSQL + - update +- title: Example data +- contents: + - appFunsTable + - fastData + - madChelsa + - madCoast + - madCoast0 + - madCoast4 + - madCover + - madCoverCats + - madDypsis + - madElev + - madForest2000 + - madForest2014 + - madLANDSAT + - madPpt + - madTmax + - madTmin + - madRivers + - vegIndices +- title: Classes +- contents: + - GLocation + - GSpatial + - GRegion + - GRaster + - GVector From a670c08bcebd3ff5629922316833a07d1542b45b Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 23:30:25 -0500 Subject: [PATCH 023/125] Update --- man/app.Rd | 4 ++-- man/dot-addLocationProject.Rd | 2 +- man/fasterRaster.Rd | 37 +++++++++++++++++++++++++++-------- man/res.Rd | 3 ++- man/stretch.Rd | 2 +- man/workDir.Rd | 9 +++++---- 6 files changed, 40 insertions(+), 17 deletions(-) diff --git a/man/app.Rd b/man/app.Rd index 466768dd..b761e11e 100644 --- a/man/app.Rd +++ b/man/app.Rd @@ -1,7 +1,7 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/app.r -\name{app,GRaster-method} -\alias{app,GRaster-method} +\name{app} +\alias{app} \alias{app,lapp} \alias{appFuns} \alias{appCheck,GRaster,character-method} diff --git a/man/dot-addLocationProject.Rd b/man/dot-addLocationProject.Rd index 7fad2810..f7528881 100644 --- a/man/dot-addLocationProject.Rd +++ b/man/dot-addLocationProject.Rd @@ -17,4 +17,4 @@ A \code{list}. \description{ Unhelpfully, starting with \strong{GRASS} 8.4, what were previously called "locations" and now called "projects." These were supposed to be back-compatible, but are not. This function takes a list of arguments that will be passed to \code{\link[rgrass:execGRASS]{rgrass::execGRASS()}} and adds either a "location" or "project" argument, depending on the version of \strong{GRASS} being used. } -\keyword{external} +\keyword{internal} diff --git a/man/fasterRaster.Rd b/man/fasterRaster.Rd index bdb5c81a..8c1a5dd6 100644 --- a/man/fasterRaster.Rd +++ b/man/fasterRaster.Rd @@ -42,7 +42,7 @@ \item \code{\link[=nonnacell]{nonnacell()}}: Number of non-\code{NA} cells \item \code{\link[=nrow]{nrow()}}: Number of rows \item \code{\link[=nlevels]{nlevels()}}: Number of categories -\item \code{\link[=res]{res()}}, \code{\link[=xres]{xres()}}, \code{\link[=yres]{yres()}}, and \code{\link[=zres]{zres()}}: Spatial resolution +\item \code{\link[=res]{res()}}, \code{\link[=res3d]{res3d()}}, \code{\link[=xres]{xres()}}, \code{\link[=yres]{yres()}}, and \code{\link[=zres]{zres()}}: Spatial resolution \item \code{\link[=sources]{sources()}}: Name of the \code{GRaster} in \strong{GRASS} \item \item \code{\link[=zext]{zext()}}: Vertical extent @@ -57,7 +57,7 @@ \item \link[=Logic-methods]{Logical operators}: \code{|}and \code{&} } -Single-layer mathematical functions (applied to each layer of a \code{GRaster}): +Mathematical functions that are applied to each layer of a \code{GRaster}: \itemize{ \item Working with \code{NA}s: \code{\link[=is.na]{is.na()}}, \code{\link[=not.na]{not.na()}}, and \code{\link[=maskNA]{maskNA()}} \item Trigonometry: \code{\link[=sin]{sin()}}, \code{\link[=cos]{cos()}}, \code{\link[=tan]{tan()}}, \code{\link[=asin]{asin()}}, \code{\link[=acos]{acos()}}, \code{\link[=atan]{atan()}}, \code{\link[=atan2]{atan2()}} @@ -66,7 +66,7 @@ Single-layer mathematical functions (applied to each layer of a \code{GRaster}): \item Signs: \code{\link[=abs]{abs()}} } -Multi-layer functions (applied across layers of a "stack" of \code{GRaster}s): +Mathematical functions that are applied across layers of a "stack" of \code{GRaster}s: \itemize{ \item Numeration: \code{\link[=sum]{sum()}}, \code{\link[=count]{count()}} \item Central tendency: \code{\link[=mean]{mean()}}, \code{\link[=mmode]{mmode()}}, \code{\link[=median]{median()}} @@ -110,7 +110,7 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] \item \code{\link[=maskNA]{maskNA()}}: Mask all non-NA cells or all NA cells \item \code{\link[=match]{match()}}, \code{\link[fasterRaster]{\%in\%}}, and \code{\link[fasterRaster]{\%notin\%}}: Find which cells of a \code{GRaster} match or do not match certain values \item \code{\link[=merge]{merge()}}: Combine two or more rasters with different extents and fill in \code{NA}s -\code{\link[fasterRaster]{names<-}}: Assign names to a \code{GRaster} +\item \code{\link[fasterRaster]{names<-}}: Assign names to a \code{GRaster} \item \code{\link[=noise]{noise()}}: Remove coarse-scale trends from a \code{GRaster}, leaving just fine-scale "noise" \item \code{\link[=pairs]{pairs()}}: Plot correlations between \code{GRaster} layers \item \code{\link[=pca]{pca()}}: Apply a principal components analysis (PCA) to a \code{GRaster} @@ -121,9 +121,11 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] \item \code{\link[=predict]{predict()}}: Make predictions to a \code{GRaster} from a linear model or generalized linear model \item \code{\link[=resample]{resample()}}: Change cell size \item \code{\link[=reorient]{reorient()}}: Convert degrees between 'north-orientation' and 'east orientation' +\item \code{\link[=sampleRast]{sampleRast()}}: Randomly sample cells from a \code{GRaster} \item \code{\link[=scale]{scale()}}, \code{\link[=scalepop]{scalepop()}}, and \code{\link[=unscale]{unscale()}}: Subtract means and divide by standard deviations, or inverse of that \item \code{\link[=selectRange]{selectRange()}}: Select values from rasters in a stack based on values in another \code{GRaster} \item \code{\link[=spatSample]{spatSample()}}: Randomly points from a \code{GRaster} +\item \code{\link[=stretch]{stretch()}}: Rescale values in a GRaster \item \code{\link[=subst]{subst()}}: Re-assign cell values \item \code{\link[=thinLines]{thinLines()}}: Reduce linear features on a \code{GRaster} so linear features are 1 cell wide \item \code{\link[=tiles]{tiles()}}: Divide a \code{GRaster} into spatially exclusive subsets (though with possible overlap) @@ -159,7 +161,7 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] } } -\subsection{Functions operating on categorical (factor) rasters}{ +\subsection{Functions operating on categorical (factor) \code{GRaster}s}{ \itemize{ \item \code{\link[fasterRaster]{\%in\%}}, and \code{\link[fasterRaster]{\%notin\%}}: Mask cells that match or do not match a given category \item \code{\link[=activeCat]{activeCat()}} and \code{\link[=activeCats]{activeCats()}}: Column(s) that defines category labels @@ -268,10 +270,17 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] \item \code{\link[=tail]{tail()}}: Last rows of a \code{GVector}'s data table \item \code{\link[=thinPoints]{thinPoints()}}: Reduce number of points in same raster cell \item \code{\link[=union]{union()}} or \code{+}: Combine two \code{GVector}s +\item \code{\link[=voronoi]{voronoi()}}: Voronoi tessellation \item \code{\link[=xor]{xor()}} or \code{/}: Select parts of polygons not shared by two \code{GVector}s } } +\subsection{Functions for creating \code{GVector}s \emph{de novo}}{ +\itemize{ +\item \code{\link[=rvoronoi]{rvoronoi()}}: Random Voronoi tesselation +} +} + \subsection{Functions for fixing issues with \code{GVector}s}{ (See also \emph{Details} \code{\link[=fast]{fast()}}.) @@ -301,8 +310,8 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] \item \code{\link[=as.float]{as.float()}}: Convert a \code{GRaster} to a floating-point raster (\strong{GRASS} data type \code{FCELL}) \item \code{\link[=as.int]{as.int()}}: Convert a \code{GRaster} to an integer raster (\strong{GRASS} data type \code{CELL}) \item \code{\link[=as.points]{as.points()}}, \code{\link[=as.lines]{as.lines()}}, and \code{\link[=as.polygons]{as.polygons()}}: Convert a \code{GRaster} to a \code{GVector} -\item \code{\link[=fast]{fast()}}: Convert a \code{SpatRaster} to a \code{GRaster}; a \code{SpatVector}, \code{sf} vector, numeric vector, \code{matrix}, \code{data.frame}, or \code{data.table} to a \code{GVector}; or load a vector or raster from a file \item \code{\link[=categories]{categories()}} and \code{\link[fasterRaster]{levels<-}}: Convert an integer raster to a categorical ("factor") raster. +\item \code{\link[=fast]{fast()}}: Convert a \code{SpatRaster} to a \code{GRaster}; a \code{SpatVector}, \code{sf} vector, numeric vector, \code{matrix}, \code{data.frame}, or \code{data.table} to a \code{GVector}; or load a vector or raster from a file \item \code{\link[=rast]{rast()}}: Convert a \code{GRaster} to a \code{SpatRaster} \item \code{\link[=rasterize]{rasterize()}}: Convert a \code{GVector} to a \code{GRaster} \item \code{\link[=st_as_sf]{st_as_sf()}}: Convert a \code{GVector} to a \code{sf} vector @@ -314,18 +323,21 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] \itemize{ \item \code{\link[=compareGeom]{compareGeom()}}: Determine if geographic metadata is same between \code{GRaster}s and/or \code{GVector}s \item \code{\link[=dropRows]{dropRows()}}: Remove rows from a \code{data.frame} or \code{data.table} -\item \code{\link[=grassInfo]{grassInfo()}}: \strong{GRASS} version and citation \item \code{\link[=grass]{grass()}}: Start the \strong{GRASS} GUI (not recommended for most users!!!) +\item \code{\link[=grassInfo]{grassInfo()}}: \strong{GRASS} version and citation +\item \code{\link[=grassStarted]{grassStarted()}}: Has a connection \strong{GRASS} been made within the current \strong{R} session? \item \code{\link[=mow]{mow()}}: Remove unused rasters and vectors from the \strong{GRASS} cache \item \code{\link[=reorient]{reorient()}}: Convert degrees between 'north-orientation' and 'east orientation' \item \code{\link[=replaceNAs]{replaceNAs()}}: Replace \code{NA}s in columns of a \code{data.table} or \code{data.frame}, or in a vector \item \code{\link[=seqToSQL]{seqToSQL()}}: Format a numeric series into an SQL value call +\item \code{\link[=workDir]{workDir()}}: \item \code{\link[=update]{update()}}: Refresh metadata in a \code{GRaster} or \code{GVector} object } } \subsection{Data objects}{ \itemize{ +\item \code{\link[=fastData]{fastData()}}: Helper function to quickly obtain example rasters and vectors \item \link{appFunsTable} (see also \code{\link[=appFuns]{appFuns()}}): Functions usable by the \code{\link[=app]{app()}} function \item \link{madChelsa}: Climate rasters for of a portion of eastern Madagascar \item \link{madCoast0}, \link{madCoast4}, and \link{madCoast}: Borders of an eastern portion of Madagascar @@ -347,7 +359,16 @@ The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster] \item Vignette on \strong{GRASS} "projects/locations" and "mapsets": \code{vignette("projects_mapsets", package = "fasterRaster")} \item Vignette on \strong{GRASS} "regions": \code{vignette("regions", package = "fasterRaster")} \item Vignette on \strong{fasteRaster} hidden functions: \code{vignette("hidden_functions", package = "fasterRaster")} -\item \code{\link[=grassStarted]{grassStarted()}}: Has a connection \strong{GRASS} been made within the current \strong{R} session? +} +} + +\subsection{Classes}{ +\itemize{ +\item \code{\link{GLocation}}: Fundamental class; points to a "location/project" in \strong{GRASS} +\item \code{\link{GSpatial}}: Basic class of any spatial object +\item \code{\link{GRegion}}: Points to a "region" of a "location/project" in \strong{GRASS} +\item \code{\link{GRaster}}: Raster class +\item \code{\link{GVector}}: Spatial vector class } } } diff --git a/man/res.Rd b/man/res.Rd index 42717504..c216481c 100644 --- a/man/res.Rd +++ b/man/res.Rd @@ -48,7 +48,7 @@ A numeric vector. For both \code{res()} and \code{res3d()}, the first value is t Spatial resolution of a \code{GRaster}: \itemize{ \item \code{res()}: 2-dimensional resolution (x and y).\cr\cr -\item \code{res3d()}: 3-dimensinal resolution (z, y, and z).\cr\cr +\item \code{res3d()}: 3-dimensional resolution (z, y, and z).\cr\cr \item \code{xres()}, \code{yres()}, and \code{zres()}: East-west resolution, north-south resolution, and top-bottom resolution. } } @@ -176,3 +176,4 @@ layerCor(landsat, fun = 'cov') # covariance \seealso{ \code{\link[terra:dimensions]{terra::res()}} } +\keyword{res} diff --git a/man/stretch.Rd b/man/stretch.Rd index caa2609a..f65ed648 100644 --- a/man/stretch.Rd +++ b/man/stretch.Rd @@ -3,7 +3,7 @@ \name{stretch,GRaster-method} \alias{stretch,GRaster-method} \alias{stretch} -\title{Rescale values in a raster} +\title{Rescale values in a GRaster} \usage{ \S4method{stretch}{GRaster}(x, minv = 0, maxv = 255, minq = 0, maxq = 1, smin = NA, smax = NA) } diff --git a/man/workDir.Rd b/man/workDir.Rd index 8402712a..113e9550 100644 --- a/man/workDir.Rd +++ b/man/workDir.Rd @@ -1,11 +1,11 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/workDir.r -\name{workDir,GLocation-method} -\alias{workDir,GLocation-method} -\alias{workDir} +\name{.workDir,GLocation-method} +\alias{.workDir,GLocation-method} +\alias{.workDir} \title{Get a GLocation's working directory} \usage{ -\S4method{workDir}{GLocation}(x) +\S4method{.workDir}{GLocation}(x) } \arguments{ \item{x}{A \code{GLocation} object.} @@ -16,3 +16,4 @@ Character. \description{ This function returns the working directory of a \code{GLocation} object. } +\keyword{internal} From ffbbf717f107ad472b714320da518621a8d8705b Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 23:30:31 -0500 Subject: [PATCH 024/125] Update res.r --- R/res.r | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/res.r b/R/res.r index cfd7c484..2979e8ae 100644 --- a/R/res.r +++ b/R/res.r @@ -2,7 +2,7 @@ #' #' @description Spatial resolution of a `GRaster`: #' * `res()`: 2-dimensional resolution (x and y).\cr\cr -#' * `res3d()`: 3-dimensinal resolution (z, y, and z).\cr\cr +#' * `res3d()`: 3-dimensional resolution (z, y, and z).\cr\cr #' * `xres()`, `yres()`, and `zres()`: East-west resolution, north-south resolution, and top-bottom resolution. #' #' @param x A `GRaster`, `GRegion`, or missing. If missing, the resolution of the currently active "region" is returned (see `vignette("regions", package = "fasterRaster")`). @@ -16,6 +16,7 @@ #' @aliases res #' @rdname res #' @exportMethod res +#' @keywords res setMethod( f = "res", signature = "missing", From 89acd9d7675324fc523c05fd22d6240a1160cbea Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 23:30:34 -0500 Subject: [PATCH 025/125] Update fasterRaster.r --- R/fasterRaster.r | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/R/fasterRaster.r b/R/fasterRaster.r index d40ec646..d97bda36 100644 --- a/R/fasterRaster.r +++ b/R/fasterRaster.r @@ -33,7 +33,7 @@ #' * [nonnacell()]: Number of non-`NA` cells #' * [nrow()]: Number of rows #' * [nlevels()]: Number of categories -#' * [res()], [xres()], [yres()], and [zres()]: Spatial resolution +#' * [res()], [res3d()], [xres()], [yres()], and [zres()]: Spatial resolution #' * [sources()]: Name of the `GRaster` in **GRASS** #' * [topology()]: Dimensionality (2D or 3D) #' * [zext()]: Vertical extent @@ -44,14 +44,14 @@ #' * [Logical comparisons][Compare-methods]: `<`, `<=`, `==`, `!=`, `>=`, and `>`, plus \code{\link[fasterRaster]{%in%}} and \code{\link[fasterRaster]{%notin%}} (for categorical rasters only) #' * [Logical operators][Logic-methods]: `|`and `&` #' -#' Single-layer mathematical functions (applied to each layer of a `GRaster`): +#' Mathematical functions that are applied to each layer of a `GRaster`: #' - Working with `NA`s: [is.na()], [not.na()], and [maskNA()] #' - Trigonometry: [sin()], [cos()], [tan()], [asin()], [acos()], [atan()], [atan2()] #' - Logarithms and powers: [exp()], [log()], [ln()], [log1p()], [log2()], [log10()], [sqrt()] #' - Rounding: [round()], [floor()], [ceiling()], [trunc()] #' - Signs: [abs()] #' -#' Multi-layer functions (applied across layers of a "stack" of `GRaster`s): +#' Mathematical functions that are applied across layers of a "stack" of `GRaster`s: #' - Numeration: [sum()], [count()] #' - Central tendency: [mean()], [mmode()], [median()] #' - Dispersion: [stdev()], [var()], [varpop()], [nunique()], [range()], [quantile()], [skewness()], [kurtosis()] @@ -92,7 +92,7 @@ #' * [maskNA()]: Mask all non-NA cells or all NA cells #' * [match()], \code{\link[fasterRaster]{%in%}}, and \code{\link[fasterRaster]{%notin%}}: Find which cells of a `GRaster` match or do not match certain values #' * [merge()]: Combine two or more rasters with different extents and fill in `NA`s -#' \code{\link[fasterRaster]{names<-}}: Assign names to a `GRaster` +#' * \code{\link[fasterRaster]{names<-}}: Assign names to a `GRaster` #' * [noise()]: Remove coarse-scale trends from a `GRaster`, leaving just fine-scale "noise" #' * [pairs()]: Plot correlations between `GRaster` layers #' * [pca()]: Apply a principal components analysis (PCA) to a `GRaster` @@ -103,9 +103,11 @@ #' * [predict()]: Make predictions to a `GRaster` from a linear model or generalized linear model #' * [resample()]: Change cell size #' * [reorient()]: Convert degrees between 'north-orientation' and 'east orientation' +#' * [sampleRast()]: Randomly sample cells from a `GRaster` #' * [scale()], [scalepop()], and [unscale()]: Subtract means and divide by standard deviations, or inverse of that #' * [selectRange()]: Select values from rasters in a stack based on values in another `GRaster` #' * [spatSample()]: Randomly points from a `GRaster` +#' * [stretch()]: Rescale values in a GRaster #' * [subst()]: Re-assign cell values #' * [thinLines()]: Reduce linear features on a `GRaster` so linear features are 1 cell wide #' * [tiles()]: Divide a `GRaster` into spatially exclusive subsets (though with possible overlap) @@ -113,14 +115,14 @@ #' * [zonal()]: Statistics (mean, sum, etc.) on areas of a `GRaster` defined by sets of cells with the same values in another `GRaster`, or by geometries in a `GVector` #' * [zonalGeog()]: Geographic statistics (area, perimeter, fractal dimension, etc.) for sets of cells with the same values #' -#' ## Functions for creating `GRaster`s *de novo* +#' ## Creating `GRaster`s *de novo* #' * [fractalRast()]: Create a fractal `GRaster` #' * [rnormRast()]: A random `GRaster` with values drawn from a normal distribution #' * [rSpatialDepRast()]: Create a random `GRaster` with or without spatial dependence #' * [runifRast()]: A random `GRaster` with values drawn from a uniform distribution #' * [sineRast()]: Sine wave rasters #' -#' ## Functions for analysis of terrain and flow of water across landscapes +#' ## Analysis of terrain and hydrology #' * [as.contour()]: Contour lines from a `GRaster` #' * [flow()]: Identify watershed basins and direction and accumulation of flow #' * [flowPath()]: Path of water flow across a landscape @@ -133,7 +135,7 @@ #' * [terrain()]: Slope, aspect, curvature, and partial slopes #' * [wetness()]: Topographic wetness index #' -#' ## Functions operating on categorical (factor) rasters +#' ## Operations on categorical (factor) `GRaster`s #' * \code{\link[fasterRaster]{%in%}}, and \code{\link[fasterRaster]{%notin%}}: Mask cells that match or do not match a given category #' * [activeCat()] and [activeCats()]: Column(s) that defines category labels #' \code{\link[fasterRaster]{activeCat<-}}: Set column that defines category labels @@ -158,7 +160,7 @@ #' * [subst()]: Re-assign category levels #' * [zonalGeog()]: Geographic statistics (area, perimeter, fractal dimension, etc.) for sets of cells with the same values #' -#' ## Functions for analysis of remote sensing rasters +#' ## Analysis of remote sensing rasters #' * [compositeRGB()]: Combine red, green, and blue color bands to make a composite `GRaster` #' * [plotRGB()]: Display a multispectral `GRaster` using red, blue, green, and alpha channels #' * [vegIndex()]: Vegetation indices from surface reflectance @@ -186,7 +188,7 @@ #' * [topology()]: Dimensionality (2D or 3D) #' * [zext()]: Vertical extent #' -#' ## Functions that operate on or create `GVector`s +#' ## Operations on `GVector`s #' * The \code{\link[fasterRaster]{[}} operator can be used to subset geometries of a `GVector`. #' * The [$] and \code{\link[fasterRaster]{[[}} operators can be used to get columns of a `GVector`'s data table. #' * The \code{\link[fasterRaster]{$<-}} operator can be used to replace specific columns of a `GVector`'s data table or to add columns. @@ -229,9 +231,13 @@ #' * [tail()]: Last rows of a `GVector`'s data table #' * [thinPoints()]: Reduce number of points in same raster cell #' * [union()] or `+`: Combine two `GVector`s +#' * [voronoi()]: Voronoi tessellation #' * [xor()] or `/`: Select parts of polygons not shared by two `GVector`s #' -#' ## Functions for fixing issues with `GVector`s +#' ## Creating `GVector`s *de novo* +#' * [rvoronoi()]: Random Voronoi tesselation +#' +#' ## Fixing issues with `GVector`s #' (See also *Details* [fast()].) #' * [breakPolys()]: Break topologically clean areas #' * [fillHoles()]: Fill "holes" of a `GVector` @@ -255,8 +261,8 @@ #' * [as.float()]: Convert a `GRaster` to a floating-point raster (**GRASS** data type `FCELL`) #' * [as.int()]: Convert a `GRaster` to an integer raster (**GRASS** data type `CELL`) #' * [as.points()], [as.lines()], and [as.polygons()]: Convert a `GRaster` to a `GVector` -#' * [fast()]: Convert a `SpatRaster` to a `GRaster`; a `SpatVector`, `sf` vector, numeric vector, `matrix`, `data.frame`, or `data.table` to a `GVector`; or load a vector or raster from a file #' * [categories()] and \code{\link[fasterRaster]{levels<-}}: Convert an integer raster to a categorical ("factor") raster. +#' * [fast()]: Convert a `SpatRaster` to a `GRaster`; a `SpatVector`, `sf` vector, numeric vector, `matrix`, `data.frame`, or `data.table` to a `GVector`; or load a vector or raster from a file #' * [rast()]: Convert a `GRaster` to a `SpatRaster` #' * [rasterize()]: Convert a `GVector` to a `GRaster` #' * [st_as_sf()]: Convert a `GVector` to a `sf` vector @@ -265,15 +271,18 @@ #' ## General purpose functions #' * [compareGeom()]: Determine if geographic metadata is same between `GRaster`s and/or `GVector`s #' * [dropRows()]: Remove rows from a `data.frame` or `data.table` -#' * [grassInfo()]: **GRASS** version and citation #' * [grass()]: Start the **GRASS** GUI (not recommended for most users!!!) +#' * [grassInfo()]: **GRASS** version and citation +#' * [grassStarted()]: Has a connection **GRASS** been made within the current **R** session? #' * [mow()]: Remove unused rasters and vectors from the **GRASS** cache #' * [reorient()]: Convert degrees between 'north-orientation' and 'east orientation' #' * [replaceNAs()]: Replace `NA`s in columns of a `data.table` or `data.frame`, or in a vector #' * [seqToSQL()]: Format a numeric series into an SQL value call +#' * [workDir()]: #' * [update()]: Refresh metadata in a `GRaster` or `GVector` object #' #' ## Data objects +#' * [fastData()]: Helper function to quickly obtain example rasters and vectors #' * [appFunsTable][appFunsTable] (see also [appFuns()]): Functions usable by the [app()] function #' * [madChelsa][madChelsa]: Climate rasters for of a portion of eastern Madagascar #' * [madCoast0][madCoast0], [madCoast4][madCoast4], and [madCoast][madCoast]: Borders of an eastern portion of Madagascar @@ -292,7 +301,13 @@ #' * Vignette on **GRASS** "projects/locations" and "mapsets": `vignette("projects_mapsets", package = "fasterRaster")` #' * Vignette on **GRASS** "regions": `vignette("regions", package = "fasterRaster")` #' * Vignette on **fasteRaster** hidden functions: `vignette("hidden_functions", package = "fasterRaster")` -#' * [grassStarted()]: Has a connection **GRASS** been made within the current **R** session? +#' +#' ## Classes +#' * [`GLocation`]: Fundamental class; points to a "location/project" in **GRASS** +#' * [`GSpatial`]: Basic class of any spatial object +#' * [`GRegion`]: Points to a "region" of a "location/project" in **GRASS** +#' * [`GRaster`]: Raster class +#' * [`GVector`]: Spatial vector class #' #' @author Adam B. Smith #' @name fasterRaster From 0b63c5496c1a10ebc647a672be498307ac6e062f Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 23:30:38 -0500 Subject: [PATCH 026/125] Update stretch.r --- R/stretch.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/stretch.r b/R/stretch.r index c6ff129c..2e361107 100644 --- a/R/stretch.r +++ b/R/stretch.r @@ -1,4 +1,4 @@ -#' Rescale values in a raster +#' Rescale values in a GRaster #' #' @description `stretch()` rescales the values in a `GRaster`. All values can be rescaled, or just values in a user-defined range. This range can be given by specifying either the lower and upper bounds of the range using `smin` and `smax`, and/or by the quantiles (across all cells of the raster) using `minq` and `maxq`. #' From e373e56bff1e59e2675eb9242d6bf8b85c519b89 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 23:30:42 -0500 Subject: [PATCH 027/125] Update app.r --- R/app.r | 1 + 1 file changed, 1 insertion(+) diff --git a/R/app.r b/R/app.r index f2a37fea..7bb38d8b 100644 --- a/R/app.r +++ b/R/app.r @@ -46,6 +46,7 @@ #' #' @example man/examples/ex_app.r #' +#' @name app #' @aliases app,lapp #' @rdname app #' @exportMethod app From 42712deff13ac8e2dbf64761a4898c37aa855519 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 23:30:46 -0500 Subject: [PATCH 028/125] Update addLocationProject.r --- R/addLocationProject.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/addLocationProject.r b/R/addLocationProject.r index bb76c824..5a7b6e0d 100644 --- a/R/addLocationProject.r +++ b/R/addLocationProject.r @@ -7,7 +7,7 @@ #' #' @returns A `list`. #' -#' @keywords external +#' @keywords internal .addLocationProject <- function(args, locProj) { ver <- grassInfo("versionNumber") From 6eb1e2b206f8ed363e755bb4a461459f0db62386 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 27 Sep 2024 23:31:09 -0500 Subject: [PATCH 029/125] Move `workDir()` to internal --- NAMESPACE | 2 +- R/01_generics.r | 2 +- R/workDir.r | 7 ++++--- vignettes/hidden_functions.Rmd | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 306662d4..9b2e86e8 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -32,6 +32,7 @@ exportMethods("addCats<-") exportMethods("addTable<-") exportMethods("levels<-") exportMethods("names<-") +exportMethods(.workDir) exportMethods(Arith) exportMethods(Compare) exportMethods(E) @@ -255,7 +256,6 @@ exportMethods(voronoi) exportMethods(wetness) exportMethods(which.max) exportMethods(which.min) -exportMethods(workDir) exportMethods(writeRaster) exportMethods(writeVector) exportMethods(xor) diff --git a/R/01_generics.r b/R/01_generics.r index 6fae3b53..5aec20a5 100644 --- a/R/01_generics.r +++ b/R/01_generics.r @@ -300,9 +300,9 @@ methods::setGeneric(name = "vegIndex", def = function(x, ...) standardGeneric("v methods::setGeneric(name = "vect", package = "terra") methods::setGeneric(name = "voronoi", package = "terra") +methods::setGeneric(name = ".workDir", def = function(x, ...) standardGeneric(".workDir")) methods::setGeneric(name = "W", def = function(x, ...) standardGeneric("W")) methods::setGeneric(name = "wetness", def = function(x, ...) standardGeneric("wetness")) -methods::setGeneric(name = "workDir", def = function(x, ...) standardGeneric("workDir")) methods::setGeneric(name = "writeRaster", package = "terra") methods::setGeneric(name = "writeVector", package = "terra") diff --git a/R/workDir.r b/R/workDir.r index b70669af..2b791f64 100644 --- a/R/workDir.r +++ b/R/workDir.r @@ -6,11 +6,12 @@ #' #' @returns Character. #' -#' @aliases workDir +#' @aliases .workDir #' @rdname workDir -#' @exportMethod workDir +#' @exportMethod .workDir +#' @keywords internal methods::setMethod( - f = "workDir", + f = ".workDir", signature = c(x = "GLocation"), function(x) x@workDir ) diff --git a/vignettes/hidden_functions.Rmd b/vignettes/hidden_functions.Rmd index abab79ab..7ad6faf0 100644 --- a/vignettes/hidden_functions.Rmd +++ b/vignettes/hidden_functions.Rmd @@ -23,8 +23,9 @@ fig.path = 'man/figures/' * `.addLocationProject()`: Add a "location" or "project" argument to a list to be passed to [rgrass::execGRASS()] * `.fileExt()`: Get file extension * `.ls()`: Lists the `sources` of all objects in the active **GRASS** "project/location" -* `.message()`: Display a warning or message if the given warning has not been displayed since **fasterRaster** was attached or if a given number or hours has passed since then +* `.message()`: Display a warning or message if the given warning has not been displayed since **fasterRaster** was attached or if a given number of hours has passed * `.quiet()`: Returns "quiet" if `faster("verbose")` is `TRUE` +* `.workDir()`: Working directory of a `GLocation` object ## Rasters and vectors * `.copyGSpatial()`: Make a copy of the **GRASS** file pointed to by a `GRaster` or `GVector` From 1b98ec634c5f9bcfaaaa94e660efaba9257b3e5a Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 28 Sep 2024 09:58:32 -0500 Subject: [PATCH 030/125] Add test with `grassStarted()` --- R/grass.r | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/R/grass.r b/R/grass.r index c0db9e5c..3db09404 100644 --- a/R/grass.r +++ b/R/grass.r @@ -16,5 +16,10 @@ methods::setMethod( f = "grass", signature = c(x = "missing"), - function() rgrass::execGRASS("g.gui", ui = "wxpython", flags = c(.quiet(), "f")) + function() { + if (grassStarted()) { + rgrass::execGRASS("g.gui", ui = "wxpython", flags = c(.quiet(), "f")) + } else { + warning("GRASS needs to be started by using `fast()` at least once before starting the GUI.") + } ) From 1f951e2427cf536e0e557d090be65e94985f1df5 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Tue, 1 Oct 2024 00:13:58 -0500 Subject: [PATCH 031/125] Update as.lines.r --- R/as.lines.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/as.lines.r b/R/as.lines.r index 0c2e91b6..6c5ca388 100644 --- a/R/as.lines.r +++ b/R/as.lines.r @@ -1,6 +1,6 @@ #' Convert a raster to a lines vector #' -#' @description [as.lines()] converts a `GRaster` to a "lines" `GVector`. Before you apply this function, you may need to run [thinLines()] on the raster to reduce linear features to a single-cell width. You may also need to use [clean geometry][breakPolys] (especially the "duplicated" and "removeDangles" `method`s) afterward to remove duplicated vertices and "dangling" lines. +#' @description [as.lines()] converts a `GRaster` to a "lines" `GVector`. Before you apply this function, you may need to run [thinLines()] on the raster to reduce linear features to a single-cell width. You may also need to use [clean geometry][breakPolys] (especially the [removeDups()] and [removeDangles()]) afterward to remove duplicated vertices and "dangling" lines. #' #' @param x A `GRaster`. If more than one layer is in the `GRaster`, only the first will be used (with a warning). #' From b320c81d998c3d71031c002f2692d51c5db130fa Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Tue, 1 Oct 2024 18:21:31 -0500 Subject: [PATCH 032/125] Fix missing `}` --- R/grass.r | 1 + 1 file changed, 1 insertion(+) diff --git a/R/grass.r b/R/grass.r index 3db09404..c2e900bf 100644 --- a/R/grass.r +++ b/R/grass.r @@ -22,4 +22,5 @@ methods::setMethod( } else { warning("GRASS needs to be started by using `fast()` at least once before starting the GUI.") } + } ) From 46a5a5dd1a13bf6697b74f9089331b6e3d10aa14 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:41:54 -0500 Subject: [PATCH 033/125] Update _pkgdown.yml --- _pkgdown.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/_pkgdown.yml b/_pkgdown.yml index 5ef48754..b2149e38 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -108,6 +108,8 @@ reference: - trunc - title: Math across GRaster layers - contents: + - allNA + - anyNA - count - kurtosis - max @@ -133,6 +135,7 @@ reference: - "`[<-`" - "`[[<-`" - add<- + - subset - title: Operations on GRasters - contents: - "`%in%`" @@ -166,7 +169,6 @@ reference: - interpIDW - kernel - layerCor - - longlat - mask - maskNA - match @@ -198,6 +200,8 @@ reference: - title: Creating GRasters *de novo* - contents: - fractalRast + - init + - longlat - rnormRast - rSpatialDepRast - runifRast @@ -287,6 +291,7 @@ reference: - "`[[`" - addTable<- - dropTable + - subset - title: Operations on GVectors - contents: - aggregate From 86e8888766d8311d180e55fc483198c84ba3e58e Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:42:04 -0500 Subject: [PATCH 034/125] Delete junk --- junk | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 junk diff --git a/junk b/junk deleted file mode 100644 index 84450a8c..00000000 --- a/junk +++ /dev/null @@ -1,4 +0,0 @@ -GISDBASE: C:/Users/adame/AppData/Local/Temp/Rtmpy6OsE5 -LOCATION_NAME: Tananarive_Paris_Laborde_Grid_gFHKwds00HtL -MAPSET: PERMANENT -GRASS_GUI: text From bf307857b318a32a7f1f5fed535823ccea1ef0a7 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:43:27 -0500 Subject: [PATCH 035/125] Add `subset()` --- R/01_generics.r | 4 ++ R/subset.r | 50 ++++++++++++++++++ man/subset.Rd | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 R/subset.r create mode 100644 man/subset.Rd diff --git a/R/01_generics.r b/R/01_generics.r index 5aec20a5..53096832 100644 --- a/R/01_generics.r +++ b/R/01_generics.r @@ -64,6 +64,8 @@ methods::setGeneric(name = "addCats", package = "terra") methods::setGeneric(name = "addCats<-", def = function(x, ..., value) standardGeneric("addCats<-")) methods::setGeneric(name = "addTable<-", def = function(x, ..., value) standardGeneric("addTable<-")) # methods::setGeneric(name = "andAndAnd", def = function(e1, e2) standardGeneric("andAndAnd")) +methods::setGeneric(name = "allNA", package = "terra") +# anyNA: generic is implicit methods::setGeneric(name = "app", package = "terra") # methods::setGeneric(name = "appFuns", def = function(x, ...) standardGeneric("appFuns")) methods::setGeneric(name = "appCheck", def = function(x, fun, ...) standardGeneric("appCheck")) @@ -156,6 +158,7 @@ methods::setGeneric(name = "hillshade", def = function(x, ...) standardGeneric(" methods::setGeneric(name = "hist", package = "terra") methods::setGeneric(name = "horizonHeight", def = function(x, ...) standardGeneric("horizonHeight")) +methods::setGeneric(name = "init", package = "terra") methods::setGeneric(name = "intersect", package = "terra") methods::setGeneric(name = "intercept", def = function(x, ...) standardGeneric("intercept")) methods::setGeneric(name = "interpIDW", package = "terra") @@ -279,6 +282,7 @@ methods::setGeneric(name = "st_buffer", package = "sf") methods::setGeneric(name = "st_crs", package = "sf") # methods::setGeneric(name = "st_distance", package = "sf") methods::setGeneric(name = "stdev", package = "terra") +methods::setGeneric(name = "subset", package = "terra") methods::setGeneric(name = "subst", package = "terra") methods::setGeneric(name = "summary", def = function(object, ...) standardGeneric("summary")) diff --git a/R/subset.r b/R/subset.r new file mode 100644 index 00000000..f1b2c578 --- /dev/null +++ b/R/subset.r @@ -0,0 +1,50 @@ +#' Subset layers from a GRaster, or specific rows from a GVector +#' +#' @description `subset()` can be used to subset or remove one or more layers from a `GRaster`. It can also be used to subset or remove rows from a `GVector` with a data table. +#' +#' @param x A `GRaster` or `GVector`. +#' +#' @param subset Numeric integer, integer, logical, or character: Indicates the layer(s) of a `GRaster` to subset, or the rows(s) of a `GVector` to return. +#' +#' @param negate Logical: If `TRUE`, all layers or rows in `subset` will be *removed* from the output. Default is `FALSE`. +#' +#' @returns A `GRaster` or `GVector`. +#' +#' @seealso \code{\link[fasterRaster]{[[}}, \code{\link[fasterRaster]{[}} +#' +#' @example man/examples/ex_GRaster_GVector_subset_assign.r +#' +#' @aliases subset +#' @docType methods +#' @rdname subset +#' @exportMethod subset +methods::setMethod( + "subset", + signature = c(x = "GRaster"), + function(x, subset, negate = FALSE) { + + # test indices + subset <- .layerIndex(subset, x, recycle = TRUE, negate = negate) + x[[subset]] + + } # EOF +) + +#' @aliases subset +#' @rdname subset +#' @exportMethod subset +methods::setMethod( + "subset", + signature = c(x = "GVector"), + function(x, subset, negate = FALSE) { + + # turn subset into integer + if (is.logical(subset)) { + if (length(subset) < nr) subset <- rep(subset, length.out = nr) + subset <- which(subset) + } + if (negate) subset <- seq_len(nr)[-subset] + x[subset] + + } # EOF +) diff --git a/man/subset.Rd b/man/subset.Rd new file mode 100644 index 00000000..a3faccac --- /dev/null +++ b/man/subset.Rd @@ -0,0 +1,136 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/subset.r +\docType{methods} +\name{subset,GRaster-method} +\alias{subset,GRaster-method} +\alias{subset} +\alias{subset,GVector-method} +\title{Subset layers from a GRaster, or specific rows from a GVector} +\usage{ +\S4method{subset}{GRaster}(x, subset, negate = FALSE) + +\S4method{subset}{GVector}(x, subset, negate = FALSE) +} +\arguments{ +\item{x}{A \code{GRaster} or \code{GVector}.} + +\item{subset}{Numeric integer, integer, logical, or character: Indicates the layer(s) of a \code{GRaster} to subset, or the rows(s) of a \code{GVector} to return.} + +\item{negate}{Logical: If \code{TRUE}, all layers or rows in \code{subset} will be \emph{removed} from the output. Default is \code{FALSE}.} +} +\value{ +A \code{GRaster} or \code{GVector}. +} +\description{ +\code{subset()} can be used to subset or remove one or more layers from a \code{GRaster}. It can also be used to subset or remove rows from a \code{GVector} with a data table. +} +\examples{ +if (grassStarted()) { + +# Setup +library(terra) + +### GRasters +############ + +# Example data +madElev <- fastData("madElev") # elevation raster +madForest2000 <- fastData("madForest2000") # forest raster +madForest2014 <- fastData("madForest2014") # forest raster + +# Convert SpatRasters to GRasters +elev <- fast(madElev) +forest2000 <- fast(madForest2000) +forest2014 <- fast(madForest2014) + +### Re-assigning values of a GRaster +constant <- elev +constant[] <- pi +names(constant) <- "pi_raster" +constant + +### Re-assigning specific values of a raster +replace <- elev +replace[replace == 1] <- -20 +replace + +### Subsetting specific values of a raster based on another raster +elevInForest <- elev[forest2000 == 1] +plot(c(elev, forest2000, elevInForest), nr = 1) + +### Adding and replacing layers of a GRaster +rasts <- c(elev, constant, forest2000) + +# Combine with another layer: +add(rasts) <- forest2014 # one way +rasts + +rasts <- c(rasts, forest2014) # another way + +### Subsetting GRaster layers + +# Subset: +rasts <- c(elev, forest2000, forest2014) +rasts[[2:3]] +subset(rasts, 2:3) +subset(rasts, c("madForest2000", "madElev")) +rasts[[c("madForest2000", "madElev")]] +rasts$madForest2000 + +# Get every other layer: +rasts[[c(FALSE, TRUE)]] + +### Replacing layers of a GRaster + +# Replace a layer +logElev <- log(elev) +names(logElev) <- "logElev" +rasts$madForest2014 <- logElev +rasts + +# Replace a layer: +rasts[[3]] <- forest2000 +rasts + +### GVectors +############ + +# example data +madDypsis <- fastData("madDypsis") # vector of points + +# Convert SpatVector to GVector +dypsis <- fast(madDypsis) + +### Retrieving GVector columns + +dypsis$species # Returns the column + +dypsis[[c("year", "species")]] # Returns a GRaster with these columns +dypsis[ , c("year", "species")] # Same as above + +### Subsetting GVector geometries + +# Subset first three geometries +dypsis[1:3] +dypsis[1:3, "species"] + +# Get geometries by data table condition +dypsis[dypsis$species == "Dypsis betsimisarakae"] + +### (Re)assigning GVector column values + +# New column +dypsis$pi <- pi + +# Re-assign values +dypsis$pi <- "pie" + +# Re-assign specific values +dypsis$institutionCode[dypsis$institutionCode == "MO"] <- + "Missouri Botanical Garden" + +} +} +\seealso{ +\code{\link[fasterRaster]{[[}}, \code{\link[fasterRaster]{[}} +} From 38eeb57ce0d0785c6ca92718ec184fbf30eb391e Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:43:53 -0500 Subject: [PATCH 036/125] Add `init()` --- R/init.r | 76 +++++++++++++++++++++++++++++++++++++++++++++ man/init.Rd | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 R/init.r create mode 100644 man/init.Rd diff --git a/R/init.r b/R/init.r new file mode 100644 index 00000000..9b43edaa --- /dev/null +++ b/R/init.r @@ -0,0 +1,76 @@ +#' GRaster with values equal to row, column, coordinate, regular, or "chess" +#' +#' @description This function can be used to make a `GRaster` with cell values equal to the cell center's longitude, latitude, row, or column, or in a "chess"-like or "regular" pattern. +#' +#' @param x A `GRaster` to be used as a template. +#' +#' @param fun Character: Any of: +#' * `"x"` or `"y"`: Cell longitude or latitude +#' * `"row"` or `"col"`: Cell row or column +#' * `"chess"`: Alternating values. +#' * `"regular"`: Evenly-spaced cells with the same value. +#' +#' @param odd Logical: If `TRUE` (default), and `fun` is `"chess"`, then the top left cell in the raster will be a "negative" cell. If `FALSE`, then the top left cell with be "positive". +#' +#' @param every Numeric or integer: If `fun` is `"regular"`, then make every `every` cell a "positive" cell, and interstitial cells "negative." The default is 2 (every other cell). +#' +#' @param vals Vector of two numeric values: If `fun` is `"chess"` or `"regular"`, then assign the first value to "positive" cells and the second value to "negative" cells. The default is `c(1, 0)` +#' +#' @returns A `GRaster` with as many layers as `x`. +#' +#' @seealso [terra::init()], [longlat()] +#' +#' @example man/examples/ex_init.r +#' +#' @aliases init +#' @rdname init +#' @exportMethod init +methods::setMethod( + f = "init", + signature = c(x = "GRaster"), + function(x, fun, odd = TRUE, vals = c(0, 1)) { + + funs <- c("x", "y", "row", "col", "chess") + fun <- omnibus::pmatchSafe(fun, funs, n = 1) + + # if (fun %in% c("chess", "regular") && (!omnibus::is.wholeNumber(every) | every < 1)) stop("The value of `every` must be a whole number > 0.") + + .locationRestore(x) + .region(x) + + vals <- as.character(vals) + vals[vals == "NA"] <- "null()" + + nLayers<- nlyr(x) + srcs <- .makeSourceName(paste0("init_", fun), "raster", nLayers) + for (i in seq_len(nLayers)) { + + if (fun != "chess") { + + ex <- paste0(srcs[i], " = ", fun, "()") + + } else if (fun == "chess") { + + if (odd) { + ex <- paste0(srcs[i], " = if((row() % 2 == 1 & col() % 2 == 1) | (row() % 2 == 0 & col() % 2 == 0), ", vals[1L], ", ", vals[2L], ")") + } else { + ex <- paste0(srcs[i], " = if((row() % 2 == 0 & col() % 2 == 1) | (row() % 2 == 1 & col() % 2 == 0), ", vals[1L], ", ", vals[2L], ")") + } + + } else if (fun == "regular") { + + if (odd) { + ex <- paste0(srcs[i], " = if(row() % 2 == 1 | col() % 2 == 1, ", vals[1L], ", ", vals[2L], ")") + } else { + ex <- paste0(srcs[i], " = if(row() % 2 == 0 | col() % 2 == 0, ", vals[1L], ", ", vals[2L], ")") + } + + } + + rgrass::execGRASS("r.mapcalc", expression = ex, flags = c(.quiet(), "overwrite")) + + } + .makeGRaster(srcs, "layer") + + } # EOF +) diff --git a/man/init.Rd b/man/init.Rd new file mode 100644 index 00000000..cb464a56 --- /dev/null +++ b/man/init.Rd @@ -0,0 +1,88 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/init.r +\name{init,GRaster-method} +\alias{init,GRaster-method} +\alias{init} +\title{GRaster with values equal to row, column, coordinate, regular, or "chess"} +\usage{ +\S4method{init}{GRaster}(x, fun, odd = TRUE, vals = c(0, 1)) +} +\arguments{ +\item{x}{A \code{GRaster} to be used as a template.} + +\item{fun}{Character: Any of: +\itemize{ +\item \code{"x"} or \code{"y"}: Cell longitude or latitude +\item \code{"row"} or \code{"col"}: Cell row or column +\item \code{"chess"}: Alternating values. +\item \code{"regular"}: Evenly-spaced cells with the same value. +}} + +\item{odd}{Logical: If \code{TRUE} (default), and \code{fun} is \code{"chess"}, then the top left cell in the raster will be a "negative" cell. If \code{FALSE}, then the top left cell with be "positive".} + +\item{vals}{Vector of two numeric values: If \code{fun} is \code{"chess"} or \code{"regular"}, then assign the first value to "positive" cells and the second value to "negative" cells. The default is \code{c(1, 0)}} + +\item{every}{Numeric or integer: If \code{fun} is \code{"regular"}, then make every \code{every} cell a "positive" cell, and interstitial cells "negative." The default is 2 (every other cell).} +} +\value{ +A \code{GRaster} with as many layers as \code{x}. +} +\description{ +This function can be used to make a \code{GRaster} with cell values equal to the cell center's longitude, latitude, row, or column, or in a "chess"-like or "regular" pattern. +} +\examples{ +if (grassStarted()) { + +# Setup +library(terra) + +# Elevation raster, rivers vector +madElev <- fastData("madElev") + +# Convert to a GRaster +elev <- fast(madElev) + +# Cell coordinates +init(elev, "x") +init(elev, "y") + +# Cell row or column +init(elev, "row") +init(elev, "col") + +# Chess +elevAgg <- aggregate(elev, 32) # make cells bigger so we can see + +chessOdd <- init(elevAgg, "chess") +chessEven <- init(elevAgg, "chess", odd = FALSE) + +chess <- c(chessOdd, chessEven) +names(chess) <- c("odd", "even") +plot(chess) + +# Chess with user-defined values +elevAgg <- aggregate(elev, 32) # make cells bigger so we can see + +chessOdd13 <- init(elevAgg, "chess", vals = c(0, 13)) +chessEven13 <- init(elevAgg, "chess", odd = FALSE, vals = c(0, 13)) + +chess13 <- c(chessOdd13, chessEven13) +names(chess13) <- c("odd", "even") +plot(chess13) + +# Regular +elevAgg <- aggregate(elev, 32) # make cells bigger so we can see + +regOdd <- init(elevAgg, "regular") +regEven <- init(elevAgg, "regular", odd = FALSE) + +reg <- c(regOdd, regEven) +names(reg) <- c("odd", "even") +plot(reg) + + +} +} +\seealso{ +\code{\link[terra:init]{terra::init()}}, \code{\link[=longlat]{longlat()}} +} From 6b40965c3a9e90a8634aa9b18c25a01718d88e09 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:44:14 -0500 Subject: [PATCH 037/125] Fix bug --- R/vAttachDatabase.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/vAttachDatabase.r b/R/vAttachDatabase.r index d37af060..472bdc5a 100644 --- a/R/vAttachDatabase.r +++ b/R/vAttachDatabase.r @@ -118,7 +118,7 @@ flags = c(.quiet(), "overwrite") ) - if (grassInfo("versionNumber") <= 8.3) args$flags <- c(arges$flags, "o") + if (grassInfo("versionNumber") <= 8.3) args$flags <- c(args$flags, "o") do.call(rgrass::execGRASS, args = args) From c4651cf85e51f278d53806862d72ef07dee280ed Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:44:29 -0500 Subject: [PATCH 038/125] Add `subset() --- R/subset_dollar.r | 2 +- R/subset_double_square_brackets.r | 2 ++ R/subset_single_bracket.r | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/R/subset_dollar.r b/R/subset_dollar.r index 59306886..bd591339 100644 --- a/R/subset_dollar.r +++ b/R/subset_dollar.r @@ -8,7 +8,7 @@ #' #' @example man/examples/ex_GRaster_GVector_subset_assign.r #' -#' @seealso \code{\link[fasterRaster]{[}}, \code{\link[fasterRaster]{[[}} +#' @seealso [subset()], \code{\link[fasterRaster]{[}}, \code{\link[fasterRaster]{[[}} #' #' @name $ #' @aliases $,GRaster-method diff --git a/R/subset_double_square_brackets.r b/R/subset_double_square_brackets.r index 61051cb5..4a85947b 100644 --- a/R/subset_double_square_brackets.r +++ b/R/subset_double_square_brackets.r @@ -10,6 +10,8 @@ #' #' @returns A `GRaster` or `GVector`. #' +#' @seealso [subset()] +#' #' @example man/examples/ex_GRaster_GVector_subset_assign.r #' #' @name [[ diff --git a/R/subset_single_bracket.r b/R/subset_single_bracket.r index 4c55fa74..112ad68f 100644 --- a/R/subset_single_bracket.r +++ b/R/subset_single_bracket.r @@ -14,7 +14,7 @@ #' #' @example man/examples/ex_GRaster_GVector_subset_assign.r #' -#' @seealso [$], \code{\link[fasterRaster]{[[}} +#' @seealso [subset()], [$], \code{\link[fasterRaster]{[[}} #' #' @name [ #' @aliases [,GVector,ANY,ANY-method From bf9b8679a0cccfb82e246a4d7d65e92d649ddf4c Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:44:36 -0500 Subject: [PATCH 039/125] Update mow.r --- R/mow.r | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/mow.r b/R/mow.r index 5577302f..e8b9dc78 100644 --- a/R/mow.r +++ b/R/mow.r @@ -72,7 +72,7 @@ mow <- function(x, type = NULL, verbose = TRUE, ask = TRUE) { if ("raster" %in% type & "raster" %in% names(grassObjs)) { toDelete <- grassObjs[names(grassObjs) == "raster"] - if (verbose) omnibus::say("Deleting ", length(toDelete), " rasters from the GRASS cache...") + if (verbose) omnibus::say("Deleting ", length(toDelete), " raster(s) from the GRASS cache...") .rm(toDelete, type = "raster", warn = TRUE, verify = FALSE) out$rasters <- out$rasters + length(toDelete) @@ -81,7 +81,7 @@ mow <- function(x, type = NULL, verbose = TRUE, ask = TRUE) { if ("vector" %in% type & "vector" %in% names(grassObjs)) { toDelete <- grassObjs[names(grassObjs) == "vector"] - if (verbose) omnibus::say("Deleting ", length(toDelete), " vectors from the GRASS cache...") + if (verbose) omnibus::say("Deleting ", length(toDelete), " vector(s) from the GRASS cache...") .rm(toDelete, type = "vector", warn = TRUE, verify = FALSE) out$vectors <- out$vectors + length(toDelete) From 427c2920427a82356e1b2eb06f2051f24c7a56d1 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:44:44 -0500 Subject: [PATCH 040/125] Add `negate` arg --- R/layerIndex.r | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/R/layerIndex.r b/R/layerIndex.r index e10dbbe4..324879c4 100644 --- a/R/layerIndex.r +++ b/R/layerIndex.r @@ -6,10 +6,12 @@ #' #' @param recycle Logical: If `TRUE` (default), and `layer` is logical and smaller in number than the number of layers, then recycle the vector of `layer`. #' +#' @param negate Logical: If `TRUE`, return indices of all layers *not* identified in `layer`. +#' #' @returns An integer vector. #' #' @keywords internal -.layerIndex <- function(layer, x, recycle = TRUE) { +.layerIndex <- function(layer, x, recycle = TRUE, negate = FALSE) { nx <- nlyr(x) @@ -23,6 +25,9 @@ if (any(layer > nx) | any(layer < 1L)) stop("Index out of bounds.") } if (any(!(layer %in% seq_len(nx)))) stop("Raster only contains ", nlyr(x), " layer(s).") + + if (negate) layer <- seq_len(nx)[-layer] + layer } From e80076a75484bb8364c777b678579257dda613e5 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:44:47 -0500 Subject: [PATCH 041/125] Update fasterRaster.r --- R/fasterRaster.r | 51 ++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/R/fasterRaster.r b/R/fasterRaster.r index d97bda36..ee1b02b3 100644 --- a/R/fasterRaster.r +++ b/R/fasterRaster.r @@ -45,21 +45,27 @@ #' * [Logical operators][Logic-methods]: `|`and `&` #' #' Mathematical functions that are applied to each layer of a `GRaster`: -#' - Working with `NA`s: [is.na()], [not.na()], and [maskNA()] -#' - Trigonometry: [sin()], [cos()], [tan()], [asin()], [acos()], [atan()], [atan2()] -#' - Logarithms and powers: [exp()], [log()], [ln()], [log1p()], [log2()], [log10()], [sqrt()] -#' - Rounding: [round()], [floor()], [ceiling()], [trunc()] -#' - Signs: [abs()] +#' * Working with `NA`s: [is.na()], [not.na()], and [maskNA()] +#' * Trigonometry: [sin()], [cos()], [tan()], [asin()], [acos()], [atan()], [atan2()] +#' * Logarithms and powers: [exp()], [log()], [ln()], [log1p()], [log2()], [log10()], [sqrt()] +#' * Rounding: [round()], [floor()], [ceiling()], [trunc()] +#' * Signs: [abs()] #' #' Mathematical functions that are applied across layers of a "stack" of `GRaster`s: -#' - Numeration: [sum()], [count()] -#' - Central tendency: [mean()], [mmode()], [median()] -#' - Dispersion: [stdev()], [var()], [varpop()], [nunique()], [range()], [quantile()], [skewness()], [kurtosis()] -#' - Extremes: [min()], [max()], [which.min()], [which.max()] +#' * Numeration: [sum()], [count()] +#' * Central tendency: [mean()], [mmode()], [median()] +#' * Dispersion: [stdev()], [var()], [varpop()], [nunique()], [range()], [quantile()], [skewness()], [kurtosis()] +#' * Extremes: [min()], [max()], [which.min()], [which.max()] +#' * `NA`s: [allNA()], [anyNA()] #' -#' The operators [$] and \code{\link[fasterRaster]{[[}} can be used to subset or remove specific layers of a `GRaster`. -#' The \code{\link[fasterRaster]{[<-}} operator can be used to replace values of cells of a `GRaster`. -#' The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster]{[[<-}}, and \code{\link[fasterRaster]{add<-}}, can be used to replace specific layers of a `GRaster`. +#' Subsetting, assigning, and replacing `GRaster` layers +#' * : Subset or remove specific layers of a `GRaster` +#' * [$], \code{\link[fasterRaster]{[[}}, or [subset()]: Subset or remove specific layers of a `GRaster` +#' * \code{\link[fasterRaster]{[<-}}: Replace values of cells of a `GRaster` +#' * \code{\link[fasterRaster]{[[<-}}: Replace specific layers of a `GRaster` +#' * [fasterRaster]{add<-}}: Replace specific layers of a `GRaster` +#' +#' Operations on `GRaster`s #' * [as.int()], [as.float()], [as.doub()]: Change data type (integer/float/double) #' * [as.lines()]: Convert a `GRaster` to a "lines" vector #' * [as.points()]: Convert a `GRaster` to a "points" vector @@ -87,7 +93,6 @@ #' * [interpIDW()]: Interpolate values at points to a `GRaster` #' * [kernel()]: Kernel density estimator of points #' * [layerCor()]: Correlation or covariance between two or more `GRaster` layers -#' * [longlat()]: Create longitude/latitude rasters #' * [mask()]: Remove values in a `GRaster` based on values in another `GRaster` or vector #' * [maskNA()]: Mask all non-NA cells or all NA cells #' * [match()], \code{\link[fasterRaster]{%in%}}, and \code{\link[fasterRaster]{%notin%}}: Find which cells of a `GRaster` match or do not match certain values @@ -117,6 +122,8 @@ #' #' ## Creating `GRaster`s *de novo* #' * [fractalRast()]: Create a fractal `GRaster` +#' * [init()]: GRaster with values equal to row, column, coordinate, regular, or "chess" +#' * [longlat()]: Create longitude/latitude rasters #' * [rnormRast()]: A random `GRaster` with values drawn from a normal distribution #' * [rSpatialDepRast()]: Create a random `GRaster` with or without spatial dependence #' * [runifRast()]: A random `GRaster` with values drawn from a uniform distribution @@ -188,11 +195,14 @@ #' * [topology()]: Dimensionality (2D or 3D) #' * [zext()]: Vertical extent #' -#' ## Operations on `GVector`s -#' * The \code{\link[fasterRaster]{[}} operator can be used to subset geometries of a `GVector`. -#' * The [$] and \code{\link[fasterRaster]{[[}} operators can be used to get columns of a `GVector`'s data table. -#' * The \code{\link[fasterRaster]{$<-}} operator can be used to replace specific columns of a `GVector`'s data table or to add columns. +#' ## Subsetting and assigning geometries or rows and columns of `GVector`s +#' * [$] or \code{\link[fasterRaster]{[[}}: Subset columns of a `GVector`'s data table +#' * \code{\link[fasterRaster]{[}} or [subset()]: Subset geometries of a `GVector` +#' * \code{\link[fasterRaster]{$<-}}: Replace specific columns of a `GVector`'s data table or add columns #' * \code{\link[fasterRaster]{addTable<-}}: Add a data table to a `GVector` +#' * [dropTable()]: Remove a `GVector`s data table +#' +#' ## Operations on `GVector`s #' * [aggregate()]: Combine `GVector` geometries #' * [as.data.frame()]: Convert a `GVector`'s attribute table to a `data.frame` #' * [as.data.table()]: Convert a `GVector`'s attribute table to a `data.table` @@ -208,7 +218,6 @@ #' * [delaunay()]: Delaunay triangulation #' * [disagg()]: Separate multipart geometries into singlepart geometries #' * [distance()]: Distance between geometries in two `GVector`, or from a `GVector` to cells of a `GRaster` -#' * [dropTable()]: Remove the data table from a `GVector` #' * [erase()] or `-`: Remove part of a `GVector` that overlaps with another #' * [expanse()]: Area of polygons or length of lines #' * [extract()]: Extract values from a `GVector` at specific points @@ -217,8 +226,8 @@ #' * [hexagons()]: Create a hexagonal grid #' * [interpIDW()]: Interpolate values at points to a `GRaster` using inverse-distance weighting #' * [interpSplines()]: Interpolate values at points to a `GRaster` using splines -#' * [intersect()] or `*`: Intersection of two `GVectors`. -#' * [kernel()]: Kernel density estimator of points. +#' * [intersect()] or `*`: Intersection of two `GVectors` +#' * [kernel()]: Kernel density estimator of points #' * [missing.cases()]: Find rows of a `GVector`'s data table that have at least `NA` in them #' \code{\link[fasterRaster]{names<-}}: Assign names to columns of a `GVector`s data table #' * [project()]: Change coordinate reference system @@ -294,7 +303,7 @@ #' * [madLANDSAT][madLANDSAT]: Surface reflectance in 2023 #' * [madPpt][madPpt], [madTmin][madTmin], [madTmax][madTmax]: Rasters of mean monthly precipitation, and minimum and maximum temperature #' * [madRivers][madRivers]: Rivers vector -#' * [vegIndices][vegIndices]: Vegetation indices that can be calculated using [vegIndex()]. +#' * [vegIndices][vegIndices]: Vegetation indices that can be calculated using [vegIndex()] #' #' ## Esoteric tutorials and arcane notes #' * Comparisons between `GRegion`s can be performed using the `==` and `!=` operators. From debf2010f632cd1e671441ce2362b9483f354cef Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:44:54 -0500 Subject: [PATCH 042/125] Update fast.r --- R/fast.r | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/R/fast.r b/R/fast.r index d143152d..a7a74343 100644 --- a/R/fast.r +++ b/R/fast.r @@ -247,7 +247,7 @@ methods::setMethod( x <- terra::vect(x) out <- fast(x, extent = extent, correct = correct, snap = snap, area = area, steps = steps, resolve = resolve, dropTable = dropTable, verbose = verbose, ...) - # x is a filename and xVect is present: we have come through a method for SpatVectors or sf objects + # x is a filename and xVect is present: we have come here through a method for SpatVectors or sf objects } else { if (!is.na(resolve)) resolve <- omnibus::pmatchSafe(resolve, c("aggregate", "disaggregate"), n = 1L) @@ -315,7 +315,6 @@ methods::setMethod( src <- .makeSourceName("fast_v_in_ogr", "vector") if (is.null(snap) & is.null(area)) { - # slower if we need to record messages suppressMessages( run <- rgrass::execGRASS( @@ -332,7 +331,11 @@ methods::setMethod( ) ) - valid <- !any(grepl(run, pattern = "WARNING: The output contains topological errors")) + valid <- !any(c( + grepl(run, pattern = "WARNING: The output contains topological errors"), + grepl(run, pattern = "Invalid argument") + )) + if (valid) { # more thorough test... slower info <- .vectInfo(src) @@ -654,7 +657,7 @@ methods::setMethod( methods::setMethod( "fast", signature(x = "sf"), - function(x, extent = NULL, correct = TRUE, snap = NULL, area = NULL, steps = 10, dropTable = FALSE, resolve = NA, verbose = TRUE) .fastVector(x, correct = correct, snap = snap, area = area, steps = steps, extent = extent, dropTable = dropTable, resolve = resolve, verbose = verbose) + function(x, extent = NULL, correct = TRUE, snap = NULL, area = NULL, steps = 10, resolve = NA, dropTable = FALSE, verbose = TRUE) .fastVector(x, correct = correct, snap = snap, area = area, steps = steps, extent = extent, dropTable = dropTable, resolve = resolve, verbose = verbose) ) #' @rdname fast @@ -877,7 +880,7 @@ methods::setMethod( # NB we ***need** a table with the GVector--otherwise, subset_single_bracket does not work as expected if (is.null(table)) xVect$DUMMYDUMMY_ <- 1L:nrow(xVect) - vectFile <- tempfile(fileext = ".gpkg") + vectFile <- paste0(faster("workDir"), "/", omnibus::rstring(1L), ".gpkg") terra::writeVector(xVect, filename = vectFile, filetype = "GPKG", overwrite = TRUE) } else { From 7dc43c95abf1496304fe8e1fe2c8595b1557d308 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:45:11 -0500 Subject: [PATCH 043/125] Fix bug swapping vector and raster! --- R/exists.r | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/exists.r b/R/exists.r index cd20a2aa..76a04e6e 100644 --- a/R/exists.r +++ b/R/exists.r @@ -18,7 +18,7 @@ methods::setMethod( .locationRestore(x) src <- sources(x) - src %in% .ls(type = "vector") + src %in% .ls(type = "raster") } # EOF ) @@ -33,7 +33,7 @@ methods::setMethod( .locationRestore(x) src <- sources(x) - src %in% .ls(type = "raster") + src %in% .ls(type = "vector") } # EOF ) From a923f6dac3e0f23ff965741bae26a801213e41cf Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:45:16 -0500 Subject: [PATCH 044/125] Update 08_logic.r --- R/08_logic.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/08_logic.r b/R/08_logic.r index d1d692d3..08edc6a6 100644 --- a/R/08_logic.r +++ b/R/08_logic.r @@ -1,6 +1,6 @@ #' Logic-methods operations on GRasters #' -#' @description You can do logical operations on `GRaster`s. Here, a value of 1 is interpreted as `TRUE`, and a value of 0 is interpreted as `FALSE`. You can compare: +#' @description You can do logical operations on `GRaster`s. A cell with a value of 1 is interpreted as `TRUE`, and a value of 0 is interpreted as `FALSE`. You can compare: #' * A `GRaster` to another `GRaster` #' * A `GRaster` to a logical value (`TRUE` or `FALSE`, but not `NA`--see [not.na()]) #' * A `GRaster` to a numeric or integer value that is 0 or 1 From 68b2697845b93d420e2364a289d95962e09dd8e4 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:45:34 -0500 Subject: [PATCH 045/125] Add `anyNA()` and `allNA()` --- R/06_GRaster_functions_acrossLayers.r | 51 ++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/R/06_GRaster_functions_acrossLayers.r b/R/06_GRaster_functions_acrossLayers.r index c34b7ae5..2456eb79 100644 --- a/R/06_GRaster_functions_acrossLayers.r +++ b/R/06_GRaster_functions_acrossLayers.r @@ -6,6 +6,7 @@ #' * Extremes: `min()`, `max()`, `which.min()` (index of raster with the minimum value), `which.max()` (index of the raster with the maximum value) #' * Dispersion: `range()`, `stdev()` (standard deviation), `var()` (sample variance), `varpop()` (population variance), `nunique()` (number of unique values), `quantile()` (use argument `probs`), `skewness()`, and `kurtosis()`. #' * Regression: Assuming we calculate a linear regression for each set of cells through all values of the cells, we can calculate its `slope()`, `intercept()`, `r2()`, and `tvalue()`. +#' * `NA`s: `anyNA()` (any cells are `NA`?), `allNA()` (are all cells `NA`?) #' #' @param x A `GRaster`. Typically, this raster will have two or more layers. Values will be calculated within cells across rasters. #' @@ -17,7 +18,7 @@ #' #' @returns A `GRaster`. #' -#' @example man/examples/ex_GRaster_arithmetic.r +#' @example man/examples/ex_GRaster_arithmetic_across_layers.r #' #' @aliases mean #' @rdname functions @@ -466,6 +467,54 @@ setMethod( } # EOF ) +#' @aliases anyNA +#' @rdname functions +#' @exportMethod anyNA +setMethod( + "anyNA", + signature(x = "GRaster"), + function(x) { + + .locationRestore(x) + .region(x) + + src <- .makeSourceName("anyNA", "raster") + + nl <- nlyr(x) + ex <- paste0("if(isnull(", sources(x), "))") + ex <- paste(ex, collapse = " | ") + ex <- paste0(src, " = ", ex) + rgrass::execGRASS("r.mapcalc", expression = ex, flags = c(.quiet(), "overwrite")) + .makeGRaster(src, "layer") + + } # EOF +) + + +#' @aliases allNA +#' @rdname functions +#' @exportMethod allNA +setMethod( + "allNA", + signature(x = "GRaster"), + function(x) { + + .locationRestore(x) + .region(x) + + src <- .makeSourceName("anyNA", "raster") + + nl <- nlyr(x) + ex <- paste0("if(isnull(", sources(x), "))") + ex <- paste(ex, collapse = " & ") + ex <- paste0(src, " = ", ex) + rgrass::execGRASS("r.mapcalc", expression = ex, flags = c(.quiet(), "overwrite")) + .makeGRaster(src, "layer") + + } # EOF +) + + # generic function for multi-layer functions # fx: name of GRASS function From c8e273388f510261d8c497dd9b5ffb1a364e36e5 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:45:44 -0500 Subject: [PATCH 046/125] Split help --- R/04_arithmetic.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/04_arithmetic.r b/R/04_arithmetic.r index 28fd9fa0..39783e5a 100644 --- a/R/04_arithmetic.r +++ b/R/04_arithmetic.r @@ -12,7 +12,7 @@ #' #' @return A `GRaster`. #' -#' @example man/examples/ex_GRaster_arithmetic.r +#' @example man/examples/ex_GRaster_arithmetic_single_layer.r #' #' @aliases Arith #' @rdname Arithmetic From 1953972c3321292763c6d33b1b871250d5b5c1a0 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:45:48 -0500 Subject: [PATCH 047/125] Update 03_options.r --- R/03_options.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/03_options.r b/R/03_options.r index 218c36fa..cfd19548 100644 --- a/R/03_options.r +++ b/R/03_options.r @@ -34,7 +34,7 @@ #' #' * `verbose` (logical): If `TRUE`, show **GRASS** messages and otherwise hidden slots in classes. This is mainly used for debugging, so most users will want to keep this at its default, `FALSE`. #' -#' * `workDir` (character): The folder in which **GRASS** rasters, vectors, and other objects are created and manipulated. By default, this is given by [tempdir()]. +#' * `workDir` (character): The folder in which **GRASS** rasters, vectors, and other objects are created and manipulated. By default, this is given by [tempdir()]. Note that on some systems, changing the default folder to somewhere else can cause problems with **fasterRaster** being able to find rasters in **GRASS** that have been created. #' #' @param restore Logical: If `TRUE`, the all options will be reset to their default values. The default is `FALSE`. #' From 8a08fa6e70ba0a3ebc4f156ab0a67b8a1e7f10a4 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:45:53 -0500 Subject: [PATCH 048/125] Update 02_defaults.r --- R/02_defaults.r | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/02_defaults.r b/R/02_defaults.r index b7a6e572..6ee1330b 100644 --- a/R/02_defaults.r +++ b/R/02_defaults.r @@ -3,7 +3,8 @@ # global PUBLIC options .grassDirDefault <- function() NA_character_ .addonsDirDefault <- function() NA_character_ -.workDirDefault <- function() file.path(omnibus::forwardSlash(tempdir())) +# .workDirDefault <- function() file.path(omnibus::forwardSlash(tempdir()), omnibus::rstring(1)) +.workDirDefault <- function() omnibus::forwardSlash(tempdir()) .locationDefault <- function() "default" # .mapsetDefault <- function() "PERMANENT" From 83702531dc8b157b671b72a827655bcf8efb8d2b Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:49:35 -0500 Subject: [PATCH 049/125] Update --- NAMESPACE | 4 ++++ NEWS.md | 13 ++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 9b2e86e8..af33c732 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -46,6 +46,8 @@ exportMethods(activeCat) exportMethods(activeCats) exportMethods(addCats) exportMethods(aggregate) +exportMethods(allNA) +exportMethods(anyNA) exportMethods(app) exportMethods(appCheck) exportMethods(as.contour) @@ -125,6 +127,7 @@ exportMethods(hexagons) exportMethods(hillshade) exportMethods(hist) exportMethods(horizonHeight) +exportMethods(init) exportMethods(intercept) exportMethods(interpIDW) exportMethods(interpSplines) @@ -231,6 +234,7 @@ exportMethods(st_crs) exportMethods(stdev) exportMethods(streams) exportMethods(stretch) +exportMethods(subset) exportMethods(subst) exportMethods(sum) exportMethods(summary) diff --git a/NEWS.md b/NEWS.md index 49be2efb..11740002 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,19 +1,22 @@ # fasterRaster 8.4.0.7027 (2024-09-XX) ### Main task for this version -o Run all examples with **GRASS 8.4**! - -### Potentially code-breaking changes -o `aggregate()` no longer has the `dissolve` argument for `GVector`s (polygons will always be dissolved). +o Test examples with **GRASS 8.4** and update functions as needed. ### Updates for **GRASS 8.4** o `addLocationProject()` adds either a `project` or `location` argument to a `list` to be passed to `rgrass::execGRASS()`. o `project()` work with **GRASS** 8.4. -o `.vAttachDatabase()` no longer has the `"o"` flag when calling `v.db.connect` when running **GRASS >=8.4**. +o `.vAttachDatabase()` no longer has the `"o"` flag when calling `v.db.connect` when running **GRASS** >=8.4. + +### Potentially code-breaking changes +o `aggregate()` no longer has the `dissolve` argument for `GVector`s (polygons will always be dissolved). ### Enhanced functionality and new functions o `extract()` now automatically projects a `GVector` to match the CRS of a `GRaster` from which extraction is being made. o `grass()` allows users to start the **GRASS** GUI. +o `layerIndex()` allows a `negate` argument to get the "opposite" indices of a `GRaster`. +o `init()` assigns to `GRaster` cells the value of their coordinates, rows, columns, or values in a regular or chessboard-like pattern. +o `subset()` subsets layers of a `GRaster` or rows/geometries of a `GVector`. ### Bug fixes o `categories()` correctly assigns active category column. From 326bc558b8839400cd8e4552e2cedd84767fc660 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 3 Oct 2024 23:49:53 -0500 Subject: [PATCH 050/125] Recover from GHD BSOD crash --- man/Arithmetic.Rd | 162 -------- man/Compare-methods.Rd | 162 -------- man/Logic-methods.Rd | 164 +------- man/add.Rd | 3 + man/as.lines.Rd | 2 +- man/bioclims.Rd | 2 +- man/dot-layerIndex.Rd | 4 +- .../ex_GRaster_GVector_subset_assign.r | 3 + .../ex_GRaster_arithmetic_across_layers.r | 61 +++ .../ex_GRaster_arithmetic_single_layer.r.r | 129 +++++++ man/examples/ex_bioclims.r | 2 +- man/examples/ex_init.r | 51 +++ man/fast.Rd | 2 +- man/faster.Rd | 2 +- man/fasterRaster.Rd | 364 ------------------ man/functions.Rd | 202 +++------- man/math.Rd | 162 -------- man/replace_dollar.Rd | 3 + man/replace_double_square_brackets.Rd | 3 + man/replace_single_square_bracket.Rd | 3 + man/subset_dollar.Rd | 5 +- man/subset_double_square_brackets.Rd | 6 + man/subset_single_bracket.Rd | 5 +- 23 files changed, 335 insertions(+), 1167 deletions(-) create mode 100644 man/examples/ex_GRaster_arithmetic_across_layers.r create mode 100644 man/examples/ex_GRaster_arithmetic_single_layer.r.r create mode 100644 man/examples/ex_init.r diff --git a/man/Arithmetic.Rd b/man/Arithmetic.Rd index 95faaa19..1925144e 100644 --- a/man/Arithmetic.Rd +++ b/man/Arithmetic.Rd @@ -43,165 +43,3 @@ A \code{GRaster}. \code{*} operator: Same as \code{\link[=intersect]{intersect()}}\cr \code{/} operator: Same as \code{\link[=xor]{xor()}}\cr } -\examples{ -if (grassStarted()) { - -# Setup -library(sf) -library(terra) - -# Example data -madElev <- fastData("madElev") - -# Convert a SpatRaster to a GRaster -elev <- fast(madElev) -elevs <- c(elev, elev, log10(elev) - 1, sqrt(elev)) -names(elevs) <- c("elev1", "elev2", "log_elev", "sqrt_elev") - -elev -elevs - -# do some math -elev + 100 -elev - 100 -elev * 100 -elev / 100 -elev ^ 2 -elev \%/\% 100 # divide then round down -elev \%\% 100 # modulus - -100 + elev -100 \%/\% elev -100 \%\% elev - -elevs + 100 -100 + elevs - -# math with logicals -elev + TRUE -elev - TRUE -elev * TRUE -elev / TRUE -elev ^ TRUE -elev \%/\% TRUE # divide then round down -elev \%\% TRUE # modulus - -elevs + TRUE -TRUE + elevs - -# Raster interacting with raster(s): -elev + elev -elev - elev -elev * elev -elev / elev -elev ^ log(elev) -elev \%/\% sqrt(elev) # divide then round down -elev \%\% sqrt(elev) # modulus - -elevs + elev -elev * elevs - -# sign -abs(-1 * elev) -abs(elevs) - -# powers -sqrt(elevs) - -# trigonometry -sin(elev) -cos(elev) -tan(elev) - -asin(elev) -acos(elev) -atan(elev) - -atan(elevs) -atan2(elev, elev^1.2) -atan2(elevs, elev^1.2) -atan2(elev, elevs^1.2) -atan2(elevs, elevs^1.2) - -# logarithms -exp(elev) -log(elev) -ln(elev) -log2(elev) -log1p(elev) -log10(elev) -log10p(elev) -log(elev, 3) - -log(elevs) - -# rounding -round(elev + 0.5) -floor(elev + 0.5) -ceiling(elev + 0.5) -trunc(elev + 0.5) - -# comparison -elev < 100 -elev <= 100 -elev == 100 -elev != 100 -elev > 100 -elev >= 100 - -elev + 100 < 2 * elev - -elevs > 10 -10 > elevs - -# logic -elev < 10 | elev > 200 -elev < 10 | cos(elev) > 0.9 - -elev < 10 | TRUE -TRUE | elev > 200 - -elev < 10 | FALSE -FALSE | elev > 200 - -elev < 10 & cos(elev) > 0.9 - -elev < 10 & TRUE -TRUE & elev > 200 - -elev < 10 & FALSE -FALSE & elev > 200 - -# Mathematical functions on GRasters with >= 2 layers: -mean(elevs) -mmode(elevs) -median(elevs) -nunique(elevs) - -sum(elevs) -count(elevs) -min(elevs) -max(elevs) -range(elevs) -skewness(elevs) -kurtosis(elevs) - -which.min(elevs) -which.max(elevs) - -slope(elevs) -intercept(elevs) -r2(elevs) -tvalue(elevs) - -stdev(elevs) -stdev(elevs, pop = FALSE) -var(elevs) -varpop(elevs) - -# Note: To get quantiles for each layer, use -# global(x, "quantile", probs = 0.2). -quantile(elevs, 0.1) - -} -} diff --git a/man/Compare-methods.Rd b/man/Compare-methods.Rd index 5e8b34ea..3f57f046 100644 --- a/man/Compare-methods.Rd +++ b/man/Compare-methods.Rd @@ -51,165 +51,3 @@ You can do comparative operations on \code{GRaster}s using normal operators in \ You can also compare two \code{GRegion}s using the \code{==} and \code{!=} operators. Most users of \strong{fasterRaster} will not have to work much with "regions" (see \code{vignette("regions", package = "fasterRaster")}), so can ignore this functionality. \code{GRegion}s are the same if they have the same coordinate reference system, location/project and mapset (see \code{vignette("projects_mapsets", package = "fasterRaster")}), topology (2D or 3D), extent, and resolution. If both are 3D, then they must also have the same vertical extent and number of depths. } -\examples{ -if (grassStarted()) { - -# Setup -library(sf) -library(terra) - -# Example data -madElev <- fastData("madElev") - -# Convert a SpatRaster to a GRaster -elev <- fast(madElev) -elevs <- c(elev, elev, log10(elev) - 1, sqrt(elev)) -names(elevs) <- c("elev1", "elev2", "log_elev", "sqrt_elev") - -elev -elevs - -# do some math -elev + 100 -elev - 100 -elev * 100 -elev / 100 -elev ^ 2 -elev \%/\% 100 # divide then round down -elev \%\% 100 # modulus - -100 + elev -100 \%/\% elev -100 \%\% elev - -elevs + 100 -100 + elevs - -# math with logicals -elev + TRUE -elev - TRUE -elev * TRUE -elev / TRUE -elev ^ TRUE -elev \%/\% TRUE # divide then round down -elev \%\% TRUE # modulus - -elevs + TRUE -TRUE + elevs - -# Raster interacting with raster(s): -elev + elev -elev - elev -elev * elev -elev / elev -elev ^ log(elev) -elev \%/\% sqrt(elev) # divide then round down -elev \%\% sqrt(elev) # modulus - -elevs + elev -elev * elevs - -# sign -abs(-1 * elev) -abs(elevs) - -# powers -sqrt(elevs) - -# trigonometry -sin(elev) -cos(elev) -tan(elev) - -asin(elev) -acos(elev) -atan(elev) - -atan(elevs) -atan2(elev, elev^1.2) -atan2(elevs, elev^1.2) -atan2(elev, elevs^1.2) -atan2(elevs, elevs^1.2) - -# logarithms -exp(elev) -log(elev) -ln(elev) -log2(elev) -log1p(elev) -log10(elev) -log10p(elev) -log(elev, 3) - -log(elevs) - -# rounding -round(elev + 0.5) -floor(elev + 0.5) -ceiling(elev + 0.5) -trunc(elev + 0.5) - -# comparison -elev < 100 -elev <= 100 -elev == 100 -elev != 100 -elev > 100 -elev >= 100 - -elev + 100 < 2 * elev - -elevs > 10 -10 > elevs - -# logic -elev < 10 | elev > 200 -elev < 10 | cos(elev) > 0.9 - -elev < 10 | TRUE -TRUE | elev > 200 - -elev < 10 | FALSE -FALSE | elev > 200 - -elev < 10 & cos(elev) > 0.9 - -elev < 10 & TRUE -TRUE & elev > 200 - -elev < 10 & FALSE -FALSE & elev > 200 - -# Mathematical functions on GRasters with >= 2 layers: -mean(elevs) -mmode(elevs) -median(elevs) -nunique(elevs) - -sum(elevs) -count(elevs) -min(elevs) -max(elevs) -range(elevs) -skewness(elevs) -kurtosis(elevs) - -which.min(elevs) -which.max(elevs) - -slope(elevs) -intercept(elevs) -r2(elevs) -tvalue(elevs) - -stdev(elevs) -stdev(elevs, pop = FALSE) -var(elevs) -varpop(elevs) - -# Note: To get quantiles for each layer, use -# global(x, "quantile", probs = 0.2). -quantile(elevs, 0.1) - -} -} diff --git a/man/Logic-methods.Rd b/man/Logic-methods.Rd index 584218f4..4de3d0d4 100644 --- a/man/Logic-methods.Rd +++ b/man/Logic-methods.Rd @@ -32,7 +32,7 @@ A binary \code{GRaster} (1 ==> \code{TRUE}, 0 ==> \code{FALSE}, plus \code{NA} when comparison results in \code{NA}). } \description{ -You can do logical operations on \code{GRaster}s. Here, a value of 1 is interpreted as \code{TRUE}, and a value of 0 is interpreted as \code{FALSE}. You can compare: +You can do logical operations on \code{GRaster}s. A cell with a value of 1 is interpreted as \code{TRUE}, and a value of 0 is interpreted as \code{FALSE}. You can compare: \itemize{ \item A \code{GRaster} to another \code{GRaster} \item A \code{GRaster} to a logical value (\code{TRUE} or \code{FALSE}, but not \code{NA}--see \code{\link[=not.na]{not.na()}}) @@ -45,165 +45,3 @@ Operators include: \item \code{&}: \code{TRUE} if both conditions are \code{TRUE} (or 1), but \code{NA} if either is \code{NA}. } } -\examples{ -if (grassStarted()) { - -# Setup -library(sf) -library(terra) - -# Example data -madElev <- fastData("madElev") - -# Convert a SpatRaster to a GRaster -elev <- fast(madElev) -elevs <- c(elev, elev, log10(elev) - 1, sqrt(elev)) -names(elevs) <- c("elev1", "elev2", "log_elev", "sqrt_elev") - -elev -elevs - -# do some math -elev + 100 -elev - 100 -elev * 100 -elev / 100 -elev ^ 2 -elev \%/\% 100 # divide then round down -elev \%\% 100 # modulus - -100 + elev -100 \%/\% elev -100 \%\% elev - -elevs + 100 -100 + elevs - -# math with logicals -elev + TRUE -elev - TRUE -elev * TRUE -elev / TRUE -elev ^ TRUE -elev \%/\% TRUE # divide then round down -elev \%\% TRUE # modulus - -elevs + TRUE -TRUE + elevs - -# Raster interacting with raster(s): -elev + elev -elev - elev -elev * elev -elev / elev -elev ^ log(elev) -elev \%/\% sqrt(elev) # divide then round down -elev \%\% sqrt(elev) # modulus - -elevs + elev -elev * elevs - -# sign -abs(-1 * elev) -abs(elevs) - -# powers -sqrt(elevs) - -# trigonometry -sin(elev) -cos(elev) -tan(elev) - -asin(elev) -acos(elev) -atan(elev) - -atan(elevs) -atan2(elev, elev^1.2) -atan2(elevs, elev^1.2) -atan2(elev, elevs^1.2) -atan2(elevs, elevs^1.2) - -# logarithms -exp(elev) -log(elev) -ln(elev) -log2(elev) -log1p(elev) -log10(elev) -log10p(elev) -log(elev, 3) - -log(elevs) - -# rounding -round(elev + 0.5) -floor(elev + 0.5) -ceiling(elev + 0.5) -trunc(elev + 0.5) - -# comparison -elev < 100 -elev <= 100 -elev == 100 -elev != 100 -elev > 100 -elev >= 100 - -elev + 100 < 2 * elev - -elevs > 10 -10 > elevs - -# logic -elev < 10 | elev > 200 -elev < 10 | cos(elev) > 0.9 - -elev < 10 | TRUE -TRUE | elev > 200 - -elev < 10 | FALSE -FALSE | elev > 200 - -elev < 10 & cos(elev) > 0.9 - -elev < 10 & TRUE -TRUE & elev > 200 - -elev < 10 & FALSE -FALSE & elev > 200 - -# Mathematical functions on GRasters with >= 2 layers: -mean(elevs) -mmode(elevs) -median(elevs) -nunique(elevs) - -sum(elevs) -count(elevs) -min(elevs) -max(elevs) -range(elevs) -skewness(elevs) -kurtosis(elevs) - -which.min(elevs) -which.max(elevs) - -slope(elevs) -intercept(elevs) -r2(elevs) -tvalue(elevs) - -stdev(elevs) -stdev(elevs, pop = FALSE) -var(elevs) -varpop(elevs) - -# Note: To get quantiles for each layer, use -# global(x, "quantile", probs = 0.2). -quantile(elevs, 0.1) - -} -} diff --git a/man/add.Rd b/man/add.Rd index a5013c13..f30e5b70 100644 --- a/man/add.Rd +++ b/man/add.Rd @@ -63,7 +63,10 @@ rasts <- c(rasts, forest2014) # another way ### Subsetting GRaster layers # Subset: +rasts <- c(elev, forest2000, forest2014) rasts[[2:3]] +subset(rasts, 2:3) +subset(rasts, c("madForest2000", "madElev")) rasts[[c("madForest2000", "madElev")]] rasts$madForest2000 diff --git a/man/as.lines.Rd b/man/as.lines.Rd index ea7a717e..8b2db4de 100644 --- a/man/as.lines.Rd +++ b/man/as.lines.Rd @@ -14,7 +14,7 @@ A \code{GVector}. } \description{ -\code{\link[=as.lines]{as.lines()}} converts a \code{GRaster} to a "lines" \code{GVector}. Before you apply this function, you may need to run \code{\link[=thinLines]{thinLines()}} on the raster to reduce linear features to a single-cell width. You may also need to use \link[=breakPolys]{clean geometry} (especially the "duplicated" and "removeDangles" \code{method}s) afterward to remove duplicated vertices and "dangling" lines. +\code{\link[=as.lines]{as.lines()}} converts a \code{GRaster} to a "lines" \code{GVector}. Before you apply this function, you may need to run \code{\link[=thinLines]{thinLines()}} on the raster to reduce linear features to a single-cell width. You may also need to use \link[=breakPolys]{clean geometry} (especially the \code{\link[=removeDups]{removeDups()}} and \code{\link[=removeDangles]{removeDangles()}}) afterward to remove duplicated vertices and "dangling" lines. } \examples{ if (grassStarted()) { diff --git a/man/bioclims.Rd b/man/bioclims.Rd index a98e1fec..2a323a7f 100644 --- a/man/bioclims.Rd +++ b/man/bioclims.Rd @@ -138,7 +138,7 @@ tmax <- fast(madTmax) # Takes longer to run compared to SpatRaster version for small rasters, so # just calculate select BIOCLIMs: -bc <- bioclims(ppt, tmin, tmax, bios = c(1, 5, 6, 12, 15), verbose = TRUE) +bc <- bioclims(ppt, tmin, tmax, bios = c(1, 5, 6, 12, 15)) bc plot(bc) diff --git a/man/dot-layerIndex.Rd b/man/dot-layerIndex.Rd index 6e20e926..76f41931 100644 --- a/man/dot-layerIndex.Rd +++ b/man/dot-layerIndex.Rd @@ -4,7 +4,7 @@ \alias{.layerIndex} \title{Get index of raster layers} \usage{ -.layerIndex(layer, x, recycle = TRUE) +.layerIndex(layer, x, recycle = TRUE, negate = FALSE) } \arguments{ \item{layer}{Integer, numeric, logical, or character: Refers to one or more layers.} @@ -12,6 +12,8 @@ \item{x}{A \code{GRaster}.} \item{recycle}{Logical: If \code{TRUE} (default), and \code{layer} is logical and smaller in number than the number of layers, then recycle the vector of \code{layer}.} + +\item{negate}{Logical: If \code{TRUE}, return indices of all layers \emph{not} identified in \code{layer}.} } \value{ An integer vector. diff --git a/man/examples/ex_GRaster_GVector_subset_assign.r b/man/examples/ex_GRaster_GVector_subset_assign.r index 3c511489..c4232c1c 100644 --- a/man/examples/ex_GRaster_GVector_subset_assign.r +++ b/man/examples/ex_GRaster_GVector_subset_assign.r @@ -43,7 +43,10 @@ rasts <- c(rasts, forest2014) # another way ### Subsetting GRaster layers # Subset: +rasts <- c(elev, forest2000, forest2014) rasts[[2:3]] +subset(rasts, 2:3) +subset(rasts, c("madForest2000", "madElev")) rasts[[c("madForest2000", "madElev")]] rasts$madForest2000 diff --git a/man/examples/ex_GRaster_arithmetic_across_layers.r b/man/examples/ex_GRaster_arithmetic_across_layers.r new file mode 100644 index 00000000..cfe4089e --- /dev/null +++ b/man/examples/ex_GRaster_arithmetic_across_layers.r @@ -0,0 +1,61 @@ +if (grassStarted()) { + +# Setup +library(sf) +library(terra) + +# Example data +madChelsa <- fastData("madChelsa") + +# Convert a SpatRaster to a GRaster +chelsa <- fast(madChelsa) +chelsa # 4 layers + +# Central tendency +mean(chelsa) +mmode(chelsa) +median(chelsa) + +# Statistics +nunique(chelsa) +sum(chelsa) +count(chelsa) +min(chelsa) +max(chelsa) +range(chelsa) +skewness(chelsa) +kurtosis(chelsa) + +stdev(chelsa) +stdev(chelsa, pop = FALSE) +var(chelsa) +varpop(chelsa) + +# Which layers have maximum/minimum? +which.min(chelsa) +which.max(chelsa) + +# Regression +slope(chelsa) +intercept(chelsa) +r2(chelsa) +tvalue(chelsa) + +# Note: To get quantiles for each layer, use +# global(x, "quantile", probs = 0.2). +quantile(chelsa, 0.1) + +# NAs +madForest2000 <- fastData("madForest2000") +forest2000 <- fast(madForest2000) +forest2000 <- project(forest2000, chelsa, method = "near") + +chelsaForest <- c(chelsa, forest2000) + +nas <- anyNA(chelsaForest) +plot(nas) + +allNas <- allNA(chelsaForest) +plot(allNas) + +} diff --git a/man/examples/ex_GRaster_arithmetic_single_layer.r.r b/man/examples/ex_GRaster_arithmetic_single_layer.r.r new file mode 100644 index 00000000..cc0525dc --- /dev/null +++ b/man/examples/ex_GRaster_arithmetic_single_layer.r.r @@ -0,0 +1,129 @@ +if (grassStarted()) { + +# Setup +library(sf) +library(terra) + +# Example data +madElev <- fastData("madElev") + +# Convert a SpatRaster to a GRaster +elev <- fast(madElev) +elevs <- c(elev, elev, log10(elev) - 1, sqrt(elev)) +names(elevs) <- c("elev1", "elev2", "log_elev", "sqrt_elev") + +elev +elevs + +# do some math +elev + 100 +elev - 100 +elev * 100 +elev / 100 +elev ^ 2 +elev %/% 100 # divide then round down +elev %% 100 # modulus + +100 + elev +100 %/% elev +100 %% elev + +elevs + 100 +100 + elevs + +# math with logicals +elev + TRUE +elev - TRUE +elev * TRUE +elev / TRUE +elev ^ TRUE +elev %/% TRUE # divide then round down +elev %% TRUE # modulus + +elevs + TRUE +TRUE + elevs + +# Raster interacting with raster(s): +elev + elev +elev - elev +elev * elev +elev / elev +elev ^ log(elev) +elev %/% sqrt(elev) # divide then round down +elev %% sqrt(elev) # modulus + +elevs + elev +elev * elevs + +# sign +abs(-1 * elev) +abs(elevs) + +# powers +sqrt(elevs) + +# trigonometry +sin(elev) +cos(elev) +tan(elev) + +asin(elev) +acos(elev) +atan(elev) + +atan(elevs) +atan2(elev, elev^1.2) +atan2(elevs, elev^1.2) +atan2(elev, elevs^1.2) +atan2(elevs, elevs^1.2) + +# logarithms +exp(elev) +log(elev) +ln(elev) +log2(elev) +log1p(elev) +log10(elev) +log10p(elev) +log(elev, 3) + +log(elevs) + +# rounding +round(elev + 0.5) +floor(elev + 0.5) +ceiling(elev + 0.5) +trunc(elev + 0.5) + +# comparison +elev < 100 +elev <= 100 +elev == 100 +elev != 100 +elev > 100 +elev >= 100 + +elev + 100 < 2 * elev + +elevs > 10 +10 > elevs + +# logic +elev < 10 | elev > 200 +elev < 10 | cos(elev) > 0.9 + +elev < 10 | TRUE +TRUE | elev > 200 + +elev < 10 | FALSE +FALSE | elev > 200 + +elev < 10 & cos(elev) > 0.9 + +elev < 10 & TRUE +TRUE & elev > 200 + +elev < 10 & FALSE +FALSE & elev > 200 + +} diff --git a/man/examples/ex_bioclims.r b/man/examples/ex_bioclims.r index d7022ae6..c3b44235 100644 --- a/man/examples/ex_bioclims.r +++ b/man/examples/ex_bioclims.r @@ -19,7 +19,7 @@ tmax <- fast(madTmax) # Takes longer to run compared to SpatRaster version for small rasters, so # just calculate select BIOCLIMs: -bc <- bioclims(ppt, tmin, tmax, bios = c(1, 5, 6, 12, 15), verbose = TRUE) +bc <- bioclims(ppt, tmin, tmax, bios = c(1, 5, 6, 12, 15)) bc plot(bc) diff --git a/man/examples/ex_init.r b/man/examples/ex_init.r new file mode 100644 index 00000000..cc00a595 --- /dev/null +++ b/man/examples/ex_init.r @@ -0,0 +1,51 @@ +if (grassStarted()) { + +# Setup +library(terra) + +# Elevation raster, rivers vector +madElev <- fastData("madElev") + +# Convert to a GRaster +elev <- fast(madElev) + +# Cell coordinates +init(elev, "x") +init(elev, "y") + +# Cell row or column +init(elev, "row") +init(elev, "col") + +# Chess +elevAgg <- aggregate(elev, 32) # make cells bigger so we can see + +chessOdd <- init(elevAgg, "chess") +chessEven <- init(elevAgg, "chess", odd = FALSE) + +chess <- c(chessOdd, chessEven) +names(chess) <- c("odd", "even") +plot(chess) + +# Chess with user-defined values +elevAgg <- aggregate(elev, 32) # make cells bigger so we can see + +chessOdd13 <- init(elevAgg, "chess", vals = c(0, 13)) +chessEven13 <- init(elevAgg, "chess", odd = FALSE, vals = c(0, 13)) + +chess13 <- c(chessOdd13, chessEven13) +names(chess13) <- c("odd", "even") +plot(chess13) + +# Regular +elevAgg <- aggregate(elev, 32) # make cells bigger so we can see + +regOdd <- init(elevAgg, "regular") +regEven <- init(elevAgg, "regular", odd = FALSE) + +reg <- c(regOdd, regEven) +names(reg) <- c("odd", "even") +plot(reg) + + +} diff --git a/man/fast.Rd b/man/fast.Rd index baa3dd46..b294a651 100644 --- a/man/fast.Rd +++ b/man/fast.Rd @@ -49,8 +49,8 @@ snap = NULL, area = NULL, steps = 10, - dropTable = FALSE, resolve = NA, + dropTable = FALSE, verbose = TRUE ) diff --git a/man/faster.Rd b/man/faster.Rd index 142fc927..2a7848e8 100644 --- a/man/faster.Rd +++ b/man/faster.Rd @@ -28,7 +28,7 @@ Options include: \item \code{clean} (logical): If \code{TRUE} (default), remove temporary files created internally by functions. If not deleted, they can eventually fill up hard drive space, but deleting them takes a little bit of time (usually <1 second for each function). See also \code{\link[=mow]{mow()}}. \item \code{useDataTable} (logical): If \code{FALSE} (default), functions that return tabular output produce \code{data.frame}s. If \code{TRUE}, output will be \code{data.table}s from the \strong{data.table} package. This can be much faster, but it might require you to know how to use \code{data.table}s if you want to manipulate them in \strong{R}. You can always convert them to \code{data.frame}s using \code{\link[base:as.data.frame]{base::as.data.frame()}}. \item \code{verbose} (logical): If \code{TRUE}, show \strong{GRASS} messages and otherwise hidden slots in classes. This is mainly used for debugging, so most users will want to keep this at its default, \code{FALSE}. -\item \code{workDir} (character): The folder in which \strong{GRASS} rasters, vectors, and other objects are created and manipulated. By default, this is given by \code{\link[=tempdir]{tempdir()}}. +\item \code{workDir} (character): The folder in which \strong{GRASS} rasters, vectors, and other objects are created and manipulated. By default, this is given by \code{\link[=tempdir]{tempdir()}}. Note that on some systems, changing the default folder to somewhere else can cause problems with \strong{fasterRaster} being able to find rasters in \strong{GRASS} that have been created. }} \item{default}{Logical: Return the default value(s) of the option(s). The default value of \code{default} is \code{FALSE}.} diff --git a/man/fasterRaster.Rd b/man/fasterRaster.Rd index 8c1a5dd6..2aaee464 100644 --- a/man/fasterRaster.Rd +++ b/man/fasterRaster.Rd @@ -6,371 +6,7 @@ \alias{fasterRaster} \title{"fasterRaster": Faster raster and spatial vector processing using "GRASS GIS"} \description{ -\strong{fasterRaster}: Processing of large-in-memory/-on disk rasters and spatial vectors in using \strong{GRASS GIS}. Most functions in the \strong{terra} and \strong{sf} packages are recreated. Processing of medium-sized and smaller spatial objects will nearly always be faster using \strong{terra} or \strong{sf}. To use most of the functions you must have the stand-alone version of \strong{GRASS GIS} version 8.3 or higher (not the \strong{OSGeoW4} installer version). Note that due to differences in how \strong{GRASS}, \strong{terra}, and \strong{sf} were implemented, results will not always be strictly comparable between functions for the same operation. -\subsection{Most useful tutorials and functions:}{ -\itemize{ -\item The quick-start guide to getting started with \strong{fasterRaster} , accessible using \code{vignette("fasterRaster", package = "fasterRaster")} -\item The vignette on types of \code{GRaster}s, accessible using \code{vignette("GRasters", package = "fasterRaster")} -\item The vignette on how to speed up \strong{fasterRaster}, accessible using \code{vignette("faster_fasterRaster", package = "fasterRaster")} -\item \code{\link[=faster]{faster()}}: Set the directory where \strong{GRASS} is installed on your system, and set or get other package-wide options. This function must be run once before using most \strong{fasterRaster} functions. -\item \code{\link[=fast]{fast()}}: Convert a \code{SpatRaster}, \code{SpatVector}, or \code{sf} vector to \strong{fasterRaster}'s raster format (\code{GRaster}s) or vector format (\code{GVector}s), or load one from a file -\item \code{\link[=rast]{rast()}}, \code{\link[=vect]{vect()}}, and \code{\link[=st_as_sf]{st_as_sf()}}: Convert \code{GRaster}s and \code{GVector}s to \code{SpatRaster}s, \code{SpatVector}s, or \code{sf} vectors -\item \code{\link[=writeRaster]{writeRaster()}} and \code{\link[=writeVector]{writeVector()}}: Save \code{GRaster}s or \code{GVector}s to disk -} -} - -\subsection{Properties of \code{GRasters}}{ -\itemize{ -\item \code{\link[=crs]{crs()}}: Coordinate reference system -\item \code{\link[=datatype]{datatype()}}: Data type -\item \code{\link[=dim]{dim()}} and \code{\link[=dim3d]{dim3d()}}: Number of rows, columns, and depths -\item \code{\link[=ext]{ext()}}, \code{\link[=N]{N()}}, \code{\link[=S]{S()}}, \code{\link[=E]{E()}}, \code{\link[=W]{W()}}, \code{\link[=top]{top()}}, and \code{\link[=bottom]{bottom()}}: Spatial extent -\item \code{\link[=freq]{freq()}}: Frequencies of cell values in a raster -\item \code{\link[=is.2d]{is.2d()}} and \code{\link[=is.3d]{is.3d()}}: Is an object 2- or 3-dimensional? -\item \code{\link[=is.int]{is.int()}}, \code{\link[=is.cell]{is.cell()}}, \code{\link[=is.float]{is.float()}}, \code{\link[=is.doub]{is.doub()}}: \code{GRaster} data type (integer/float/double) -\item \code{\link[=is.factor]{is.factor()}}: Does a raster represent categorical data? -\item \code{\link[=is.lonlat]{is.lonlat()}}: Is an object projected (e.g., in WGS84)? -\item \code{\link[=levels]{levels()}}: Names of levels in a categorical \code{GRaster} -\item \code{\link[=minmax]{minmax()}}: Minimum and maximum values across all non-\code{NA} cells -\item \code{\link[=names]{names()}}: \code{GRaster} names -\item \code{\link[=ncol]{ncol()}}: Number of columns -\item \code{\link[=nacell]{nacell()}}: Number of \code{NA} cells -\item \code{\link[=ncell]{ncell()}}: Number of cells -\item \code{\link[=ncell3d]{ncell3d()}}: Number of cells of a 3D \code{GRaster} -\item \code{\link[=ndepth]{ndepth()}}: Number of depths of a 3D \code{GRaster} -\item \code{\link[=nlyr]{nlyr()}}: Number of layers -\item \code{\link[=nonnacell]{nonnacell()}}: Number of non-\code{NA} cells -\item \code{\link[=nrow]{nrow()}}: Number of rows -\item \code{\link[=nlevels]{nlevels()}}: Number of categories -\item \code{\link[=res]{res()}}, \code{\link[=res3d]{res3d()}}, \code{\link[=xres]{xres()}}, \code{\link[=yres]{yres()}}, and \code{\link[=zres]{zres()}}: Spatial resolution -\item \code{\link[=sources]{sources()}}: Name of the \code{GRaster} in \strong{GRASS} -\item -\item \code{\link[=zext]{zext()}}: Vertical extent -\item \code{\link[=zres]{zres()}}: Vertical resolution -} -} - -\subsection{Functions that operate on or create \code{GRasters}}{ -\itemize{ -\item \link{Arithmetic}: Mathematical operations on \code{GRaster}s: \code{+}, \code{-}, \code{*}, \code{/}, \code{^}, \code{\%\%} (modulus), \code{\%/\%} (integer division) -\item \link[=Compare-methods]{Logical comparisons}: \code{<}, \code{<=}, \code{==}, \code{!=}, \code{>=}, and \code{>}, plus \code{\link[fasterRaster]{\%in\%}} and \code{\link[fasterRaster]{\%notin\%}} (for categorical rasters only) -\item \link[=Logic-methods]{Logical operators}: \code{|}and \code{&} -} - -Mathematical functions that are applied to each layer of a \code{GRaster}: -\itemize{ -\item Working with \code{NA}s: \code{\link[=is.na]{is.na()}}, \code{\link[=not.na]{not.na()}}, and \code{\link[=maskNA]{maskNA()}} -\item Trigonometry: \code{\link[=sin]{sin()}}, \code{\link[=cos]{cos()}}, \code{\link[=tan]{tan()}}, \code{\link[=asin]{asin()}}, \code{\link[=acos]{acos()}}, \code{\link[=atan]{atan()}}, \code{\link[=atan2]{atan2()}} -\item Logarithms and powers: \code{\link[=exp]{exp()}}, \code{\link[=log]{log()}}, \code{\link[=ln]{ln()}}, \code{\link[=log1p]{log1p()}}, \code{\link[=log2]{log2()}}, \code{\link[=log10]{log10()}}, \code{\link[=sqrt]{sqrt()}} -\item Rounding: \code{\link[=round]{round()}}, \code{\link[=floor]{floor()}}, \code{\link[=ceiling]{ceiling()}}, \code{\link[=trunc]{trunc()}} -\item Signs: \code{\link[=abs]{abs()}} -} - -Mathematical functions that are applied across layers of a "stack" of \code{GRaster}s: -\itemize{ -\item Numeration: \code{\link[=sum]{sum()}}, \code{\link[=count]{count()}} -\item Central tendency: \code{\link[=mean]{mean()}}, \code{\link[=mmode]{mmode()}}, \code{\link[=median]{median()}} -\item Dispersion: \code{\link[=stdev]{stdev()}}, \code{\link[=var]{var()}}, \code{\link[=varpop]{varpop()}}, \code{\link[=nunique]{nunique()}}, \code{\link[=range]{range()}}, \code{\link[=quantile]{quantile()}}, \code{\link[=skewness]{skewness()}}, \code{\link[=kurtosis]{kurtosis()}} -\item Extremes: \code{\link[=min]{min()}}, \code{\link[=max]{max()}}, \code{\link[=which.min]{which.min()}}, \code{\link[=which.max]{which.max()}} -} - -The operators \link{$} and \code{\link[fasterRaster]{[[}} can be used to subset or remove specific layers of a \code{GRaster}. -The \code{\link[fasterRaster]{[<-}} operator can be used to replace values of cells of a \code{GRaster}. -The assign operators, \code{\link[fasterRaster]{$<-}}, \code{\link[fasterRaster]{[[<-}}, and \code{\link[fasterRaster]{add<-}}, can be used to replace specific layers of a \code{GRaster}. -\itemize{ -\item \code{\link[=as.int]{as.int()}}, \code{\link[=as.float]{as.float()}}, \code{\link[=as.doub]{as.doub()}}: Change data type (integer/float/double) -\item \code{\link[=as.lines]{as.lines()}}: Convert a \code{GRaster} to a "lines" vector -\item \code{\link[=as.points]{as.points()}}: Convert a \code{GRaster} to a "points" vector -\item \code{\link[=as.polygons]{as.polygons()}}: Convert a \code{GRaster} to a "polygons" vector -\item \code{\link[=aggregate]{aggregate()}}: Aggregate values of \code{GRaster} cells into larger cells -\item \code{\link[=bioclims]{bioclims()}}: BIOCLIM rasters (classic set and extended set) -\item \code{\link[=buffer]{buffer()}}: Create a buffer around non-\code{NA} cells -\item \code{\link[=app]{app()}}: Apply a user-defined function to multiple layers of a \code{GRaster} (with helper functions \code{\link[=appFuns]{appFuns()}} and \code{\link[=appCheck]{appCheck()}}) -\item \code{\link[=c]{c()}}: "Stack" two or more rasters -\item \code{\link[=cellSize]{cellSize()}}: Cell area -\item \code{\link[=classify]{classify()}}: Partition cell values into strata -\item \code{\link[=clump]{clump()}}: Group adjacent cells with similar values -\item \code{\link[=combineCats]{combineCats()}}: Combine values from two or more categorical and/or integer rasters -\item \code{\link[=combineLevels]{combineLevels()}}: Combine the "levels" tables of two or more categorical \code{GRaster}s -\item \code{\link[=crop]{crop()}}: Remove parts of a \code{GRaster} -\item \code{\link[=denoise]{denoise()}}: Remove "noise" from a \code{GRaster} using a principal components analysis (PCA) -\item \code{\link[=distance]{distance()}}: Distance to non-\code{NA} cells, or vice versa -\item \code{\link[=extend]{extend()}}: Add rows and columns to a \code{GRaster} -\item \code{\link[=extract]{extract()}}: Extract values from a \code{GRaster} at locations of a \code{GVector} -\item \code{\link[=fillNAs]{fillNAs()}}: Fill \code{NA} cells -\item \code{\link[=focal]{focal()}}: Calculate cell values based on values of nearby cells -\item \code{\link[=fragmentation]{fragmentation()}}: Landscape fragmentation class from Riitters et al. (2020) -\item \code{\link[=global]{global()}}: Summary statistics across cells of each \code{GRaster} layer -\item \code{\link[=hist]{hist()}}: Histogram of \code{GRaster} values -\item \code{\link[=interpIDW]{interpIDW()}}: Interpolate values at points to a \code{GRaster} -\item \code{\link[=kernel]{kernel()}}: Kernel density estimator of points -\item \code{\link[=layerCor]{layerCor()}}: Correlation or covariance between two or more \code{GRaster} layers -\item \code{\link[=longlat]{longlat()}}: Create longitude/latitude rasters -\item \code{\link[=mask]{mask()}}: Remove values in a \code{GRaster} based on values in another \code{GRaster} or vector -\item \code{\link[=maskNA]{maskNA()}}: Mask all non-NA cells or all NA cells -\item \code{\link[=match]{match()}}, \code{\link[fasterRaster]{\%in\%}}, and \code{\link[fasterRaster]{\%notin\%}}: Find which cells of a \code{GRaster} match or do not match certain values -\item \code{\link[=merge]{merge()}}: Combine two or more rasters with different extents and fill in \code{NA}s -\item \code{\link[fasterRaster]{names<-}}: Assign names to a \code{GRaster} -\item \code{\link[=noise]{noise()}}: Remove coarse-scale trends from a \code{GRaster}, leaving just fine-scale "noise" -\item \code{\link[=pairs]{pairs()}}: Plot correlations between \code{GRaster} layers -\item \code{\link[=pca]{pca()}}: Apply a principal components analysis (PCA) to a \code{GRaster} -\item \code{\link[=pcs]{pcs()}}: Retrieve a principal components model from a PCA \code{GRaster} generated using \code{pca()} -\item \code{\link[=plot]{plot()}}: Display a \code{GRaster} -\item \code{\link[=plotRGB]{plotRGB()}}: Display a multispectral \code{GRaster} using red, blue, green, and alpha channels -\item \code{\link[=project]{project()}}: Change coordinate reference system and cell size -\item \code{\link[=predict]{predict()}}: Make predictions to a \code{GRaster} from a linear model or generalized linear model -\item \code{\link[=resample]{resample()}}: Change cell size -\item \code{\link[=reorient]{reorient()}}: Convert degrees between 'north-orientation' and 'east orientation' -\item \code{\link[=sampleRast]{sampleRast()}}: Randomly sample cells from a \code{GRaster} -\item \code{\link[=scale]{scale()}}, \code{\link[=scalepop]{scalepop()}}, and \code{\link[=unscale]{unscale()}}: Subtract means and divide by standard deviations, or inverse of that -\item \code{\link[=selectRange]{selectRange()}}: Select values from rasters in a stack based on values in another \code{GRaster} -\item \code{\link[=spatSample]{spatSample()}}: Randomly points from a \code{GRaster} -\item \code{\link[=stretch]{stretch()}}: Rescale values in a GRaster -\item \code{\link[=subst]{subst()}}: Re-assign cell values -\item \code{\link[=thinLines]{thinLines()}}: Reduce linear features on a \code{GRaster} so linear features are 1 cell wide -\item \code{\link[=tiles]{tiles()}}: Divide a \code{GRaster} into spatially exclusive subsets (though with possible overlap) -\item \code{\link[=trim]{trim()}}: Remove rows and columns from a \code{GRaster} that are all \code{NA} -\item \code{\link[=zonal]{zonal()}}: Statistics (mean, sum, etc.) on areas of a \code{GRaster} defined by sets of cells with the same values in another \code{GRaster}, or by geometries in a \code{GVector} -\item \code{\link[=zonalGeog]{zonalGeog()}}: Geographic statistics (area, perimeter, fractal dimension, etc.) for sets of cells with the same values -} -} - -\subsection{Functions for creating \code{GRaster}s \emph{de novo}}{ -\itemize{ -\item \code{\link[=fractalRast]{fractalRast()}}: Create a fractal \code{GRaster} -\item \code{\link[=rnormRast]{rnormRast()}}: A random \code{GRaster} with values drawn from a normal distribution -\item \code{\link[=rSpatialDepRast]{rSpatialDepRast()}}: Create a random \code{GRaster} with or without spatial dependence -\item \code{\link[=runifRast]{runifRast()}}: A random \code{GRaster} with values drawn from a uniform distribution -\item \code{\link[=sineRast]{sineRast()}}: Sine wave rasters -} -} - -\subsection{Functions for analysis of terrain and flow of water across landscapes}{ -\itemize{ -\item \code{\link[=as.contour]{as.contour()}}: Contour lines from a \code{GRaster} -\item \code{\link[=flow]{flow()}}: Identify watershed basins and direction and accumulation of flow -\item \code{\link[=flowPath]{flowPath()}}: Path of water flow across a landscape -\item \code{\link[=geomorphons]{geomorphons()}}: Identify terrain feature types -\item \code{\link[=shade]{hillshade()}}: Create a hillshade \code{GRaster} -\item \code{\link[=horizonHeight]{horizonHeight()}}: Horizon height -\item \code{\link[=sun]{sun()}}: Solar radiance and irradiance -\item \code{\link[=ruggedness]{ruggedness()}}: Terrain Ruggedness Index -\item \code{\link[=streams]{streams()}}: Create stream network -\item \code{\link[=terrain]{terrain()}}: Slope, aspect, curvature, and partial slopes -\item \code{\link[=wetness]{wetness()}}: Topographic wetness index -} -} - -\subsection{Functions operating on categorical (factor) \code{GRaster}s}{ -\itemize{ -\item \code{\link[fasterRaster]{\%in\%}}, and \code{\link[fasterRaster]{\%notin\%}}: Mask cells that match or do not match a given category -\item \code{\link[=activeCat]{activeCat()}} and \code{\link[=activeCats]{activeCats()}}: Column(s) that defines category labels -\code{\link[fasterRaster]{activeCat<-}}: Set column that defines category labels -\item \code{\link[=addCats]{addCats()}}: Add new columns to a "levels" table -\code{\link[fasterRaster]{addCats<-}}: Add new rows (levels) to a "levels" table -\item \code{\link[=categories]{categories()}}: Set "levels" table for specific layers of a categorical raster -\item \code{\link[=catNames]{catNames()}}: Column names of each "levels" table -\item \code{\link[=cats]{cats()}}: "Levels" table of a categorical raster -\item \code{\link[=combineCats]{combineCats()}}: Combine categories from two or more categorical rasters -\item \code{\link[=combineLevels]{combineLevels()}}: Combine the "levels" tables of two or more categorical \code{GRaster}s -\item \code{\link[=complete.cases]{complete.cases()}}: Find rows of a categorical \code{GRaster}'s "levels" table that have no \code{NA}s in them -\item \code{\link[=droplevels]{droplevels()}}: Remove one or more levels -\item \code{\link[=freq]{freq()}}: Frequency of each category across cells of a raster -\item \code{\link[=is.factor]{is.factor()}}: Is a raster categorical? -\item \code{\link[=levels]{levels()}}: "Levels" table of a categorical raster -\item \code{\link[fasterRaster]{levels<-}}: Set "levels" table of a categorical raster -\item \code{\link[=match]{match()}}, \code{\link[fasterRaster]{\%in\%}}, and \code{\link[fasterRaster]{\%notin\%}}: Find which cells of a \code{GRaster} match or do not match certain category labels -\item \code{\link[=minmax]{minmax()}}: "Lowest" and "highest" category values of categorical rasters (when argument \code{levels = TRUE}) -\item \code{\link[=missing.cases]{missing.cases()}}: Find rows of a categorical \code{GRaster}'s "levels" table that have at least one \code{NA} in them -\item \code{\link[=missingCats]{missingCats()}}: Values that have no category assigned to them -\item \code{\link[=nlevels]{nlevels()}}: Number of levels -\item \code{\link[=subst]{subst()}}: Re-assign category levels -\item \code{\link[=zonalGeog]{zonalGeog()}}: Geographic statistics (area, perimeter, fractal dimension, etc.) for sets of cells with the same values -} -} - -\subsection{Functions for analysis of remote sensing rasters}{ -\itemize{ -\item \code{\link[=compositeRGB]{compositeRGB()}}: Combine red, green, and blue color bands to make a composite \code{GRaster} -\item \code{\link[=plotRGB]{plotRGB()}}: Display a multispectral \code{GRaster} using red, blue, green, and alpha channels -\item \code{\link[=vegIndex]{vegIndex()}}: Vegetation indices from surface reflectance -} -} - -\subsection{Functions that operate on \strong{terra} \code{SpatRaster}s}{ -\itemize{ -\item \code{\link[=bioclims]{bioclims()}}: BIOCLIM rasters (classic set and extended set) -\item \code{\link[=fragmentation]{fragmentation()}}: Landscape fragmentation class from Riitters et al. (2020) -} -} - -\subsection{Properties of \code{GVector}s}{ -\itemize{ -\item \code{\link[=crs]{crs()}}: Coordinate reference system -\item \code{\link[=datatype]{datatype()}}: Data type of fields -\item \code{\link[=dim]{dim()}}: Number of geometries and columns -\item \code{\link[=expanse]{expanse()}}: Area of polygons or length of lines -\item \code{\link[=ext]{ext()}}, \code{\link[=N]{N()}}, \code{\link[=S]{S()}}, \code{\link[=E]{E()}}, \code{\link[=W]{W()}}, \code{\link[=top]{top()}}, and \code{\link[=bottom]{bottom()}}: Spatial extent -\item \code{\link[=geomtype]{geomtype()}}: Type of vector (points, lines, polygons) -\item \code{\link[=is.2d]{is.2d()}} and \code{\link[=is.3d]{is.3d()}}: Is an object 2- or 3-dimensional? -\item \code{\link[=is.lonlat]{is.lonlat()}}: Is an object projected (e.g., in WGS84)? -\item \code{\link[=is.points]{is.points()}}, \code{\link[=is.lines]{is.lines()}}, \code{\link[=is.polygons]{is.polygons()}}: Does a \code{GVector} represent points, lines, or polygons? -\item \code{\link[=names]{names()}}: Names of \code{GVector} fields -\item \code{\link[=ncol]{ncol()}}: Number of fields -\item \code{\link[=ngeom]{ngeom()}}: Number of geometries (points, lines, polygons) -\item \code{\link[=nrow]{nrow()}}: Number of rows in a vector data table -\item \code{\link[=nsubgeom]{nsubgeom()}}: Number of subgeometries (points, lines, polygons that make up single- and multipart geometries) -\item \code{\link[=sources]{sources()}}: Name of the vector in \strong{GRASS} -\item -\item \code{\link[=zext]{zext()}}: Vertical extent -} -} - -\subsection{Functions that operate on or create \code{GVector}s}{ -\itemize{ -\item The \code{\link[fasterRaster]{[}} operator can be used to subset geometries of a \code{GVector}. -\item The \link{$} and \code{\link[fasterRaster]{[[}} operators can be used to get columns of a \code{GVector}'s data table. -\item The \code{\link[fasterRaster]{$<-}} operator can be used to replace specific columns of a \code{GVector}'s data table or to add columns. -\item \code{\link[fasterRaster]{addTable<-}}: Add a data table to a \code{GVector} -\item \code{\link[=aggregate]{aggregate()}}: Combine \code{GVector} geometries -\item \code{\link[=as.data.frame]{as.data.frame()}}: Convert a \code{GVector}'s attribute table to a \code{data.frame} -\item \code{\link[=as.data.table]{as.data.table()}}: Convert a \code{GVector}'s attribute table to a \code{data.table} -\item \code{\link[=as.points]{as.points()}}: Extract vertex coordinates from a "lines" or "polygons" \code{GVector} -\item \code{\link[=buffer]{buffer()}}: Create a polygon around/inside a \code{GVector} -\item \code{\link[=clusterPoints]{clusterPoints()}}: Identify clusters of points -\item \code{\link[=colbind]{colbind()}}: Add columns to the data table of a \code{GVector} -\item \code{\link[=complete.cases]{complete.cases()}}: Find rows of a \code{GVector}'s data table that have no \code{NA}s in them -\item \code{\link[=connectors]{connectors()}}: Create lines connecting nearest features of two \code{GVector}s -\item \code{\link[=convHull]{convHull()}}: Minimum convex hull -\item \code{\link[=crds]{crds()}}: Extract coordinates of a \code{GVector} -\item \code{\link[=crop]{crop()}}: Remove parts of a \code{GVector} -\item \code{\link[=delaunay]{delaunay()}}: Delaunay triangulation -\item \code{\link[=disagg]{disagg()}}: Separate multipart geometries into singlepart geometries -\item \code{\link[=distance]{distance()}}: Distance between geometries in two \code{GVector}, or from a \code{GVector} to cells of a \code{GRaster} -\item \code{\link[=dropTable]{dropTable()}}: Remove the data table from a \code{GVector} -\item \code{\link[=erase]{erase()}} or \code{-}: Remove part of a \code{GVector} that overlaps with another -\item \code{\link[=expanse]{expanse()}}: Area of polygons or length of lines -\item \code{\link[=extract]{extract()}}: Extract values from a \code{GVector} at specific points -\item \code{\link[=grid]{grid()}}: Create a grid \code{GVector} -\item \code{\link[=head]{head()}}: First rows of a \code{GVector}'s data table -\item \code{\link[=hexagons]{hexagons()}}: Create a hexagonal grid -\item \code{\link[=interpIDW]{interpIDW()}}: Interpolate values at points to a \code{GRaster} using inverse-distance weighting -\item \code{\link[=interpSplines]{interpSplines()}}: Interpolate values at points to a \code{GRaster} using splines -\item \code{\link[=intersect]{intersect()}} or \code{*}: Intersection of two \code{GVectors}. -\item \code{\link[=kernel]{kernel()}}: Kernel density estimator of points. -\item \code{\link[=missing.cases]{missing.cases()}}: Find rows of a \code{GVector}'s data table that have at least \code{NA} in them -\code{\link[fasterRaster]{names<-}}: Assign names to columns of a \code{GVector}s data table -\item \code{\link[=project]{project()}}: Change coordinate reference system -\item \code{\link[=rasterize]{rasterize()}}: Convert a \code{GVector} to a \code{GRaster} -\item \code{\link[=rbind]{rbind()}}: Combine \code{GVectors} -\item \code{\link[=simplifyGeom]{simplifyGeom()}}: Remove vertices -\item \code{\link[=smoothGeom]{smoothGeom()}}: Remove "angular" aspects of features -\item \code{\link[=st_as_sf]{st_as_sf()}}: Convert a \code{GVector} to a \code{sf} vector -\item \code{\link[=st_buffer]{st_buffer()}}: Create a polygon around/inside a \code{GVector} -\item \code{\link[=tail]{tail()}}: Last rows of a \code{GVector}'s data table -\item \code{\link[=thinPoints]{thinPoints()}}: Reduce number of points in same raster cell -\item \code{\link[=union]{union()}} or \code{+}: Combine two \code{GVector}s -\item \code{\link[=voronoi]{voronoi()}}: Voronoi tessellation -\item \code{\link[=xor]{xor()}} or \code{/}: Select parts of polygons not shared by two \code{GVector}s -} -} - -\subsection{Functions for creating \code{GVector}s \emph{de novo}}{ -\itemize{ -\item \code{\link[=rvoronoi]{rvoronoi()}}: Random Voronoi tesselation -} -} - -\subsection{Functions for fixing issues with \code{GVector}s}{ - -(See also \emph{Details} \code{\link[=fast]{fast()}}.) -\itemize{ -\item \code{\link[=breakPolys]{breakPolys()}}: Break topologically clean areas -\item \code{\link[=fillHoles]{fillHoles()}}: Fill "holes" of a \code{GVector} -\item \code{\link[=fixBridges]{fixBridges()}}: Change "bridges" to "islands" -\item \code{\link[=fixDangles]{fixDangles()}}: Change "dangles" hanging off boundaries to lines -\item \code{\link[=fixLines]{fixLines()}}: Break lines at intersections and lines that form closed loops -\item \code{\link[=remove0]{remove0()}}: Remove all boundaries and lines with a length of 0 -\item \code{\link[=removeAngles]{removeAngles()}}: Collapse lines that diverge at an angle that is computationally equivalent to 0 -\item \code{\link[=removeBridges]{removeBridges()}}: Remove "bridges" to "islands" -\item \code{\link[=removeDangles]{removeDangles()}}: Remove "dangling" lines -\item \code{\link[=removeDupCentroids]{removeDupCentroids()}}: Remove duplicated area centroids -\item \code{\link[=removeDups]{removeDups()}}: Remove duplicated features and area centroids -\item \code{\link[=removeSmallPolys]{removeSmallPolys()}}: Remove small polygons -\item \code{\link[=snap]{snap()}}: Snap lines/boundaries to each other -} -} -\subsection{Converting between data types}{ -\itemize{ -\item \code{\link[=as.contour]{as.contour()}}: Convert a \code{GRaster} to a \code{GVector} representing contour lines -\item \code{\link[=as.doub]{as.doub()}}: Convert a \code{GRaster} to a double-floating point raster (\strong{GRASS} data type \code{DCELL}) -\item \code{\link[=as.data.frame]{as.data.frame()}}: Convert \code{GVector} to a \code{data.frame} -\item \code{\link[=as.data.table]{as.data.table()}}: Convert \code{GVector} to a \code{data.table} -\item \code{\link[=as.float]{as.float()}}: Convert a \code{GRaster} to a floating-point raster (\strong{GRASS} data type \code{FCELL}) -\item \code{\link[=as.int]{as.int()}}: Convert a \code{GRaster} to an integer raster (\strong{GRASS} data type \code{CELL}) -\item \code{\link[=as.points]{as.points()}}, \code{\link[=as.lines]{as.lines()}}, and \code{\link[=as.polygons]{as.polygons()}}: Convert a \code{GRaster} to a \code{GVector} -\item \code{\link[=categories]{categories()}} and \code{\link[fasterRaster]{levels<-}}: Convert an integer raster to a categorical ("factor") raster. -\item \code{\link[=fast]{fast()}}: Convert a \code{SpatRaster} to a \code{GRaster}; a \code{SpatVector}, \code{sf} vector, numeric vector, \code{matrix}, \code{data.frame}, or \code{data.table} to a \code{GVector}; or load a vector or raster from a file -\item \code{\link[=rast]{rast()}}: Convert a \code{GRaster} to a \code{SpatRaster} -\item \code{\link[=rasterize]{rasterize()}}: Convert a \code{GVector} to a \code{GRaster} -\item \code{\link[=st_as_sf]{st_as_sf()}}: Convert a \code{GVector} to a \code{sf} vector -\item \code{\link[=vect]{vect()}}: Convert a \code{GVector} to a \code{SpatVector} -} -} - -\subsection{General purpose functions}{ -\itemize{ -\item \code{\link[=compareGeom]{compareGeom()}}: Determine if geographic metadata is same between \code{GRaster}s and/or \code{GVector}s -\item \code{\link[=dropRows]{dropRows()}}: Remove rows from a \code{data.frame} or \code{data.table} -\item \code{\link[=grass]{grass()}}: Start the \strong{GRASS} GUI (not recommended for most users!!!) -\item \code{\link[=grassInfo]{grassInfo()}}: \strong{GRASS} version and citation -\item \code{\link[=grassStarted]{grassStarted()}}: Has a connection \strong{GRASS} been made within the current \strong{R} session? -\item \code{\link[=mow]{mow()}}: Remove unused rasters and vectors from the \strong{GRASS} cache -\item \code{\link[=reorient]{reorient()}}: Convert degrees between 'north-orientation' and 'east orientation' -\item \code{\link[=replaceNAs]{replaceNAs()}}: Replace \code{NA}s in columns of a \code{data.table} or \code{data.frame}, or in a vector -\item \code{\link[=seqToSQL]{seqToSQL()}}: Format a numeric series into an SQL value call -\item \code{\link[=workDir]{workDir()}}: -\item \code{\link[=update]{update()}}: Refresh metadata in a \code{GRaster} or \code{GVector} object -} -} - -\subsection{Data objects}{ -\itemize{ -\item \code{\link[=fastData]{fastData()}}: Helper function to quickly obtain example rasters and vectors -\item \link{appFunsTable} (see also \code{\link[=appFuns]{appFuns()}}): Functions usable by the \code{\link[=app]{app()}} function -\item \link{madChelsa}: Climate rasters for of a portion of eastern Madagascar -\item \link{madCoast0}, \link{madCoast4}, and \link{madCoast}: Borders of an eastern portion of Madagascar -\item \link{madCover}: Land cover raster -\item \link{madCoverCats}: Table of land cover classes -\item \link{madDypsis}: Specimens records of species in the genus \emph{Dypsis} -\item \link{madElev}: Elevation raster -\item \link{madForest2000} and \link{madForest2014}: Forest cover in 2000 and 2014 -\item \link{madLANDSAT}: Surface reflectance in 2023 -\item \link{madPpt}, \link{madTmin}, \link{madTmax}: Rasters of mean monthly precipitation, and minimum and maximum temperature -\item \link{madRivers}: Rivers vector -\item \link{vegIndices}: Vegetation indices that can be calculated using \code{\link[=vegIndex]{vegIndex()}}. -} -} - -\subsection{Esoteric tutorials and arcane notes}{ -\itemize{ -\item Comparisons between \code{GRegion}s can be performed using the \code{==} and \code{!=} operators. -\item Vignette on \strong{GRASS} "projects/locations" and "mapsets": \code{vignette("projects_mapsets", package = "fasterRaster")} -\item Vignette on \strong{GRASS} "regions": \code{vignette("regions", package = "fasterRaster")} -\item Vignette on \strong{fasteRaster} hidden functions: \code{vignette("hidden_functions", package = "fasterRaster")} -} -} - -\subsection{Classes}{ -\itemize{ -\item \code{\link{GLocation}}: Fundamental class; points to a "location/project" in \strong{GRASS} -\item \code{\link{GSpatial}}: Basic class of any spatial object -\item \code{\link{GRegion}}: Points to a "region" of a "location/project" in \strong{GRASS} -\item \code{\link{GRaster}}: Raster class -\item \code{\link{GVector}}: Spatial vector class -} -} } \seealso{ Useful links: diff --git a/man/functions.Rd b/man/functions.Rd index 08664e1b..3a26b209 100644 --- a/man/functions.Rd +++ b/man/functions.Rd @@ -46,6 +46,10 @@ \alias{range} \alias{quantile,GRaster-method} \alias{quantile} +\alias{anyNA,GRaster-method} +\alias{anyNA} +\alias{allNA,GRaster-method} +\alias{allNA} \title{Mathematical operations on two or more GRasters} \usage{ \S4method{mean}{GRaster}(x, na.rm = FALSE) @@ -93,6 +97,10 @@ \S4method{range}{GRaster}(x, na.rm = FALSE) \S4method{quantile}{GRaster}(x, prob, na.rm = FALSE) + +\S4method{anyNA}{GRaster}(x) + +\S4method{allNA}{GRaster}(x) } \arguments{ \item{x}{A \code{GRaster}. Typically, this raster will have two or more layers. Values will be calculated within cells across rasters.} @@ -114,6 +122,7 @@ These functions can be applied to a "stack" of \code{GRaster}s with two or more \item Extremes: \code{min()}, \code{max()}, \code{which.min()} (index of raster with the minimum value), \code{which.max()} (index of the raster with the maximum value) \item Dispersion: \code{range()}, \code{stdev()} (standard deviation), \code{var()} (sample variance), \code{varpop()} (population variance), \code{nunique()} (number of unique values), \code{quantile()} (use argument \code{probs}), \code{skewness()}, and \code{kurtosis()}. \item Regression: Assuming we calculate a linear regression for each set of cells through all values of the cells, we can calculate its \code{slope()}, \code{intercept()}, \code{r2()}, and \code{tvalue()}. +\item \code{NA}s: \code{anyNA()} (any cells are \code{NA}?), \code{allNA()} (are all cells \code{NA}?) } } \examples{ @@ -124,157 +133,58 @@ library(sf) library(terra) # Example data -madElev <- fastData("madElev") +madChelsa <- fastData("madChelsa") # Convert a SpatRaster to a GRaster -elev <- fast(madElev) -elevs <- c(elev, elev, log10(elev) - 1, sqrt(elev)) -names(elevs) <- c("elev1", "elev2", "log_elev", "sqrt_elev") - -elev -elevs - -# do some math -elev + 100 -elev - 100 -elev * 100 -elev / 100 -elev ^ 2 -elev \%/\% 100 # divide then round down -elev \%\% 100 # modulus - -100 + elev -100 \%/\% elev -100 \%\% elev - -elevs + 100 -100 + elevs - -# math with logicals -elev + TRUE -elev - TRUE -elev * TRUE -elev / TRUE -elev ^ TRUE -elev \%/\% TRUE # divide then round down -elev \%\% TRUE # modulus - -elevs + TRUE -TRUE + elevs - -# Raster interacting with raster(s): -elev + elev -elev - elev -elev * elev -elev / elev -elev ^ log(elev) -elev \%/\% sqrt(elev) # divide then round down -elev \%\% sqrt(elev) # modulus - -elevs + elev -elev * elevs - -# sign -abs(-1 * elev) -abs(elevs) - -# powers -sqrt(elevs) - -# trigonometry -sin(elev) -cos(elev) -tan(elev) - -asin(elev) -acos(elev) -atan(elev) - -atan(elevs) -atan2(elev, elev^1.2) -atan2(elevs, elev^1.2) -atan2(elev, elevs^1.2) -atan2(elevs, elevs^1.2) - -# logarithms -exp(elev) -log(elev) -ln(elev) -log2(elev) -log1p(elev) -log10(elev) -log10p(elev) -log(elev, 3) - -log(elevs) - -# rounding -round(elev + 0.5) -floor(elev + 0.5) -ceiling(elev + 0.5) -trunc(elev + 0.5) - -# comparison -elev < 100 -elev <= 100 -elev == 100 -elev != 100 -elev > 100 -elev >= 100 - -elev + 100 < 2 * elev - -elevs > 10 -10 > elevs - -# logic -elev < 10 | elev > 200 -elev < 10 | cos(elev) > 0.9 - -elev < 10 | TRUE -TRUE | elev > 200 - -elev < 10 | FALSE -FALSE | elev > 200 - -elev < 10 & cos(elev) > 0.9 - -elev < 10 & TRUE -TRUE & elev > 200 - -elev < 10 & FALSE -FALSE & elev > 200 - -# Mathematical functions on GRasters with >= 2 layers: -mean(elevs) -mmode(elevs) -median(elevs) -nunique(elevs) - -sum(elevs) -count(elevs) -min(elevs) -max(elevs) -range(elevs) -skewness(elevs) -kurtosis(elevs) - -which.min(elevs) -which.max(elevs) - -slope(elevs) -intercept(elevs) -r2(elevs) -tvalue(elevs) - -stdev(elevs) -stdev(elevs, pop = FALSE) -var(elevs) -varpop(elevs) +chelsa <- fast(madChelsa) +chelsa # 4 layers + +# Central tendency +mean(chelsa) +mmode(chelsa) +median(chelsa) + +# Statistics +nunique(chelsa) +sum(chelsa) +count(chelsa) +min(chelsa) +max(chelsa) +range(chelsa) +skewness(chelsa) +kurtosis(chelsa) + +stdev(chelsa) +stdev(chelsa, pop = FALSE) +var(chelsa) +varpop(chelsa) + +# Which layers have maximum/minimum? +which.min(chelsa) +which.max(chelsa) + +# Regression +slope(chelsa) +intercept(chelsa) +r2(chelsa) +tvalue(chelsa) # Note: To get quantiles for each layer, use # global(x, "quantile", probs = 0.2). -quantile(elevs, 0.1) +quantile(chelsa, 0.1) + +# NAs +madForest2000 <- fastData("madForest2000") +forest2000 <- fast(madForest2000) +forest2000 <- project(forest2000, chelsa, method = "near") + +chelsaForest <- c(chelsa, forest2000) + +nas <- anyNA(chelsaForest) +plot(nas) + +allNas <- allNA(chelsaForest) +plot(allNas) } } diff --git a/man/math.Rd b/man/math.Rd index bc4102f3..70f703c8 100644 --- a/man/math.Rd +++ b/man/math.Rd @@ -146,165 +146,3 @@ You can apply mathematical functions to each layer of a \code{GRaster}. The outp } } } -\examples{ -if (grassStarted()) { - -# Setup -library(sf) -library(terra) - -# Example data -madElev <- fastData("madElev") - -# Convert a SpatRaster to a GRaster -elev <- fast(madElev) -elevs <- c(elev, elev, log10(elev) - 1, sqrt(elev)) -names(elevs) <- c("elev1", "elev2", "log_elev", "sqrt_elev") - -elev -elevs - -# do some math -elev + 100 -elev - 100 -elev * 100 -elev / 100 -elev ^ 2 -elev \%/\% 100 # divide then round down -elev \%\% 100 # modulus - -100 + elev -100 \%/\% elev -100 \%\% elev - -elevs + 100 -100 + elevs - -# math with logicals -elev + TRUE -elev - TRUE -elev * TRUE -elev / TRUE -elev ^ TRUE -elev \%/\% TRUE # divide then round down -elev \%\% TRUE # modulus - -elevs + TRUE -TRUE + elevs - -# Raster interacting with raster(s): -elev + elev -elev - elev -elev * elev -elev / elev -elev ^ log(elev) -elev \%/\% sqrt(elev) # divide then round down -elev \%\% sqrt(elev) # modulus - -elevs + elev -elev * elevs - -# sign -abs(-1 * elev) -abs(elevs) - -# powers -sqrt(elevs) - -# trigonometry -sin(elev) -cos(elev) -tan(elev) - -asin(elev) -acos(elev) -atan(elev) - -atan(elevs) -atan2(elev, elev^1.2) -atan2(elevs, elev^1.2) -atan2(elev, elevs^1.2) -atan2(elevs, elevs^1.2) - -# logarithms -exp(elev) -log(elev) -ln(elev) -log2(elev) -log1p(elev) -log10(elev) -log10p(elev) -log(elev, 3) - -log(elevs) - -# rounding -round(elev + 0.5) -floor(elev + 0.5) -ceiling(elev + 0.5) -trunc(elev + 0.5) - -# comparison -elev < 100 -elev <= 100 -elev == 100 -elev != 100 -elev > 100 -elev >= 100 - -elev + 100 < 2 * elev - -elevs > 10 -10 > elevs - -# logic -elev < 10 | elev > 200 -elev < 10 | cos(elev) > 0.9 - -elev < 10 | TRUE -TRUE | elev > 200 - -elev < 10 | FALSE -FALSE | elev > 200 - -elev < 10 & cos(elev) > 0.9 - -elev < 10 & TRUE -TRUE & elev > 200 - -elev < 10 & FALSE -FALSE & elev > 200 - -# Mathematical functions on GRasters with >= 2 layers: -mean(elevs) -mmode(elevs) -median(elevs) -nunique(elevs) - -sum(elevs) -count(elevs) -min(elevs) -max(elevs) -range(elevs) -skewness(elevs) -kurtosis(elevs) - -which.min(elevs) -which.max(elevs) - -slope(elevs) -intercept(elevs) -r2(elevs) -tvalue(elevs) - -stdev(elevs) -stdev(elevs, pop = FALSE) -var(elevs) -varpop(elevs) - -# Note: To get quantiles for each layer, use -# global(x, "quantile", probs = 0.2). -quantile(elevs, 0.1) - -} -} diff --git a/man/replace_dollar.Rd b/man/replace_dollar.Rd index 60f8f1e2..aa7e7590 100644 --- a/man/replace_dollar.Rd +++ b/man/replace_dollar.Rd @@ -70,7 +70,10 @@ rasts <- c(rasts, forest2014) # another way ### Subsetting GRaster layers # Subset: +rasts <- c(elev, forest2000, forest2014) rasts[[2:3]] +subset(rasts, 2:3) +subset(rasts, c("madForest2000", "madElev")) rasts[[c("madForest2000", "madElev")]] rasts$madForest2000 diff --git a/man/replace_double_square_brackets.Rd b/man/replace_double_square_brackets.Rd index f7a5f978..5901826a 100644 --- a/man/replace_double_square_brackets.Rd +++ b/man/replace_double_square_brackets.Rd @@ -64,7 +64,10 @@ rasts <- c(rasts, forest2014) # another way ### Subsetting GRaster layers # Subset: +rasts <- c(elev, forest2000, forest2014) rasts[[2:3]] +subset(rasts, 2:3) +subset(rasts, c("madForest2000", "madElev")) rasts[[c("madForest2000", "madElev")]] rasts$madForest2000 diff --git a/man/replace_single_square_bracket.Rd b/man/replace_single_square_bracket.Rd index 7dc98170..54987c9d 100644 --- a/man/replace_single_square_bracket.Rd +++ b/man/replace_single_square_bracket.Rd @@ -72,7 +72,10 @@ rasts <- c(rasts, forest2014) # another way ### Subsetting GRaster layers # Subset: +rasts <- c(elev, forest2000, forest2014) rasts[[2:3]] +subset(rasts, 2:3) +subset(rasts, c("madForest2000", "madElev")) rasts[[c("madForest2000", "madElev")]] rasts$madForest2000 diff --git a/man/subset_dollar.Rd b/man/subset_dollar.Rd index 336e58c0..3074ecd0 100644 --- a/man/subset_dollar.Rd +++ b/man/subset_dollar.Rd @@ -68,7 +68,10 @@ rasts <- c(rasts, forest2014) # another way ### Subsetting GRaster layers # Subset: +rasts <- c(elev, forest2000, forest2014) rasts[[2:3]] +subset(rasts, 2:3) +subset(rasts, c("madForest2000", "madElev")) rasts[[c("madForest2000", "madElev")]] rasts$madForest2000 @@ -127,5 +130,5 @@ dypsis$institutionCode[dypsis$institutionCode == "MO"] <- } } \seealso{ -\code{\link[fasterRaster]{[}}, \code{\link[fasterRaster]{[[}} +\code{\link[=subset]{subset()}}, \code{\link[fasterRaster]{[}}, \code{\link[fasterRaster]{[[}} } diff --git a/man/subset_double_square_brackets.Rd b/man/subset_double_square_brackets.Rd index a254a791..bdac7600 100644 --- a/man/subset_double_square_brackets.Rd +++ b/man/subset_double_square_brackets.Rd @@ -70,7 +70,10 @@ rasts <- c(rasts, forest2014) # another way ### Subsetting GRaster layers # Subset: +rasts <- c(elev, forest2000, forest2014) rasts[[2:3]] +subset(rasts, 2:3) +subset(rasts, c("madForest2000", "madElev")) rasts[[c("madForest2000", "madElev")]] rasts$madForest2000 @@ -128,3 +131,6 @@ dypsis$institutionCode[dypsis$institutionCode == "MO"] <- } } +\seealso{ +\code{\link[=subset]{subset()}} +} diff --git a/man/subset_single_bracket.Rd b/man/subset_single_bracket.Rd index 30ac8f11..9d933160 100644 --- a/man/subset_single_bracket.Rd +++ b/man/subset_single_bracket.Rd @@ -72,7 +72,10 @@ rasts <- c(rasts, forest2014) # another way ### Subsetting GRaster layers # Subset: +rasts <- c(elev, forest2000, forest2014) rasts[[2:3]] +subset(rasts, 2:3) +subset(rasts, c("madForest2000", "madElev")) rasts[[c("madForest2000", "madElev")]] rasts$madForest2000 @@ -131,5 +134,5 @@ dypsis$institutionCode[dypsis$institutionCode == "MO"] <- } } \seealso{ -\link{$}, \code{\link[fasterRaster]{[[}} +\code{\link[=subset]{subset()}}, \link{$}, \code{\link[fasterRaster]{[[}} } From dbb99a6736c3a3ffbdcc872cb89f1b5cace2ea79 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 00:06:19 -0500 Subject: [PATCH 051/125] Rename files and help files to coincide --- R/04_arithmetic.r | 2 +- ...ayer.r => 05_GRaster_functions_by_layer.r} | 2 +- ...r => 06_GRaster_functions_across_layers.r} | 0 R/07_comparison.r | 2 +- R/08_logic.r | 2 +- man/examples/ex_GRaster_arithmetic.r | 160 ------------------ ...r.r.r => ex_GRaster_arithmetic_by_layer.r} | 31 ---- man/examples/ex_GRaster_comparison_logic.r | 48 ++++++ 8 files changed, 52 insertions(+), 195 deletions(-) rename R/{05_GRaster_functions_byLayer.r => 05_GRaster_functions_by_layer.r} (99%) rename R/{06_GRaster_functions_acrossLayers.r => 06_GRaster_functions_across_layers.r} (100%) delete mode 100644 man/examples/ex_GRaster_arithmetic.r rename man/examples/{ex_GRaster_arithmetic_single_layer.r.r => ex_GRaster_arithmetic_by_layer.r} (77%) create mode 100644 man/examples/ex_GRaster_comparison_logic.r diff --git a/R/04_arithmetic.r b/R/04_arithmetic.r index 39783e5a..5a1fc93b 100644 --- a/R/04_arithmetic.r +++ b/R/04_arithmetic.r @@ -12,7 +12,7 @@ #' #' @return A `GRaster`. #' -#' @example man/examples/ex_GRaster_arithmetic_single_layer.r +#' @example man/examples/ex_GRaster_arithmetic_by_layer.r #' #' @aliases Arith #' @rdname Arithmetic diff --git a/R/05_GRaster_functions_byLayer.r b/R/05_GRaster_functions_by_layer.r similarity index 99% rename from R/05_GRaster_functions_byLayer.r rename to R/05_GRaster_functions_by_layer.r index cd75b368..d55f2c8e 100644 --- a/R/05_GRaster_functions_byLayer.r +++ b/R/05_GRaster_functions_by_layer.r @@ -41,7 +41,7 @@ #' #' @returns A `GRaster`. #' -#' @example man/examples/ex_GRaster_arithmetic.r +#' @example man/examples/ex_GRaster_arithmetic_by_layer.r #' #' @aliases is.na #' @rdname math diff --git a/R/06_GRaster_functions_acrossLayers.r b/R/06_GRaster_functions_across_layers.r similarity index 100% rename from R/06_GRaster_functions_acrossLayers.r rename to R/06_GRaster_functions_across_layers.r diff --git a/R/07_comparison.r b/R/07_comparison.r index 70adfd32..c292d883 100644 --- a/R/07_comparison.r +++ b/R/07_comparison.r @@ -13,7 +13,7 @@ #' #' Comparing `GRegion`s: Output is logical. #' -#' @example man/examples/ex_GRaster_arithmetic.r +#' @example man/examples/ex_GRaster_comparison_logic.r #' #' @aliases Compare-methods #' @rdname Compare-methods diff --git a/R/08_logic.r b/R/08_logic.r index 08edc6a6..beb49fe4 100644 --- a/R/08_logic.r +++ b/R/08_logic.r @@ -13,7 +13,7 @@ #' #' @returns A binary `GRaster` (1 ==> `TRUE`, 0 ==> `FALSE`, plus `NA` when comparison results in `NA`). #' -#' @example man/examples/ex_GRaster_arithmetic.r +#' @example man/examples/ex_GRaster_comparison_logic.r #' #' @aliases Logic-methods #' @rdname Logic-methods diff --git a/man/examples/ex_GRaster_arithmetic.r b/man/examples/ex_GRaster_arithmetic.r deleted file mode 100644 index f555b7d4..00000000 --- a/man/examples/ex_GRaster_arithmetic.r +++ /dev/null @@ -1,160 +0,0 @@ -if (grassStarted()) { - -# Setup -library(sf) -library(terra) - -# Example data -madElev <- fastData("madElev") - -# Convert a SpatRaster to a GRaster -elev <- fast(madElev) -elevs <- c(elev, elev, log10(elev) - 1, sqrt(elev)) -names(elevs) <- c("elev1", "elev2", "log_elev", "sqrt_elev") - -elev -elevs - -# do some math -elev + 100 -elev - 100 -elev * 100 -elev / 100 -elev ^ 2 -elev %/% 100 # divide then round down -elev %% 100 # modulus - -100 + elev -100 %/% elev -100 %% elev - -elevs + 100 -100 + elevs - -# math with logicals -elev + TRUE -elev - TRUE -elev * TRUE -elev / TRUE -elev ^ TRUE -elev %/% TRUE # divide then round down -elev %% TRUE # modulus - -elevs + TRUE -TRUE + elevs - -# Raster interacting with raster(s): -elev + elev -elev - elev -elev * elev -elev / elev -elev ^ log(elev) -elev %/% sqrt(elev) # divide then round down -elev %% sqrt(elev) # modulus - -elevs + elev -elev * elevs - -# sign -abs(-1 * elev) -abs(elevs) - -# powers -sqrt(elevs) - -# trigonometry -sin(elev) -cos(elev) -tan(elev) - -asin(elev) -acos(elev) -atan(elev) - -atan(elevs) -atan2(elev, elev^1.2) -atan2(elevs, elev^1.2) -atan2(elev, elevs^1.2) -atan2(elevs, elevs^1.2) - -# logarithms -exp(elev) -log(elev) -ln(elev) -log2(elev) -log1p(elev) -log10(elev) -log10p(elev) -log(elev, 3) - -log(elevs) - -# rounding -round(elev + 0.5) -floor(elev + 0.5) -ceiling(elev + 0.5) -trunc(elev + 0.5) - -# comparison -elev < 100 -elev <= 100 -elev == 100 -elev != 100 -elev > 100 -elev >= 100 - -elev + 100 < 2 * elev - -elevs > 10 -10 > elevs - -# logic -elev < 10 | elev > 200 -elev < 10 | cos(elev) > 0.9 - -elev < 10 | TRUE -TRUE | elev > 200 - -elev < 10 | FALSE -FALSE | elev > 200 - -elev < 10 & cos(elev) > 0.9 - -elev < 10 & TRUE -TRUE & elev > 200 - -elev < 10 & FALSE -FALSE & elev > 200 - -# Mathematical functions on GRasters with >= 2 layers: -mean(elevs) -mmode(elevs) -median(elevs) -nunique(elevs) - -sum(elevs) -count(elevs) -min(elevs) -max(elevs) -range(elevs) -skewness(elevs) -kurtosis(elevs) - -which.min(elevs) -which.max(elevs) - -slope(elevs) -intercept(elevs) -r2(elevs) -tvalue(elevs) - -stdev(elevs) -stdev(elevs, pop = FALSE) -var(elevs) -varpop(elevs) - -# Note: To get quantiles for each layer, use -# global(x, "quantile", probs = 0.2). -quantile(elevs, 0.1) - -} diff --git a/man/examples/ex_GRaster_arithmetic_single_layer.r.r b/man/examples/ex_GRaster_arithmetic_by_layer.r similarity index 77% rename from man/examples/ex_GRaster_arithmetic_single_layer.r.r rename to man/examples/ex_GRaster_arithmetic_by_layer.r index cc0525dc..b9b538cd 100644 --- a/man/examples/ex_GRaster_arithmetic_single_layer.r.r +++ b/man/examples/ex_GRaster_arithmetic_by_layer.r @@ -95,35 +95,4 @@ floor(elev + 0.5) ceiling(elev + 0.5) trunc(elev + 0.5) -# comparison -elev < 100 -elev <= 100 -elev == 100 -elev != 100 -elev > 100 -elev >= 100 - -elev + 100 < 2 * elev - -elevs > 10 -10 > elevs - -# logic -elev < 10 | elev > 200 -elev < 10 | cos(elev) > 0.9 - -elev < 10 | TRUE -TRUE | elev > 200 - -elev < 10 | FALSE -FALSE | elev > 200 - -elev < 10 & cos(elev) > 0.9 - -elev < 10 & TRUE -TRUE & elev > 200 - -elev < 10 & FALSE -FALSE & elev > 200 - } diff --git a/man/examples/ex_GRaster_comparison_logic.r b/man/examples/ex_GRaster_comparison_logic.r new file mode 100644 index 00000000..e75fc8f6 --- /dev/null +++ b/man/examples/ex_GRaster_comparison_logic.r @@ -0,0 +1,48 @@ +if (grassStarted()) { + +# Setup +library(terra) + +# Example data +madElev <- fastData("madElev") + +# Convert a SpatRaster to a GRaster +elev <- fast(madElev) +elevs <- c(elev, elev, log10(elev) - 1, sqrt(elev)) +names(elevs) <- c("elev1", "elev2", "log_elev", "sqrt_elev") + +elev +elevs + +# Comparisons +elev < 100 +elev <= 100 +elev == 100 +elev != 100 +elev > 100 +elev >= 100 + +elev + 100 < 2 * elev + +elevs > 10 +10 > elevs + +# logic +elev < 10 | elev > 200 +elev < 10 | cos(elev) > 0.9 + +elev < 10 | TRUE +TRUE | elev > 200 + +elev < 10 | FALSE +FALSE | elev > 200 + +elev < 10 & cos(elev) > 0.9 + +elev < 10 & TRUE +TRUE & elev > 200 + +elev < 10 & FALSE +FALSE & elev > 200 + +} From 300f5ffd039d726c02eb58cde937e21fdfc6b817 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 00:06:23 -0500 Subject: [PATCH 052/125] Update fasterRaster.r --- R/fasterRaster.r | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/R/fasterRaster.r b/R/fasterRaster.r index ee1b02b3..a3da8f42 100644 --- a/R/fasterRaster.r +++ b/R/fasterRaster.r @@ -51,7 +51,7 @@ #' * Rounding: [round()], [floor()], [ceiling()], [trunc()] #' * Signs: [abs()] #' -#' Mathematical functions that are applied across layers of a "stack" of `GRaster`s: +#' Mathematical functions that are applied across layers of multi-layered `GRaster`s: #' * Numeration: [sum()], [count()] #' * Central tendency: [mean()], [mmode()], [median()] #' * Dispersion: [stdev()], [var()], [varpop()], [nunique()], [range()], [quantile()], [skewness()], [kurtosis()] @@ -59,11 +59,10 @@ #' * `NA`s: [allNA()], [anyNA()] #' #' Subsetting, assigning, and replacing `GRaster` layers -#' * : Subset or remove specific layers of a `GRaster` #' * [$], \code{\link[fasterRaster]{[[}}, or [subset()]: Subset or remove specific layers of a `GRaster` #' * \code{\link[fasterRaster]{[<-}}: Replace values of cells of a `GRaster` #' * \code{\link[fasterRaster]{[[<-}}: Replace specific layers of a `GRaster` -#' * [fasterRaster]{add<-}}: Replace specific layers of a `GRaster` +#' * \code{\link[fasterRaster]{add<-}}: Replace specific layers of a `GRaster` #' #' Operations on `GRaster`s #' * [as.int()], [as.float()], [as.doub()]: Change data type (integer/float/double) From 0794d984426e41cec6c441091e0d588d9bde0c31 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 00:06:34 -0500 Subject: [PATCH 053/125] Update --- man/Arithmetic.Rd | 100 +++++++++++ man/Compare-methods.Rd | 50 ++++++ man/Logic-methods.Rd | 50 ++++++ man/fasterRaster.Rd | 377 +++++++++++++++++++++++++++++++++++++++++ man/functions.Rd | 2 +- man/math.Rd | 102 ++++++++++- 6 files changed, 679 insertions(+), 2 deletions(-) diff --git a/man/Arithmetic.Rd b/man/Arithmetic.Rd index 1925144e..988d3d5d 100644 --- a/man/Arithmetic.Rd +++ b/man/Arithmetic.Rd @@ -43,3 +43,103 @@ A \code{GRaster}. \code{*} operator: Same as \code{\link[=intersect]{intersect()}}\cr \code{/} operator: Same as \code{\link[=xor]{xor()}}\cr } +\examples{ +if (grassStarted()) { + +# Setup +library(sf) +library(terra) + +# Example data +madElev <- fastData("madElev") + +# Convert a SpatRaster to a GRaster +elev <- fast(madElev) +elevs <- c(elev, elev, log10(elev) - 1, sqrt(elev)) +names(elevs) <- c("elev1", "elev2", "log_elev", "sqrt_elev") + +elev +elevs + +# do some math +elev + 100 +elev - 100 +elev * 100 +elev / 100 +elev ^ 2 +elev \%/\% 100 # divide then round down +elev \%\% 100 # modulus + +100 + elev +100 \%/\% elev +100 \%\% elev + +elevs + 100 +100 + elevs + +# math with logicals +elev + TRUE +elev - TRUE +elev * TRUE +elev / TRUE +elev ^ TRUE +elev \%/\% TRUE # divide then round down +elev \%\% TRUE # modulus + +elevs + TRUE +TRUE + elevs + +# Raster interacting with raster(s): +elev + elev +elev - elev +elev * elev +elev / elev +elev ^ log(elev) +elev \%/\% sqrt(elev) # divide then round down +elev \%\% sqrt(elev) # modulus + +elevs + elev +elev * elevs + +# sign +abs(-1 * elev) +abs(elevs) + +# powers +sqrt(elevs) + +# trigonometry +sin(elev) +cos(elev) +tan(elev) + +asin(elev) +acos(elev) +atan(elev) + +atan(elevs) +atan2(elev, elev^1.2) +atan2(elevs, elev^1.2) +atan2(elev, elevs^1.2) +atan2(elevs, elevs^1.2) + +# logarithms +exp(elev) +log(elev) +ln(elev) +log2(elev) +log1p(elev) +log10(elev) +log10p(elev) +log(elev, 3) + +log(elevs) + +# rounding +round(elev + 0.5) +floor(elev + 0.5) +ceiling(elev + 0.5) +trunc(elev + 0.5) + +} +} diff --git a/man/Compare-methods.Rd b/man/Compare-methods.Rd index 3f57f046..54388c05 100644 --- a/man/Compare-methods.Rd +++ b/man/Compare-methods.Rd @@ -51,3 +51,53 @@ You can do comparative operations on \code{GRaster}s using normal operators in \ You can also compare two \code{GRegion}s using the \code{==} and \code{!=} operators. Most users of \strong{fasterRaster} will not have to work much with "regions" (see \code{vignette("regions", package = "fasterRaster")}), so can ignore this functionality. \code{GRegion}s are the same if they have the same coordinate reference system, location/project and mapset (see \code{vignette("projects_mapsets", package = "fasterRaster")}), topology (2D or 3D), extent, and resolution. If both are 3D, then they must also have the same vertical extent and number of depths. } +\examples{ +if (grassStarted()) { + +# Setup +library(terra) + +# Example data +madElev <- fastData("madElev") + +# Convert a SpatRaster to a GRaster +elev <- fast(madElev) +elevs <- c(elev, elev, log10(elev) - 1, sqrt(elev)) +names(elevs) <- c("elev1", "elev2", "log_elev", "sqrt_elev") + +elev +elevs + +# Comparisons +elev < 100 +elev <= 100 +elev == 100 +elev != 100 +elev > 100 +elev >= 100 + +elev + 100 < 2 * elev + +elevs > 10 +10 > elevs + +# logic +elev < 10 | elev > 200 +elev < 10 | cos(elev) > 0.9 + +elev < 10 | TRUE +TRUE | elev > 200 + +elev < 10 | FALSE +FALSE | elev > 200 + +elev < 10 & cos(elev) > 0.9 + +elev < 10 & TRUE +TRUE & elev > 200 + +elev < 10 & FALSE +FALSE & elev > 200 + +} +} diff --git a/man/Logic-methods.Rd b/man/Logic-methods.Rd index 4de3d0d4..371381b8 100644 --- a/man/Logic-methods.Rd +++ b/man/Logic-methods.Rd @@ -45,3 +45,53 @@ Operators include: \item \code{&}: \code{TRUE} if both conditions are \code{TRUE} (or 1), but \code{NA} if either is \code{NA}. } } +\examples{ +if (grassStarted()) { + +# Setup +library(terra) + +# Example data +madElev <- fastData("madElev") + +# Convert a SpatRaster to a GRaster +elev <- fast(madElev) +elevs <- c(elev, elev, log10(elev) - 1, sqrt(elev)) +names(elevs) <- c("elev1", "elev2", "log_elev", "sqrt_elev") + +elev +elevs + +# Comparisons +elev < 100 +elev <= 100 +elev == 100 +elev != 100 +elev > 100 +elev >= 100 + +elev + 100 < 2 * elev + +elevs > 10 +10 > elevs + +# logic +elev < 10 | elev > 200 +elev < 10 | cos(elev) > 0.9 + +elev < 10 | TRUE +TRUE | elev > 200 + +elev < 10 | FALSE +FALSE | elev > 200 + +elev < 10 & cos(elev) > 0.9 + +elev < 10 & TRUE +TRUE & elev > 200 + +elev < 10 & FALSE +FALSE & elev > 200 + +} +} diff --git a/man/fasterRaster.Rd b/man/fasterRaster.Rd index 2aaee464..52617ff7 100644 --- a/man/fasterRaster.Rd +++ b/man/fasterRaster.Rd @@ -6,7 +6,384 @@ \alias{fasterRaster} \title{"fasterRaster": Faster raster and spatial vector processing using "GRASS GIS"} \description{ +\strong{fasterRaster}: Processing of large-in-memory/-on disk rasters and spatial vectors in using \strong{GRASS GIS}. Most functions in the \strong{terra} and \strong{sf} packages are recreated. Processing of medium-sized and smaller spatial objects will nearly always be faster using \strong{terra} or \strong{sf}. To use most of the functions you must have the stand-alone version of \strong{GRASS GIS} version 8.3 or higher (not the \strong{OSGeoW4} installer version). Note that due to differences in how \strong{GRASS}, \strong{terra}, and \strong{sf} were implemented, results will not always be strictly comparable between functions for the same operation. +\subsection{Most useful tutorials and functions:}{ +\itemize{ +\item The quick-start guide to getting started with \strong{fasterRaster} , accessible using \code{vignette("fasterRaster", package = "fasterRaster")} +\item The vignette on types of \code{GRaster}s, accessible using \code{vignette("GRasters", package = "fasterRaster")} +\item The vignette on how to speed up \strong{fasterRaster}, accessible using \code{vignette("faster_fasterRaster", package = "fasterRaster")} +\item \code{\link[=faster]{faster()}}: Set the directory where \strong{GRASS} is installed on your system, and set or get other package-wide options. This function must be run once before using most \strong{fasterRaster} functions. +\item \code{\link[=fast]{fast()}}: Convert a \code{SpatRaster}, \code{SpatVector}, or \code{sf} vector to \strong{fasterRaster}'s raster format (\code{GRaster}s) or vector format (\code{GVector}s), or load one from a file +\item \code{\link[=rast]{rast()}}, \code{\link[=vect]{vect()}}, and \code{\link[=st_as_sf]{st_as_sf()}}: Convert \code{GRaster}s and \code{GVector}s to \code{SpatRaster}s, \code{SpatVector}s, or \code{sf} vectors +\item \code{\link[=writeRaster]{writeRaster()}} and \code{\link[=writeVector]{writeVector()}}: Save \code{GRaster}s or \code{GVector}s to disk +} +} + +\subsection{Properties of \code{GRasters}}{ +\itemize{ +\item \code{\link[=crs]{crs()}}: Coordinate reference system +\item \code{\link[=datatype]{datatype()}}: Data type +\item \code{\link[=dim]{dim()}} and \code{\link[=dim3d]{dim3d()}}: Number of rows, columns, and depths +\item \code{\link[=ext]{ext()}}, \code{\link[=N]{N()}}, \code{\link[=S]{S()}}, \code{\link[=E]{E()}}, \code{\link[=W]{W()}}, \code{\link[=top]{top()}}, and \code{\link[=bottom]{bottom()}}: Spatial extent +\item \code{\link[=freq]{freq()}}: Frequencies of cell values in a raster +\item \code{\link[=is.2d]{is.2d()}} and \code{\link[=is.3d]{is.3d()}}: Is an object 2- or 3-dimensional? +\item \code{\link[=is.int]{is.int()}}, \code{\link[=is.cell]{is.cell()}}, \code{\link[=is.float]{is.float()}}, \code{\link[=is.doub]{is.doub()}}: \code{GRaster} data type (integer/float/double) +\item \code{\link[=is.factor]{is.factor()}}: Does a raster represent categorical data? +\item \code{\link[=is.lonlat]{is.lonlat()}}: Is an object projected (e.g., in WGS84)? +\item \code{\link[=levels]{levels()}}: Names of levels in a categorical \code{GRaster} +\item \code{\link[=minmax]{minmax()}}: Minimum and maximum values across all non-\code{NA} cells +\item \code{\link[=names]{names()}}: \code{GRaster} names +\item \code{\link[=ncol]{ncol()}}: Number of columns +\item \code{\link[=nacell]{nacell()}}: Number of \code{NA} cells +\item \code{\link[=ncell]{ncell()}}: Number of cells +\item \code{\link[=ncell3d]{ncell3d()}}: Number of cells of a 3D \code{GRaster} +\item \code{\link[=ndepth]{ndepth()}}: Number of depths of a 3D \code{GRaster} +\item \code{\link[=nlyr]{nlyr()}}: Number of layers +\item \code{\link[=nonnacell]{nonnacell()}}: Number of non-\code{NA} cells +\item \code{\link[=nrow]{nrow()}}: Number of rows +\item \code{\link[=nlevels]{nlevels()}}: Number of categories +\item \code{\link[=res]{res()}}, \code{\link[=res3d]{res3d()}}, \code{\link[=xres]{xres()}}, \code{\link[=yres]{yres()}}, and \code{\link[=zres]{zres()}}: Spatial resolution +\item \code{\link[=sources]{sources()}}: Name of the \code{GRaster} in \strong{GRASS} +\item +\item \code{\link[=zext]{zext()}}: Vertical extent +\item \code{\link[=zres]{zres()}}: Vertical resolution +} +} + +\subsection{Functions that operate on or create \code{GRasters}}{ +\itemize{ +\item \link{Arithmetic}: Mathematical operations on \code{GRaster}s: \code{+}, \code{-}, \code{*}, \code{/}, \code{^}, \code{\%\%} (modulus), \code{\%/\%} (integer division) +\item \link[=Compare-methods]{Logical comparisons}: \code{<}, \code{<=}, \code{==}, \code{!=}, \code{>=}, and \code{>}, plus \code{\link[fasterRaster]{\%in\%}} and \code{\link[fasterRaster]{\%notin\%}} (for categorical rasters only) +\item \link[=Logic-methods]{Logical operators}: \code{|}and \code{&} +} + +Mathematical functions that are applied to each layer of a \code{GRaster}: +\itemize{ +\item Working with \code{NA}s: \code{\link[=is.na]{is.na()}}, \code{\link[=not.na]{not.na()}}, and \code{\link[=maskNA]{maskNA()}} +\item Trigonometry: \code{\link[=sin]{sin()}}, \code{\link[=cos]{cos()}}, \code{\link[=tan]{tan()}}, \code{\link[=asin]{asin()}}, \code{\link[=acos]{acos()}}, \code{\link[=atan]{atan()}}, \code{\link[=atan2]{atan2()}} +\item Logarithms and powers: \code{\link[=exp]{exp()}}, \code{\link[=log]{log()}}, \code{\link[=ln]{ln()}}, \code{\link[=log1p]{log1p()}}, \code{\link[=log2]{log2()}}, \code{\link[=log10]{log10()}}, \code{\link[=sqrt]{sqrt()}} +\item Rounding: \code{\link[=round]{round()}}, \code{\link[=floor]{floor()}}, \code{\link[=ceiling]{ceiling()}}, \code{\link[=trunc]{trunc()}} +\item Signs: \code{\link[=abs]{abs()}} +} + +Mathematical functions that are applied across layers of multi-layered \code{GRaster}s: +\itemize{ +\item Numeration: \code{\link[=sum]{sum()}}, \code{\link[=count]{count()}} +\item Central tendency: \code{\link[=mean]{mean()}}, \code{\link[=mmode]{mmode()}}, \code{\link[=median]{median()}} +\item Dispersion: \code{\link[=stdev]{stdev()}}, \code{\link[=var]{var()}}, \code{\link[=varpop]{varpop()}}, \code{\link[=nunique]{nunique()}}, \code{\link[=range]{range()}}, \code{\link[=quantile]{quantile()}}, \code{\link[=skewness]{skewness()}}, \code{\link[=kurtosis]{kurtosis()}} +\item Extremes: \code{\link[=min]{min()}}, \code{\link[=max]{max()}}, \code{\link[=which.min]{which.min()}}, \code{\link[=which.max]{which.max()}} +\item \code{NA}s: \code{\link[=allNA]{allNA()}}, \code{\link[=anyNA]{anyNA()}} +} + +Subsetting, assigning, and replacing \code{GRaster} layers +\itemize{ +\item \link{$}, \code{\link[fasterRaster]{[[}}, or \code{\link[=subset]{subset()}}: Subset or remove specific layers of a \code{GRaster} +\item \code{\link[fasterRaster]{[<-}}: Replace values of cells of a \code{GRaster} +\item \code{\link[fasterRaster]{[[<-}}: Replace specific layers of a \code{GRaster} +\item \code{\link[fasterRaster]{add<-}}: Replace specific layers of a \code{GRaster} +} + +Operations on \code{GRaster}s +\itemize{ +\item \code{\link[=as.int]{as.int()}}, \code{\link[=as.float]{as.float()}}, \code{\link[=as.doub]{as.doub()}}: Change data type (integer/float/double) +\item \code{\link[=as.lines]{as.lines()}}: Convert a \code{GRaster} to a "lines" vector +\item \code{\link[=as.points]{as.points()}}: Convert a \code{GRaster} to a "points" vector +\item \code{\link[=as.polygons]{as.polygons()}}: Convert a \code{GRaster} to a "polygons" vector +\item \code{\link[=aggregate]{aggregate()}}: Aggregate values of \code{GRaster} cells into larger cells +\item \code{\link[=bioclims]{bioclims()}}: BIOCLIM rasters (classic set and extended set) +\item \code{\link[=buffer]{buffer()}}: Create a buffer around non-\code{NA} cells +\item \code{\link[=app]{app()}}: Apply a user-defined function to multiple layers of a \code{GRaster} (with helper functions \code{\link[=appFuns]{appFuns()}} and \code{\link[=appCheck]{appCheck()}}) +\item \code{\link[=c]{c()}}: "Stack" two or more rasters +\item \code{\link[=cellSize]{cellSize()}}: Cell area +\item \code{\link[=classify]{classify()}}: Partition cell values into strata +\item \code{\link[=clump]{clump()}}: Group adjacent cells with similar values +\item \code{\link[=combineCats]{combineCats()}}: Combine values from two or more categorical and/or integer rasters +\item \code{\link[=combineLevels]{combineLevels()}}: Combine the "levels" tables of two or more categorical \code{GRaster}s +\item \code{\link[=crop]{crop()}}: Remove parts of a \code{GRaster} +\item \code{\link[=denoise]{denoise()}}: Remove "noise" from a \code{GRaster} using a principal components analysis (PCA) +\item \code{\link[=distance]{distance()}}: Distance to non-\code{NA} cells, or vice versa +\item \code{\link[=extend]{extend()}}: Add rows and columns to a \code{GRaster} +\item \code{\link[=extract]{extract()}}: Extract values from a \code{GRaster} at locations of a \code{GVector} +\item \code{\link[=fillNAs]{fillNAs()}}: Fill \code{NA} cells +\item \code{\link[=focal]{focal()}}: Calculate cell values based on values of nearby cells +\item \code{\link[=fragmentation]{fragmentation()}}: Landscape fragmentation class from Riitters et al. (2020) +\item \code{\link[=global]{global()}}: Summary statistics across cells of each \code{GRaster} layer +\item \code{\link[=hist]{hist()}}: Histogram of \code{GRaster} values +\item \code{\link[=interpIDW]{interpIDW()}}: Interpolate values at points to a \code{GRaster} +\item \code{\link[=kernel]{kernel()}}: Kernel density estimator of points +\item \code{\link[=layerCor]{layerCor()}}: Correlation or covariance between two or more \code{GRaster} layers +\item \code{\link[=mask]{mask()}}: Remove values in a \code{GRaster} based on values in another \code{GRaster} or vector +\item \code{\link[=maskNA]{maskNA()}}: Mask all non-NA cells or all NA cells +\item \code{\link[=match]{match()}}, \code{\link[fasterRaster]{\%in\%}}, and \code{\link[fasterRaster]{\%notin\%}}: Find which cells of a \code{GRaster} match or do not match certain values +\item \code{\link[=merge]{merge()}}: Combine two or more rasters with different extents and fill in \code{NA}s +\item \code{\link[fasterRaster]{names<-}}: Assign names to a \code{GRaster} +\item \code{\link[=noise]{noise()}}: Remove coarse-scale trends from a \code{GRaster}, leaving just fine-scale "noise" +\item \code{\link[=pairs]{pairs()}}: Plot correlations between \code{GRaster} layers +\item \code{\link[=pca]{pca()}}: Apply a principal components analysis (PCA) to a \code{GRaster} +\item \code{\link[=pcs]{pcs()}}: Retrieve a principal components model from a PCA \code{GRaster} generated using \code{pca()} +\item \code{\link[=plot]{plot()}}: Display a \code{GRaster} +\item \code{\link[=plotRGB]{plotRGB()}}: Display a multispectral \code{GRaster} using red, blue, green, and alpha channels +\item \code{\link[=project]{project()}}: Change coordinate reference system and cell size +\item \code{\link[=predict]{predict()}}: Make predictions to a \code{GRaster} from a linear model or generalized linear model +\item \code{\link[=resample]{resample()}}: Change cell size +\item \code{\link[=reorient]{reorient()}}: Convert degrees between 'north-orientation' and 'east orientation' +\item \code{\link[=sampleRast]{sampleRast()}}: Randomly sample cells from a \code{GRaster} +\item \code{\link[=scale]{scale()}}, \code{\link[=scalepop]{scalepop()}}, and \code{\link[=unscale]{unscale()}}: Subtract means and divide by standard deviations, or inverse of that +\item \code{\link[=selectRange]{selectRange()}}: Select values from rasters in a stack based on values in another \code{GRaster} +\item \code{\link[=spatSample]{spatSample()}}: Randomly points from a \code{GRaster} +\item \code{\link[=stretch]{stretch()}}: Rescale values in a GRaster +\item \code{\link[=subst]{subst()}}: Re-assign cell values +\item \code{\link[=thinLines]{thinLines()}}: Reduce linear features on a \code{GRaster} so linear features are 1 cell wide +\item \code{\link[=tiles]{tiles()}}: Divide a \code{GRaster} into spatially exclusive subsets (though with possible overlap) +\item \code{\link[=trim]{trim()}}: Remove rows and columns from a \code{GRaster} that are all \code{NA} +\item \code{\link[=zonal]{zonal()}}: Statistics (mean, sum, etc.) on areas of a \code{GRaster} defined by sets of cells with the same values in another \code{GRaster}, or by geometries in a \code{GVector} +\item \code{\link[=zonalGeog]{zonalGeog()}}: Geographic statistics (area, perimeter, fractal dimension, etc.) for sets of cells with the same values +} +} + +\subsection{Creating \code{GRaster}s \emph{de novo}}{ +\itemize{ +\item \code{\link[=fractalRast]{fractalRast()}}: Create a fractal \code{GRaster} +\item \code{\link[=init]{init()}}: GRaster with values equal to row, column, coordinate, regular, or "chess" +\item \code{\link[=longlat]{longlat()}}: Create longitude/latitude rasters +\item \code{\link[=rnormRast]{rnormRast()}}: A random \code{GRaster} with values drawn from a normal distribution +\item \code{\link[=rSpatialDepRast]{rSpatialDepRast()}}: Create a random \code{GRaster} with or without spatial dependence +\item \code{\link[=runifRast]{runifRast()}}: A random \code{GRaster} with values drawn from a uniform distribution +\item \code{\link[=sineRast]{sineRast()}}: Sine wave rasters +} +} +\subsection{Analysis of terrain and hydrology}{ +\itemize{ +\item \code{\link[=as.contour]{as.contour()}}: Contour lines from a \code{GRaster} +\item \code{\link[=flow]{flow()}}: Identify watershed basins and direction and accumulation of flow +\item \code{\link[=flowPath]{flowPath()}}: Path of water flow across a landscape +\item \code{\link[=geomorphons]{geomorphons()}}: Identify terrain feature types +\item \code{\link[=shade]{hillshade()}}: Create a hillshade \code{GRaster} +\item \code{\link[=horizonHeight]{horizonHeight()}}: Horizon height +\item \code{\link[=sun]{sun()}}: Solar radiance and irradiance +\item \code{\link[=ruggedness]{ruggedness()}}: Terrain Ruggedness Index +\item \code{\link[=streams]{streams()}}: Create stream network +\item \code{\link[=terrain]{terrain()}}: Slope, aspect, curvature, and partial slopes +\item \code{\link[=wetness]{wetness()}}: Topographic wetness index +} +} + +\subsection{Operations on categorical (factor) \code{GRaster}s}{ +\itemize{ +\item \code{\link[fasterRaster]{\%in\%}}, and \code{\link[fasterRaster]{\%notin\%}}: Mask cells that match or do not match a given category +\item \code{\link[=activeCat]{activeCat()}} and \code{\link[=activeCats]{activeCats()}}: Column(s) that defines category labels +\code{\link[fasterRaster]{activeCat<-}}: Set column that defines category labels +\item \code{\link[=addCats]{addCats()}}: Add new columns to a "levels" table +\code{\link[fasterRaster]{addCats<-}}: Add new rows (levels) to a "levels" table +\item \code{\link[=categories]{categories()}}: Set "levels" table for specific layers of a categorical raster +\item \code{\link[=catNames]{catNames()}}: Column names of each "levels" table +\item \code{\link[=cats]{cats()}}: "Levels" table of a categorical raster +\item \code{\link[=combineCats]{combineCats()}}: Combine categories from two or more categorical rasters +\item \code{\link[=combineLevels]{combineLevels()}}: Combine the "levels" tables of two or more categorical \code{GRaster}s +\item \code{\link[=complete.cases]{complete.cases()}}: Find rows of a categorical \code{GRaster}'s "levels" table that have no \code{NA}s in them +\item \code{\link[=droplevels]{droplevels()}}: Remove one or more levels +\item \code{\link[=freq]{freq()}}: Frequency of each category across cells of a raster +\item \code{\link[=is.factor]{is.factor()}}: Is a raster categorical? +\item \code{\link[=levels]{levels()}}: "Levels" table of a categorical raster +\item \code{\link[fasterRaster]{levels<-}}: Set "levels" table of a categorical raster +\item \code{\link[=match]{match()}}, \code{\link[fasterRaster]{\%in\%}}, and \code{\link[fasterRaster]{\%notin\%}}: Find which cells of a \code{GRaster} match or do not match certain category labels +\item \code{\link[=minmax]{minmax()}}: "Lowest" and "highest" category values of categorical rasters (when argument \code{levels = TRUE}) +\item \code{\link[=missing.cases]{missing.cases()}}: Find rows of a categorical \code{GRaster}'s "levels" table that have at least one \code{NA} in them +\item \code{\link[=missingCats]{missingCats()}}: Values that have no category assigned to them +\item \code{\link[=nlevels]{nlevels()}}: Number of levels +\item \code{\link[=subst]{subst()}}: Re-assign category levels +\item \code{\link[=zonalGeog]{zonalGeog()}}: Geographic statistics (area, perimeter, fractal dimension, etc.) for sets of cells with the same values +} +} + +\subsection{Analysis of remote sensing rasters}{ +\itemize{ +\item \code{\link[=compositeRGB]{compositeRGB()}}: Combine red, green, and blue color bands to make a composite \code{GRaster} +\item \code{\link[=plotRGB]{plotRGB()}}: Display a multispectral \code{GRaster} using red, blue, green, and alpha channels +\item \code{\link[=vegIndex]{vegIndex()}}: Vegetation indices from surface reflectance +} +} + +\subsection{Functions that operate on \strong{terra} \code{SpatRaster}s}{ +\itemize{ +\item \code{\link[=bioclims]{bioclims()}}: BIOCLIM rasters (classic set and extended set) +\item \code{\link[=fragmentation]{fragmentation()}}: Landscape fragmentation class from Riitters et al. (2020) +} +} + +\subsection{Properties of \code{GVector}s}{ +\itemize{ +\item \code{\link[=crs]{crs()}}: Coordinate reference system +\item \code{\link[=datatype]{datatype()}}: Data type of fields +\item \code{\link[=dim]{dim()}}: Number of geometries and columns +\item \code{\link[=expanse]{expanse()}}: Area of polygons or length of lines +\item \code{\link[=ext]{ext()}}, \code{\link[=N]{N()}}, \code{\link[=S]{S()}}, \code{\link[=E]{E()}}, \code{\link[=W]{W()}}, \code{\link[=top]{top()}}, and \code{\link[=bottom]{bottom()}}: Spatial extent +\item \code{\link[=geomtype]{geomtype()}}: Type of vector (points, lines, polygons) +\item \code{\link[=is.2d]{is.2d()}} and \code{\link[=is.3d]{is.3d()}}: Is an object 2- or 3-dimensional? +\item \code{\link[=is.lonlat]{is.lonlat()}}: Is an object projected (e.g., in WGS84)? +\item \code{\link[=is.points]{is.points()}}, \code{\link[=is.lines]{is.lines()}}, \code{\link[=is.polygons]{is.polygons()}}: Does a \code{GVector} represent points, lines, or polygons? +\item \code{\link[=names]{names()}}: Names of \code{GVector} fields +\item \code{\link[=ncol]{ncol()}}: Number of fields +\item \code{\link[=ngeom]{ngeom()}}: Number of geometries (points, lines, polygons) +\item \code{\link[=nrow]{nrow()}}: Number of rows in a vector data table +\item \code{\link[=nsubgeom]{nsubgeom()}}: Number of subgeometries (points, lines, polygons that make up single- and multipart geometries) +\item \code{\link[=sources]{sources()}}: Name of the vector in \strong{GRASS} +\item +\item \code{\link[=zext]{zext()}}: Vertical extent +} +} + +\subsection{Subsetting and assigning geometries or rows and columns of \code{GVector}s}{ +\itemize{ +\item \link{$} or \code{\link[fasterRaster]{[[}}: Subset columns of a \code{GVector}'s data table +\item \code{\link[fasterRaster]{[}} or \code{\link[=subset]{subset()}}: Subset geometries of a \code{GVector} +\item \code{\link[fasterRaster]{$<-}}: Replace specific columns of a \code{GVector}'s data table or add columns +\item \code{\link[fasterRaster]{addTable<-}}: Add a data table to a \code{GVector} +\item \code{\link[=dropTable]{dropTable()}}: Remove a \code{GVector}s data table +} +} + +\subsection{Operations on \code{GVector}s}{ +\itemize{ +\item \code{\link[=aggregate]{aggregate()}}: Combine \code{GVector} geometries +\item \code{\link[=as.data.frame]{as.data.frame()}}: Convert a \code{GVector}'s attribute table to a \code{data.frame} +\item \code{\link[=as.data.table]{as.data.table()}}: Convert a \code{GVector}'s attribute table to a \code{data.table} +\item \code{\link[=as.points]{as.points()}}: Extract vertex coordinates from a "lines" or "polygons" \code{GVector} +\item \code{\link[=buffer]{buffer()}}: Create a polygon around/inside a \code{GVector} +\item \code{\link[=clusterPoints]{clusterPoints()}}: Identify clusters of points +\item \code{\link[=colbind]{colbind()}}: Add columns to the data table of a \code{GVector} +\item \code{\link[=complete.cases]{complete.cases()}}: Find rows of a \code{GVector}'s data table that have no \code{NA}s in them +\item \code{\link[=connectors]{connectors()}}: Create lines connecting nearest features of two \code{GVector}s +\item \code{\link[=convHull]{convHull()}}: Minimum convex hull +\item \code{\link[=crds]{crds()}}: Extract coordinates of a \code{GVector} +\item \code{\link[=crop]{crop()}}: Remove parts of a \code{GVector} +\item \code{\link[=delaunay]{delaunay()}}: Delaunay triangulation +\item \code{\link[=disagg]{disagg()}}: Separate multipart geometries into singlepart geometries +\item \code{\link[=distance]{distance()}}: Distance between geometries in two \code{GVector}, or from a \code{GVector} to cells of a \code{GRaster} +\item \code{\link[=erase]{erase()}} or \code{-}: Remove part of a \code{GVector} that overlaps with another +\item \code{\link[=expanse]{expanse()}}: Area of polygons or length of lines +\item \code{\link[=extract]{extract()}}: Extract values from a \code{GVector} at specific points +\item \code{\link[=grid]{grid()}}: Create a grid \code{GVector} +\item \code{\link[=head]{head()}}: First rows of a \code{GVector}'s data table +\item \code{\link[=hexagons]{hexagons()}}: Create a hexagonal grid +\item \code{\link[=interpIDW]{interpIDW()}}: Interpolate values at points to a \code{GRaster} using inverse-distance weighting +\item \code{\link[=interpSplines]{interpSplines()}}: Interpolate values at points to a \code{GRaster} using splines +\item \code{\link[=intersect]{intersect()}} or \code{*}: Intersection of two \code{GVectors} +\item \code{\link[=kernel]{kernel()}}: Kernel density estimator of points +\item \code{\link[=missing.cases]{missing.cases()}}: Find rows of a \code{GVector}'s data table that have at least \code{NA} in them +\code{\link[fasterRaster]{names<-}}: Assign names to columns of a \code{GVector}s data table +\item \code{\link[=project]{project()}}: Change coordinate reference system +\item \code{\link[=rasterize]{rasterize()}}: Convert a \code{GVector} to a \code{GRaster} +\item \code{\link[=rbind]{rbind()}}: Combine \code{GVectors} +\item \code{\link[=simplifyGeom]{simplifyGeom()}}: Remove vertices +\item \code{\link[=smoothGeom]{smoothGeom()}}: Remove "angular" aspects of features +\item \code{\link[=st_as_sf]{st_as_sf()}}: Convert a \code{GVector} to a \code{sf} vector +\item \code{\link[=st_buffer]{st_buffer()}}: Create a polygon around/inside a \code{GVector} +\item \code{\link[=tail]{tail()}}: Last rows of a \code{GVector}'s data table +\item \code{\link[=thinPoints]{thinPoints()}}: Reduce number of points in same raster cell +\item \code{\link[=union]{union()}} or \code{+}: Combine two \code{GVector}s +\item \code{\link[=voronoi]{voronoi()}}: Voronoi tessellation +\item \code{\link[=xor]{xor()}} or \code{/}: Select parts of polygons not shared by two \code{GVector}s +} +} + +\subsection{Creating \code{GVector}s \emph{de novo}}{ +\itemize{ +\item \code{\link[=rvoronoi]{rvoronoi()}}: Random Voronoi tesselation +} +} + +\subsection{Fixing issues with \code{GVector}s}{ + +(See also \emph{Details} \code{\link[=fast]{fast()}}.) +\itemize{ +\item \code{\link[=breakPolys]{breakPolys()}}: Break topologically clean areas +\item \code{\link[=fillHoles]{fillHoles()}}: Fill "holes" of a \code{GVector} +\item \code{\link[=fixBridges]{fixBridges()}}: Change "bridges" to "islands" +\item \code{\link[=fixDangles]{fixDangles()}}: Change "dangles" hanging off boundaries to lines +\item \code{\link[=fixLines]{fixLines()}}: Break lines at intersections and lines that form closed loops +\item \code{\link[=remove0]{remove0()}}: Remove all boundaries and lines with a length of 0 +\item \code{\link[=removeAngles]{removeAngles()}}: Collapse lines that diverge at an angle that is computationally equivalent to 0 +\item \code{\link[=removeBridges]{removeBridges()}}: Remove "bridges" to "islands" +\item \code{\link[=removeDangles]{removeDangles()}}: Remove "dangling" lines +\item \code{\link[=removeDupCentroids]{removeDupCentroids()}}: Remove duplicated area centroids +\item \code{\link[=removeDups]{removeDups()}}: Remove duplicated features and area centroids +\item \code{\link[=removeSmallPolys]{removeSmallPolys()}}: Remove small polygons +\item \code{\link[=snap]{snap()}}: Snap lines/boundaries to each other +} +} + +\subsection{Converting between data types}{ +\itemize{ +\item \code{\link[=as.contour]{as.contour()}}: Convert a \code{GRaster} to a \code{GVector} representing contour lines +\item \code{\link[=as.doub]{as.doub()}}: Convert a \code{GRaster} to a double-floating point raster (\strong{GRASS} data type \code{DCELL}) +\item \code{\link[=as.data.frame]{as.data.frame()}}: Convert \code{GVector} to a \code{data.frame} +\item \code{\link[=as.data.table]{as.data.table()}}: Convert \code{GVector} to a \code{data.table} +\item \code{\link[=as.float]{as.float()}}: Convert a \code{GRaster} to a floating-point raster (\strong{GRASS} data type \code{FCELL}) +\item \code{\link[=as.int]{as.int()}}: Convert a \code{GRaster} to an integer raster (\strong{GRASS} data type \code{CELL}) +\item \code{\link[=as.points]{as.points()}}, \code{\link[=as.lines]{as.lines()}}, and \code{\link[=as.polygons]{as.polygons()}}: Convert a \code{GRaster} to a \code{GVector} +\item \code{\link[=categories]{categories()}} and \code{\link[fasterRaster]{levels<-}}: Convert an integer raster to a categorical ("factor") raster. +\item \code{\link[=fast]{fast()}}: Convert a \code{SpatRaster} to a \code{GRaster}; a \code{SpatVector}, \code{sf} vector, numeric vector, \code{matrix}, \code{data.frame}, or \code{data.table} to a \code{GVector}; or load a vector or raster from a file +\item \code{\link[=rast]{rast()}}: Convert a \code{GRaster} to a \code{SpatRaster} +\item \code{\link[=rasterize]{rasterize()}}: Convert a \code{GVector} to a \code{GRaster} +\item \code{\link[=st_as_sf]{st_as_sf()}}: Convert a \code{GVector} to a \code{sf} vector +\item \code{\link[=vect]{vect()}}: Convert a \code{GVector} to a \code{SpatVector} +} +} + +\subsection{General purpose functions}{ +\itemize{ +\item \code{\link[=compareGeom]{compareGeom()}}: Determine if geographic metadata is same between \code{GRaster}s and/or \code{GVector}s +\item \code{\link[=dropRows]{dropRows()}}: Remove rows from a \code{data.frame} or \code{data.table} +\item \code{\link[=grass]{grass()}}: Start the \strong{GRASS} GUI (not recommended for most users!!!) +\item \code{\link[=grassInfo]{grassInfo()}}: \strong{GRASS} version and citation +\item \code{\link[=grassStarted]{grassStarted()}}: Has a connection \strong{GRASS} been made within the current \strong{R} session? +\item \code{\link[=mow]{mow()}}: Remove unused rasters and vectors from the \strong{GRASS} cache +\item \code{\link[=reorient]{reorient()}}: Convert degrees between 'north-orientation' and 'east orientation' +\item \code{\link[=replaceNAs]{replaceNAs()}}: Replace \code{NA}s in columns of a \code{data.table} or \code{data.frame}, or in a vector +\item \code{\link[=seqToSQL]{seqToSQL()}}: Format a numeric series into an SQL value call +\item \code{\link[=workDir]{workDir()}}: +\item \code{\link[=update]{update()}}: Refresh metadata in a \code{GRaster} or \code{GVector} object +} +} + +\subsection{Data objects}{ +\itemize{ +\item \code{\link[=fastData]{fastData()}}: Helper function to quickly obtain example rasters and vectors +\item \link{appFunsTable} (see also \code{\link[=appFuns]{appFuns()}}): Functions usable by the \code{\link[=app]{app()}} function +\item \link{madChelsa}: Climate rasters for of a portion of eastern Madagascar +\item \link{madCoast0}, \link{madCoast4}, and \link{madCoast}: Borders of an eastern portion of Madagascar +\item \link{madCover}: Land cover raster +\item \link{madCoverCats}: Table of land cover classes +\item \link{madDypsis}: Specimens records of species in the genus \emph{Dypsis} +\item \link{madElev}: Elevation raster +\item \link{madForest2000} and \link{madForest2014}: Forest cover in 2000 and 2014 +\item \link{madLANDSAT}: Surface reflectance in 2023 +\item \link{madPpt}, \link{madTmin}, \link{madTmax}: Rasters of mean monthly precipitation, and minimum and maximum temperature +\item \link{madRivers}: Rivers vector +\item \link{vegIndices}: Vegetation indices that can be calculated using \code{\link[=vegIndex]{vegIndex()}} +} +} + +\subsection{Esoteric tutorials and arcane notes}{ +\itemize{ +\item Comparisons between \code{GRegion}s can be performed using the \code{==} and \code{!=} operators. +\item Vignette on \strong{GRASS} "projects/locations" and "mapsets": \code{vignette("projects_mapsets", package = "fasterRaster")} +\item Vignette on \strong{GRASS} "regions": \code{vignette("regions", package = "fasterRaster")} +\item Vignette on \strong{fasteRaster} hidden functions: \code{vignette("hidden_functions", package = "fasterRaster")} +} +} + +\subsection{Classes}{ +\itemize{ +\item \code{\link{GLocation}}: Fundamental class; points to a "location/project" in \strong{GRASS} +\item \code{\link{GSpatial}}: Basic class of any spatial object +\item \code{\link{GRegion}}: Points to a "region" of a "location/project" in \strong{GRASS} +\item \code{\link{GRaster}}: Raster class +\item \code{\link{GVector}}: Spatial vector class +} +} } \seealso{ Useful links: diff --git a/man/functions.Rd b/man/functions.Rd index 3a26b209..3843ceb7 100644 --- a/man/functions.Rd +++ b/man/functions.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/06_GRaster_functions_acrossLayers.r +% Please edit documentation in R/06_GRaster_functions_across_layers.r \name{mean,GRaster-method} \alias{mean,GRaster-method} \alias{mean} diff --git a/man/math.Rd b/man/math.Rd index 70f703c8..f1372b04 100644 --- a/man/math.Rd +++ b/man/math.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/05_GRaster_functions_byLayer.r +% Please edit documentation in R/05_GRaster_functions_by_layer.r \name{is.na,GRaster-method} \alias{is.na,GRaster-method} \alias{is.na} @@ -146,3 +146,103 @@ You can apply mathematical functions to each layer of a \code{GRaster}. The outp } } } +\examples{ +if (grassStarted()) { + +# Setup +library(sf) +library(terra) + +# Example data +madElev <- fastData("madElev") + +# Convert a SpatRaster to a GRaster +elev <- fast(madElev) +elevs <- c(elev, elev, log10(elev) - 1, sqrt(elev)) +names(elevs) <- c("elev1", "elev2", "log_elev", "sqrt_elev") + +elev +elevs + +# do some math +elev + 100 +elev - 100 +elev * 100 +elev / 100 +elev ^ 2 +elev \%/\% 100 # divide then round down +elev \%\% 100 # modulus + +100 + elev +100 \%/\% elev +100 \%\% elev + +elevs + 100 +100 + elevs + +# math with logicals +elev + TRUE +elev - TRUE +elev * TRUE +elev / TRUE +elev ^ TRUE +elev \%/\% TRUE # divide then round down +elev \%\% TRUE # modulus + +elevs + TRUE +TRUE + elevs + +# Raster interacting with raster(s): +elev + elev +elev - elev +elev * elev +elev / elev +elev ^ log(elev) +elev \%/\% sqrt(elev) # divide then round down +elev \%\% sqrt(elev) # modulus + +elevs + elev +elev * elevs + +# sign +abs(-1 * elev) +abs(elevs) + +# powers +sqrt(elevs) + +# trigonometry +sin(elev) +cos(elev) +tan(elev) + +asin(elev) +acos(elev) +atan(elev) + +atan(elevs) +atan2(elev, elev^1.2) +atan2(elevs, elev^1.2) +atan2(elev, elevs^1.2) +atan2(elevs, elevs^1.2) + +# logarithms +exp(elev) +log(elev) +ln(elev) +log2(elev) +log1p(elev) +log10(elev) +log10p(elev) +log(elev, 3) + +log(elevs) + +# rounding +round(elev + 0.5) +floor(elev + 0.5) +ceiling(elev + 0.5) +trunc(elev + 0.5) + +} +} From 9e885fab05a133f680795540eb6396921c20f82e Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 00:06:39 -0500 Subject: [PATCH 054/125] Update DESCRIPTION --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index a41d068e..698646a0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -47,4 +47,4 @@ VignetteBuilder: knitr Encoding: UTF-8 LazyData: true LazyLoad: yes -RoxygenNote: 7.3.1 +RoxygenNote: 7.3.2 From abb89e8e7ec090afb4b69fcd8c1e11e6dc61fa5b Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 09:31:40 -0500 Subject: [PATCH 055/125] Add `segregate()` --- NAMESPACE | 1 + NEWS.md | 1 + R/01_generics.r | 1 + R/fasterRaster.r | 1 + R/segregate.r | 86 +++++++++++++++++++++++++++++++++++++ man/examples/ex_segregate.r | 26 +++++++++++ man/fasterRaster.Rd | 1 + man/segregate.Rd | 59 +++++++++++++++++++++++++ 8 files changed, 176 insertions(+) create mode 100644 R/segregate.r create mode 100644 man/examples/ex_segregate.r create mode 100644 man/segregate.Rd diff --git a/NAMESPACE b/NAMESPACE index af33c732..11d132ec 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -216,6 +216,7 @@ exportMethods(sampleRast) exportMethods(scale) exportMethods(scalepop) exportMethods(sdpop) +exportMethods(segregate) exportMethods(selectRange) exportMethods(show) exportMethods(simplifyGeom) diff --git a/NEWS.md b/NEWS.md index 11740002..8975b595 100644 --- a/NEWS.md +++ b/NEWS.md @@ -17,6 +17,7 @@ o `grass()` allows users to start the **GRASS** GUI. o `layerIndex()` allows a `negate` argument to get the "opposite" indices of a `GRaster`. o `init()` assigns to `GRaster` cells the value of their coordinates, rows, columns, or values in a regular or chessboard-like pattern. o `subset()` subsets layers of a `GRaster` or rows/geometries of a `GVector`. +o `segregate()` creates one layer per unique value in an input `GRaster`, with values in the output coded 1 or 0 depending on whether cells in the input had the unique value or not. ### Bug fixes o `categories()` correctly assigns active category column. diff --git a/R/01_generics.r b/R/01_generics.r index 53096832..ea909b23 100644 --- a/R/01_generics.r +++ b/R/01_generics.r @@ -263,6 +263,7 @@ methods::setGeneric(name = "scale", package = "terra") methods::setGeneric(name = "scalepop", def = function(x, ...) standardGeneric("scalepop")) methods::setGeneric(name = "sdpop", def = function(x, ...) standardGeneric("sdpop")) methods::setGeneric(name = "selectRange", def = function(x, ...) standardGeneric("selectRange")) +methods::setGeneric(name = "segregate", def = function(x, ...) standardGeneric("segregate")) methods::setGeneric(name = "show", package = "methods") methods::setGeneric(name = "simplifyGeom", package = "terra") methods::setGeneric(name = "sineRast", def = function(x, ...) standardGeneric("sineRast")) diff --git a/R/fasterRaster.r b/R/fasterRaster.r index a3da8f42..0def317c 100644 --- a/R/fasterRaster.r +++ b/R/fasterRaster.r @@ -163,6 +163,7 @@ #' * [missing.cases()]: Find rows of a categorical `GRaster`'s "levels" table that have at least one `NA` in them #' * [missingCats()]: Values that have no category assigned to them #' * [nlevels()]: Number of levels +#' * [segregate()]: Create one GRaster layer per unique value in a GRaster #' * [subst()]: Re-assign category levels #' * [zonalGeog()]: Geographic statistics (area, perimeter, fractal dimension, etc.) for sets of cells with the same values #' diff --git a/R/segregate.r b/R/segregate.r new file mode 100644 index 00000000..9c104135 --- /dev/null +++ b/R/segregate.r @@ -0,0 +1,86 @@ +#' Create one GRaster layer per unique value in a GRaster +#' +#' @description This function creates a multi-layered `GRaster` for every unique values in an input `GRaster`. By default, the output will have a value of 1 wherever the input has the given value, and 0 elsewhere. This is useful for creating dummy variable `GRaster` layers for use with models that have factors, especially if the input `GRaster` is categorical. +#' +#' @param x A `GRaster`. +#' +#' @param classes Either `NULL` (default) or a character vector with category labels for which to create outputs. If the input is not a categorical/factor or `integer` `GRaster`, this is ignored. +#' +#' @param keep Logical: If `FALSE` (default), then the original value in the input `GRaster` will be retained in the each of the output `GRaster` layers wherever the input had the respective value. Other cells will be assigned a value of `other`. +#' +#' @param other Numeric or `NA`: Value to assign to cells that do not have the target value. +#' +#' @param bins Numeric: Number of bins in which to put values. This is only used for `GRaster`s that are not categorical/factor rasters or `integer` rasters. +#' +#' @param digits Numeric: Number of digits to which to round input if it is a `numeric` or `double` `GRaster` (see `vignettes("GRasters", package = "fasterRaster")`). +#' +#' @returns If the input `x` is a single-layered `GRaster`, the output will be a multi-layered `GRaster` with one layer per value in the input, or one layer per values in `classes`. If the input is a multi-layered `GRaster`, the output will be a `list` of multi-layered `GRaster`s. +#' +#' @example man/examples/ex_segregate.r +#' +#' @seealso [terra::segregate()] +#' +#' @aliases segregate +#' @rdname segregate +#' @exportMethod segregate +methods::setMethod( + f = "segregate", + signature = c(x = "GRaster"), + function(x, classes = NULL, keep = FALSE, other = 0, bins = 100, digits = 3) { + + .locationRestore(x) + .region(x) + + if (!is.null(classes) & (all(is.factor(x)) | all(is.integer(x)))) x <- x[x %in% classes] + if (is.na(other)) other <- "null()" + + out <- list() + nLayers <- nlyr(x) + for (i in seq_len(nLayers)) { + + # unique values + dtype <- datatype(x, type = "GRASS")[i] + freqs <- freq(x[[i]], digits = digits, bins = bins) + freqs <- freqs[count > 0] + vals <- freqs$value + + nVals <- length(vals) + if (nVals == 0) { + thisOut <- NA + } else { + + thisSrcs <- .makeSourceName("segregate", "raster", nVals) + for (j in seq_len(nVals)) { + + if (keep) { + thisKeep <- vals[j] + } else { + thisKeep <- 1 + } + ex <- paste0(thisSrcs[j], " = if(", sources(x)[i], " == ", vals[j], ", ", thisKeep, ", ", other, ")") + rgrass::execGRASS("r.mapcalc", expression = ex, flags = c(.quiet(), "overwrite")) + + } + + names <- if (is.factor(x)[i]) { + freqs[[3]] + } else { + as.character(vals) + } + thisOut <- .makeGRaster(thisSrcs, names = names) + + } + + out[[i]] <- thisOut + + } + + if (nLayers == 1L) { + out <- out[[1L]] + } else { + names(out) <- names(x) + } + out + + } # EOF +) diff --git a/man/examples/ex_segregate.r b/man/examples/ex_segregate.r new file mode 100644 index 00000000..f414aaa0 --- /dev/null +++ b/man/examples/ex_segregate.r @@ -0,0 +1,26 @@ +if (grassStarted()) { + +# Setup +library(terra) + +# Elevation and land cover raster +madElev <- fastData("madElev") # integer raster +madCover <- fastData("madCover") # categorical raster + +# Convert to GRasters +elev <- fast(madElev) +cover <- fast(madCover) + +# Subset elevation raster to just a few values to make example faster: +elevSubset <- elev[elev <= 3] +segregate(elevSubset) +segregate(elevSubset, keep = TRUE, other = -1) + +# Segregate the factor raster +segregate(cover) + +classes <- c("Grassland with mosaic forest", "Mosaic cropland/vegetation") +seg <- segregate(cover, classes = classes) +plot(seg) + +} diff --git a/man/fasterRaster.Rd b/man/fasterRaster.Rd index 52617ff7..a5dabb64 100644 --- a/man/fasterRaster.Rd +++ b/man/fasterRaster.Rd @@ -192,6 +192,7 @@ Operations on \code{GRaster}s \item \code{\link[=missing.cases]{missing.cases()}}: Find rows of a categorical \code{GRaster}'s "levels" table that have at least one \code{NA} in them \item \code{\link[=missingCats]{missingCats()}}: Values that have no category assigned to them \item \code{\link[=nlevels]{nlevels()}}: Number of levels +\item \code{\link[=segregate]{segregate()}}: Create one GRaster layer per unique value in a GRaster \item \code{\link[=subst]{subst()}}: Re-assign category levels \item \code{\link[=zonalGeog]{zonalGeog()}}: Geographic statistics (area, perimeter, fractal dimension, etc.) for sets of cells with the same values } diff --git a/man/segregate.Rd b/man/segregate.Rd new file mode 100644 index 00000000..e5fd6ab3 --- /dev/null +++ b/man/segregate.Rd @@ -0,0 +1,59 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/segregate.r +\name{segregate,GRaster-method} +\alias{segregate,GRaster-method} +\alias{segregate} +\title{Create one GRaster layer per unique value in a GRaster} +\usage{ +\S4method{segregate}{GRaster}(x, classes = NULL, keep = FALSE, other = 0, bins = 100, digits = 3) +} +\arguments{ +\item{x}{A \code{GRaster}.} + +\item{classes}{Either \code{NULL} (default) or a character vector with category labels for which to create outputs. If the input is not a categorical/factor or \code{integer} \code{GRaster}, this is ignored.} + +\item{keep}{Logical: If \code{FALSE} (default), then the original value in the input \code{GRaster} will be retained in the each of the output \code{GRaster} layers wherever the input had the respective value. Other cells will be assigned a value of \code{other}.} + +\item{other}{Numeric or \code{NA}: Value to assign to cells that do not have the target value.} + +\item{bins}{Numeric: Number of bins in which to put values. This is only used for \code{GRaster}s that are not categorical/factor rasters or \code{integer} rasters.} + +\item{digits}{Numeric: Number of digits to which to round input if it is a \code{numeric} or \code{double} \code{GRaster} (see \code{vignettes("GRasters", package = "fasterRaster")}).} +} +\value{ +If the input \code{x} is a single-layered \code{GRaster}, the output will be a multi-layered \code{GRaster} with one layer per value in the input, or one layer per values in \code{classes}. If the input is a multi-layered \code{GRaster}, the output will be a \code{list} of multi-layered \code{GRaster}s. +} +\description{ +This function creates a multi-layered \code{GRaster} for every unique values in an input \code{GRaster}. By default, the output will have a value of 1 wherever the input has the given value, and 0 elsewhere. This is useful for creating dummy variable \code{GRaster} layers for use with models that have factors, especially if the input \code{GRaster} is categorical. +} +\examples{ +if (grassStarted()) { + +# Setup +library(terra) + +# Elevation and land cover raster +madElev <- fastData("madElev") # integer raster +madCover <- fastData("madCover") # categorical raster + +# Convert to GRasters +elev <- fast(madElev) +cover <- fast(madCover) + +# Subset elevation raster to just a few values to make example faster: +elevSubset <- elev[elev <= 3] +segregate(elevSubset) +segregate(elevSubset, keep = TRUE, other = -1) + +# Segregate the factor raster +segregate(cover) + +classes <- c("Grassland with mosaic forest", "Mosaic cropland/vegetation") +seg <- segregate(cover, classes = classes) +plot(seg) + +} +} +\seealso{ +\code{\link[terra:segregate]{terra::segregate()}} +} From b618e8fd1099fb153df845add756818db858c569 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 09:32:02 -0500 Subject: [PATCH 056/125] Retain `levels` in output --- R/subset_single_bracket.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/subset_single_bracket.r b/R/subset_single_bracket.r index 112ad68f..40177da2 100644 --- a/R/subset_single_bracket.r +++ b/R/subset_single_bracket.r @@ -336,7 +336,7 @@ methods::setMethod( rgrass::execGRASS("r.mapcalc", expression = ex, flags = c(.quiet(), "overwrite")) } - .makeGRaster(srcs, names(x)) + .makeGRaster(srcs, names(x), levels = cats(x), ac = activeCats(x)) } # EOF ) From c3f97b992e572d82c4c1e891eaca1f11e0bb3180 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 16:55:40 -0500 Subject: [PATCH 057/125] Deprecate individual regression fxs; add `regress()` --- R/01_generics.r | 5 +- R/06_GRaster_functions_across_layers.r | 61 ------------------ R/fasterRaster.r | 1 + R/regress.r | 49 +++++++++++++++ _pkgdown.yml | 1 + man/fasterRaster.Rd | 1 + man/regress.Rd | 87 ++++++++++++++++++++++++++ 7 files changed, 140 insertions(+), 65 deletions(-) create mode 100644 R/regress.r create mode 100644 man/regress.Rd diff --git a/R/01_generics.r b/R/01_generics.r index ea909b23..3b36717d 100644 --- a/R/01_generics.r +++ b/R/01_generics.r @@ -160,7 +160,6 @@ methods::setGeneric(name = "horizonHeight", def = function(x, ...) standardGener methods::setGeneric(name = "init", package = "terra") methods::setGeneric(name = "intersect", package = "terra") -methods::setGeneric(name = "intercept", def = function(x, ...) standardGeneric("intercept")) methods::setGeneric(name = "interpIDW", package = "terra") methods::setGeneric(name = "interpSplines", def = function(x, y, ...) standardGeneric("interpSplines")) methods::setGeneric(name = "is.2d", def = function(x) standardGeneric("is.2d")) @@ -232,7 +231,6 @@ methods::setGeneric(name = "project", package = "terra") methods::setGeneric(name = "quantile", package = "terra") -methods::setGeneric(name = "r2", def = function(x, ...) standardGeneric("r2")) methods::setGeneric(name = "rast", package = "terra") methods::setGeneric(name = "rasterize", package = "terra") # methods::setGeneric(name = "rbind") @@ -241,6 +239,7 @@ methods::setGeneric(name = "rasterize", package = "terra") methods::getGeneric("rbind") methods::setGeneric(name = "rbind", signature = "...") +methods::setGeneric(name = "regress", package = "terra") methods::setGeneric(name = "remove0", def = function(x, ...) standardGeneric("remove0")) methods::setGeneric(name = "removeAngles", def = function(x, ...) standardGeneric("removeAngles")) methods::setGeneric(name = "removeBridges", def = function(x, ...) standardGeneric("removeBridges")) @@ -269,7 +268,6 @@ methods::setGeneric(name = "simplifyGeom", package = "terra") methods::setGeneric(name = "sineRast", def = function(x, ...) standardGeneric("sineRast")) methods::setGeneric(name = "smoothGeom", def = function(x, ...) standardGeneric("smoothGeom")) methods::setGeneric(name = "skewness", def = function(x, ...) standardGeneric("skewness")) -methods::setGeneric(name = "slope", def = function(x, ...) standardGeneric("slope")) methods::setGeneric(name = "snap", package = "terra") methods::setGeneric(name = "sources", package = "terra") methods::setGeneric(name = "spatSample", package = "terra") @@ -294,7 +292,6 @@ methods::setGeneric(name = "tiles", def = function(x, ...) standardGeneric("tile methods::setGeneric(name = "top", def = function(x, ...) standardGeneric("top")) methods::setGeneric(name = "topology", def = function(x, ...) standardGeneric("topology")) methods::setGeneric(name = "trim", package = "terra") -methods::setGeneric(name = "tvalue", def = function(x, ...) standardGeneric("tvalue")) methods::setGeneric(name = "update", package = "terra") methods::setGeneric(name = "union", package = "terra") diff --git a/R/06_GRaster_functions_across_layers.r b/R/06_GRaster_functions_across_layers.r index 2456eb79..85b6214e 100644 --- a/R/06_GRaster_functions_across_layers.r +++ b/R/06_GRaster_functions_across_layers.r @@ -5,7 +5,6 @@ #' * Central tendency: `mean()`, `mmode()` (mode), `median()`. #' * Extremes: `min()`, `max()`, `which.min()` (index of raster with the minimum value), `which.max()` (index of the raster with the maximum value) #' * Dispersion: `range()`, `stdev()` (standard deviation), `var()` (sample variance), `varpop()` (population variance), `nunique()` (number of unique values), `quantile()` (use argument `probs`), `skewness()`, and `kurtosis()`. -#' * Regression: Assuming we calculate a linear regression for each set of cells through all values of the cells, we can calculate its `slope()`, `intercept()`, `r2()`, and `tvalue()`. #' * `NA`s: `anyNA()` (any cells are `NA`?), `allNA()` (are all cells `NA`?) #' #' @param x A `GRaster`. Typically, this raster will have two or more layers. Values will be calculated within cells across rasters. @@ -358,66 +357,6 @@ setMethod( } # EOF ) -#' @aliases slope -#' @rdname functions -#' @exportMethod slope -setMethod( - "slope", - signature(x = "GRaster"), - function(x, na.rm = FALSE) { - - fx <- "slope" - fxName <- "slope" - .genericMultiLayer(fx = fx, fxName = fxName, x = x, na.rm = na.rm) - - } # EOF -) - -#' @aliases intercept -#' @rdname functions -#' @exportMethod intercept -setMethod( - "intercept", - signature(x = "GRaster"), - function(x, na.rm = FALSE) { - - fx <- "offset" - fxName <- "intercept" - .genericMultiLayer(fx = fx, fxName = fxName, x = x, na.rm = na.rm) - - } # EOF -) - -#' @aliases r2 -#' @rdname functions -#' @exportMethod r2 -setMethod( - "r2", - signature(x = "GRaster"), - function(x, na.rm = FALSE) { - - fx <- "detcoeff" - fxName <- "r2" - .genericMultiLayer(fx = fx, fxName = fxName, x = x, na.rm = na.rm) - - } # EOF -) - -#' @aliases tvalue -#' @rdname functions -#' @exportMethod tvalue -setMethod( - "tvalue", - signature(x = "GRaster"), - function(x, na.rm = FALSE) { - - fx <- "tvalue" - fxName <- "tvalue" - .genericMultiLayer(fx = fx, fxName = fxName, x = x, na.rm = na.rm) - - } # EOF -) - #' @aliases range #' @rdname functions #' @exportMethod range diff --git a/R/fasterRaster.r b/R/fasterRaster.r index 0def317c..586a4fd4 100644 --- a/R/fasterRaster.r +++ b/R/fasterRaster.r @@ -105,6 +105,7 @@ #' * [plotRGB()]: Display a multispectral `GRaster` using red, blue, green, and alpha channels #' * [project()]: Change coordinate reference system and cell size #' * [predict()]: Make predictions to a `GRaster` from a linear model or generalized linear model +#' * [regress()]: Regression intercept, slope, r2, and t-value across each set of cells #' * [resample()]: Change cell size #' * [reorient()]: Convert degrees between 'north-orientation' and 'east orientation' #' * [sampleRast()]: Randomly sample cells from a `GRaster` diff --git a/R/regress.r b/R/regress.r new file mode 100644 index 00000000..d6f14e77 --- /dev/null +++ b/R/regress.r @@ -0,0 +1,49 @@ +#' Regression intercept, slope, r2, and t-value across each set of cells +#' +#' @description This function performs a regression on each set of cells in a multi-layered `GRaster`. The output is a `GRaster` with the intercept, slope, r^2 value, and Student's t value. The regression formula is as `y ~ 1 + x`, where `x` is the layer number of each layer (e.g., 1 for the first or top layer in the input `GRaster`, 2 for the second or second-to-top layer, etc.). Note that this is restricted version of the functionality in [terra::regress()]. +#' +#' @param y A multi-layer `GRaster`. +#' +#' @param x Ignored. +#' +#' @param na.rm Logical: If `FALSE`, any series of cells with `NA` in at least one cell results in an `NA` in the output. +#' +#' @returns A multi-layer `GRaster`. +#' +#' @seealso [terra::regress()] +#' +#' @example man/examples/ex_GRaster_arithmetic_across_layers.r +#' +#' @aliases regress +#' @rdname regress +#' @exportMethod regress +methods::setMethod( + f = "regress", + signature = c(y = "GRaster", x = "missing"), + function(y, x, na.rm = FALSE) { + + if (nlyr(y) < 2) stop("The `GRaster` must have >1 layer.") + + .locationRestore(y) + .region(y) + + fx <- "offset" + fxName <- "intercept" + intercept <- .genericMultiLayer(fx = fx, fxName = fxName, x = y, na.rm = na.rm) + + fx <- "slope" + fxName <- "slope" + slope <- .genericMultiLayer(fx = fx, fxName = fxName, x = y, na.rm = na.rm) + + fx <- "detcoeff" + fxName <- "r2" + r2 <- .genericMultiLayer(fx = fx, fxName = fxName, x = y, na.rm = na.rm) + + fx <- "tvalue" + fxName <- "tvalue" + tvalue <- .genericMultiLayer(fx = fx, fxName = fxName, x = y, na.rm = na.rm) + + c(intercept, slope, r2, tvalue) + + } # EOF +) diff --git a/_pkgdown.yml b/_pkgdown.yml index b2149e38..7e6ff59f 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -182,6 +182,7 @@ reference: - plotRGB - project - predict + - regress - resample - reorient - sampleRast diff --git a/man/fasterRaster.Rd b/man/fasterRaster.Rd index a5dabb64..3af2ff00 100644 --- a/man/fasterRaster.Rd +++ b/man/fasterRaster.Rd @@ -125,6 +125,7 @@ Operations on \code{GRaster}s \item \code{\link[=plotRGB]{plotRGB()}}: Display a multispectral \code{GRaster} using red, blue, green, and alpha channels \item \code{\link[=project]{project()}}: Change coordinate reference system and cell size \item \code{\link[=predict]{predict()}}: Make predictions to a \code{GRaster} from a linear model or generalized linear model +\item \code{\link[=regress]{regress()}}: Regression intercept, slope, r2, and t-value across each set of cells \item \code{\link[=resample]{resample()}}: Change cell size \item \code{\link[=reorient]{reorient()}}: Convert degrees between 'north-orientation' and 'east orientation' \item \code{\link[=sampleRast]{sampleRast()}}: Randomly sample cells from a \code{GRaster} diff --git a/man/regress.Rd b/man/regress.Rd new file mode 100644 index 00000000..37340da6 --- /dev/null +++ b/man/regress.Rd @@ -0,0 +1,87 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/regress.r +\name{regress,GRaster,missing-method} +\alias{regress,GRaster,missing-method} +\alias{regress} +\title{Regression intercept, slope, r2, and t-value across each set of cells} +\usage{ +\S4method{regress}{GRaster,missing}(y, x, na.rm = FALSE) +} +\arguments{ +\item{y}{A multi-layer \code{GRaster}.} + +\item{x}{Ignored.} + +\item{na.rm}{Logical: If \code{FALSE}, any series of cells with \code{NA} in at least one cell results in an \code{NA} in the output.} +} +\value{ +A multi-layer \code{GRaster}. +} +\description{ +This function performs a regression on each set of cells in a multi-layered \code{GRaster}. The output is a \code{GRaster} with the intercept, slope, r^2 value, and Student's t value. The regression formula is as \code{y ~ 1 + x}, where \code{x} is the layer number of each layer (e.g., 1 for the first or top layer in the input \code{GRaster}, 2 for the second or second-to-top layer, etc.). Note that this is restricted version of the functionality in \code{\link[terra:regress]{terra::regress()}}. +} +\examples{ +if (grassStarted()) { + +# Setup +library(sf) +library(terra) + +# Example data +madChelsa <- fastData("madChelsa") + +# Convert a SpatRaster to a GRaster +chelsa <- fast(madChelsa) +chelsa # 4 layers + +# Central tendency +mean(chelsa) +mmode(chelsa) +median(chelsa) + +# Statistics +nunique(chelsa) +sum(chelsa) +count(chelsa) +min(chelsa) +max(chelsa) +range(chelsa) +skewness(chelsa) +kurtosis(chelsa) + +stdev(chelsa) +stdev(chelsa, pop = FALSE) +var(chelsa) +varpop(chelsa) + +# Which layers have maximum/minimum? +which.min(chelsa) +which.max(chelsa) + +# Regression +# Note the intercept is different for fasterRaster::regress(). +regress(chelsa) +regress(madChelsa) + +# Note: To get quantiles for each layer, use +# global(x, "quantile", probs = 0.2). +quantile(chelsa, 0.1) + +# NAs +madForest2000 <- fastData("madForest2000") +forest2000 <- fast(madForest2000) +forest2000 <- project(forest2000, chelsa, method = "near") + +chelsaForest <- c(chelsa, forest2000) + +nas <- anyNA(chelsaForest) +plot(nas) + +allNas <- allNA(chelsaForest) +plot(allNas) + +} +} +\seealso{ +\code{\link[terra:regress]{terra::regress()}} +} From 36d3a3cac06632a4e8a944d266a515486acf70c3 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 16:55:46 -0500 Subject: [PATCH 058/125] Update segregate.r --- R/segregate.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/segregate.r b/R/segregate.r index 9c104135..7b6f6f46 100644 --- a/R/segregate.r +++ b/R/segregate.r @@ -1,6 +1,6 @@ #' Create one GRaster layer per unique value in a GRaster #' -#' @description This function creates a multi-layered `GRaster` for every unique values in an input `GRaster`. By default, the output will have a value of 1 wherever the input has the given value, and 0 elsewhere. This is useful for creating dummy variable `GRaster` layers for use with models that have factors, especially if the input `GRaster` is categorical. +#' @description This function creates a multi-layered `GRaster` for every unique values in an input `GRaster`. By default, the output will have a value of 1 wherever the input has the given value, and 0 elsewhere. This is useful for creating dummy variable `GRaster` layers for use with models that have factors, especially if the input `GRaster` is categorical. Note that the [predict()] function in **fasterRaster** usually does not need this treatment of `GRaster`s since it can handle categorical rasters already. #' #' @param x A `GRaster`. #' From 4a998560bff71f8aa0f3cf47e95e914313806c3d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 16:55:50 -0500 Subject: [PATCH 059/125] Update madChelsa.r --- R/madChelsa.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/madChelsa.r b/R/madChelsa.r index 93ca2685..31eba43b 100644 --- a/R/madChelsa.r +++ b/R/madChelsa.r @@ -14,7 +14,7 @@ #' #' @keywords climate Madagascar #' -#' @references Karger, D.N., Conrad, O., Böhner, J., Kawohl, T., Kreft, H., Soria-Auza, R.W., Zimmermann, N.E., Linder, H.P., and Kessler, M. 2017. Climatologies at high resolution for the earth's land surface areas. *Scientific Data* 4:170122. \doi{10.1038/sdata.2017.122} +#' @references Karger, D.N., Conrad, O., Bohner, J., Kawohl, T., Kreft, H., Soria-Auza, R.W., Zimmermann, N.E., Linder, H.P., and Kessler, M. 2017. Climatologies at high resolution for the earth's land surface areas. *Scientific Data* 4:170122. \doi{10.1038/sdata.2017.122} #' #' @source \doi{https://doi.org/10.1038/sdata.2017.122} #' From 0829655d0769c8e04a0d1189d339d952c5ac2ef1 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 16:56:01 -0500 Subject: [PATCH 060/125] Update --- NAMESPACE | 5 +--- NEWS.md | 2 ++ .../ex_GRaster_arithmetic_across_layers.r | 7 +++--- man/functions.Rd | 24 +++---------------- man/madChelsa.Rd | 2 +- man/segregate.Rd | 2 +- 6 files changed, 11 insertions(+), 31 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 11d132ec..4acd5974 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -128,7 +128,6 @@ exportMethods(hillshade) exportMethods(hist) exportMethods(horizonHeight) exportMethods(init) -exportMethods(intercept) exportMethods(interpIDW) exportMethods(interpSplines) exportMethods(intersect) @@ -189,12 +188,12 @@ exportMethods(plotRGB) exportMethods(predict) exportMethods(project) exportMethods(quantile) -exportMethods(r2) exportMethods(rSpatialDepRast) exportMethods(range) exportMethods(rast) exportMethods(rasterize) exportMethods(rbind) +exportMethods(regress) exportMethods(remove0) exportMethods(removeAngles) exportMethods(removeBridges) @@ -223,7 +222,6 @@ exportMethods(simplifyGeom) exportMethods(sin) exportMethods(sineRast) exportMethods(skewness) -exportMethods(slope) exportMethods(smoothGeom) exportMethods(snap) exportMethods(sources) @@ -249,7 +247,6 @@ exportMethods(top) exportMethods(topology) exportMethods(trim) exportMethods(trunc) -exportMethods(tvalue) exportMethods(union) exportMethods(unscale) exportMethods(update) diff --git a/NEWS.md b/NEWS.md index 8975b595..d09db9c8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,12 +10,14 @@ o `.vAttachDatabase()` no longer has the `"o"` flag when calling `v.db.connect` ### Potentially code-breaking changes o `aggregate()` no longer has the `dissolve` argument for `GVector`s (polygons will always be dissolved). +o `intercept()`, `slope()`, `r2()`, and `tvalue()` have been replaced by the single function `regress()`. ### Enhanced functionality and new functions o `extract()` now automatically projects a `GVector` to match the CRS of a `GRaster` from which extraction is being made. o `grass()` allows users to start the **GRASS** GUI. o `layerIndex()` allows a `negate` argument to get the "opposite" indices of a `GRaster`. o `init()` assigns to `GRaster` cells the value of their coordinates, rows, columns, or values in a regular or chessboard-like pattern. +o `regress()` replaces individual functions `intercept()`, `slope()`, `r2()`, and `tvalue()`. o `subset()` subsets layers of a `GRaster` or rows/geometries of a `GVector`. o `segregate()` creates one layer per unique value in an input `GRaster`, with values in the output coded 1 or 0 depending on whether cells in the input had the unique value or not. diff --git a/man/examples/ex_GRaster_arithmetic_across_layers.r b/man/examples/ex_GRaster_arithmetic_across_layers.r index cfe4089e..2943d36a 100644 --- a/man/examples/ex_GRaster_arithmetic_across_layers.r +++ b/man/examples/ex_GRaster_arithmetic_across_layers.r @@ -36,10 +36,9 @@ which.min(chelsa) which.max(chelsa) # Regression -slope(chelsa) -intercept(chelsa) -r2(chelsa) -tvalue(chelsa) +# Note the intercept is different for fasterRaster::regress(). +regress(chelsa) +regress(madChelsa) # Note: To get quantiles for each layer, use # global(x, "quantile", probs = 0.2). diff --git a/man/functions.Rd b/man/functions.Rd index 3843ceb7..1953a762 100644 --- a/man/functions.Rd +++ b/man/functions.Rd @@ -34,14 +34,6 @@ \alias{skewness} \alias{kurtosis,GRaster-method} \alias{kurtosis} -\alias{slope,GRaster-method} -\alias{slope} -\alias{intercept,GRaster-method} -\alias{intercept} -\alias{r2,GRaster-method} -\alias{r2} -\alias{tvalue,GRaster-method} -\alias{tvalue} \alias{range,GRaster-method} \alias{range} \alias{quantile,GRaster-method} @@ -86,14 +78,6 @@ \S4method{kurtosis}{GRaster}(x, na.rm = FALSE) -\S4method{slope}{GRaster}(x, na.rm = FALSE) - -\S4method{intercept}{GRaster}(x, na.rm = FALSE) - -\S4method{r2}{GRaster}(x, na.rm = FALSE) - -\S4method{tvalue}{GRaster}(x, na.rm = FALSE) - \S4method{range}{GRaster}(x, na.rm = FALSE) \S4method{quantile}{GRaster}(x, prob, na.rm = FALSE) @@ -121,7 +105,6 @@ These functions can be applied to a "stack" of \code{GRaster}s with two or more \item Central tendency: \code{mean()}, \code{mmode()} (mode), \code{median()}. \item Extremes: \code{min()}, \code{max()}, \code{which.min()} (index of raster with the minimum value), \code{which.max()} (index of the raster with the maximum value) \item Dispersion: \code{range()}, \code{stdev()} (standard deviation), \code{var()} (sample variance), \code{varpop()} (population variance), \code{nunique()} (number of unique values), \code{quantile()} (use argument \code{probs}), \code{skewness()}, and \code{kurtosis()}. -\item Regression: Assuming we calculate a linear regression for each set of cells through all values of the cells, we can calculate its \code{slope()}, \code{intercept()}, \code{r2()}, and \code{tvalue()}. \item \code{NA}s: \code{anyNA()} (any cells are \code{NA}?), \code{allNA()} (are all cells \code{NA}?) } } @@ -164,10 +147,9 @@ which.min(chelsa) which.max(chelsa) # Regression -slope(chelsa) -intercept(chelsa) -r2(chelsa) -tvalue(chelsa) +# Note the intercept is different for fasterRaster::regress(). +regress(chelsa) +regress(madChelsa) # Note: To get quantiles for each layer, use # global(x, "quantile", probs = 0.2). diff --git a/man/madChelsa.Rd b/man/madChelsa.Rd index 1b5a73b1..d0ef388e 100644 --- a/man/madChelsa.Rd +++ b/man/madChelsa.Rd @@ -92,7 +92,7 @@ catNames(madCover) # names of categories table plot(madCover) } \references{ -Karger, D.N., Conrad, O., Böhner, J., Kawohl, T., Kreft, H., Soria-Auza, R.W., Zimmermann, N.E., Linder, H.P., and Kessler, M. 2017. Climatologies at high resolution for the earth's land surface areas. \emph{Scientific Data} 4:170122. \doi{10.1038/sdata.2017.122} +Karger, D.N., Conrad, O., Bohner, J., Kawohl, T., Kreft, H., Soria-Auza, R.W., Zimmermann, N.E., Linder, H.P., and Kessler, M. 2017. Climatologies at high resolution for the earth's land surface areas. \emph{Scientific Data} 4:170122. \doi{10.1038/sdata.2017.122} } \keyword{Madagascar} \keyword{climate} diff --git a/man/segregate.Rd b/man/segregate.Rd index e5fd6ab3..0658ffee 100644 --- a/man/segregate.Rd +++ b/man/segregate.Rd @@ -24,7 +24,7 @@ If the input \code{x} is a single-layered \code{GRaster}, the output will be a multi-layered \code{GRaster} with one layer per value in the input, or one layer per values in \code{classes}. If the input is a multi-layered \code{GRaster}, the output will be a \code{list} of multi-layered \code{GRaster}s. } \description{ -This function creates a multi-layered \code{GRaster} for every unique values in an input \code{GRaster}. By default, the output will have a value of 1 wherever the input has the given value, and 0 elsewhere. This is useful for creating dummy variable \code{GRaster} layers for use with models that have factors, especially if the input \code{GRaster} is categorical. +This function creates a multi-layered \code{GRaster} for every unique values in an input \code{GRaster}. By default, the output will have a value of 1 wherever the input has the given value, and 0 elsewhere. This is useful for creating dummy variable \code{GRaster} layers for use with models that have factors, especially if the input \code{GRaster} is categorical. Note that the \code{\link[=predict]{predict()}} function in \strong{fasterRaster} usually does not need this treatment of \code{GRaster}s since it can handle categorical rasters already. } \examples{ if (grassStarted()) { From e0bec5eb9d303b9dc75f9d833eff20ab1ff41c11 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 23:51:37 -0500 Subject: [PATCH 061/125] `pca()` --> `princomp()` --- NAMESPACE | 2 +- R/01_generics.r | 3 +-- R/fasterRaster.r | 5 ++--- R/{pca.r => princomp.r} | 18 +++++++++--------- _pkgdown.yml | 2 +- man/examples/{ex_pca.r => ex_princomp.r} | 2 +- man/fasterRaster.Rd | 5 ++--- man/pcs.Rd | 8 ++++---- man/{pca.Rd => princomp.Rd} | 14 +++++++------- 9 files changed, 28 insertions(+), 31 deletions(-) rename R/{pca.r => princomp.r} (90%) rename man/examples/{ex_pca.r => ex_princomp.r} (89%) rename man/{pca.Rd => princomp.Rd} (82%) diff --git a/NAMESPACE b/NAMESPACE index 4acd5974..a4435cfc 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -182,10 +182,10 @@ exportMethods(nrow) exportMethods(nsubgeom) exportMethods(nunique) exportMethods(pairs) -exportMethods(pca) exportMethods(plot) exportMethods(plotRGB) exportMethods(predict) +exportMethods(princomp) exportMethods(project) exportMethods(quantile) exportMethods(rSpatialDepRast) diff --git a/R/01_generics.r b/R/01_generics.r index 3b36717d..921945f7 100644 --- a/R/01_generics.r +++ b/R/01_generics.r @@ -221,11 +221,10 @@ methods::setGeneric(name = "nunique", def = function(x, ...) standardGeneric("nu methods::setGeneric(name = "reorient", def = function(x, ...) standardGeneric("reorient")) methods::setGeneric(name = "pairs", package = "terra") -methods::setGeneric(name = "pca", def = function(x, ...) standardGeneric("pca")) methods::setGeneric(name = "plot", package = "terra") methods::setGeneric(name = "plotRGB", package = "terra") methods::setGeneric(name = "predict", package = "terra") -# methods::setGeneric(name = "print", def = function(x, ...) standardGeneric("print")) # base +methods::setGeneric(name = "princomp", package = "terra") methods::setGeneric(name = "print") # base methods::setGeneric(name = "project", package = "terra") diff --git a/R/fasterRaster.r b/R/fasterRaster.r index 586a4fd4..1e6da44a 100644 --- a/R/fasterRaster.r +++ b/R/fasterRaster.r @@ -99,12 +99,11 @@ #' * \code{\link[fasterRaster]{names<-}}: Assign names to a `GRaster` #' * [noise()]: Remove coarse-scale trends from a `GRaster`, leaving just fine-scale "noise" #' * [pairs()]: Plot correlations between `GRaster` layers -#' * [pca()]: Apply a principal components analysis (PCA) to a `GRaster` -#' * [pcs()]: Retrieve a principal components model from a PCA `GRaster` generated using `pca()` +#' * [pcs()]: Retrieve a principal components model from a PCA `GRaster` generated using `princomp()` #' * [plot()]: Display a `GRaster` -#' * [plotRGB()]: Display a multispectral `GRaster` using red, blue, green, and alpha channels #' * [project()]: Change coordinate reference system and cell size #' * [predict()]: Make predictions to a `GRaster` from a linear model or generalized linear model +#' * [princomp()]: Apply a principal components analysis (PCA) to a `GRaster` #' * [regress()]: Regression intercept, slope, r2, and t-value across each set of cells #' * [resample()]: Change cell size #' * [reorient()]: Convert degrees between 'north-orientation' and 'east orientation' diff --git a/R/pca.r b/R/princomp.r similarity index 90% rename from R/pca.r rename to R/princomp.r index bcf0612f..a51486f4 100644 --- a/R/pca.r +++ b/R/princomp.r @@ -10,15 +10,15 @@ #' #' @returns A multi-layer `GRaster` with one layer per principal component axis. The [pcs()] function can be used on the output raster to retrieve a `prcomp` object from the raster, which includes rotations (loadings) and proportions of variance explained. #' -#' @seealso [stats::prcomp()]; module `i.pca` in **GRASS** +#' @seealso [terra::princomp()], [terra::prcomp()] #' -#' @example man/examples/ex_pca.r +#' @example man/examples/ex_princomp.r #' -#' @aliases pca -#' @rdname pca -#' @exportMethod pca +#' @aliases princomp +#' @rdname princomp +#' @exportMethod princomp methods::setMethod( - f = "pca", + f = "princomp", signature = c(x = "GRaster"), function(x, scale = TRUE, scores = FALSE) { @@ -121,13 +121,13 @@ methods::setMethod( #' Retrieve a principal components model from a PCA GRaster #' -#' @param x A `GRaster` created by [pca()] +#' @param x A `GRaster` created by [princomp()] #' #' @returns An object of class `prcomp`. #' -#' @seealso [pca()], [stats::prcomp()], module `i.pca` in **GRASS** +#' @seealso [princomp()], [terra::princomp()], module `i.pca` in **GRASS** #' -#' @example man/examples/ex_pca.r +#' @example man/examples/ex_princomp.r #' #' @aliases pcs #' @rdname pcs diff --git a/_pkgdown.yml b/_pkgdown.yml index 7e6ff59f..959d1a0e 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -176,10 +176,10 @@ reference: - names<- - noise - pairs - - pca - pcs - plot - plotRGB + - princomp - project - predict - regress diff --git a/man/examples/ex_pca.r b/man/examples/ex_princomp.r similarity index 89% rename from man/examples/ex_pca.r rename to man/examples/ex_princomp.r index e696a94e..9e91ecdb 100644 --- a/man/examples/ex_pca.r +++ b/man/examples/ex_princomp.r @@ -10,7 +10,7 @@ madChelsa <- fastData("madChelsa") chelsa <- fast(madChelsa) # Generate raster with layers representing principal component predictions: -pcRast <- pca(chelsa, scale = TRUE) +pcRast <- princomp(chelsa, scale = TRUE) plot(pcRast) # Get information on the PCA: diff --git a/man/fasterRaster.Rd b/man/fasterRaster.Rd index 3af2ff00..81a6a79f 100644 --- a/man/fasterRaster.Rd +++ b/man/fasterRaster.Rd @@ -119,12 +119,11 @@ Operations on \code{GRaster}s \item \code{\link[fasterRaster]{names<-}}: Assign names to a \code{GRaster} \item \code{\link[=noise]{noise()}}: Remove coarse-scale trends from a \code{GRaster}, leaving just fine-scale "noise" \item \code{\link[=pairs]{pairs()}}: Plot correlations between \code{GRaster} layers -\item \code{\link[=pca]{pca()}}: Apply a principal components analysis (PCA) to a \code{GRaster} -\item \code{\link[=pcs]{pcs()}}: Retrieve a principal components model from a PCA \code{GRaster} generated using \code{pca()} +\item \code{\link[=pcs]{pcs()}}: Retrieve a principal components model from a PCA \code{GRaster} generated using \code{princomp()} \item \code{\link[=plot]{plot()}}: Display a \code{GRaster} -\item \code{\link[=plotRGB]{plotRGB()}}: Display a multispectral \code{GRaster} using red, blue, green, and alpha channels \item \code{\link[=project]{project()}}: Change coordinate reference system and cell size \item \code{\link[=predict]{predict()}}: Make predictions to a \code{GRaster} from a linear model or generalized linear model +\item \code{\link[=princomp]{princomp()}}: Apply a principal components analysis (PCA) to a \code{GRaster} \item \code{\link[=regress]{regress()}}: Regression intercept, slope, r2, and t-value across each set of cells \item \code{\link[=resample]{resample()}}: Change cell size \item \code{\link[=reorient]{reorient()}}: Convert degrees between 'north-orientation' and 'east orientation' diff --git a/man/pcs.Rd b/man/pcs.Rd index 5bda1687..bfedd09c 100644 --- a/man/pcs.Rd +++ b/man/pcs.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/pca.r +% Please edit documentation in R/princomp.r \name{pcs} \alias{pcs} \title{Retrieve a principal components model from a PCA GRaster} @@ -7,7 +7,7 @@ pcs(x) } \arguments{ -\item{x}{A \code{GRaster} created by \code{\link[=pca]{pca()}}} +\item{x}{A \code{GRaster} created by \code{\link[=princomp]{princomp()}}} } \value{ An object of class \code{prcomp}. @@ -28,7 +28,7 @@ madChelsa <- fastData("madChelsa") chelsa <- fast(madChelsa) # Generate raster with layers representing principal component predictions: -pcRast <- pca(chelsa, scale = TRUE) +pcRast <- princomp(chelsa, scale = TRUE) plot(pcRast) # Get information on the PCA: @@ -41,5 +41,5 @@ plot(prinComp) } } \seealso{ -\code{\link[=pca]{pca()}}, \code{\link[stats:prcomp]{stats::prcomp()}}, module \code{i.pca} in \strong{GRASS} +\code{\link[=princomp]{princomp()}}, \code{\link[terra:princomp]{terra::princomp()}}, module \code{i.pca} in \strong{GRASS} } diff --git a/man/pca.Rd b/man/princomp.Rd similarity index 82% rename from man/pca.Rd rename to man/princomp.Rd index 4b9ef564..813487f7 100644 --- a/man/pca.Rd +++ b/man/princomp.Rd @@ -1,11 +1,11 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/pca.r -\name{pca,GRaster-method} -\alias{pca,GRaster-method} -\alias{pca} +% Please edit documentation in R/princomp.r +\name{princomp,GRaster-method} +\alias{princomp,GRaster-method} +\alias{princomp} \title{Apply a principal component analysis (PCA) to layers of a GRaster} \usage{ -\S4method{pca}{GRaster}(x, scale = TRUE, scores = FALSE) +\S4method{princomp}{GRaster}(x, scale = TRUE, scores = FALSE) } \arguments{ \item{x}{A \code{GRaster} with two or more layers.} @@ -33,7 +33,7 @@ madChelsa <- fastData("madChelsa") chelsa <- fast(madChelsa) # Generate raster with layers representing principal component predictions: -pcRast <- pca(chelsa, scale = TRUE) +pcRast <- princomp(chelsa, scale = TRUE) plot(pcRast) # Get information on the PCA: @@ -46,5 +46,5 @@ plot(prinComp) } } \seealso{ -\code{\link[stats:prcomp]{stats::prcomp()}}; module \code{i.pca} in \strong{GRASS} +\code{\link[terra:princomp]{terra::princomp()}}, \code{\link[terra:prcomp]{terra::prcomp()}} } From cb57e1617e1af9710293260f3dc3fb820c69ef31 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 23:51:46 -0500 Subject: [PATCH 062/125] Better error --- R/locationRestore.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/locationRestore.r b/R/locationRestore.r index 79e1017e..2b5e96af 100644 --- a/R/locationRestore.r +++ b/R/locationRestore.r @@ -63,7 +63,7 @@ methods::setMethod( index <- .locationFind(location, return = "index") if (is.null(index)) { - stop("Location has not been created.") + stop("Location has not been created. You must use faster() to set the installation directory where GRASS is installed, then use fast() at least once to start GRASS.") } opts <- faster() From 765b1e266683cd31f03e52b08f57b732c77d06bd Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 23:51:50 -0500 Subject: [PATCH 063/125] Update combineCats.r --- R/combineCats.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/combineCats.r b/R/combineCats.r index c9a7dfb1..dcd53dc3 100644 --- a/R/combineCats.r +++ b/R/combineCats.r @@ -25,7 +25,7 @@ #' #' @example man/examples/ex_GRaster_categorical.r #' -#' @seealso [combineLevels()], `vignette("GRasters", package = "fasterRaster")` +#' @seealso [combineLevels()], `vignette("GRasters", package = "fasterRaster")`, [terra::crosstab()] #' #' @aliases combineCats #' @rdname combineCats From 6c8b861e7e4a4f6fccab75e12dc18fbcce02edab Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Fri, 4 Oct 2024 23:51:53 -0500 Subject: [PATCH 064/125] Update combineCats.Rd --- man/combineCats.Rd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/combineCats.Rd b/man/combineCats.Rd index 4ae90124..b2596129 100644 --- a/man/combineCats.Rd +++ b/man/combineCats.Rd @@ -158,5 +158,5 @@ levels(combinedNA) } } \seealso{ -\code{\link[=combineLevels]{combineLevels()}}, \code{vignette("GRasters", package = "fasterRaster")} +\code{\link[=combineLevels]{combineLevels()}}, \code{vignette("GRasters", package = "fasterRaster")}, \code{\link[terra:crosstab]{terra::crosstab()}} } From cae26be9cebb027fce9684a9682dec7dc9f0f24f Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 00:13:40 -0500 Subject: [PATCH 065/125] `combineCats()` --> `concats() --- NAMESPACE | 2 +- R/01_generics.r | 2 +- R/combineLevels.r | 4 ++-- R/{combineCats.r => concats.r} | 10 +++++----- R/fasterRaster.r | 4 ++-- man/activeCat.Rd | 4 ++-- man/addCats.Rd | 4 ++-- man/catNames.Rd | 4 ++-- man/combineLevels.Rd | 8 ++++---- man/{combineCats.Rd => concats.Rd} | 16 ++++++++-------- man/droplevels.Rd | 4 ++-- man/examples/ex_GRaster_categorical.r | 4 ++-- man/fasterRaster.Rd | 4 ++-- man/levels.Rd | 4 ++-- man/missingCats.Rd | 4 ++-- man/nlevels.Rd | 4 ++-- 16 files changed, 41 insertions(+), 41 deletions(-) rename R/{combineCats.r => concats.r} (95%) rename man/{combineCats.Rd => concats.Rd} (92%) diff --git a/NAMESPACE b/NAMESPACE index a4435cfc..5ddfde2d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -76,11 +76,11 @@ exportMethods(classify) exportMethods(clump) exportMethods(clusterPoints) exportMethods(colbind) -exportMethods(combineCats) exportMethods(combineLevels) exportMethods(compareGeom) exportMethods(complete.cases) exportMethods(compositeRGB) +exportMethods(concats) exportMethods(connectors) exportMethods(convHull) exportMethods(cos) diff --git a/R/01_generics.r b/R/01_generics.r index 921945f7..3b9c0a32 100644 --- a/R/01_generics.r +++ b/R/01_generics.r @@ -104,8 +104,8 @@ methods::setGeneric(name = "clusterPoints", def = function(x, ...) standardGener methods::setGeneric(name = "compareGeom", package = "terra") methods::setGeneric("complete.cases", function(...) standardGeneric("complete.cases"), package = "stats") methods::setGeneric(name = "compositeRGB", def = function(r, ...) standardGeneric("compositeRGB")) -methods::setGeneric(name = "combineCats", def = function(x, ...) standardGeneric("combineCats")) methods::setGeneric(name = "combineLevels", def = function(x, ...) standardGeneric("combineLevels")) +methods::setGeneric(name = "concats", def = function(x, ...) standardGeneric("concats")) methods::setGeneric(name = "connectors", def = function(x, y, ...) standardGeneric("connectors")) methods::setGeneric(name = "convHull", package = "terra") methods::setGeneric(name = "count", def = function(x, ...) standardGeneric("count")) diff --git a/R/combineLevels.r b/R/combineLevels.r index 9f5900ac..81827541 100644 --- a/R/combineLevels.r +++ b/R/combineLevels.r @@ -2,7 +2,7 @@ #' #' @description This function creates a single "levels" table from the levels tables of one or more categorical `GRaster`s. #' -#' The difference between this function and [combineCats()] is that `combineCats()` creates a "combined" `GRaster` with a combined levels table, whereas this one just merges the levels tables. +#' The difference between this function and [concats()] is that `concats()` creates a "combined" `GRaster` with a combined levels table, whereas this one just merges the levels tables. #' #' @param x A `GRaster` or a `list` of `GRaster`s. #' @param ... Arguments to pass to [data.table::merge()]. @@ -11,7 +11,7 @@ #' #' @example man/examples/ex_GRaster_categorical.r #' -#' @seealso [combineCats()], `vignette("GRasters", package = "fasterRaster")` +#' @seealso [concats()], [terra::concats], `vignette("GRasters", package = "fasterRaster")` #' #' @aliases combineLevels #' @rdname combineLevels diff --git a/R/combineCats.r b/R/concats.r similarity index 95% rename from R/combineCats.r rename to R/concats.r index dcd53dc3..58485a05 100644 --- a/R/combineCats.r +++ b/R/concats.r @@ -25,13 +25,13 @@ #' #' @example man/examples/ex_GRaster_categorical.r #' -#' @seealso [combineLevels()], `vignette("GRasters", package = "fasterRaster")`, [terra::crosstab()] +#' @seealso [combineLevels()], [terra::concats()], `vignette("GRasters", package = "fasterRaster")` #' -#' @aliases combineCats -#' @rdname combineCats -#' @exportMethod combineCats +#' @aliases concats +#' @rdname concats +#' @exportMethod concats methods::setMethod( - f = "combineCats", + f = "concats", signature = c(x = "GRaster"), function(x, ..., na.rm = TRUE) { diff --git a/R/fasterRaster.r b/R/fasterRaster.r index 1e6da44a..cb67fe9e 100644 --- a/R/fasterRaster.r +++ b/R/fasterRaster.r @@ -77,8 +77,8 @@ #' * [cellSize()]: Cell area #' * [classify()]: Partition cell values into strata #' * [clump()]: Group adjacent cells with similar values -#' * [combineCats()]: Combine values from two or more categorical and/or integer rasters #' * [combineLevels()]: Combine the "levels" tables of two or more categorical `GRaster`s +#' * [concats()]: Combine values from two or more categorical and/or integer rasters by concatenating them #' * [crop()]: Remove parts of a `GRaster` #' * [denoise()]: Remove "noise" from a `GRaster` using a principal components analysis (PCA) #' * [distance()]: Distance to non-`NA` cells, or vice versa @@ -150,9 +150,9 @@ #' * [categories()]: Set "levels" table for specific layers of a categorical raster #' * [catNames()]: Column names of each "levels" table #' * [cats()]: "Levels" table of a categorical raster -#' * [combineCats()]: Combine categories from two or more categorical rasters #' * [combineLevels()]: Combine the "levels" tables of two or more categorical `GRaster`s #' * [complete.cases()]: Find rows of a categorical `GRaster`'s "levels" table that have no `NA`s in them +#' * [concats()]: Combine categories from two or more categorical rasters by concatenating them #' * [droplevels()]: Remove one or more levels #' * [freq()]: Frequency of each category across cells of a raster #' * [is.factor()]: Is a raster categorical? diff --git a/man/activeCat.Rd b/man/activeCat.Rd index a1b3b498..6cb4014e 100644 --- a/man/activeCat.Rd +++ b/man/activeCat.Rd @@ -152,12 +152,12 @@ levs <- data.frame( levels(elevCat) <- list(levs) # Combine levels: -combined <- combineCats(cover, elevCat) +combined <- concats(cover, elevCat) combined levels(combined) # Combine levels, treating value/NA combinations as new categories: -combinedNA <- combineCats(cover, elevCat, na.rm = FALSE) +combinedNA <- concats(cover, elevCat, na.rm = FALSE) combinedNA levels(combinedNA) diff --git a/man/addCats.Rd b/man/addCats.Rd index 57cc3376..5e5ddf8a 100644 --- a/man/addCats.Rd +++ b/man/addCats.Rd @@ -145,12 +145,12 @@ levs <- data.frame( levels(elevCat) <- list(levs) # Combine levels: -combined <- combineCats(cover, elevCat) +combined <- concats(cover, elevCat) combined levels(combined) # Combine levels, treating value/NA combinations as new categories: -combinedNA <- combineCats(cover, elevCat, na.rm = FALSE) +combinedNA <- concats(cover, elevCat, na.rm = FALSE) combinedNA levels(combinedNA) diff --git a/man/catNames.Rd b/man/catNames.Rd index 85482736..e73af3be 100644 --- a/man/catNames.Rd +++ b/man/catNames.Rd @@ -134,12 +134,12 @@ levs <- data.frame( levels(elevCat) <- list(levs) # Combine levels: -combined <- combineCats(cover, elevCat) +combined <- concats(cover, elevCat) combined levels(combined) # Combine levels, treating value/NA combinations as new categories: -combinedNA <- combineCats(cover, elevCat, na.rm = FALSE) +combinedNA <- concats(cover, elevCat, na.rm = FALSE) combinedNA levels(combinedNA) diff --git a/man/combineLevels.Rd b/man/combineLevels.Rd index ad5137f8..9e286585 100644 --- a/man/combineLevels.Rd +++ b/man/combineLevels.Rd @@ -21,7 +21,7 @@ A \code{list} with a "levels" table (a \code{data.frame} or \code{data.table}), \description{ This function creates a single "levels" table from the levels tables of one or more categorical \code{GRaster}s. -The difference between this function and \code{\link[=combineCats]{combineCats()}} is that \code{combineCats()} creates a "combined" \code{GRaster} with a combined levels table, whereas this one just merges the levels tables. +The difference between this function and \code{\link[=concats]{concats()}} is that \code{concats()} creates a "combined" \code{GRaster} with a combined levels table, whereas this one just merges the levels tables. } \examples{ if (grassStarted()) { @@ -136,17 +136,17 @@ levs <- data.frame( levels(elevCat) <- list(levs) # Combine levels: -combined <- combineCats(cover, elevCat) +combined <- concats(cover, elevCat) combined levels(combined) # Combine levels, treating value/NA combinations as new categories: -combinedNA <- combineCats(cover, elevCat, na.rm = FALSE) +combinedNA <- concats(cover, elevCat, na.rm = FALSE) combinedNA levels(combinedNA) } } \seealso{ -\code{\link[=combineCats]{combineCats()}}, \code{vignette("GRasters", package = "fasterRaster")} +\code{\link[=concats]{concats()}}, \link[terra:concats]{terra::concats}, \code{vignette("GRasters", package = "fasterRaster")} } diff --git a/man/combineCats.Rd b/man/concats.Rd similarity index 92% rename from man/combineCats.Rd rename to man/concats.Rd index b2596129..bb1197c6 100644 --- a/man/combineCats.Rd +++ b/man/concats.Rd @@ -1,11 +1,11 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/combineCats.r -\name{combineCats,GRaster-method} -\alias{combineCats,GRaster-method} -\alias{combineCats} +% Please edit documentation in R/concats.r +\name{concats,GRaster-method} +\alias{concats,GRaster-method} +\alias{concats} \title{Combine values/categories of multiple GRasters into a single GRaster} \usage{ -\S4method{combineCats}{GRaster}(x, ..., na.rm = TRUE) +\S4method{concats}{GRaster}(x, ..., na.rm = TRUE) } \arguments{ \item{x}{A \code{GRaster} with one or more layers, each of which must be have cells that represent integers or categories (factors).} @@ -146,17 +146,17 @@ levs <- data.frame( levels(elevCat) <- list(levs) # Combine levels: -combined <- combineCats(cover, elevCat) +combined <- concats(cover, elevCat) combined levels(combined) # Combine levels, treating value/NA combinations as new categories: -combinedNA <- combineCats(cover, elevCat, na.rm = FALSE) +combinedNA <- concats(cover, elevCat, na.rm = FALSE) combinedNA levels(combinedNA) } } \seealso{ -\code{\link[=combineLevels]{combineLevels()}}, \code{vignette("GRasters", package = "fasterRaster")}, \code{\link[terra:crosstab]{terra::crosstab()}} +\code{\link[=combineLevels]{combineLevels()}}, \code{\link[terra:concats]{terra::concats()}}, \code{vignette("GRasters", package = "fasterRaster")} } diff --git a/man/droplevels.Rd b/man/droplevels.Rd index cb8dfb94..f4be6b59 100644 --- a/man/droplevels.Rd +++ b/man/droplevels.Rd @@ -133,12 +133,12 @@ levs <- data.frame( levels(elevCat) <- list(levs) # Combine levels: -combined <- combineCats(cover, elevCat) +combined <- concats(cover, elevCat) combined levels(combined) # Combine levels, treating value/NA combinations as new categories: -combinedNA <- combineCats(cover, elevCat, na.rm = FALSE) +combinedNA <- concats(cover, elevCat, na.rm = FALSE) combinedNA levels(combinedNA) diff --git a/man/examples/ex_GRaster_categorical.r b/man/examples/ex_GRaster_categorical.r index 1d57cd9a..8819ea8b 100644 --- a/man/examples/ex_GRaster_categorical.r +++ b/man/examples/ex_GRaster_categorical.r @@ -110,12 +110,12 @@ levs <- data.frame( levels(elevCat) <- list(levs) # Combine levels: -combined <- combineCats(cover, elevCat) +combined <- concats(cover, elevCat) combined levels(combined) # Combine levels, treating value/NA combinations as new categories: -combinedNA <- combineCats(cover, elevCat, na.rm = FALSE) +combinedNA <- concats(cover, elevCat, na.rm = FALSE) combinedNA levels(combinedNA) diff --git a/man/fasterRaster.Rd b/man/fasterRaster.Rd index 81a6a79f..450394c0 100644 --- a/man/fasterRaster.Rd +++ b/man/fasterRaster.Rd @@ -97,8 +97,8 @@ Operations on \code{GRaster}s \item \code{\link[=cellSize]{cellSize()}}: Cell area \item \code{\link[=classify]{classify()}}: Partition cell values into strata \item \code{\link[=clump]{clump()}}: Group adjacent cells with similar values -\item \code{\link[=combineCats]{combineCats()}}: Combine values from two or more categorical and/or integer rasters \item \code{\link[=combineLevels]{combineLevels()}}: Combine the "levels" tables of two or more categorical \code{GRaster}s +\item \code{\link[=concats]{concats()}}: Combine values from two or more categorical and/or integer rasters by concatenating them \item \code{\link[=crop]{crop()}}: Remove parts of a \code{GRaster} \item \code{\link[=denoise]{denoise()}}: Remove "noise" from a \code{GRaster} using a principal components analysis (PCA) \item \code{\link[=distance]{distance()}}: Distance to non-\code{NA} cells, or vice versa @@ -179,9 +179,9 @@ Operations on \code{GRaster}s \item \code{\link[=categories]{categories()}}: Set "levels" table for specific layers of a categorical raster \item \code{\link[=catNames]{catNames()}}: Column names of each "levels" table \item \code{\link[=cats]{cats()}}: "Levels" table of a categorical raster -\item \code{\link[=combineCats]{combineCats()}}: Combine categories from two or more categorical rasters \item \code{\link[=combineLevels]{combineLevels()}}: Combine the "levels" tables of two or more categorical \code{GRaster}s \item \code{\link[=complete.cases]{complete.cases()}}: Find rows of a categorical \code{GRaster}'s "levels" table that have no \code{NA}s in them +\item \code{\link[=concats]{concats()}}: Combine categories from two or more categorical rasters by concatenating them \item \code{\link[=droplevels]{droplevels()}}: Remove one or more levels \item \code{\link[=freq]{freq()}}: Frequency of each category across cells of a raster \item \code{\link[=is.factor]{is.factor()}}: Is a raster categorical? diff --git a/man/levels.Rd b/man/levels.Rd index 6e76fce3..70aa30ec 100644 --- a/man/levels.Rd +++ b/man/levels.Rd @@ -167,12 +167,12 @@ levs <- data.frame( levels(elevCat) <- list(levs) # Combine levels: -combined <- combineCats(cover, elevCat) +combined <- concats(cover, elevCat) combined levels(combined) # Combine levels, treating value/NA combinations as new categories: -combinedNA <- combineCats(cover, elevCat, na.rm = FALSE) +combinedNA <- concats(cover, elevCat, na.rm = FALSE) combinedNA levels(combinedNA) diff --git a/man/missingCats.Rd b/man/missingCats.Rd index 211088b0..d00a5065 100644 --- a/man/missingCats.Rd +++ b/man/missingCats.Rd @@ -133,12 +133,12 @@ levs <- data.frame( levels(elevCat) <- list(levs) # Combine levels: -combined <- combineCats(cover, elevCat) +combined <- concats(cover, elevCat) combined levels(combined) # Combine levels, treating value/NA combinations as new categories: -combinedNA <- combineCats(cover, elevCat, na.rm = FALSE) +combinedNA <- concats(cover, elevCat, na.rm = FALSE) combinedNA levels(combinedNA) diff --git a/man/nlevels.Rd b/man/nlevels.Rd index c0eb7e80..2f6c5814 100644 --- a/man/nlevels.Rd +++ b/man/nlevels.Rd @@ -129,12 +129,12 @@ levs <- data.frame( levels(elevCat) <- list(levs) # Combine levels: -combined <- combineCats(cover, elevCat) +combined <- concats(cover, elevCat) combined levels(combined) # Combine levels, treating value/NA combinations as new categories: -combinedNA <- combineCats(cover, elevCat, na.rm = FALSE) +combinedNA <- concats(cover, elevCat, na.rm = FALSE) combinedNA levels(combinedNA) From d9976fffdba83d700a99bcf9a508658113451235 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 00:14:00 -0500 Subject: [PATCH 066/125] Update --- NEWS.md | 4 +++- R/cleanGeom.r | 2 +- R/xor.r | 4 ++-- _pkgdown.yml | 4 ++-- man/breakPolys.Rd | 2 +- man/xor.Rd | 4 ++-- vignettes/GRasters.Rmd | 2 +- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/NEWS.md b/NEWS.md index d09db9c8..43d9fa2e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,7 +10,9 @@ o `.vAttachDatabase()` no longer has the `"o"` flag when calling `v.db.connect` ### Potentially code-breaking changes o `aggregate()` no longer has the `dissolve` argument for `GVector`s (polygons will always be dissolved). -o `intercept()`, `slope()`, `r2()`, and `tvalue()` have been replaced by the single function `regress()`. +o `combineCats()` has been renamed `concats()` to align with **terra**. +o `intercept()`, `slope()`, `r2()`, and `tvalue()` have been replaced by the single function `regress()` to align with **terra**. +o `pca()` has been renamed `princomp()`. ### Enhanced functionality and new functions o `extract()` now automatically projects a `GVector` to match the CRS of a `GRaster` from which extraction is being made. diff --git a/R/cleanGeom.r b/R/cleanGeom.r index 2ef7d075..3bee3d15 100644 --- a/R/cleanGeom.r +++ b/R/cleanGeom.r @@ -19,7 +19,7 @@ #' #' @param tolerance Numeric or `NULL` (default): Minimum distance in map units (degrees for unprojected, usually meters for projected) or minimum area (in meters-squared, regardless of projection). #' -#' @seealso [terra::topology()], [fillHoles()], *Details* section in [fast()], [simplifyGeom()], [smoothGeom()] +#' @seealso [terra::topology()], [fillHoles()], [terra::removeDupNodes()], *Details* section in [fast()], [simplifyGeom()], [smoothGeom()] #' #' @returns A `GVector`. #' diff --git a/R/xor.r b/R/xor.r index 390304a3..0a4c3d5f 100644 --- a/R/xor.r +++ b/R/xor.r @@ -1,12 +1,12 @@ #' Select parts of polygons not shared between two GVectors #' -#' @description The `xor()` function selects the area that does not overlap between two "polygon" `GVector`s. You can also use the `/` operator, as in `vect1 / vect2`. +#' @description The `xor()` function selects the area that does *not* overlap between two "polygon" `GVector`s. You can also use the `/` operator, as in `vect1 / vect2`. #' #' @param x,y `GVector`s. #' #' @returns A `GVector`. #' -#' @seealso [c()], [aggregate()], [crop()], [intersect()], [union()], [erase()] +#' @seealso [crop()], [intersect()], [union()], [erase()] #' #' @example man/examples/ex_union_intersect_xor_erase.r #' diff --git a/_pkgdown.yml b/_pkgdown.yml index 959d1a0e..d8b58a34 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -155,7 +155,7 @@ reference: - classify - clump - combineCats - - combineLevels + - concats - crop - denoise - distance @@ -232,9 +232,9 @@ reference: - categories - catNames - cats - - combineCats - combineLevels - complete.cases + - concats - droplevels - freq - is.factor diff --git a/man/breakPolys.Rd b/man/breakPolys.Rd index 13efd3e0..d5a6636f 100644 --- a/man/breakPolys.Rd +++ b/man/breakPolys.Rd @@ -158,5 +158,5 @@ legend("bottom", } } \seealso{ -\code{\link[terra:topology]{terra::topology()}}, \code{\link[=fillHoles]{fillHoles()}}, \emph{Details} section in \code{\link[=fast]{fast()}}, \code{\link[=simplifyGeom]{simplifyGeom()}}, \code{\link[=smoothGeom]{smoothGeom()}} +\code{\link[terra:topology]{terra::topology()}}, \code{\link[=fillHoles]{fillHoles()}}, \code{\link[terra:topology]{terra::removeDupNodes()}}, \emph{Details} section in \code{\link[=fast]{fast()}}, \code{\link[=simplifyGeom]{simplifyGeom()}}, \code{\link[=smoothGeom]{smoothGeom()}} } diff --git a/man/xor.Rd b/man/xor.Rd index 64240bb5..06cd28b0 100644 --- a/man/xor.Rd +++ b/man/xor.Rd @@ -14,7 +14,7 @@ A \code{GVector}. } \description{ -The \code{xor()} function selects the area that does not overlap between two "polygon" \code{GVector}s. You can also use the \code{/} operator, as in \code{vect1 / vect2}. +The \code{xor()} function selects the area that does \emph{not} overlap between two "polygon" \code{GVector}s. You can also use the \code{/} operator, as in \code{vect1 / vect2}. } \examples{ if (grassStarted()) { @@ -68,5 +68,5 @@ minus <- coast4 - hull # same as erase() } } \seealso{ -\code{\link[=c]{c()}}, \code{\link[=aggregate]{aggregate()}}, \code{\link[=crop]{crop()}}, \code{\link[=intersect]{intersect()}}, \code{\link[=union]{union()}}, \code{\link[=erase]{erase()}} +\code{\link[=crop]{crop()}}, \code{\link[=intersect]{intersect()}}, \code{\link[=union]{union()}}, \code{\link[=erase]{erase()}} } diff --git a/vignettes/GRasters.Rmd b/vignettes/GRasters.Rmd index 59f7892c..98fabe35 100644 --- a/vignettes/GRasters.Rmd +++ b/vignettes/GRasters.Rmd @@ -47,9 +47,9 @@ Categorical rasters (also called "factor" rasters) are actually integer rasters, * [`addCats<-`](https://adamlilith.github.io/fasterRaster/reference/addCats.html) add new levels to a "levels" table. * [`catNames()`](https://adamlilith.github.io/fasterRaster/reference/catNames.html) reports the column names of the "levels" table of each layer of a raster. * [`cats()`](https://adamlilith.github.io/fasterRaster/reference/cats.html) returns the entire "levels" table of a categorical raster. -* [`combineCats()`](https://adamlilith.github.io/fasterRaster/reference/combineCats.html) combines levels of two or more categorical or integer rasters. * [`combineLevels()`](https://adamlilith.github.io/fasterRaster/reference/combineLevels.html): Combine the "levels" tables of two or more categorical `GRaster`s. * [`complete.cases()`](https://adamlilith.github.io/fasterRaster/reference/complete.cases.html) finds rows in the levels table that have no `NA`s. +* [`concats()`](https://adamlilith.github.io/fasterRaster/reference/combineCats.html) combines levels of two or more categorical or integer rasters by concatenating them. * [`droplevels()`](https://adamlilith.github.io/fasterRaster/reference/dropevels.html) removes "unused" levels in a "levels" table. * [`levels()`](https://adamlilith.github.io/fasterRaster/reference/levels.html) returns the "levels" table of a categorical raster (just the value column and the active column). * [`levels<-`](https://adamlilith.github.io/fasterRaster/reference/levels.html) and [`categories()`](https://adamlilith.github.io/fasterRaster/reference/categories.html) can be used to assign categories to an integer raster and make it categorical (i.e., a "factor" raster). From 85f2e55c751d8b4894254291f7cb5e457a0515d7 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 00:24:10 -0500 Subject: [PATCH 067/125] Update pkgdown.yml --- inst/pkgdown.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inst/pkgdown.yml b/inst/pkgdown.yml index 204f2ae5..9e89f617 100644 --- a/inst/pkgdown.yml +++ b/inst/pkgdown.yml @@ -1,4 +1,4 @@ -pandoc: 3.1.1 +pandoc: 3.1.11 pkgdown: 2.1.1 pkgdown_sha: ~ articles: @@ -8,7 +8,7 @@ articles: hidden_functions: hidden_functions.html projects_mapsets: projects_mapsets.html regions: regions.html -last_built: 2024-09-25T15:32Z +last_built: 2024-10-05T05:16Z urls: reference: https://github.com/adamlilith/fasterRaster/reference article: https://github.com/adamlilith/fasterRaster/articles From 89ff94d771cac196c2819368566bf41a6d812f78 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 00:28:59 -0500 Subject: [PATCH 068/125] Update --- _pkgdown.yml | 2 +- inst/CITATION | 2 -- inst/pkgdown.yml | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/_pkgdown.yml b/_pkgdown.yml index d8b58a34..073cf0cc 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -154,7 +154,7 @@ reference: - cellSize - classify - clump - - combineCats + - combineLevels - concats - crop - denoise diff --git a/inst/CITATION b/inst/CITATION index daeecbaa..d0e1bf82 100644 --- a/inst/CITATION +++ b/inst/CITATION @@ -4,7 +4,6 @@ note <- sprintf("R package version %s", meta$Version) doi <- "NA" title <- paste0(meta$Package, ": ", meta$Title) - citHeader("To cite fasterRaster in publications use:") bibentry(bibtype = "Manual", key = key, @@ -16,4 +15,3 @@ bibentry(bibtype = "Manual", note = note, doi = doi ) - diff --git a/inst/pkgdown.yml b/inst/pkgdown.yml index 9e89f617..34b69565 100644 --- a/inst/pkgdown.yml +++ b/inst/pkgdown.yml @@ -8,7 +8,7 @@ articles: hidden_functions: hidden_functions.html projects_mapsets: projects_mapsets.html regions: regions.html -last_built: 2024-10-05T05:16Z +last_built: 2024-10-05T05:28Z urls: reference: https://github.com/adamlilith/fasterRaster/reference article: https://github.com/adamlilith/fasterRaster/articles From fa452a9fb89beaabebb09f38d4373db12ff9b769 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 00:37:28 -0500 Subject: [PATCH 069/125] pkgdown --- DESCRIPTION | 2 +- _pkgdown.yml | 2 ++ inst/pkgdown.yml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 698646a0..e260b2fc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -41,7 +41,7 @@ Suggests: Roxygen: list(markdown = TRUE) License: GPL (>=3) + file LICENSE SystemRequirements: GRASS (>= 8) -URL: http://www.earthSkySea.org, https://adamlilith.github.io/fasterRaster/ +URL: https://github.com/adamlilith/fasterRaster, https://adamlilith.github.io/fasterRaster/ BugReports: https://github.com/adamlilith/fasterRaster/issues VignetteBuilder: knitr Encoding: UTF-8 diff --git a/_pkgdown.yml b/_pkgdown.yml index 073cf0cc..4509e5ff 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -186,6 +186,7 @@ reference: - resample - reorient - sampleRast + - segregate - stretch - scale - scalepop @@ -245,6 +246,7 @@ reference: - missing.cases - missingCats - nlevels + - segregate - subst - zonalGeog - title: Remote sensing GRasters diff --git a/inst/pkgdown.yml b/inst/pkgdown.yml index 34b69565..21261531 100644 --- a/inst/pkgdown.yml +++ b/inst/pkgdown.yml @@ -8,7 +8,7 @@ articles: hidden_functions: hidden_functions.html projects_mapsets: projects_mapsets.html regions: regions.html -last_built: 2024-10-05T05:28Z +last_built: 2024-10-05T05:30Z urls: reference: https://github.com/adamlilith/fasterRaster/reference article: https://github.com/adamlilith/fasterRaster/articles From f16bec3d79bba368f3e7452cef5e686cb8b62ac0 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 22:34:51 -0500 Subject: [PATCH 070/125] Update appFunsTable.rda --- data/appFunsTable.rda | Bin 1414 -> 1398 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/appFunsTable.rda b/data/appFunsTable.rda index 20012f624e30f3038d6acf5ea0065737e8e6b274..d9e80ecac5cceb0daffc2d915937263e1d17eb67 100644 GIT binary patch literal 1398 zcmV-+1&R6}iwFP!000002JKkgZ`(EychmSQYnwF11}sZ67F{sAk)5P_*+V-ZSd+a3 z8Q>&9fFK)anYM{aq)SS%ou~bU`;(R(C6S^W$;rB{`#>KQ>3B!p{qBB8(&@Z8`)28E zsZ=W6FD)*@-!gob%BLp>t#3=E#RU-Vl@?2Nct60S(Lu=M3GR9XET1Stcvj$fnb_21 zfLRZV-v30d(*AZZBXROg zb2Xz`_G#X5&F4KE^a{u%$8Lq?JAA?lJ;Ib+mycP2j|$^@*n&2Uf zmZo*rYJyZ!FjGn4$~4zYBi21C7?B-q=5Cpq-ND%L%Nn-JWvP&X>EWlLCVijsY6rmR&DiLL_u=t=&%j>}av z0hk0F#Rp<*B#28TD# zm}-9Q4vG8LEOoT!pa_uKV^o;%d9ZhQyl)O(3=ITXfeq0v!?Ko97Xwebnov5Mrq6;Q z2Yp<7jXd7@=Gtr+BG0pGChe{22p56S3nf4XK@cBP)+3@B1-Y$RvZk&JbC+z-Mc@E^ zT`%k@CaLX4Y9`HtR5WROKE|SHTg4cPrhKt$3SLCVqP}{ju1efb{K?QAs&)tvMz3B+ z1k|Npiw>BWevH6Qt$T_HP0<_+A2t9w@**VKbECksxfv>xv5Natn4wcsDm#PwDU?LwU zHeYHRI3z=#6`6g-Lca?DL9MQXDk7KrqcFhHhA7AYUb>}I+j!CW=9@Z#Ny~-X6_7>k z(*E|{LGjeVn|j~SHjIm4*pfU9hBvJ?)kz-#(%B-Or%)wD^a13JCEDNdhlIV0c*|Pftqy^k|%`Db0#Ui-v0+?`1)+f=mkRR#~ybN37H%Oeu8vkd^qTGM-KM zh>x?8)dh4~Ba_ma-~o)73CoF`PPu1B?6l^2qRF=+DWXPms-r5ZqtXeg2~tVHOeKXY*IX}*SXaG7 z*&vHdM!xF5!Jqj|l&VRZc_DAH6t{`CnWz05Z_piR2N72iO%*ecA7AoCz??KxD%(jW zk^od!6bp*3Dam7wrpiom=C`({5Uj;iM+~=x_v*BBcA#w^8?qND*J8ToH2H<+KGQEh zYJh+bb_P?Ln6Nj*sT$;J8!^}*%NDv4Ms+AIWR+#vTxBtxi*28T5jD{TZ1Do45q93- z0lC!L$xLlb>Z9vKKiq{i3KGwx`=t?eP71H$|}OlcKNne5!=2QCXpdf0{Y5=`I>9f;@* zRfu6bL@+LqKg7&{f0hFs7O!hNTt(x9iO*4dAht$=I8;*3P;|0E&!Z6spUIp(bxa0W zCtNBIE|os%A?R~qL+v1z7>!iRUMCYj1vO^?nL_A&XjotQ$kb~!ib6q!J)H`#M7xP8%HfZ|K;(^R(*qEunqN9Y;=D0S9q-#H0;KjB6(+nM?jM~Tn1dHX z13^|`L(G<8Ma!s*fu~(fD4k8yXa10bKCZn+9`AfJZ8i;&XIV9q_EvR-i$LH85+H*h zh=(cb5z&l-+|evqSJ#D^OSWesuz|jg8}t;D)OMq9 z;AM0y>#L{gs>Hp-Ux>az7`=QIQBN0rJvv%q;xPhywC*M%G&u__JZOD7aw81d*>-{5 z5GMtv@j*x0wLO=_VT>ntFgCylmVRrjiUyTa=WXhJQ`;~u{9#M-z#rbU z+ENF61W2cfxUNE#RL~=^9cxn8j`w3$PM!JpB?g{gl?}+m$3g3XISsx$I67+nWut$< z*$0@F_m}DSSiXN1eWE)VY8?LEgUj(PL0xvHe(IFMDMqZOXWi&Ig)QO!=d91^>-qv34!!!Rg{DZGYzf6bbxNt>ILeqoGJ@~%e U?hBX?(y!|I9l9JOEZ-IY08_u2wg3PC From 69f9a38235f01ac9c5cacf7e71fbcf4d3c7e3f7d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 22:35:59 -0500 Subject: [PATCH 071/125] Fix `appFuns()` so it opens shiny --- R/app.r | 53 ++++++++++++++++++++++++----------------------------- man/app.Rd | 8 ++++---- 2 files changed, 28 insertions(+), 33 deletions(-) diff --git a/R/app.r b/R/app.r index 7bb38d8b..2b48cc15 100644 --- a/R/app.r +++ b/R/app.r @@ -6,15 +6,15 @@ #' #' `appCheck()` tests whether a formula supplied to `app()` has any "forbidden" function calls. #' -#' The `app()` function operates in a manner slightly different from [terra::app()]. The function to be applied *must* be written as a character string. For example, if the raster had layer names "`x1`" and "`x2`", then the function might be like `"= max(sqrt(x1), log(x2))"`. Rasters **cannot** have the same names as functions used in the formula. In this example, the rasters could not be named "max", "sqrt", or "log". +#' The `app()` function operates in a manner somewhat different from [terra::app()]. The function to be applied *must* be written as a character string. For example, if the `GRaster` had layer names "`x1`" and "`x2`", then the function might be like `"= max(sqrt(x1), log(x2))"`. Rasters **cannot** have the same names as functions used in the formula. In this example, the rasters could not be named "max", "sqrt", or "log". Note that the name of a `GRaster` is given by [names()]--this can be different from the name of the object in **R**. #' -#' The `app()` function will automatically check for raster names that appear also to be functions that appear in the formula. However, you can check a formula before running `app()` by using the `appCheck()` function. You can obtain a list of `app()` functions using `appFuns()`. Note that these are sometimes different from how they are applied in **R**. +#' The `app()` function will automatically check for `GRaster` names that appear also to be functions that appear in the formula. However, you can check a formula before running `app()` by using the `appCheck()` function. You can obtain a list of `app()` functions using `appFuns()`. Note that these are sometimes different from how they are applied in **R**. #' #' Tips: #' * Make sure your `GRaster`s have `names()`. The function matches on these, not the name of the variable you use in **R** for the `GRaster`. -#' * In **GRASS**, use `null()` instead of `NA`, and use `isnull()` instead of `is.na()`. +#' * Use `null()` instead of `NA`, and use `isnull()` instead of `is.na()`. #' * If you want to calculate values using while ignoring `NA` (or `null`) values, see the functions that begin with `n` (like `nmean`). -#' * Be mindful of the data type that a function returns. In **GRASS**, these are `CELL` (integer), `FCELL` (floating point values--precise to about the 7th decimal place), and `DCELL` (double-floating point values--precise to about the 15th decimal place). In cases where you want to datatype a raster to be treated like a float or double data type raster, wrap the raster in the `float()` or `double()` functions to datatype it is treated as such. This is especially useful if the raster might be assumed to be the `CELL` type because it only contains integer values. You can get the data type of a raster using [datatype()] with the `type` argument set to `GRASS`. You can change the data type of a `GRaster` using [as.int()], [as.float()], and [as.doub()]. Note that categorical rasters are really `CELL` (integer) rasters with an associated "levels" table. You can also change a `CELL` raster to a `FCELL` raster by adding then subtracting a decimal value, as in `x - 0.1 + 0.1`. See `vignette("GRasters", package = "fasterRaster")`. +#' * Be mindful of the data type that a function returns. In **GRASS**, these are `CELL` (integer), `FCELL` (floating point values--precise to about the 7th decimal place), and `DCELL` (double-floating point values--precise to about the 15th decimal place; commensurate with the **R** `numeric` type). In cases where you want a `GRaster` to be treated like a float or double type raster, wrap the name of the `GRaster` in the `float()` or `double()` functions. This is especially useful if the `GRaster` might be assumed to be the `CELL` type because it only contains integer values. You can get the data type of a raster using [datatype()] with the `type` argument set to `GRASS`. You can change the data type of a `GRaster` using [as.int()], [as.float()], and [as.doub()]. Note that categorical rasters are really `CELL` (integer) rasters with an associated "levels" table. You can also change a `CELL` raster to a `FCELL` raster by adding then subtracting a decimal value, as in `x - 0.1 + 0.1`. See `vignette("GRasters", package = "fasterRaster")`. #' * The `rand()` function returns integer values by default. If you want non-integer values, use the tricks mentioned above to datatype non-integer values. For example, if you want uniform random values in the range between 0 and 1, use something like `= float(rand(0 + 0.1, 1 + 0.1) - 0.1)`. #' #' @param x A `GRaster` with one or more named layers. @@ -118,30 +118,24 @@ methods::setMethod( } # EOF ) -# #' @aliases appFuns -# #' @rdname app -# #' @export -# methods::setMethod( - # f = "appFuns", - # signature = c(x = "logical"), - # function(x = FALSE) { - #' @aliases appFuns #' @rdname app #' @export appFuns <- function(warn = TRUE) { - # appFunsTable <- NULL + appFunsTable <- NULL utils::data("appFunsTable", envir = environment(), package = "fasterRaster") - if (interactive()) { - showableCols <- c("Type", "GRASS_Function", "R_Function", "Definition", "Returns") + if (interactive()) { + + showableCols <- c("Type", "GRASS_Function", "R_Function", "Definition", "Returns") shiny::shinyApp( ui = shiny::fluidPage(DT::DTOutput("tbl")), server = function(input, output) { output$tbl <- DT::renderDT( - appFunsTable[ , showableCols], + # appFunsTable[ , showableCols], + appFunsTable[ , c("Type", "GRASS_Function", "R_Function", "Definition", "Returns")], caption = shiny::HTML("Functions that can be used in the fasterRaster app() function and their equivalents in R.
Note that in GRASS, 'null()' is the same as 'NA'."), options = list( pageLength = nrow(appFunsTable), @@ -157,14 +151,8 @@ appFuns <- function(warn = TRUE) { warning("You must be running R interactively to view the table using appFuns().") } - if (faster("useDataTable")) appFunsTable <- data.table::data.table(appFunsTable) - invisible(appFunsTable) - } - # } # EOF -# ) - #' @aliases appCheck #' @rdname app #' @exportMethod appCheck @@ -175,13 +163,20 @@ methods::setMethod( # any forbidden names in rasters? ns <- names(x) - funs <- appFuns(warn = FALSE) - if (inherits(funs, "data.table")) { - funs <- funs[["GRASS_Function"]] + appFunsTable <- NULL + utils::data("appFunsTable", envir = environment(), package = "fasterRaster") + + if (inherits(appFunsTable, "data.table")) { + funs <- appFunsTable[["GRASS_Function"]] } else { - funs$GRASS_Function + funs <- appFunsTable$GRASS_Function } + + parens <- regexec(funs, pattern = "\\(") + parens <- unlist(parens) + parens <- parens - 1L + funs <- substr(funs, 1L, parens) bads <- funs[funs %in% ns] @@ -199,15 +194,15 @@ methods::setMethod( if (length(realBads) > 0L) { - msg <- "At least one raster has a forbidden name that seems to appear in the string." + msg <- "At least one raster has a forbidden name that seems to appear in the equation:" if (failOnBad) { - stop(msg, "\n", realBads) + stop(msg, "\n", paste(realBads, collapse = " ")) } else { warning(msg) return(realBads) } } else if (msgOnGood) { - msg <- "Rasters have one or more forbidden names, but they do not seem to appear in the string." + msg <- "The GRasters have one or more forbidden names, but they do not seem to appear in\n the equation. Use the equation with caution, or rename your GRasters." warning(msg) } } diff --git a/man/app.Rd b/man/app.Rd index b761e11e..1852aaca 100644 --- a/man/app.Rd +++ b/man/app.Rd @@ -51,16 +51,16 @@ A \code{GRaster}. \code{appCheck()} tests whether a formula supplied to \code{app()} has any "forbidden" function calls. -The \code{app()} function operates in a manner slightly different from \code{\link[terra:app]{terra::app()}}. The function to be applied \emph{must} be written as a character string. For example, if the raster had layer names "\code{x1}" and "\code{x2}", then the function might be like \code{"= max(sqrt(x1), log(x2))"}. Rasters \strong{cannot} have the same names as functions used in the formula. In this example, the rasters could not be named "max", "sqrt", or "log". +The \code{app()} function operates in a manner somewhat different from \code{\link[terra:app]{terra::app()}}. The function to be applied \emph{must} be written as a character string. For example, if the \code{GRaster} had layer names "\code{x1}" and "\code{x2}", then the function might be like \code{"= max(sqrt(x1), log(x2))"}. Rasters \strong{cannot} have the same names as functions used in the formula. In this example, the rasters could not be named "max", "sqrt", or "log". Note that the name of a \code{GRaster} is given by \code{\link[=names]{names()}}--this can be different from the name of the object in \strong{R}. -The \code{app()} function will automatically check for raster names that appear also to be functions that appear in the formula. However, you can check a formula before running \code{app()} by using the \code{appCheck()} function. You can obtain a list of \code{app()} functions using \code{appFuns()}. Note that these are sometimes different from how they are applied in \strong{R}. +The \code{app()} function will automatically check for \code{GRaster} names that appear also to be functions that appear in the formula. However, you can check a formula before running \code{app()} by using the \code{appCheck()} function. You can obtain a list of \code{app()} functions using \code{appFuns()}. Note that these are sometimes different from how they are applied in \strong{R}. Tips: \itemize{ \item Make sure your \code{GRaster}s have \code{names()}. The function matches on these, not the name of the variable you use in \strong{R} for the \code{GRaster}. -\item In \strong{GRASS}, use \code{null()} instead of \code{NA}, and use \code{isnull()} instead of \code{is.na()}. +\item Use \code{null()} instead of \code{NA}, and use \code{isnull()} instead of \code{is.na()}. \item If you want to calculate values using while ignoring \code{NA} (or \code{null}) values, see the functions that begin with \code{n} (like \code{nmean}). -\item Be mindful of the data type that a function returns. In \strong{GRASS}, these are \code{CELL} (integer), \code{FCELL} (floating point values--precise to about the 7th decimal place), and \code{DCELL} (double-floating point values--precise to about the 15th decimal place). In cases where you want to datatype a raster to be treated like a float or double data type raster, wrap the raster in the \code{float()} or \code{double()} functions to datatype it is treated as such. This is especially useful if the raster might be assumed to be the \code{CELL} type because it only contains integer values. You can get the data type of a raster using \code{\link[=datatype]{datatype()}} with the \code{type} argument set to \code{GRASS}. You can change the data type of a \code{GRaster} using \code{\link[=as.int]{as.int()}}, \code{\link[=as.float]{as.float()}}, and \code{\link[=as.doub]{as.doub()}}. Note that categorical rasters are really \code{CELL} (integer) rasters with an associated "levels" table. You can also change a \code{CELL} raster to a \code{FCELL} raster by adding then subtracting a decimal value, as in \code{x - 0.1 + 0.1}. See \code{vignette("GRasters", package = "fasterRaster")}. +\item Be mindful of the data type that a function returns. In \strong{GRASS}, these are \code{CELL} (integer), \code{FCELL} (floating point values--precise to about the 7th decimal place), and \code{DCELL} (double-floating point values--precise to about the 15th decimal place; commensurate with the \strong{R} \code{numeric} type). In cases where you want a \code{GRaster} to be treated like a float or double type raster, wrap the name of the \code{GRaster} in the \code{float()} or \code{double()} functions. This is especially useful if the \code{GRaster} might be assumed to be the \code{CELL} type because it only contains integer values. You can get the data type of a raster using \code{\link[=datatype]{datatype()}} with the \code{type} argument set to \code{GRASS}. You can change the data type of a \code{GRaster} using \code{\link[=as.int]{as.int()}}, \code{\link[=as.float]{as.float()}}, and \code{\link[=as.doub]{as.doub()}}. Note that categorical rasters are really \code{CELL} (integer) rasters with an associated "levels" table. You can also change a \code{CELL} raster to a \code{FCELL} raster by adding then subtracting a decimal value, as in \code{x - 0.1 + 0.1}. See \code{vignette("GRasters", package = "fasterRaster")}. \item The \code{rand()} function returns integer values by default. If you want non-integer values, use the tricks mentioned above to datatype non-integer values. For example, if you want uniform random values in the range between 0 and 1, use something like \verb{= float(rand(0 + 0.1, 1 + 0.1) - 0.1)}. } } From 17543905ea979cad2d9b50a88c213d6347d18119 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 22:36:08 -0500 Subject: [PATCH 072/125] Create elev_rivers.png --- articles/reference/figures/elev_rivers.png | Bin 0 -> 86526 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 articles/reference/figures/elev_rivers.png diff --git a/articles/reference/figures/elev_rivers.png b/articles/reference/figures/elev_rivers.png new file mode 100644 index 0000000000000000000000000000000000000000..ccdc41664da872aa9ad199d8aa54bd048f3c3045 GIT binary patch literal 86526 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 literal 0 HcmV?d00001 From d9a5f55f0d07743298240f1e44eadfe7ded51cef Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 22:36:11 -0500 Subject: [PATCH 073/125] Create dist_to_rivers.png --- articles/reference/figures/dist_to_rivers.png | Bin 0 -> 37319 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 articles/reference/figures/dist_to_rivers.png diff --git a/articles/reference/figures/dist_to_rivers.png b/articles/reference/figures/dist_to_rivers.png new file mode 100644 index 0000000000000000000000000000000000000000..76fd527c8eb89692d23f685c81777c3126c4f624 GIT binary patch literal 37319 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 N11Vtgs&%-K`Y*#Ef5`v< literal 0 HcmV?d00001 From 6dfa14ff8a6f449656918c625fde83142ba2a365 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 22:36:27 -0500 Subject: [PATCH 074/125] Remove all `sQuote()` --- R/00b_GSpatial_class.r | 4 ++-- R/00d_GRaster_class.r | 2 +- R/00e_GVector_class.r | 4 ++-- R/06_GRaster_functions_across_layers.r | 2 +- R/aggregate.r | 6 +++--- R/as.contour.r | 4 ++-- R/clump.r | 2 +- R/clusterPoints.r | 2 +- R/compositeRGB.r | 4 ++-- R/connectors.r | 6 +++--- R/datatype.r | 4 ++-- R/denoise_noise.r | 4 ++-- R/distance.r | 20 ++++++++++---------- R/extend.r | 6 +++--- R/extract.r | 12 ++++++------ R/fillNAs.r | 4 ++-- R/fractalRast.r | 2 +- R/fragmentation.r | 2 +- R/freq.r | 2 +- R/grid.r | 2 +- R/hillshade.r | 4 ++-- R/interpIDW.r | 6 +++--- R/interpSplines.r | 10 +++++----- R/makeSourceName.r | 2 +- R/region.r | 4 ++-- R/replaceNAs.r | 8 ++++---- R/rnormRast.r | 2 +- R/runifRast.r | 2 +- R/selectRange.r | 2 +- R/simplifyGeom.r | 2 +- R/smoothGeom.r | 4 ++-- R/trim.r | 2 +- R/vegIndex.r | 4 ++-- R/writeRaster.r | 4 ++-- R/writeVector.r | 2 +- man/fasterRaster.Rd | 2 +- 36 files changed, 77 insertions(+), 77 deletions(-) diff --git a/R/00b_GSpatial_class.r b/R/00b_GSpatial_class.r index 9f6e18d6..666ea42a 100644 --- a/R/00b_GSpatial_class.r +++ b/R/00b_GSpatial_class.r @@ -29,9 +29,9 @@ methods::setValidity("GSpatial", } else if (length(object@crs) != 1L) { "@crs can only be a single character string." } else if (!all(object@topology %in% c(NA_character_, "2D", "3D"))) { - paste0("@topology can only be a NA, ", sQuote("2D"), ", or ", sQuite("3D"), ".") + paste0("@topology can only be a NA, `2D` or `3D`.") } else if (object@topology == "3D" && any(is.na(object@zextent))) { - paste0("@topology is ", sQuote("3D"), " but @zextent has at least one NA value.") + paste0("@topology is `3D` but @zextent has at least one NA value.") } else if (!anyNA(object@zextent[1L]) & is.na(object@zextent[2L]) | (is.na(object@zextent[1L]) & !is.na(object@zextent[2L]))) { "Both values of @zextent must be NA or must be numeric values." } else if (!anyNA(object@zextent) && object@zextent[2L] < object@zextent[1L]) { diff --git a/R/00d_GRaster_class.r b/R/00d_GRaster_class.r index 167fd060..5ce4e28b 100644 --- a/R/00d_GRaster_class.r +++ b/R/00d_GRaster_class.r @@ -113,7 +113,7 @@ GRaster <- methods::setClass( methods::setValidity("GRaster", function(object) { if (!all(object@datatypeGRASS %in% c("CELL", "FCELL", "DCELL"))) { - paste0("@datatypeGRASS can only be NA, ", sQuote("CELL"), ", ", sQuote("FCELL"), ", or ", sQuote("DCELL"), ".") + paste0("@datatypeGRASS can only be NA, ``CELL`, `FCELL`, or `DCELL`.") } else if (!is.na(object@dimensions[3L]) && object@dimensions[3L] <= 0L) { "Third value in @dimensions must be NA or a positive integer." } else if (!is.na(object@resolution[3L]) && object@resolution[3L] <= 0) { diff --git a/R/00e_GVector_class.r b/R/00e_GVector_class.r index 18f27580..af1143df 100644 --- a/R/00e_GVector_class.r +++ b/R/00e_GVector_class.r @@ -31,9 +31,9 @@ methods::setValidity("GVector", if (!info$catsValid) { "Vector has invalid topology. See the *Details* section in `fast()` on how to correct topology." } else if (!all(object@geometry %in% c(NA_character_, "points", "lines", "polygons"))) { - paste0("@geometry can only be NA, ", sQuote("points"), ", ", sQuote("lines"), ", or ", sQuote("polygons"), ".") + paste0("@geometry can only be NA, `points`, `lines`, or `polygons`.") # } else if (length(unique(.vCats(object)) != object@nGeometries)) { - # "The number of @nGeometries is not the same as the number of unique ", sQuote("cat"), " values in the vector attribute table in GRASS." + # "The number of @nGeometries is not the same as the number of unique `cat` values in the vector attribute table in GRASS." # } else if (object@nGeometries > object@nSubgeometries) { # "The number of sub-geometries in @nSubgeometries must be <= the number of geometries in @nGeometries." } else if (object@nGeometries == 0L) { diff --git a/R/06_GRaster_functions_across_layers.r b/R/06_GRaster_functions_across_layers.r index 85b6214e..1dddbdd4 100644 --- a/R/06_GRaster_functions_across_layers.r +++ b/R/06_GRaster_functions_across_layers.r @@ -491,7 +491,7 @@ setMethod( } else if (return == "source") { out <- src } else { - stop("Invalid value for ", sQuote("return"), ".") + stop("Invalid value for `return`.") } out diff --git a/R/aggregate.r b/R/aggregate.r index 957ca1fd..592b5dbe 100644 --- a/R/aggregate.r +++ b/R/aggregate.r @@ -51,7 +51,7 @@ methods::setMethod( na.rm = FALSE ) { - if (any(fact <= 0)) stop("Values of ", sQuote("fact"), " must be > 0.") + if (any(fact <= 0)) stop("Values of `fact` must be > 0.") funs <- c("mean", "median", "mode", "min", "maximum", "range", "quantile", "sum", "varpop", "sdpop", "count", "diversity") fun <- omnibus::pmatchSafe(tolower(fun), funs) @@ -68,9 +68,9 @@ methods::setMethod( fun <- "stdev" } else if (fun == "quantile") { - if (is.null(prob)) stop("A value must be specified for ", sQuote("prob"), " if the aggregating function is ", sQuote("quantile"), ".") + if (is.null(prob)) stop("A value must be specified for `prob` if the aggregating function is `quantile`.") - if (prob < 0 | prob > 1) stop("Argument ", sQuote("prob"), " must be in the range [0, 1].") + if (prob < 0 | prob > 1) stop("Argument `prob` must be in the range [0, 1].") } diff --git a/R/as.contour.r b/R/as.contour.r index 19c9ec9d..61875512 100644 --- a/R/as.contour.r +++ b/R/as.contour.r @@ -24,8 +24,8 @@ setMethod( .locationRestore(x) .region(x) - if (!missing(nlevels) & !missing(levels)) stop("Please specify either ", sQuote("nlevels"), " or ", sQuote("levels"), ", but not both.") - if (missing(nlevels) & missing(levels)) stop("Please specify either ", sQuote("nlevels"), " or ", sQuote("levels"), ".") + if (!missing(nlevels) & !missing(levels)) stop("Please specify either `nlevels` or `levels`, but not both.") + if (missing(nlevels) & missing(levels)) stop("Please specify either `nlevels` or `levels`.") if (!missing(nlevels)) { mm <- minmax(x) diff --git a/R/clump.r b/R/clump.r index ad7009f7..55a1b247 100644 --- a/R/clump.r +++ b/R/clump.r @@ -22,7 +22,7 @@ methods::setMethod( signature = c(x = "GRaster"), function(x, minDiff = 0, minClumpSize = 1, diagonal = TRUE) { - if (minDiff < 0 | minDiff >= 1) stop(sQuote("minDiff"), " must be >= 0 and < 1.") + if (minDiff < 0 | minDiff >= 1) stop("Argument `minDiff` must be >= 0 and < 1.") .locationRestore(x) .region(x) diff --git a/R/clusterPoints.r b/R/clusterPoints.r index e9e8e0be..09bf5181 100644 --- a/R/clusterPoints.r +++ b/R/clusterPoints.r @@ -31,7 +31,7 @@ methods::setMethod( if (!is.null(maxDist)) { - if (is.infinite(maxDist)) stop("Argument ", sQuote("maxDist"), " cannot be infinite.") + if (is.infinite(maxDist)) stop("Argument `maxDist` cannot be infinite.") } diff --git a/R/compositeRGB.r b/R/compositeRGB.r index 9c89a5c4..dc3ae1eb 100644 --- a/R/compositeRGB.r +++ b/R/compositeRGB.r @@ -24,7 +24,7 @@ methods::setMethod( signature(r = "GRaster"), function(r, g = NULL, b = NULL, levels = 256, dither = FALSE) { - msg <- paste0("Argument ", sQuote("r"), " must have 1 band (in which case arguments ", sQuote("g"), " and ", sQuote("b"), " must also be single-layer GRasters),\n or ", sQuote("r"), " must have 3 bands (and ", sQuote("g"), " and ", sQuote("b"), " must be NULL).") + msg <- paste0("Argument `r` must have 1 band (in which case arguments `g` and `b` must also be single-layer GRasters),\n or `r` must have 3 bands (and `g` and `b` must be NULL).") if (!(nlyr(r) %in% c(1L, 3L))) stop(msg) @@ -42,7 +42,7 @@ methods::setMethod( .locationRestore(r) .region(r) - if (!(length(levels %in% c(1L, 3L)))) stop("Argument ", sQuote("levels"), " must have 1 or 3 values.") + if (!(length(levels %in% c(1L, 3L)))) stop("Argument `levels` must have 1 or 3 values.") if (length(levels) == 1L) levels <- rep(levels, 3L) src <- .makeSourceName("r_composite", "raster") diff --git a/R/connectors.r b/R/connectors.r index 83d3a3fb..447aa4bf 100644 --- a/R/connectors.r +++ b/R/connectors.r @@ -19,9 +19,9 @@ methods::setMethod( signature(x = "GVector", y = "GVector"), function(x, y, minDist = NULL, maxDist = NULL) { - if (!is.null(minDist) && minDist < 0) stop("Argument ", sQuote("minDist"), " must be positive or NULL.") - if (!is.null(maxDist) && maxDist < 0) stop("Argument ", sQuote("maxDist"), " must be positive or NULL.") - if ((!is.null(maxDist) & !is.null(maxDist)) && (minDist > maxDist)) stop("Argument ", sQuote("minDist"), " is greater than ", sQuote("maxDist"), ".") + if (!is.null(minDist) && minDist < 0) stop("Argument `minDist` must be positive or NULL.") + if (!is.null(maxDist) && maxDist < 0) stop("Argument `maxDist` must be positive or NULL.") + if ((!is.null(maxDist) & !is.null(maxDist)) && (minDist > maxDist)) stop("Argument `minDist` is greater than `maxDist`.") compareGeom(x, y) .locationRestore(x) diff --git a/R/datatype.r b/R/datatype.r index 80e17db0..447194f9 100644 --- a/R/datatype.r +++ b/R/datatype.r @@ -71,7 +71,7 @@ methods::setMethod( } else if (out[i] == "DCELL") { out[i] <- if (type == "GDAL") { "Float64" } else { "FLT8S" } } else { - warning("Values are too small/large to represent. Assigning ", sQuote("double"), " type.") + warning("Values are too small/large to represent. Assigning `double` type.") out[i] <- if (type == "GDAL") { "Float64" } else { "FLT8S" } } @@ -141,7 +141,7 @@ methods::setMethod( # # # } else if (min[i] > -1.79e+308 & max[i] < 1.79e+308) { # # # out[i] <- if (type == "GDAL") { "Float64" } else if (type == "terra") { "FLT8S" } else if (type == "GRASS") { "DCELL" } else if (type == "fasterRaster") { "double" } # # # } else { -# # # warning("Values are too small/large to represent. Assigning ", sQuote("double"), " type.") +# # # warning("Values are too small/large to represent. Assigning `double` type.") # # # out[i] <- if (type == "GDAL") { "Float64" } else if (type == "terra") { "FLT8S" } else if (type == "GRASS") { "DCELL"} else if (type == "fasterRaster") { "double" } # # # } # # # } diff --git a/R/denoise_noise.r b/R/denoise_noise.r index c9ee1b48..a4fb704d 100644 --- a/R/denoise_noise.r +++ b/R/denoise_noise.r @@ -28,11 +28,11 @@ methods::setMethod( # if (percent %% 1 != 0) { if (!omnibus::is.wholeNumber(percent)) { - warning("Argument ", sQuote("percent"), " must be an integer. Value will be rounded.") + warning("Argument `percent` must be an integer. Value will be rounded.") percent <- round(percent) } - if (percent < 50 | percent > 99) stop("Argument ", sQuote("percent"), " must be an integer in the range [50, 99].") + if (percent < 50 | percent > 99) stop("Argument `percent` must be an integer in the range [50, 99].") .locationRestore(x) .region(x) diff --git a/R/distance.r b/R/distance.r index cd81673d..0210bfd2 100644 --- a/R/distance.r +++ b/R/distance.r @@ -66,14 +66,14 @@ methods::setMethod( maxDist = NULL ) { - if (!is.null(minDist) && minDist < 0) stop("Argument ", sQuote("minDist"), " must be positive or NULL.") - if (!is.null(maxDist) && maxDist < 0) stop("Argument ", sQuote("maxDist"), " must be positive or NULL.") - if ((!is.null(maxDist) & !is.null(maxDist)) && (minDist > maxDist)) stop("Argument ", sQuote("minDist"), " is greater than ", sQuote("maxDist"), ".") + if (!is.null(minDist) && minDist < 0) stop("Argument `minDist` must be positive or NULL.") + if (!is.null(maxDist) && maxDist < 0) stop("Argument `maxDist` must be positive or NULL.") + if ((!is.null(maxDist) & !is.null(maxDist)) && (minDist > maxDist)) stop("Argument `minDist` is greater than `maxDist`.") method <- tolower(method) methods <- c("euclidean", "squared", "maximum", "manhattan", "geodesic") method <- omnibus::pmatchSafe(method, methods) - if (is.lonlat(x) & method != "geodesic") warning("Argument ", sQuote("method"), " should be ", sQuote("geodesic"), " for rasters with longitude/latitude coordinate reference systems.") + if (is.lonlat(x) & method != "geodesic") warning("Argument `method` should be `geodesic` for rasters with longitude/latitude coordinate reference systems.") .locationRestore(x) .region(x) @@ -134,13 +134,13 @@ methods::setMethod( maxDist = NULL ) { - if (!is.null(minDist) && minDist < 0) stop("Argument ", sQuote("minDist"), " must be positive or NULL.") - if (!is.null(maxDist) && maxDist < 0) stop("Argument ", sQuote("maxDist"), " must be positive or NULL.") - if ((!is.null(maxDist) & !is.null(maxDist)) && (minDist > maxDist)) stop("Argument ", sQuote("minDist"), " is greater than ", sQuote("maxDist"), ".") + if (!is.null(minDist) && minDist < 0) stop("Argument `minDist` must be positive or NULL.") + if (!is.null(maxDist) && maxDist < 0) stop("Argument `maxDist` must be positive or NULL.") + if ((!is.null(maxDist) & !is.null(maxDist)) && (minDist > maxDist)) stop("Argument `minDist` is greater than `maxDist`.") methods <- c("euclidean", "squared", "maximum", "manhattan", "geodesic") method <- omnibus::pmatchSafe(method, methods) - if (is.lonlat(x) & method != "geodesic") warning("Argument ", sQuote("method"), " should be ", sQuote("geodesic"), " for rasters with longitude/latitude coordinate reference systems.") + if (is.lonlat(x) & method != "geodesic") warning("Argument `method` should be `geodesic` for rasters with longitude/latitude coordinate reference systems.") compareGeom(x, y) .locationRestore(x) @@ -195,8 +195,8 @@ methods::setMethod( maxDist = NULL ) { - if (!is.null(minDist) && minDist < 0) stop("Argument ", sQuote("minDist"), " must be positive or NULL.") - if (!is.null(maxDist) && maxDist < 0) stop("Argument ", sQuote("maxDist"), " must be positive or NULL.") + if (!is.null(minDist) && minDist < 0) stop("Argument `minDist` must be positive or NULL.") + if (!is.null(maxDist) && maxDist < 0) stop("Argument `maxDist` must be positive or NULL.") if ((!is.null(maxDist) & !is.null(maxDist)) && (minDist > maxDist)) stop("Argument `minDist` is greater than `maxDist`.") compareGeom(x, y) diff --git a/R/extend.r b/R/extend.r index 481853fd..5b53c945 100644 --- a/R/extend.r +++ b/R/extend.r @@ -90,14 +90,14 @@ methods::setMethod( if (inherits(y, "numeric")) { if (!(length(y) %in% c(1L, 2L, 4L))) { - stop("Argument ", sQuote("y"), " is invalid.") + stop("Argument `y` is invalid.") } else if (length(y) == 1L) { y <- rep(y, 4L) } else if (length(y) == 2L) { y <- rep(y, each = 2L) } - if (any(!omnibus::is.wholeNumber(y))) stop("Values of ", sQuote("y"), " must be numeric integers.") + if (any(!omnibus::is.wholeNumber(y))) stop("Values of `y` must be numeric integers.") if (any(y < 0L)) y[y < 0L] <- 0L } else { @@ -108,7 +108,7 @@ methods::setMethod( } else if (inherits(y, "GSpatial")) { extent <- ext(y, vector = TRUE) } else { - stop("Argument ", sQuote("y"), " is invalid.") + stop("Argument `y` is invalid.") } # by how many rows and columns do we grow? diff --git a/R/extract.r b/R/extract.r index e2d61c19..e0e35be9 100644 --- a/R/extract.r +++ b/R/extract.r @@ -145,8 +145,8 @@ methods::setMethod( if (any(fun == "quantile")) { - if (length(prob) > 1L) stop("Argument ", sQuote("prob"), " can only have one value.") - if (prob > 1 | prob < 0) stop("Argument ", sQuote("prob"), " must be in the range [0, 1].") + if (length(prob) > 1L) stop("Argument `prob` can only have one value.") + if (prob > 1 | prob < 0) stop("Argument `prob` must be in the range [0, 1].") prob <- round(100 * prob) } @@ -374,8 +374,8 @@ methods::setMethod( cats = TRUE ) { - if (ncol(y) < 2L) stop("Argument ", sQuote("y"), " must have at least two columns. The first must represent longitude and the second latitude.") - if (ncol(y) > 2L) warning("Argument ", sQuote("y"), " has more than two columns. The first will be assumed to represent longitude and the second latitude.") + if (ncol(y) < 2L) stop("Argument `y` must have at least two columns. The first must represent longitude and the second latitude.") + if (ncol(y) > 2L) warning("Argument `y` has more than two columns. The first will be assumed to represent longitude and the second latitude.") y <- terra::vect(y, geom = colnames(y)[1L:2L], crs = crs(x), keepgeom = FALSE) y <- fast(y) @@ -435,7 +435,7 @@ methods::setMethod( cats = TRUE ) { - if (length(y) != 2L) stop("Argument ", sQuote("y"), " must have two values, longitude and latitude.") + if (length(y) != 2L) stop("Argument `y` must have two values, longitude and latitude.") y <- cbind(y) colnames(y) <- c("x", "y") extract(x = x, y = y, xy = xy, cats = cats) @@ -451,7 +451,7 @@ methods::setMethod( signature = c(x = "GVector", y = "GVector"), function(x, y, xy = FALSE) { - if (geomtype(y) != "points") stop("Argument", sQuote("y"), " must be a points vector.") + if (geomtype(y) != "points") stop("Argument`y` must be a points vector.") if (is.3d(y)) warning("Coordinates in the z-dimension will be ignored.") .locationRestore(x) diff --git a/R/fillNAs.r b/R/fillNAs.r index 78487590..bf15ca02 100644 --- a/R/fillNAs.r +++ b/R/fillNAs.r @@ -40,9 +40,9 @@ methods::setMethod( if (length(max) < nLayers) max <- rep(max, length.out = nLayers) mm <- minmax(x) - if (any(min > mm["min", ])) warning("The ", sQuote("min"), " value is greater than the actual minimum value in at least one raster layer.\n Observed values will be truncated.") + if (any(min > mm["min", ])) warning("The `min` value is greater than the actual minimum value in at least one raster layer.\n Observed values will be truncated.") - if (any(max > mm["max", ])) warning("The ", sQuote("max"), " value is less than the actual maximum value in at least one raster layer.\n Observed values will be truncated.") + if (any(max > mm["max", ])) warning("The `max` value is less than the actual maximum value in at least one raster layer.\n Observed values will be truncated.") srcs <- .makeSourceName("r_fillnulls", "raster", nLayers) diff --git a/R/fractalRast.r b/R/fractalRast.r index 1ebaa11f..1ad8dfea 100644 --- a/R/fractalRast.r +++ b/R/fractalRast.r @@ -28,7 +28,7 @@ methods::setMethod( sigma = 1, dimension = 2.05) { - if (any(dimension <= 2) | any(dimension >= 3)) stop("Argument ", sQuote("dimension"), " must be in the range (2, 3).") + if (any(dimension <= 2) | any(dimension >= 3)) stop("Argument `dimension` must be in the range (2, 3).") mu <- rep(mu, length.out = n) sigma <- rep(sigma, length.out = n) diff --git a/R/fragmentation.r b/R/fragmentation.r index c842350d..e38ecbca 100644 --- a/R/fragmentation.r +++ b/R/fragmentation.r @@ -42,7 +42,7 @@ methods::setMethod( function(x, w = 3, undet = "undetermined", none = NA, na.rm = TRUE, cores = faster("cores"), verbose = TRUE) { # errors? - if (!omnibus::is.wholeNumber(w) || w < 1L || w %% 2 == 0) stop("Argument ", sQuote("w"), " must be an odd, positive integer.") + if (!omnibus::is.wholeNumber(w) || w < 1L || w %% 2 == 0) stop("Argument `w` must be an odd, positive integer.") undet <- omnibus::pmatchSafe(undet, c("undetermined", "perforated", "edge"), nmax = 1L) # setup diff --git a/R/freq.r b/R/freq.r index 85e90f55..d93092c5 100644 --- a/R/freq.r +++ b/R/freq.r @@ -55,7 +55,7 @@ methods::setMethod( nLayers <- length(src) - if (any(dtype != "CELL") & bins <= 0) stop("Argument ", sQuote("bins"), " must be a positive integer.") + if (any(dtype != "CELL") & bins <= 0) stop("Argument `bins` must be a positive integer.") # get values for each raster out <- list() diff --git a/R/grid.r b/R/grid.r index f5d4ba0c..9fb06c51 100644 --- a/R/grid.r +++ b/R/grid.r @@ -49,7 +49,7 @@ methods::setMethod( # calculate cell number and re-calibrate GRASS region if (is.null(nx) & is.null(ny)) { - stop("At least one of ", sQuote("nx"), " or ", sQuote("ny"), " must be defined.") + stop("At least one of `nx` or `ny` must be defined.") } else if (!is.null(nx) & is.null(ny)) { if (use == "number") { diff --git a/R/hillshade.r b/R/hillshade.r index 8a4b47a6..e65990a0 100644 --- a/R/hillshade.r +++ b/R/hillshade.r @@ -24,8 +24,8 @@ methods::setMethod( signature = c(x = "GRaster"), definition = function(x, angle = 45, direction = 0, zscale = 1) { - if (angle < 0 | angle > 90) stop("Argument ", sQuote("angle"), " must be in the range [0, 90].") - if (direction < 0 | direction > 360) stop("Argument ", sQuote("angle"), " must be in the range [0, 360].") + if (angle < 0 | angle > 90) stop("Argument `angle` must be in the range [0, 90].") + if (direction < 0 | direction > 360) stop("Argument `angle` must be in the range [0, 360].") .locationRestore(x) .region(x) diff --git a/R/interpIDW.r b/R/interpIDW.r index 35bdbb23..a5596e15 100644 --- a/R/interpIDW.r +++ b/R/interpIDW.r @@ -24,7 +24,7 @@ methods::setMethod( signature = c(x = "GVector", y = "GRaster"), function(x, y, field, nPoints = Inf, power = 2) { - if (geomtype(x, grass = TRUE) != "point") stop("Argument ", sQuote("x"), " must be a points GVector.") + if (geomtype(x, grass = TRUE) != "point") stop("Argument `x` must be a points GVector.") compareGeom(x, y) .locationRestore(x) @@ -34,14 +34,14 @@ methods::setMethod( # copy values field to x if (is.null(field) & is.2d(x)) { - stop("Argument ", sQuote("field"), " cannot be NULL if ", sQuote("x"), " is a 2-dimensional points GVector.") + stop("Argument `field` cannot be NULL if `x` is a 2-dimensional points GVector.") } else if (is.numeric(field) | is.integer(field)) { field <- names(x)[field] } if (!is.null(field)) { - if (anyNA(x@table[[field]])) stop("The column ", sQuote(field), " has at least one NA in it. NAs are not permissible.") + if (anyNA(x@table[[field]])) stop("The column `field` has at least one NA in it. NAs are not permissible.") cats <- .vCats(x, db = FALSE) db <- data.table::data.table(frid = cats, TEMPTEMP_ = x@table[[field]]) diff --git a/R/interpSplines.r b/R/interpSplines.r index 6fe3b5a3..09cb387f 100644 --- a/R/interpSplines.r +++ b/R/interpSplines.r @@ -117,16 +117,16 @@ methods::setMethod( ) { if (!is.null(lambda)) { - if (lambda <= 0) stop("Argument ", sQuote("lambda"), " must be NULL or > 0.") + if (lambda <= 0) stop("Argument `lambda` must be NULL or > 0.") } if (!is.null(lambda)) interpolate <- TRUE - if (geomtype(x, grass = TRUE) != "point") stop("Argument ", sQuote("x"), " must be a points GVector.") + if (geomtype(x, grass = TRUE) != "point") stop("Argument `x` must be a points GVector.") if (inherits(y, "GVector")) { - if (geomtype(y, grass = TRUE) != "point") stop("Argument ", sQuote("y"), " must be a points GVector or a GRaster.") + if (geomtype(y, grass = TRUE) != "point") stop("Argument `y` must be a points GVector or a GRaster.") } @@ -139,14 +139,14 @@ methods::setMethod( # copy values field to x if (is.null(field) & is.2d(x)) { - stop("Argument ", sQuote("field"), " cannot be NULL if ", sQuote("x"), " is a 2-dimensional points GVector.") + stop("Argument `field` cannot be NULL if `x` is a 2-dimensional points GVector.") } else if (is.numeric(field) | is.integer(field)) { field <- names(x)[field] } if (!is.null(field)) { - if (anyNA(x@table[[field]])) stop("The column ", sQuote(field), " has at least one NA in it. NAs are not permissible.") + if (anyNA(x@table[[field]])) stop("The column `field` has at least one NA in it. NAs are not permissible.") cats <- .vCats(x, db = FALSE) db <- data.table::data.table(frid = cats, TEMPTEMP_ = x@table[[field]]) diff --git a/R/makeSourceName.r b/R/makeSourceName.r index 0ff97b40..5175783b 100644 --- a/R/makeSourceName.r +++ b/R/makeSourceName.r @@ -13,7 +13,7 @@ #' @keywords internal .makeSourceName <- function(x = NULL, type = NULL, n = 1L, name = NULL) { - if (is.null(x) & is.null(type)) stop("Both ", sQuote("x"), " and ", sQuote("type"), " cannot be ", dQuote("NULL"), " at the same time.") + if (is.null(x) & is.null(type)) stop("Both `x` and `type` cannot be NULL at the same time.") type <- tolower(type) diff --git a/R/region.r b/R/region.r index a5c062d9..489db362 100644 --- a/R/region.r +++ b/R/region.r @@ -314,10 +314,10 @@ methods::setMethod( if (!is.null(trim)) { trimToTopo <- topology(trim) - if (any(!(topo %in% trimToTopo))) stop("Topology of ", sQuote("trim"), " does not match topology of ", sQuote("x"), ".") + if (any(!(topo %in% trimToTopo))) stop("Topology of `trim` does not match topology of `x`.") trim <- sources(trim) - if (length(trim) != 1L) stop("Argument ", sQuote("trim"), " can have only one layer.") + if (length(trim) != 1L) stop("Argument `trim` can have only one layer.") } diff --git a/R/replaceNAs.r b/R/replaceNAs.r index f07f1222..16af5830 100644 --- a/R/replaceNAs.r +++ b/R/replaceNAs.r @@ -20,7 +20,7 @@ methods::setMethod( signature = c(x = "data.frame"), function(x, replace, cols = NULL) { - if (length(replace) != 1L) stop("Argument ", sQuote("replace"), " must be a single value.") + if (length(replace) != 1L) stop("Argument `replace` must be a single value.") if (is.null(cols)) cols <- seq_len(ncol(x)) @@ -49,7 +49,7 @@ methods::setMethod( signature = c(x = "matrix"), function(x, replace, cols = NULL) { - if (length(replace) != 1L) stop("Argument ", sQuote("replace"), " must be a single value.") + if (length(replace) != 1L) stop("Argument `replace` must be a single value.") if (is.null(cols)) cols <- seq_len(ncol(x)) @@ -78,7 +78,7 @@ methods::setMethod( signature = c(x = "data.table"), function(x, replace, cols = NULL) { - if (length(replace) != 1L) stop("Argument ", sQuote("replace"), " must be a single value.") + if (length(replace) != 1L) stop("Argument `replace` must be a single value.") if (is.null(cols)) cols <- seq_len(ncol(x)) @@ -151,7 +151,7 @@ methods::setMethod( #' @noRd .replaceNAsAtomic <- function(x, replace) { - if (length(replace) != 1L) stop("Argument ", sQuote("replace"), " must be a single value.") + if (length(replace) != 1L) stop("Argument `replace` must be a single value.") x[is.na(x)] <- replace x diff --git a/R/rnormRast.r b/R/rnormRast.r index 598be641..fc5f1b6a 100644 --- a/R/rnormRast.r +++ b/R/rnormRast.r @@ -28,7 +28,7 @@ methods::setMethod( sigma = 1, seed = NULL) { - if (!is.null(seed)) if (length(seed) != n) stop("You must provide one value of ", sQuote("seed"), " per raster, or set it to NULL.") + if (!is.null(seed)) if (length(seed) != n) stop("You must provide one value of `seed` per raster, or set it to NULL.") mu <- rep(mu, length.out = n) sigma <- rep(sigma, length.out = n) diff --git a/R/runifRast.r b/R/runifRast.r index fe5b089d..16b2e84e 100644 --- a/R/runifRast.r +++ b/R/runifRast.r @@ -30,7 +30,7 @@ methods::setMethod( seed = NULL ) { - if (!is.null(seed)) if (length(seed) != n) stop("You must provide one value of ", sQuote("seed"), " per raster, or set it to NULL.") + if (!is.null(seed)) if (length(seed) != n) stop("You must provide one value of `seed` per raster, or set it to NULL.") .locationRestore(x) .region(x) diff --git a/R/selectRange.r b/R/selectRange.r index f64a0a36..be531298 100644 --- a/R/selectRange.r +++ b/R/selectRange.r @@ -22,7 +22,7 @@ methods::setMethod( .region(x) if (nlyr(y) > 1L) { - warning("The ", sQuote("y"), " raster has more than one layer. On the first will be used.") + warning("The`y` raster has more than one layer. On the first will be used.") y <- y[[1L]] } if (datatype(y, "GRASS") != "CELL") y <- round(y) diff --git a/R/simplifyGeom.r b/R/simplifyGeom.r index 9a6c062e..ddb045eb 100644 --- a/R/simplifyGeom.r +++ b/R/simplifyGeom.r @@ -60,7 +60,7 @@ methods::setMethod( "reumann" } - if (method == "reumann" && (prop < 0 | prop > 1)) stop("Argument ", sQuote("prop"), " must be in the range [0, 1].") + if (method == "reumann" && (prop < 0 | prop > 1)) stop("Argument `prop` must be in the range [0, 1].") src <- .makeSourceName("generalized", "vect") args <- list( diff --git a/R/smoothGeom.r b/R/smoothGeom.r index 0f9824ce..fe719377 100644 --- a/R/smoothGeom.r +++ b/R/smoothGeom.r @@ -35,7 +35,7 @@ methods::setMethod( zext <- diff(zext) dist <- 0.02 * min(xext, yext, zext, na.rm=TRUE) } else { - if (dist < 0) stop("Argument ", sQuote("dist"), " must be > 0.") + if (dist < 0) stop("Argument `dist` must be > 0.") } .locationRestore(x) @@ -43,7 +43,7 @@ methods::setMethod( method <- tolower(method) method <- omnibus::pmatchSafe(method, c("hermite", "chaiken")) - if (method == "hermite" && angle < 0) stop("Argument ", sQuote("angle"), " must be >0.") + if (method == "hermite" && angle < 0) stop("Argument `angle` must be >0.") src <- .makeSourceName("generalized", "vect") args <- list( diff --git a/R/trim.r b/R/trim.r index 7b8625c4..358fa97b 100644 --- a/R/trim.r +++ b/R/trim.r @@ -22,7 +22,7 @@ methods::setMethod( signature = c(x = "GRaster"), function(x, pad = 0) { - if (pad < 0) stop("Argument ", sQuote("pad"), " must be >= 0.") + if (pad < 0) stop("Argument `pad` must be >= 0.") .locationRestore(x) .region(x) diff --git a/R/vegIndex.r b/R/vegIndex.r index 3e655ff1..0d08637c 100644 --- a/R/vegIndex.r +++ b/R/vegIndex.r @@ -51,11 +51,11 @@ methods::setMethod( if (is.null(bits)) { - if (any(mm > 1)) stop("Raster values should be between 0 and 1 if ", sQuote("bits"), " is NULL.") + if (any(mm > 1)) stop("Raster values should be between 0 and 1 if `bits` is NULL.") } else { - if (!all(is.int(x))) stop("If argument ", sQuote("bits"), " is defined, then rasters must be of type ", sQuote("integer"), ".") + if (!all(is.int(x))) stop("If argument `bits` is defined, then rasters must be of type `integer`.") maxVal <- 2^bits if (any(mm > maxVal)) stop("Maximum value for ", bits, " bits is ", maxVal, ".\n At least one raster surpasses this.") diff --git a/R/writeRaster.r b/R/writeRaster.r index 04482558..233fdf94 100644 --- a/R/writeRaster.r +++ b/R/writeRaster.r @@ -112,7 +112,7 @@ setMethod( ### going to overwrite anything? if (!overwrite) { - if (file.exists(filename)) stop("File already exists and ", sQuote("overwrite"), " is FALSE:\n ", filename) + if (file.exists(filename)) stop("File already exists and `overwrite` is FALSE:\n ", filename) } ### format @@ -235,7 +235,7 @@ setMethod( # if (any(!(datatype(x, "GDAL") %in% datatype))) { # flags <- c(flags, "f") - # if (warn) warning("Argument ", sQuote("datatype"), " does not match the data type of the raster. Data may be lost.") + # if (warn) warning("Argument `datatype` does not match the data type of the raster. Data may be lost.") # } if (!("createopt" %in% names(dots))) createopt <- NULL diff --git a/R/writeVector.r b/R/writeVector.r index 5b70bdc6..13cd796d 100644 --- a/R/writeVector.r +++ b/R/writeVector.r @@ -53,7 +53,7 @@ setMethod( ) { ### going to overwrite anything? - if (!overwrite && file.exists(filename)) stop(paste0("File already exists and ", sQuote("overwrite"), " is FALSE:\n ", filename)) + if (!overwrite && file.exists(filename)) stop(paste0("File already exists and `overwrite` is FALSE:\n ", filename)) .locationRestore(x) diff --git a/man/fasterRaster.Rd b/man/fasterRaster.Rd index 450394c0..209d1c8e 100644 --- a/man/fasterRaster.Rd +++ b/man/fasterRaster.Rd @@ -389,7 +389,7 @@ Operations on \code{GRaster}s \seealso{ Useful links: \itemize{ - \item \url{http://www.earthSkySea.org} + \item \url{https://github.com/adamlilith/fasterRaster} \item \url{https://adamlilith.github.io/fasterRaster/} \item Report bugs at \url{https://github.com/adamlilith/fasterRaster/issues} } From 840d00f70abd112e9dedfd17cd59c3f95a15fd2b Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 22:38:17 -0500 Subject: [PATCH 075/125] Update NEWS.md --- NEWS.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 43d9fa2e..47ef1848 100644 --- a/NEWS.md +++ b/NEWS.md @@ -23,11 +23,13 @@ o `regress()` replaces individual functions `intercept()`, `slope()`, `r2()`, an o `subset()` subsets layers of a `GRaster` or rows/geometries of a `GVector`. o `segregate()` creates one layer per unique value in an input `GRaster`, with values in the output coded 1 or 0 depending on whether cells in the input had the unique value or not. -### Bug fixes +### Bug and issue fixes +o `appFuns()` opens a a **shiny** table with `app()` functions. o `categories()` correctly assigns active category column. o `simplifyGeom()` works for 2-dimensional `GVector`s. o `rasterize()` works when `by` is not `NULL`. -o `.layerIndex()` (called by `catergories()` and other functions related to catrgorical `GRaster`s) does not fail. +o `.layerIndex()` (called by `catergories()` and other functions related to categorical `GRaster`s) does not fail. +o Removed all instances of `sQuote()`. # fasterRaster 8.3.0.7026 (2024-09-22) From f913fb2ec0afd1e42df420d43bc534693fe6fdde Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 22:50:45 -0500 Subject: [PATCH 076/125] Update .Rbuildignore --- .Rbuildignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.Rbuildignore b/.Rbuildignore index a8a3f1dd..faa1d29f 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -19,3 +19,5 @@ CODE_OF_CONDUCT.md ^\vignettes\^junk junk fasterRaster_workspace.code-workspace +^doc$ +^Meta$ From d0011fe670ac3e3a3d3957ef79f36e80d8bc5b92 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sat, 5 Oct 2024 22:50:48 -0500 Subject: [PATCH 077/125] Update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index b285dd9a..f397d85d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ docs inst/doc junk /vignettes/junk +/doc/ +/Meta/ From 4fbfbdb0ed0e52cb6c79a0e7f6a92fbf94b4aa3d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Sun, 6 Oct 2024 00:56:48 -0500 Subject: [PATCH 078/125] Update fasterRaster.r --- R/fasterRaster.r | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/fasterRaster.r b/R/fasterRaster.r index cb67fe9e..e74560f5 100644 --- a/R/fasterRaster.r +++ b/R/fasterRaster.r @@ -3,9 +3,9 @@ #' @description **fasterRaster**: Processing of large-in-memory/-on disk rasters and spatial vectors in using **GRASS GIS**. Most functions in the **terra** and **sf** packages are recreated. Processing of medium-sized and smaller spatial objects will nearly always be faster using **terra** or **sf**. To use most of the functions you must have the stand-alone version of **GRASS GIS** version 8.3 or higher (not the **OSGeoW4** installer version). Note that due to differences in how **GRASS**, **terra**, and **sf** were implemented, results will not always be strictly comparable between functions for the same operation. #' #' ## Most useful tutorials and functions: -#' * The quick-start guide to getting started with **fasterRaster** , accessible using `vignette("fasterRaster", package = "fasterRaster")` -#' * The vignette on types of `GRaster`s, accessible using `vignette("GRasters", package = "fasterRaster")` -#' * The vignette on how to speed up **fasterRaster**, accessible using `vignette("faster_fasterRaster", package = "fasterRaster")` +#' * The quick-start guide to getting started with **fasterRaster**, accessible using `vignette("fasterRaster", package = "fasterRaster")` +#' * Types of `GRaster`s, accessible using `vignette("GRasters", package = "fasterRaster")` +#' * How to speed up **fasterRaster**, accessible using `vignette("faster_fasterRaster", package = "fasterRaster")` #' * [faster()]: Set the directory where **GRASS** is installed on your system, and set or get other package-wide options. This function must be run once before using most **fasterRaster** functions. #' * [fast()]: Convert a `SpatRaster`, `SpatVector`, or `sf` vector to **fasterRaster**'s raster format (`GRaster`s) or vector format (`GVector`s), or load one from a file #' * [rast()], [vect()], and [st_as_sf()]: Convert `GRaster`s and `GVector`s to `SpatRaster`s, `SpatVector`s, or `sf` vectors From feb9de946f8d785f483fd5522cc9eef60582588d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 10:29:53 -0500 Subject: [PATCH 079/125] Fix damned vignettes issues * Vignettes did not show with `vigtnette()` * External images in vignettes did not show --- inst/pkgdown.yml | 2 +- man/fasterRaster.Rd | 6 +++--- vignettes/fasterRaster.Rmd | 6 +++--- vignettes/images/dist_to_rivers.png | Bin 0 -> 37319 bytes vignettes/images/elev_rivers.png | Bin 0 -> 86526 bytes 5 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 vignettes/images/dist_to_rivers.png create mode 100644 vignettes/images/elev_rivers.png diff --git a/inst/pkgdown.yml b/inst/pkgdown.yml index 21261531..d40e95d8 100644 --- a/inst/pkgdown.yml +++ b/inst/pkgdown.yml @@ -8,7 +8,7 @@ articles: hidden_functions: hidden_functions.html projects_mapsets: projects_mapsets.html regions: regions.html -last_built: 2024-10-05T05:30Z +last_built: 2024-10-07T14:52Z urls: reference: https://github.com/adamlilith/fasterRaster/reference article: https://github.com/adamlilith/fasterRaster/articles diff --git a/man/fasterRaster.Rd b/man/fasterRaster.Rd index 209d1c8e..c8c2b08a 100644 --- a/man/fasterRaster.Rd +++ b/man/fasterRaster.Rd @@ -9,9 +9,9 @@ \strong{fasterRaster}: Processing of large-in-memory/-on disk rasters and spatial vectors in using \strong{GRASS GIS}. Most functions in the \strong{terra} and \strong{sf} packages are recreated. Processing of medium-sized and smaller spatial objects will nearly always be faster using \strong{terra} or \strong{sf}. To use most of the functions you must have the stand-alone version of \strong{GRASS GIS} version 8.3 or higher (not the \strong{OSGeoW4} installer version). Note that due to differences in how \strong{GRASS}, \strong{terra}, and \strong{sf} were implemented, results will not always be strictly comparable between functions for the same operation. \subsection{Most useful tutorials and functions:}{ \itemize{ -\item The quick-start guide to getting started with \strong{fasterRaster} , accessible using \code{vignette("fasterRaster", package = "fasterRaster")} -\item The vignette on types of \code{GRaster}s, accessible using \code{vignette("GRasters", package = "fasterRaster")} -\item The vignette on how to speed up \strong{fasterRaster}, accessible using \code{vignette("faster_fasterRaster", package = "fasterRaster")} +\item The quick-start guide to getting started with \strong{fasterRaster}, accessible using \code{vignette("fasterRaster", package = "fasterRaster")} +\item Types of \code{GRaster}s, accessible using \code{vignette("GRasters", package = "fasterRaster")} +\item How to speed up \strong{fasterRaster}, accessible using \code{vignette("faster_fasterRaster", package = "fasterRaster")} \item \code{\link[=faster]{faster()}}: Set the directory where \strong{GRASS} is installed on your system, and set or get other package-wide options. This function must be run once before using most \strong{fasterRaster} functions. \item \code{\link[=fast]{fast()}}: Convert a \code{SpatRaster}, \code{SpatVector}, or \code{sf} vector to \strong{fasterRaster}'s raster format (\code{GRaster}s) or vector format (\code{GVector}s), or load one from a file \item \code{\link[=rast]{rast()}}, \code{\link[=vect]{vect()}}, and \code{\link[=st_as_sf]{st_as_sf()}}: Convert \code{GRaster}s and \code{GVector}s to \code{SpatRaster}s, \code{SpatVector}s, or \code{sf} vectors diff --git a/vignettes/fasterRaster.Rmd b/vignettes/fasterRaster.Rmd index 17b6a9a9..af2c6ee5 100644 --- a/vignettes/fasterRaster.Rmd +++ b/vignettes/fasterRaster.Rmd @@ -153,7 +153,7 @@ You can do operations on `GRaster`s and `GVector`s as if they were `SpatRaster`s plot(elev) plot(rivers, col = 'lightblue', add = TRUE) ``` -![](man/figures/elev_rivers.png) +![Elevation and rivers](./images/elev_rivers.png) You can use mathematical operators and functions: @@ -191,7 +191,7 @@ max. value : 2.75587485567249 You can also use the many **fasterRaster** functions. In general, these functions have the same names as their **terra** counterparts and often the same arguments. Note that even many **terra** and **fasterRaster** functions have the same name, they do not necessarily produce the exact same output. Much care has been taken to ensure they do, but sometimes there are multiple ways to do the same task, so choices made by the authors of **terra** and **GRASS** can lead to differences. -The following code creates a a) raster where cell values reflect the distance between them and the nearest river; b) creates a buffer around the rivers; then c) plots the output: +The following code 1) creates a raster where cell values reflect the distance between them and the nearest river; b) makes a buffer around the rivers; then c) plots the output: ```{r distance_buffers, eval = FALSE} dist <- distance(elev, rivers) dist @@ -224,7 +224,7 @@ plot(dist) plot(rivers, col = 'lightblue', add = TRUE) plot(river_buff, border = 'white', add = TRUE) ``` -![](man/figures/dist_to_rivers.png) +![Distance between each cell and nearest major river](./images/dist_to_rivers.png) And that's how you get started! Now that you have a raster and a vector in your **fasterRaster** "location", you can start doing manipulations and analyses using any of the **fasterRaster** functions! To see an annotated list of these functions, use `?fasterRaster`. diff --git a/vignettes/images/dist_to_rivers.png b/vignettes/images/dist_to_rivers.png new file mode 100644 index 0000000000000000000000000000000000000000..76fd527c8eb89692d23f685c81777c3126c4f624 GIT binary patch literal 37319 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 N11Vtgs&%-K`Y*#Ef5`v< literal 0 HcmV?d00001 diff --git a/vignettes/images/elev_rivers.png b/vignettes/images/elev_rivers.png new file mode 100644 index 0000000000000000000000000000000000000000..ccdc41664da872aa9ad199d8aa54bd048f3c3045 GIT binary patch literal 86526 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 literal 0 HcmV?d00001 From 5cf372cebabc46409a8ea77026dc67828ee20398 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 22:33:26 -0500 Subject: [PATCH 080/125] Add `grassHelp()` --- R/fasterRaster.r | 3 ++- R/grassHelp.r | 37 +++++++++++++++++++++++++++++++++++++ _pkgdown.yml | 3 ++- man/examples/ex_grassHelp.r | 13 +++++++++++++ man/fasterRaster.Rd | 3 ++- 5 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 R/grassHelp.r create mode 100644 man/examples/ex_grassHelp.r diff --git a/R/fasterRaster.r b/R/fasterRaster.r index e74560f5..8254f616 100644 --- a/R/fasterRaster.r +++ b/R/fasterRaster.r @@ -280,7 +280,8 @@ #' ## General purpose functions #' * [compareGeom()]: Determine if geographic metadata is same between `GRaster`s and/or `GVector`s #' * [dropRows()]: Remove rows from a `data.frame` or `data.table` -#' * [grass()]: Start the **GRASS** GUI (not recommended for most users!!!) +#' * [grassGUI()]: Start the **GRASS** GUI (not recommended for most users!!!) +#' * [grassHelp()]: Open the help page for a **GRASS** module. #' * [grassInfo()]: **GRASS** version and citation #' * [grassStarted()]: Has a connection **GRASS** been made within the current **R** session? #' * [mow()]: Remove unused rasters and vectors from the **GRASS** cache diff --git a/R/grassHelp.r b/R/grassHelp.r new file mode 100644 index 00000000..72354d75 --- /dev/null +++ b/R/grassHelp.r @@ -0,0 +1,37 @@ +#' Open the help page for a GRASS module +#' +#' @description This function opens the manual page for a **GRASS** module (function) in your browser. +#' +#' @param x Character: Any of: +#' * The name of a **GRASS** module (e.g., `"r.mapcalc"`). +#' * `"type"`: Display a page wherein modules are classified by types. +#' * `"topics"`: Display an index of topics. +#' +#' @param online If `FALSE` (default), show the manual page that is included with your installation of **GRASS**. If `FALSE`, show the manual page online (requires an Internet connection). +#' +#' @returns Nothing (opens a web page). +#' +#' @example man/examples/ex_grassHelp.r +#' +#' @aliases grassHelp +#' @rdname grassHelp +#' @export +grassHelp <- function(x, online = FALSE) { + + x <- tolower(x) + if (length(x) != 1L) stop("Only one help page can be opened at a time.") + + args <- list( + cmd = "g.manual", + flags = .quiet() + ) + + if (x == "type") { + args$flags <- c(args$flags, "i") + } else if (x == "index") { + args$flags <- c(args$flags, "t") + } + + do.call(rgrass::execGRASS, args = args) + +} diff --git a/_pkgdown.yml b/_pkgdown.yml index 4509e5ff..53320789 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -377,7 +377,8 @@ reference: - contents: - compareGeom - dropRows - - grass + - grassGUI + - grassHelp - grassInfo - grassStarted - mow diff --git a/man/examples/ex_grassHelp.r b/man/examples/ex_grassHelp.r new file mode 100644 index 00000000..0015bc1c --- /dev/null +++ b/man/examples/ex_grassHelp.r @@ -0,0 +1,13 @@ +if (grassStarted() & interactive()) { + +# Open help pages for `r.mapcalc` and `r.sun`: +grassHelp("r.mapcalc") +grassHelp("r.sun") + +# Open topics page: +grassHelp("topics") + +# Open index page: +grassHelp("index") + +} diff --git a/man/fasterRaster.Rd b/man/fasterRaster.Rd index c8c2b08a..2f9763be 100644 --- a/man/fasterRaster.Rd +++ b/man/fasterRaster.Rd @@ -337,7 +337,8 @@ Operations on \code{GRaster}s \itemize{ \item \code{\link[=compareGeom]{compareGeom()}}: Determine if geographic metadata is same between \code{GRaster}s and/or \code{GVector}s \item \code{\link[=dropRows]{dropRows()}}: Remove rows from a \code{data.frame} or \code{data.table} -\item \code{\link[=grass]{grass()}}: Start the \strong{GRASS} GUI (not recommended for most users!!!) +\item \code{\link[=grassGUI]{grassGUI()}}: Start the \strong{GRASS} GUI (not recommended for most users!!!) +\item \code{\link[=grassHelp]{grassHelp()}}: Open the help page for a \strong{GRASS} module. \item \code{\link[=grassInfo]{grassInfo()}}: \strong{GRASS} version and citation \item \code{\link[=grassStarted]{grassStarted()}}: Has a connection \strong{GRASS} been made within the current \strong{R} session? \item \code{\link[=mow]{mow()}}: Remove unused rasters and vectors from the \strong{GRASS} cache From 1dcd581a7d579bfa64de0428e0d4ef5fc1236f5a Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 22:34:04 -0500 Subject: [PATCH 081/125] Change `grass()` to `grassGUI()` --- R/{grass.r => grassGUI.r} | 10 +++++----- man/examples/ex_grassGUI.r | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) rename R/{grass.r => grassGUI.r} (91%) create mode 100644 man/examples/ex_grassGUI.r diff --git a/R/grass.r b/R/grassGUI.r similarity index 91% rename from R/grass.r rename to R/grassGUI.r index c2e900bf..6cd7b917 100644 --- a/R/grass.r +++ b/R/grassGUI.r @@ -8,13 +8,13 @@ #' #' @seealso [mow()] #' -#' @example man/examples/ex_grass.r +#' @example man/examples/ex_grassGUI.r #' -#' @aliases grass -#' @rdname grass -#' @exportMethod grass +#' @aliases grassGUI +#' @rdname grassGUI +#' @exportMethod grassGUI methods::setMethod( - f = "grass", + f = "grassGUI", signature = c(x = "missing"), function() { if (grassStarted()) { diff --git a/man/examples/ex_grassGUI.r b/man/examples/ex_grassGUI.r new file mode 100644 index 00000000..cfa1aac9 --- /dev/null +++ b/man/examples/ex_grassGUI.r @@ -0,0 +1,7 @@ +if (grassStarted()) { + +# Starting the GRASS GUI and making changes to rasters or vectors can "break" +# the GRasters and GVectors they are associated with in R. +if (interactive() & FALSE) grassGUI() + +} From bbaf641d703405d9571d9436cec641027cf389b1 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 22:34:25 -0500 Subject: [PATCH 082/125] Fix bug when `fun = "regular"` --- R/init.r | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/R/init.r b/R/init.r index 9b43edaa..f39913f0 100644 --- a/R/init.r +++ b/R/init.r @@ -30,7 +30,7 @@ methods::setMethod( signature = c(x = "GRaster"), function(x, fun, odd = TRUE, vals = c(0, 1)) { - funs <- c("x", "y", "row", "col", "chess") + funs <- c("x", "y", "row", "col", "chess", "regular") fun <- omnibus::pmatchSafe(fun, funs, n = 1) # if (fun %in% c("chess", "regular") && (!omnibus::is.wholeNumber(every) | every < 1)) stop("The value of `every` must be a whole number > 0.") @@ -45,11 +45,7 @@ methods::setMethod( srcs <- .makeSourceName(paste0("init_", fun), "raster", nLayers) for (i in seq_len(nLayers)) { - if (fun != "chess") { - - ex <- paste0(srcs[i], " = ", fun, "()") - - } else if (fun == "chess") { + if (fun == "chess") { if (odd) { ex <- paste0(srcs[i], " = if((row() % 2 == 1 & col() % 2 == 1) | (row() % 2 == 0 & col() % 2 == 0), ", vals[1L], ", ", vals[2L], ")") @@ -65,8 +61,11 @@ methods::setMethod( ex <- paste0(srcs[i], " = if(row() % 2 == 0 | col() % 2 == 0, ", vals[1L], ", ", vals[2L], ")") } - } + } else if (fun != "chess") { + + ex <- paste0(srcs[i], " = ", fun, "()") + } rgrass::execGRASS("r.mapcalc", expression = ex, flags = c(.quiet(), "overwrite")) } From bc382821e5621cbea95874c2b2a8371faeb33ee0 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 22:34:45 -0500 Subject: [PATCH 083/125] Add argument `keep` --- R/mow.r | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/R/mow.r b/R/mow.r index e8b9dc78..51fac1be 100644 --- a/R/mow.r +++ b/R/mow.r @@ -1,15 +1,15 @@ #' Remove unused rasters and vectors from the GRASS cache #' -#' @description **fasterRaster** attempts to remove intermediate rasters and vectors from the **GRASS** cache as they are not needed, but files can still accumulate, especially if you remove `GRaster`s or `GVector`s from the **R** working environment (e.g., by using [rm()] or simply using the same variable name for different rasters/vectors). This function will a) search the **GRASS** cache for all rasters/vectors there; and b) remove any of them that are not pointed to by an object in the active **R** environment. +#' @description **fasterRaster** attempts to delete rasters and vectors in the **GRASS** cache. This function will a) search the current **GRASS** "project/location" cache for all rasters/vectors there; and b) remove any of them that are not pointed to by an object in the active **R** environment. Only objects in the currently active **GRASS** project/location will be removed (see `vignette("project_mapset", package = "fasterRaster")`). #' -#' Note that calling this function inside another function's environment can be very dangerous, as it will only be able to see objects in that environment, and thus delete any rasters/vectors outside that environment. -#' -#' Note also that this function will only clean the current **GRASS** "project"/"location" (see `vignette("projects_mapsets", package = "fasterRaster")`). +#' Note that calling this function inside another function's environment without providing an argument for `x` can be very **dangerous**, as it will detect objects outside that environment, and thus delete any rasters/vectors outside that environment. #' #' @param x Either missing (default) or an environment. #' #' @param type Either `NULL` or a character vector. If `NULL`, all rasters and vectors in the **GRASS** cache are candidates for deletion. Otherwise, this can be either `"raster"`, `"vector"`, or both. #' +#' @param keeps Either `NULL` (default) or a `list()` of `GRaster`s and/or `GVector`s that you want to retain. The rasters and vectors in **GRASS** pointed to by these objects will not be deleted. +#' #' @param verbose Logical: If `TRUE` (default), report progress. #' #' @param ask Logical: If `TRUE` (default), prompt for reassurance. @@ -23,13 +23,17 @@ #' @aliases mow #' @rdname mow #' @export mow -mow <- function(x, type = NULL, verbose = TRUE, ask = TRUE) { +mow <- function(x, type = NULL, keeps = NULL, verbose = TRUE, ask = TRUE) { if (ask) { response <- readline("Are you sure you want to clean the GRASS cache? (y/n) ") if (response != "y") return(invisible(list(rasters = 0, vectors = 0))) } + if (!is.null(keeps)) { + if (!is.list(keeps)) stop("Argument `keeps` must be a list or NULL.") + } + if (missing(x)) { x <- ls(envir = parent.frame(n = 1L)) } else { @@ -57,6 +61,10 @@ mow <- function(x, type = NULL, verbose = TRUE, ask = TRUE) { } # next object in environment + if (!is.null(GSpatials)) { + GSpatials <- GSpatials[!(GSpatials$rObject %in% keeps)] + } + if (nrow(GSpatials) == 0L) return(invisible(list(rasters = 0, vectors = 0))) # see what's in GRASS From e364bf3aaed86ee8386b313a438fec93c2060fbf Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 22:36:59 -0500 Subject: [PATCH 084/125] Add argument `keep` --- R/mow.r | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/R/mow.r b/R/mow.r index 51fac1be..5f70636f 100644 --- a/R/mow.r +++ b/R/mow.r @@ -8,7 +8,7 @@ #' #' @param type Either `NULL` or a character vector. If `NULL`, all rasters and vectors in the **GRASS** cache are candidates for deletion. Otherwise, this can be either `"raster"`, `"vector"`, or both. #' -#' @param keeps Either `NULL` (default) or a `list()` of `GRaster`s and/or `GVector`s that you want to retain. The rasters and vectors in **GRASS** pointed to by these objects will not be deleted. +#' @param keep Either `NULL` (default) or a `list()` of `GRaster`s and/or `GVector`s that you want to retain. The rasters and vectors in **GRASS** pointed to by these objects will not be deleted. #' #' @param verbose Logical: If `TRUE` (default), report progress. #' @@ -23,15 +23,15 @@ #' @aliases mow #' @rdname mow #' @export mow -mow <- function(x, type = NULL, keeps = NULL, verbose = TRUE, ask = TRUE) { +mow <- function(x, type = NULL, keep = NULL, verbose = TRUE, ask = TRUE) { if (ask) { response <- readline("Are you sure you want to clean the GRASS cache? (y/n) ") if (response != "y") return(invisible(list(rasters = 0, vectors = 0))) } - if (!is.null(keeps)) { - if (!is.list(keeps)) stop("Argument `keeps` must be a list or NULL.") + if (!is.null(keep)) { + if (!is.list(keep)) stop("Argument `keeps` must be a list or NULL.") } if (missing(x)) { @@ -62,7 +62,9 @@ mow <- function(x, type = NULL, keeps = NULL, verbose = TRUE, ask = TRUE) { } # next object in environment if (!is.null(GSpatials)) { - GSpatials <- GSpatials[!(GSpatials$rObject %in% keeps)] + for (i in seq_along(keep)) { + GSpatials <- GSpatials[!(GSpatials$rObject %in% keep[[i]])] + } } if (nrow(GSpatials) == 0L) return(invisible(list(rasters = 0, vectors = 0))) From bc973757741d366c632818034361fa51a5e0893d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 22:37:06 -0500 Subject: [PATCH 085/125] Update NAMESPACE --- NAMESPACE | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NAMESPACE b/NAMESPACE index 5ddfde2d..46e7b006 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,6 +3,7 @@ export(appFuns) export(fastData) export(faster) +export(grassHelp) export(grassInfo) export(grassStarted) export(is.polygons) @@ -120,7 +121,7 @@ exportMethods(freq) exportMethods(geomorphons) exportMethods(geomtype) exportMethods(global) -exportMethods(grass) +exportMethods(grassGUI) exportMethods(grid) exportMethods(head) exportMethods(hexagons) From 5a9cb60e0d836e9ec0c6ba66cd2717974d04058d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 22:38:08 -0500 Subject: [PATCH 086/125] Change URLs to `grassHelp()` calls --- R/app.r | 4 +- R/clusterPoints.r | 4 +- R/fast.r | 10 +- R/fillNAs.r | 2 +- R/flow.r | 4 +- R/flowPath.r | 2 +- R/geomorphons.r | 6 +- R/interpIDW.r | 2 +- R/interpSplines.r | 2 +- R/rasterize.r | 2 +- R/resample.r | 2 +- R/streams.r | 4 +- R/sun.r | 6 +- R/wetness.r | 2 +- R/writeRaster.r | 4 +- R/writeVector.r | 6 +- man/app.Rd | 4 +- man/clusterPoints.Rd | 2 +- man/complete.cases.Rd | 2 - man/compositeRGB.Rd | 3 - .../ex_GRaster_arithmetic_across_layers.r | 2 +- man/examples/ex_complete.cases.r | 2 - man/examples/ex_flowPath.r | 8 +- man/examples/ex_grass.r | 7 -- man/examples/ex_init.r | 1 - man/examples/ex_location_mapset.r | 46 +++++----- man/examples/ex_noise_denoise.r | 2 +- man/examples/ex_plot.r | 3 - man/fast.Rd | 4 +- man/fillNAs.Rd | 2 +- man/flow.Rd | 4 +- man/flowPath.Rd | 10 +- man/functions.Rd | 2 +- man/geomorphons.Rd | 6 +- man/{grass.Rd => grassGUI.Rd} | 12 +-- man/grassHelp.Rd | 39 ++++++++ man/hist.Rd | 3 - man/init.Rd | 1 - man/interpIDW.Rd | 2 +- man/interpSplines.Rd | 2 +- man/location.Rd | 92 ++++++++++--------- man/locationCreate.Rd | 46 +++++----- man/locationFind.Rd | 46 +++++----- man/locationRestore.Rd | 46 +++++----- man/locations.Rd | 46 +++++----- man/longlat.Rd | 5 +- man/mapset.Rd | 46 +++++----- man/mow.Rd | 10 +- man/plot.Rd | 3 - man/plotRGB.Rd | 3 - man/rasterize.Rd | 2 +- man/regress.Rd | 2 +- man/resample.Rd | 2 +- man/streams.Rd | 4 +- man/sun.Rd | 6 +- man/wetness.Rd | 2 +- man/writeRaster.Rd | 4 +- man/writeVector.Rd | 6 +- 58 files changed, 325 insertions(+), 277 deletions(-) delete mode 100644 man/examples/ex_grass.r rename man/{grass.Rd => grassGUI.Rd} (87%) create mode 100644 man/grassHelp.Rd diff --git a/R/app.r b/R/app.r index 2b48cc15..5089818a 100644 --- a/R/app.r +++ b/R/app.r @@ -24,7 +24,7 @@ #' * It must use typical arithmetic operators like `+`, `-`, `*`, `/` and/or functions that can be seen using `appFuns(TRUE)`. #' * The [names()] of the rasters do not match any of the functions in the `appFuns(TRUE)` table. Note that `x` and `y` are forbidden names :( #' -#' The help page for **GRASS** module [`r.mapcalc`](https://grass.osgeo.org/grass84/manuals/r.mapcalc.html) will be especially helpful. +#' The help page for **GRASS** module `r.mapcalc` will be especially helpful. You can see this page using `grassHelp("r.mapcalc")`. #' #' @param datatype Character: This ensures that rasters are treated as a certain type before they are operated on. This is useful when using rasters that have all integer values, which **GRASS** can assume represent integers, even if they are not supposed to. In this case, the output of operations on this raster might be an integer if otherwise not corrected. Partial matching is used, and options include: #' * `"integer"`: Force all rasters to integers by truncating their values. The output may still be of type `float` if the operation creates non-integer values. @@ -42,7 +42,7 @@ #' #' @returns A `GRaster`. #' -#' @seealso [terra::app()], [terra::lapp()], [subst()], [classify()], and modules [`r.mapcalc`](https://grass.osgeo.org/grass84/manuals/r.mapcalc.html) and `r.mapcalc.simple` in **GRASS**. +#' @seealso [terra::app()], [terra::lapp()], [subst()], [classify()], and modules `r.mapcalc` in **GRASS** (viewable using `grassHelp("r.mapcalc")`) #' #' @example man/examples/ex_app.r #' diff --git a/R/clusterPoints.r b/R/clusterPoints.r index 09bf5181..27c5d5d1 100644 --- a/R/clusterPoints.r +++ b/R/clusterPoints.r @@ -4,7 +4,7 @@ #' #' @param x A "points" `GVector`. #' -#' @param method Character: Method used to identify clusters. Explanations of methods are provided in the help page for the **GRASS** module [https://grass.osgeo.org/grass84/manuals/v.cluster.html](v.cluster). +#' @param method Character: Method used to identify clusters. Explanations of methods are provided in the help page for the **GRASS** module `v.cluster`, available using `grassHelp("v.cluster")`. #' * `"DBSCAN"` (default): Density-Based Spatial Clustering of Applications with Noise. #' * `"DBSCAN2"`: A modification of DBSCAN. #' * `"density"`: Cluster points by relative density. @@ -30,9 +30,7 @@ methods::setMethod( if (geomtype(x) != "points") stop("Only points GVectors can be clustered.") if (!is.null(maxDist)) { - if (is.infinite(maxDist)) stop("Argument `maxDist` cannot be infinite.") - } method <- tolower(method) diff --git a/R/fast.r b/R/fast.r index a7a74343..57f8b9cd 100644 --- a/R/fast.r +++ b/R/fast.r @@ -2,7 +2,7 @@ #' #' @description `fast()` creates a `GRaster` or `GVector` from 1) a file; 2) from a `SpatRaster`, `SpatVector`, or `sf` vector; or 3) from a numeric vector, `matrix`, `data.frame`, or `data.table`. Behind the scenes, this function will also create a connection to **GRASS** if none has yet been made yet. #' -#' **GRASS** supports loading from disk a variety of raster formats (see the **GRASS** manual page for [`r.in.gdal`](https://grass.osgeo.org/grass84/manuals/r.in.gdal.html)) and vector formats ([`v.in.ogr`](https://grass.osgeo.org/grass84/manuals/v.in.ogr.html)), though not all of them will work with this function. +#' **GRASS** supports loading from disk a variety of raster formats (see the **GRASS** manual page for `r.in.gdal` (see `grassHelp("r.in.gdal")`) and vector formats `v.in.ogr` (see grassHelp("v.in.ogr")`), though not all of them will work with this function. #' #' Note that `GVectors` may fail to be created if they contain issues that do not coincide with the topological data model used by **GRASS**. The most common of these is overlapping polygons. See *Details* on how to fix these kinds of issues. #' @@ -69,7 +69,7 @@ #' * **Correction outside of *fasterRaster***: Before you convert the vector into **fasterRaster**'s `GVector` format, you can also try using the [terra::makeValid()] or [sf::st_make_valid()] tools to fix issues, then use `fast()`. #' * **Post-conversion to a `GVector`**: If you do get a vector loaded into `GVector` format, you can also use a set of **fasterRaster** vector-manipulation [tools][breakPolys] or [fillHoles()] to fix issues. #' -#' @seealso [rgrass::read_RAST()] and [rgrass::read_VECT()], [vector cleaning][breakPolys], [fillHoles()], plus modules [`v.in.ogr`](https://grass.osgeo.org/grass84/manuals/v.in.ogr.html) and [`r.import`](https://grass.osgeo.org/grass84/manuals/r.import.html) in **GRASS**. +#' @seealso [rgrass::read_RAST()] and [rgrass::read_VECT()], [vector cleaning][breakPolys], [fillHoles()], plus **GRASS** modules `v.in.ogr` (see `grassHelp("v.in.ogr")`) and `r.import` (see `grassHelp("r.import")`) #' #' @returns A `GRaster` or `GVector`. #' @@ -241,13 +241,13 @@ methods::setMethod( } else if (rastOrVect == "vector") { - # x is a filename and xVect is missing: we have not come through methods for SpatVectors or sf objects + # x is a filename and xVect is missing: we have NOT come through methods for SpatVectors or sf objects if (!any(dotNames == "xVect")) { x <- terra::vect(x) out <- fast(x, extent = extent, correct = correct, snap = snap, area = area, steps = steps, resolve = resolve, dropTable = dropTable, verbose = verbose, ...) - # x is a filename and xVect is present: we have come here through a method for SpatVectors or sf objects + # x is a filename and xVect is present: we HAVE come here through a method for SpatVectors or sf objects } else { if (!is.na(resolve)) resolve <- omnibus::pmatchSafe(resolve, c("aggregate", "disaggregate"), n = 1L) @@ -313,7 +313,7 @@ methods::setMethod( #} src <- .makeSourceName("fast_v_in_ogr", "vector") - if (is.null(snap) & is.null(area)) { + if (is.null(snap) & (is.null(area) || area == 0)) { # slower if we need to record messages suppressMessages( diff --git a/R/fillNAs.r b/R/fillNAs.r index bf15ca02..226dae8c 100644 --- a/R/fillNAs.r +++ b/R/fillNAs.r @@ -18,7 +18,7 @@ #' #' @example man/examples/ex_fillNAs.r #' -#' @seealso [terra::interpNear()]; module [r.fillnulls](https://grass.osgeo.org/grass84/manuals/r.fillnulls.html) in **GRASS** +#' @seealso [terra::interpNear()], **GRASS** module `r.fillnulls` (see `grassHelp("r.fillnulls")`) #' #' @aliases fillNAs #' @rdname fillNAs diff --git a/R/flow.r b/R/flow.r index 96d8e56e..d0f5f409 100644 --- a/R/flow.r +++ b/R/flow.r @@ -7,7 +7,7 @@ #' * Flooded areas; and/or #' * Topographic convergence (log of flow accumulation divided by local slope). #' -#' More details about the computations can be found at the help page for the [`r.terraflow`](https://grass.osgeo.org/grass84/manuals/r.terraflow.html) module for **GRASS**. +#' More details about the computations can be found at the help page for the **GRASS** module `r.terraflow`] (see `grassHelp("r.terraflow")`) #' #' @param x A `GRaster` with a single layer, typically representing elevation. #' @@ -23,7 +23,7 @@ #' * `"TCI"`: Topographic convergence index #' * `"*"`: All of the above #' -#' @seealso [flowPath()], [streams()], the [`r.terraflow`](https://grass.osgeo.org/grass84/manuals/r.terraflow.html) module for **GRASS** +#' @seealso [flowPath()], [streams()], the **GRASS** module `r.terraflow` (see `grassHelp("r.terraflow")`) #' #' @param scratchDir Character: Directory in which to store temporary files. The **GRASS** module `r.terraflow` makes a lot of temporary files. The default is given by [tempdir()]. #' diff --git a/R/flowPath.r b/R/flowPath.r index e488ff95..b9baeb27 100644 --- a/R/flowPath.r +++ b/R/flowPath.r @@ -17,7 +17,7 @@ #' #' @example man/examples/ex_flowPath.r #' -#' @seealso [flow()], [streams()], the [`r.drain`](https://grass.osgeo.org/grass84/manuals/r.drain.html) module for **GRASS** +#' @seealso [flow()], [streams()], the **GRASS** module `r.drain` (see `grassHelp("r.drain")`) #' #' @aliases flowPath #' @rdname flowPath diff --git a/R/geomorphons.r b/R/geomorphons.r index fc50845a..ae3f984b 100644 --- a/R/geomorphons.r +++ b/R/geomorphons.r @@ -1,6 +1,6 @@ #' Identify terrain feature types #' -#' @description Geomorphons are idealized terrain types calculated from an elevator raster based on a moving window of a given size. The window is a torus (which can have an inner radius of 0, so can also be a circle), which allows it to identify geomorphons of a given size while ignoring ones larger or smaller. There are 10 basic geomorphons. Consult the The **GRASS** module [`r.geomorphon`](https://grass.osgeo.org/grass84/manuals/r.geomorphon.html) for more details and diagrams of each type of geomorphon. They include: +#' @description Geomorphons are idealized terrain types calculated from an elevator raster based on a moving window of a given size. The window is a torus (which can have an inner radius of 0, so can also be a circle), which allows it to identify geomorphons of a given size while ignoring ones larger or smaller. There are 10 basic geomorphons. Consult the the manual for **GRASS** module `r.geomorphon` using `grassHelp("r.geomorphon")` for more details and diagrams of each type of geomorphon. Geomorphon types include: #' 1. Flat areas: Focal area has approximately the same elevation as surrounding areas #' 2. Pits: An area is lower than all other surrounding areas #' 3. Valley: Focal area has elevation similar to two opposing side of the window but lower than the other two opposing sides @@ -23,13 +23,13 @@ #' @param flatDist Numeric: Distance (in meters) to correct for the effect of large distances on the diminished capacity to identify "flat" geomorphons. If the distance between the focal area and a surrounding area surpasses this distance, then the effective value of `flat` will be reduced #' #' @param mode Character: Method for implementing the zenith/line-of-site search. Partial matching is used: -#' * `"1"` (default): The "original" geomorphon mode (in **GRASS** module [`r.geomorphon`](https://grass.osgeo.org/grass84/manuals/r.geomorphon.html), the "anglev1" method) +#' * `"1"` (default): The "original" geomorphon mode (in **GRASS** module `r.geomorphon`, the "anglev1" method) #' * `"2"`: Better handling of cases with equal zenith/nadir angles (the "anglev2" method) #' * `"2d"`: As `"2"`, but takes into account zenith/nadir distance ("anglev2_distance" method) #' #' @returns A categorical `GRaster` where each geomorphon is a category (see `vignette("GRasters", package = "fasterRaster")`). #' -#' @seealso Module [`r.geomorphon`](https://grass.osgeo.org/grass84/manuals/r.geomorphon.html) in **GRASS** +#' @seealso **GRASS** module `r.geomorphon` (see `grassHelp("r.geomorphon")`) #' #' @example man/examples/ex_geomorphons.r #' diff --git a/R/interpIDW.r b/R/interpIDW.r index a5596e15..41eb15c2 100644 --- a/R/interpIDW.r +++ b/R/interpIDW.r @@ -14,7 +14,7 @@ #' #' @returns A `GRaster`. #' -#' @seealso [terra::interpIDW()], [interpSplines()], [fillNAs()], module [`v.surf.idw`](https://grass.osgeo.org/grass84/manuals/v.surf.idw.html) in **GRASS** +#' @seealso [terra::interpIDW()], [interpSplines()], [fillNAs()], **GRASS** module `v.surf.idw` (se `grasshelp("v.surf.idw")`) #' #' @aliases interpIDW #' @rdname interpIDW diff --git a/R/interpSplines.r b/R/interpSplines.r index 09cb387f..7be9aa2f 100644 --- a/R/interpSplines.r +++ b/R/interpSplines.r @@ -29,7 +29,7 @@ #' * `lambda` is `NULL` and `interpolate` is `FALSE`: A `data.frame` with values of `lambdas` that were assessed, plus `mean` (mean residual value) and `rms` (root mean square error). You can see the table using `attr(output_raster, "lambdas", exact = TRUE)`. #' * `lambda` is a number (`interpolate` is ignored): A `GRaster`. #' -#' @seealso [interpIDW()], [fillNAs()], module [`v.surf.bspline`](https://grass.osgeo.org/grass84/manuals/v.surf.bspline.html) in **GRASS** +#' @seealso [interpIDW()], [fillNAs()], **GRASS** module `v.surf.bspline` (see `grassHelp("v.surf.bspline")`) #' #' @aliases interpSplines #' @rdname interpSplines diff --git a/R/rasterize.r b/R/rasterize.r index f8d62fb4..92cdf001 100644 --- a/R/rasterize.r +++ b/R/rasterize.r @@ -16,7 +16,7 @@ #' #' @returns A `GRaster`. #' -#' @seealso [terra::rasterize()], module [`v.to.rast`](https://grass.osgeo.org/grass84/manuals/v.to.rast.html) in **GRASS** +#' @seealso [terra::rasterize()], **GRASS** module `v.to.rast` (see `grassHelp("v.to.rast")`) #' #' @example man/examples/ex_rasterize.r #' diff --git a/R/resample.r b/R/resample.r index dab35c9f..4b604d19 100644 --- a/R/resample.r +++ b/R/resample.r @@ -18,7 +18,7 @@ #' #' @returns A `GRaster`. #' -#' @seealso [terra::resample()], modules [`r.resample`](https://grass.osgeo.org/grass84/manuals/r.resample.html) and [`r.resamp.interp`](https://grass.osgeo.org/grass84/manuals/r.resamp.interp.html) in **GRASS** +#' @seealso [terra::resample()], **GRASS** modules `r.resample` and `r.resamp.interp` (see `grassHelp("`r.resample`") and `grassHelp("`r.resamp.interp`")`) #' #' @example man/examples/ex_resample.r #' diff --git a/R/streams.r b/R/streams.r index 795c5299..939f5423 100644 --- a/R/streams.r +++ b/R/streams.r @@ -1,6 +1,6 @@ #' Create stream network #' -#' @description This function estimates the course of streams and rivers from an elevation raster. It is based on the **GRASS** module `\href{https://grass.osgeo.org/grass84/manuals/r.stream.extract.html}{r.stream.extract}`, where more details can be found. +#' @description This function estimates the course of streams and rivers from an elevation raster. It is based on the **GRASS** module `r.stream.extract`, where more details can be found (see `grassHelp("r.stream.extract")`) #' #' @param x A `GRaster` representing elevation. #' @@ -20,7 +20,7 @@ #' #' @example man/examples/ex_streams.r #' -#' @seealso [flow()], [flowPath()], the `\href{https://grass.osgeo.org/grass84/manuals/r.stream.extract.html}{r.stream.extract}` module in **GRASS** +#' @seealso [flow()], [flowPath()], **GRASS** module `r.stream.extract` (see `grassHelp("r.stream.extract")`) #' #' @aliases streams #' @rdname streams diff --git a/R/sun.r b/R/sun.r index c51b0874..d5c7e1e4 100644 --- a/R/sun.r +++ b/R/sun.r @@ -1,6 +1,6 @@ #' Solar radiance and irradiance #' -#' The `sun()` function calculates beam (direct), diffuse and ground reflected solar irradiation for a given day and set of topographic and atmospheric conditions. The function relies on the **GRASS** module [`r.sun`](https://grass.osgeo.org/grass84/manuals/r.sun.html), which contains a detailed explanation. +#' The `sun()` function calculates beam (direct), diffuse and ground reflected solar irradiation for a given day and set of topographic and atmospheric conditions. The function relies on the **GRASS** module `r.sun`, the manual page for which contains a detailed explanation (see `grassHelp("r.sun")`) #' #' @param elevation A `GRaster` with values representing elevation (typically in meters). #' @@ -18,7 +18,7 @@ #' #' @param albedo A `GRaster` or a numeric value: This is either a raster with values of ground albedo or a numeric value (in which case albedo is assumed to be the same everywhere). Albedo is unit-less, and the default value is 0.2. #' -#' @param linke A `GRaster` or a numeric value: This is either a raster with values of the Linke atmospheric turbidity coefficient or a numeric value (in which case the same value is assumed for all locations). The Linke coefficient is unit-less. The default value is 3, but see also the **GRASS** manual page for module [`r.sun`](https://grass.osgeo.org/grass84/manuals/r.sun.html). +#' @param linke A `GRaster` or a numeric value: This is either a raster with values of the Linke atmospheric turbidity coefficient or a numeric value (in which case the same value is assumed for all locations). The Linke coefficient is unit-less. The default value is 3, but see also the **GRASS** manual page for module `r.sun` (`grassHelp("r.sun")`). #' #' @param day Positive integer between 1 to 365, inclusive: Day of year for which to calculate ir/radiation. Default is 1 (January 1st). #' @@ -51,7 +51,7 @@ #' * `glob_rad`: Global radiation (Watt-hours/m2/day) #' * `insol_time`: Insolation duration (hours) #' -#' @seealso [terrain()], [horizonHeight()], module [`r.sun`](https://grass.osgeo.org/grass84/manuals/r.sun.html) in **GRASS** +#' @seealso [terrain()], [horizonHeight()], **GRASS** module `r.sun`(see `grassHelp("r.sun")`) #' #' @example man/examples/ex_sun.r #' diff --git a/R/wetness.r b/R/wetness.r index d9d32be4..14d05249 100644 --- a/R/wetness.r +++ b/R/wetness.r @@ -6,7 +6,7 @@ #' #' @returns A `GRaster`. #' -#' @seealso [terrain()], [ruggedness()], [geomorphons()], module [`r.topidx`](https://grass.osgeo.org/grass84/manuals/r.topidx.html) in **GRASS** +#' @seealso [terrain()], [ruggedness()], [geomorphons()], **GRASS** module `r.topidx` (see `grassHelp("r.topidx")`) #' #' @example man/examples/ex_ruggedness_wetness.r #' diff --git a/R/writeRaster.r b/R/writeRaster.r index 233fdf94..e3f26ff6 100644 --- a/R/writeRaster.r +++ b/R/writeRaster.r @@ -3,7 +3,7 @@ #' @description #' This function saves a `GRaster` to disk directly from a **GRASS** session. It is faster than using [rast()], then saving the output of that to disk (because `rast()` actually save the raster to disk, anyway). #' -#' The function will attempt to ascertain the file type to be ascertained from the file extension, but you can specify the format using the `format` argument (see entry for `...`). You can see a list of supported formats by simply using this function with no arguments, as in `writeRaster()`, or by consulting the online help page for the **GRASS** module [`r.out.gdal`](https://grass.osgeo.org/grass84/manuals/r.out.gdal.html). Only the `GeoTIFF` file format is guaranteed to work for multi-layered rasters. +#' The function will attempt to ascertain the file type to be ascertained from the file extension, but you can specify the format using the `format` argument (see entry for `...`). You can see a list of supported formats by simply using this function with no arguments, as in `writeRaster()`, or by consulting the online help page for the **GRASS** module `r.out.gdal` (see `grassHelp("r.out.gdal")`). Only the `GeoTIFF` file format is guaranteed to work for multi-layered rasters. #' #' The function will attempt to optimize the `datatype` argument, but this can take a long time. You can speed this up by setting `datatype` manually. Note that if you are saving a "stack" of `GRaster`s with different `datatype`s, the one with the highest information density will be used (e.g., low-bit integer < high-bit integer < floating-point < double-floating point). This can make rasters with lower datatypes much larger on disk. In these cases, it make be best to save rasters with similar `datatype`s together. #' @@ -51,7 +51,7 @@ #' #' @returns A `GRaster` (invisibly). A raster is also saved to disk. #' -#' @seealso [terra::writeRaster()], module [`r.out.gdal`](https://grass.osgeo.org/grass84/manuals/r.out.gdal.html) in **GRASS** +#' @seealso [terra::writeRaster()], **GRASS** module `r.out.gdal` (see `grassHelp("r.out.gdal")`) #' #' @example man/examples/ex_writeRaster.r #' diff --git a/R/writeVector.r b/R/writeVector.r index 13cd796d..c735c276 100644 --- a/R/writeVector.r +++ b/R/writeVector.r @@ -2,7 +2,7 @@ #' #' @description This function saves a `GVector` to disk directly from a **GRASS** session. #' -#' By default, files will be of OGC GeoPackage format (extension "`.gpkg`"), but this can be changed with the `format` argument. You can see a list of supported formats by simply using this function with no arguments, as in `writeVector()`, or by consulting the online help page for **GRASS** module [`v.out.ogr`](https://grass.osgeo.org/grass84/manuals/v.out.ogr.html). +#' By default, files will be of OGC GeoPackage format (extension "`.gpkg`"), but this can be changed with the `format` argument. You can see a list of supported formats by simply using this function with no arguments, as in `writeVector()`, or by consulting the online help page for **GRASS** module `v.out.ogr` (see `grassHelp("v.out.ogr")`). #' #' Note that if the vector has a data table attached and at least one numeric or integer column has an `NA` or `NaN` value, the function will yield a warning like: #' ``` @@ -27,11 +27,11 @@ #' #' @param attachTable Logical: If `TRUE` (default), attach the attribute to table to the vector before saving it. If `FALSE`, the attribute table will not be attached. #' -#' @param ... Additional arguments to send to **GRASS** module [`v.out.ogr`](https://grass.osgeo.org/grass84/manuals/v.out.ogr.html) in **GRASS**. +#' @param ... Additional arguments to send to **GRASS** module `v.out.ogr` (see `grassHelp("v.out.ogr")`). #' #' @returns Invisibly returns a `GRaster` (the input, `x`). Also saves the vector to disk. #' -#' @seealso [terra::writeVector()], [sf::st_write()], module [`v.out.ogr`](https://grass.osgeo.org/grass84/manuals/v.out.ogr.html) in **GRASS** +#' @seealso [terra::writeVector()], [sf::st_write()], **GRASS** module `v.out.ogr` (see `grassHelp("v.out.ogr")`) #' #' @example man/examples/ex_writeVector.r #' diff --git a/man/app.Rd b/man/app.Rd index 1852aaca..0c8dce74 100644 --- a/man/app.Rd +++ b/man/app.Rd @@ -23,7 +23,7 @@ appFuns(warn = TRUE) \item The \code{\link[=names]{names()}} of the rasters do not match any of the functions in the \code{appFuns(TRUE)} table. Note that \code{x} and \code{y} are forbidden names :( } -The help page for \strong{GRASS} module \href{https://grass.osgeo.org/grass84/manuals/r.mapcalc.html}{\code{r.mapcalc}} will be especially helpful.} +The help page for \strong{GRASS} module \code{r.mapcalc} will be especially helpful. You can see this page using \code{grassHelp("r.mapcalc")}.} \item{datatype}{Character: This ensures that rasters are treated as a certain type before they are operated on. This is useful when using rasters that have all integer values, which \strong{GRASS} can assume represent integers, even if they are not supposed to. In this case, the output of operations on this raster might be an integer if otherwise not corrected. Partial matching is used, and options include: \itemize{ @@ -129,5 +129,5 @@ freq(rand) # cell frequencies } } \seealso{ -\code{\link[terra:app]{terra::app()}}, \code{\link[terra:lapp]{terra::lapp()}}, \code{\link[=subst]{subst()}}, \code{\link[=classify]{classify()}}, and modules \href{https://grass.osgeo.org/grass84/manuals/r.mapcalc.html}{\code{r.mapcalc}} and \code{r.mapcalc.simple} in \strong{GRASS}. +\code{\link[terra:app]{terra::app()}}, \code{\link[terra:lapp]{terra::lapp()}}, \code{\link[=subst]{subst()}}, \code{\link[=classify]{classify()}}, and modules \code{r.mapcalc} in \strong{GRASS} (viewable using \code{grassHelp("r.mapcalc")}) } diff --git a/man/clusterPoints.Rd b/man/clusterPoints.Rd index 6416ca5b..c2cb56d0 100644 --- a/man/clusterPoints.Rd +++ b/man/clusterPoints.Rd @@ -10,7 +10,7 @@ \arguments{ \item{x}{A "points" \code{GVector}.} -\item{method}{Character: Method used to identify clusters. Explanations of methods are provided in the help page for the \strong{GRASS} module \href{v.cluster}{https://grass.osgeo.org/grass84/manuals/v.cluster.html}. +\item{method}{Character: Method used to identify clusters. Explanations of methods are provided in the help page for the \strong{GRASS} module \code{v.cluster}, available using \code{grassHelp("v.cluster")}. \itemize{ \item \code{"DBSCAN"} (default): Density-Based Spatial Clustering of Applications with Noise. \item \code{"DBSCAN2"}: A modification of DBSCAN. diff --git a/man/complete.cases.Rd b/man/complete.cases.Rd index 29c73785..6a64e70d 100644 --- a/man/complete.cases.Rd +++ b/man/complete.cases.Rd @@ -52,8 +52,6 @@ madCover <- fastData("madCover") dypsis <- fast(madDypsis) cover <- fast(madCover) -levels(cover) - ### GVector # Look at the data table: diff --git a/man/compositeRGB.Rd b/man/compositeRGB.Rd index 1a801be7..8f437149 100644 --- a/man/compositeRGB.Rd +++ b/man/compositeRGB.Rd @@ -27,9 +27,6 @@ This function takes as arguments three rasters typically representing red, green \examples{ if (grassStarted()) { -# Setup -library(terra) - # Example data madElev <- fastData("madElev") # elevation raster madLANDSAT <- fastData("madLANDSAT") # multi-layer raster diff --git a/man/examples/ex_GRaster_arithmetic_across_layers.r b/man/examples/ex_GRaster_arithmetic_across_layers.r index 2943d36a..a41247a4 100644 --- a/man/examples/ex_GRaster_arithmetic_across_layers.r +++ b/man/examples/ex_GRaster_arithmetic_across_layers.r @@ -38,7 +38,7 @@ which.max(chelsa) # Regression # Note the intercept is different for fasterRaster::regress(). regress(chelsa) -regress(madChelsa) +regress(madChelsa, 1:nlyr(madChelsa)) # Note: To get quantiles for each layer, use # global(x, "quantile", probs = 0.2). diff --git a/man/examples/ex_complete.cases.r b/man/examples/ex_complete.cases.r index 54d81b78..17c8223e 100644 --- a/man/examples/ex_complete.cases.r +++ b/man/examples/ex_complete.cases.r @@ -12,8 +12,6 @@ madCover <- fastData("madCover") dypsis <- fast(madDypsis) cover <- fast(madCover) -levels(cover) - ### GVector # Look at the data table: diff --git a/man/examples/ex_flowPath.r b/man/examples/ex_flowPath.r index de9e147b..d6699e35 100644 --- a/man/examples/ex_flowPath.r +++ b/man/examples/ex_flowPath.r @@ -14,7 +14,7 @@ ant <- coast4[coast4$NAME_4 == "Antanambe"] elevAnt <- crop(elev, ant) # Create a set of random points to serve as starting points: -starts <- spatSample(elevAnt, 10, as.points = TRUE, seed = 1) +starts <- spatSample(elevAnt, 10, as.points = TRUE, seed = 2) # Remove points in water: starts <- starts[complete.cases(starts)] @@ -23,14 +23,16 @@ starts <- starts[complete.cases(starts)] paths <- flowPath(elevAnt, starts) paths -plot(paths) +plot(elevAnt, legend = FALSE, main = "Flow path for each point") +plot(paths, add = TRUE) plot(starts, pch = 1, add = TRUE) # Calculate flow paths with cell values indicating the number # of cells from each start: seqs <- flowPath(elevAnt, starts, return = "seq") -plot(seqs) +plot(elevAnt, legend = FALSE, main = "Sequentially-numbered flow paths") +plot(seqs, add = TRUE) plot(starts, pch = 1, add = TRUE) # We can convert flow paths to lines: diff --git a/man/examples/ex_grass.r b/man/examples/ex_grass.r deleted file mode 100644 index c6d56948..00000000 --- a/man/examples/ex_grass.r +++ /dev/null @@ -1,7 +0,0 @@ -if (grassStarted()) { - -# Starting the GRASS GUI and making changes to rasters or vectors can "break" -# the GRasters and GVectors they are associated with in R. -if (FALSE) grass() - -} diff --git a/man/examples/ex_init.r b/man/examples/ex_init.r index cc00a595..eff2faa5 100644 --- a/man/examples/ex_init.r +++ b/man/examples/ex_init.r @@ -47,5 +47,4 @@ reg <- c(regOdd, regEven) names(reg) <- c("odd", "even") plot(reg) - } diff --git a/man/examples/ex_location_mapset.r b/man/examples/ex_location_mapset.r index c594e38c..d769c42a 100644 --- a/man/examples/ex_location_mapset.r +++ b/man/examples/ex_location_mapset.r @@ -14,32 +14,36 @@ elev <- fast(madElev) chelsa1 <- fast(madChelsa1) # Name of the currently active location -.location() -.location(elev) -.location(chelsa1) +if (FALSE) { + + .location() + .location(elev) + .location(chelsa1) -# All available GRASS locations -.locations() + # All available GRASS locations + .locations() -# Find location of an object among all active locations -.locationFind(elev) -.locationFind(chelsa1) -.locationFind(chelsa1, return = "index") -.locationFind(chelsa1, return = "crs") + # Find location of an object among all active locations + .locationFind(elev) + .locationFind(chelsa1) + .locationFind(chelsa1, return = "index") + .locationFind(chelsa1, return = "crs") -# Switch between locations -.locationRestore(elev) -.locationRestore(chelsa1) + # Switch between locations + .locationRestore(elev) + .locationRestore(chelsa1) -loc <- .location(elev) -.locationRestore(loc) + loc <- .location(elev) + .locationRestore(loc) -# We could use .locationDelete(elev) to delete -# the location where "elev" is stored. + # Delete the location where "elev" is stored. + if (FALSE) .locationDelete(elev) # dangerous! -# Mapsets are always "PERMANENT" in fasterRaster -.mapset() -.mapset(elev) -.mapset(chelsa1) + # Mapsets are always "PERMANENT" in fasterRaster + .mapset() + .mapset(elev) + .mapset(chelsa1) + +} } diff --git a/man/examples/ex_noise_denoise.r b/man/examples/ex_noise_denoise.r index e696a94e..9e91ecdb 100644 --- a/man/examples/ex_noise_denoise.r +++ b/man/examples/ex_noise_denoise.r @@ -10,7 +10,7 @@ madChelsa <- fastData("madChelsa") chelsa <- fast(madChelsa) # Generate raster with layers representing principal component predictions: -pcRast <- pca(chelsa, scale = TRUE) +pcRast <- princomp(chelsa, scale = TRUE) plot(pcRast) # Get information on the PCA: diff --git a/man/examples/ex_plot.r b/man/examples/ex_plot.r index 796ddef6..71dfb779 100644 --- a/man/examples/ex_plot.r +++ b/man/examples/ex_plot.r @@ -1,8 +1,5 @@ if (grassStarted()) { -# Setup -library(terra) - # Example data madElev <- fastData("madElev") # elevation raster madLANDSAT <- fastData("madLANDSAT") # multi-layer raster diff --git a/man/fast.Rd b/man/fast.Rd index b294a651..33341c7a 100644 --- a/man/fast.Rd +++ b/man/fast.Rd @@ -124,7 +124,7 @@ A \code{GRaster} or \code{GVector}. \description{ \code{fast()} creates a \code{GRaster} or \code{GVector} from 1) a file; 2) from a \code{SpatRaster}, \code{SpatVector}, or \code{sf} vector; or 3) from a numeric vector, \code{matrix}, \code{data.frame}, or \code{data.table}. Behind the scenes, this function will also create a connection to \strong{GRASS} if none has yet been made yet. -\strong{GRASS} supports loading from disk a variety of raster formats (see the \strong{GRASS} manual page for \href{https://grass.osgeo.org/grass84/manuals/r.in.gdal.html}{\code{r.in.gdal}}) and vector formats (\href{https://grass.osgeo.org/grass84/manuals/v.in.ogr.html}{\code{v.in.ogr}}), though not all of them will work with this function. +\strong{GRASS} supports loading from disk a variety of raster formats (see the \strong{GRASS} manual page for \code{r.in.gdal} (see \code{grassHelp("r.in.gdal")}) and vector formats \code{v.in.ogr} (see grassHelp("v.in.ogr")`), though not all of them will work with this function. Note that \code{GVectors} may fail to be created if they contain issues that do not coincide with the topological data model used by \strong{GRASS}. The most common of these is overlapping polygons. See \emph{Details} on how to fix these kinds of issues. @@ -231,5 +231,5 @@ mat <- fast(mat, crs = "epsg:4326", keepgeom = TRUE) # WGS84 } } \seealso{ -\code{\link[rgrass:readRAST]{rgrass::read_RAST()}} and \code{\link[rgrass:readVECT]{rgrass::read_VECT()}}, \link[=breakPolys]{vector cleaning}, \code{\link[=fillHoles]{fillHoles()}}, plus modules \href{https://grass.osgeo.org/grass84/manuals/v.in.ogr.html}{\code{v.in.ogr}} and \href{https://grass.osgeo.org/grass84/manuals/r.import.html}{\code{r.import}} in \strong{GRASS}. +\code{\link[rgrass:readRAST]{rgrass::read_RAST()}} and \code{\link[rgrass:readVECT]{rgrass::read_VECT()}}, \link[=breakPolys]{vector cleaning}, \code{\link[=fillHoles]{fillHoles()}}, plus \strong{GRASS} modules \code{v.in.ogr} (see \code{grassHelp("v.in.ogr")}) and \code{r.import} (see \code{grassHelp("r.import")}) } diff --git a/man/fillNAs.Rd b/man/fillNAs.Rd index 045552fa..36271944 100644 --- a/man/fillNAs.Rd +++ b/man/fillNAs.Rd @@ -71,5 +71,5 @@ plot(maps) } } \seealso{ -\code{\link[terra:interpNear]{terra::interpNear()}}; module \href{https://grass.osgeo.org/grass84/manuals/r.fillnulls.html}{r.fillnulls} in \strong{GRASS} +\code{\link[terra:interpNear]{terra::interpNear()}}, \strong{GRASS} module \code{r.fillnulls} (see \code{grassHelp("r.fillnulls")}) } diff --git a/man/flow.Rd b/man/flow.Rd index f2f6a85a..994e1b09 100644 --- a/man/flow.Rd +++ b/man/flow.Rd @@ -45,7 +45,7 @@ The \code{flow()} function uses a raster representing elevation to compute other \item Topographic convergence (log of flow accumulation divided by local slope). } -More details about the computations can be found at the help page for the \href{https://grass.osgeo.org/grass84/manuals/r.terraflow.html}{\code{r.terraflow}} module for \strong{GRASS}. +More details about the computations can be found at the help page for the \strong{GRASS} module \code{r.terraflow}] (see \code{grassHelp("r.terraflow")}) } \examples{ if (grassStarted()) { @@ -67,5 +67,5 @@ plot(elevWater) } } \seealso{ -\code{\link[=flowPath]{flowPath()}}, \code{\link[=streams]{streams()}}, the \href{https://grass.osgeo.org/grass84/manuals/r.terraflow.html}{\code{r.terraflow}} module for \strong{GRASS} +\code{\link[=flowPath]{flowPath()}}, \code{\link[=streams]{streams()}}, the \strong{GRASS} module \code{r.terraflow} (see \code{grassHelp("r.terraflow")}) } diff --git a/man/flowPath.Rd b/man/flowPath.Rd index 5f57df16..589a4003 100644 --- a/man/flowPath.Rd +++ b/man/flowPath.Rd @@ -44,7 +44,7 @@ ant <- coast4[coast4$NAME_4 == "Antanambe"] elevAnt <- crop(elev, ant) # Create a set of random points to serve as starting points: -starts <- spatSample(elevAnt, 10, as.points = TRUE, seed = 1) +starts <- spatSample(elevAnt, 10, as.points = TRUE, seed = 2) # Remove points in water: starts <- starts[complete.cases(starts)] @@ -53,14 +53,16 @@ starts <- starts[complete.cases(starts)] paths <- flowPath(elevAnt, starts) paths -plot(paths) +plot(elevAnt, legend = FALSE, main = "Flow path for each point") +plot(paths, add = TRUE) plot(starts, pch = 1, add = TRUE) # Calculate flow paths with cell values indicating the number # of cells from each start: seqs <- flowPath(elevAnt, starts, return = "seq") -plot(seqs) +plot(elevAnt, legend = FALSE, main = "Sequentially-numbered flow paths") +plot(seqs, add = TRUE) plot(starts, pch = 1, add = TRUE) # We can convert flow paths to lines: @@ -71,5 +73,5 @@ seqLines } } \seealso{ -\code{\link[=flow]{flow()}}, \code{\link[=streams]{streams()}}, the \href{https://grass.osgeo.org/grass84/manuals/r.drain.html}{\code{r.drain}} module for \strong{GRASS} +\code{\link[=flow]{flow()}}, \code{\link[=streams]{streams()}}, the \strong{GRASS} module \code{r.drain} (see \code{grassHelp("r.drain")}) } diff --git a/man/functions.Rd b/man/functions.Rd index 1953a762..f5d65f7f 100644 --- a/man/functions.Rd +++ b/man/functions.Rd @@ -149,7 +149,7 @@ which.max(chelsa) # Regression # Note the intercept is different for fasterRaster::regress(). regress(chelsa) -regress(madChelsa) +regress(madChelsa, 1:nlyr(madChelsa)) # Note: To get quantiles for each layer, use # global(x, "quantile", probs = 0.2). diff --git a/man/geomorphons.Rd b/man/geomorphons.Rd index bb9a245d..3cd113b5 100644 --- a/man/geomorphons.Rd +++ b/man/geomorphons.Rd @@ -28,7 +28,7 @@ \item{mode}{Character: Method for implementing the zenith/line-of-site search. Partial matching is used: \itemize{ -\item \code{"1"} (default): The "original" geomorphon mode (in \strong{GRASS} module \href{https://grass.osgeo.org/grass84/manuals/r.geomorphon.html}{\code{r.geomorphon}}, the "anglev1" method) +\item \code{"1"} (default): The "original" geomorphon mode (in \strong{GRASS} module \code{r.geomorphon}, the "anglev1" method) \item \code{"2"}: Better handling of cases with equal zenith/nadir angles (the "anglev2" method) \item \code{"2d"}: As \code{"2"}, but takes into account zenith/nadir distance ("anglev2_distance" method) }} @@ -37,7 +37,7 @@ A categorical \code{GRaster} where each geomorphon is a category (see \code{vignette("GRasters", package = "fasterRaster")}). } \description{ -Geomorphons are idealized terrain types calculated from an elevator raster based on a moving window of a given size. The window is a torus (which can have an inner radius of 0, so can also be a circle), which allows it to identify geomorphons of a given size while ignoring ones larger or smaller. There are 10 basic geomorphons. Consult the The \strong{GRASS} module \href{https://grass.osgeo.org/grass84/manuals/r.geomorphon.html}{\code{r.geomorphon}} for more details and diagrams of each type of geomorphon. They include: +Geomorphons are idealized terrain types calculated from an elevator raster based on a moving window of a given size. The window is a torus (which can have an inner radius of 0, so can also be a circle), which allows it to identify geomorphons of a given size while ignoring ones larger or smaller. There are 10 basic geomorphons. Consult the the manual for \strong{GRASS} module \code{r.geomorphon} using \code{grassHelp("r.geomorphon")} for more details and diagrams of each type of geomorphon. Geomorphon types include: \enumerate{ \item Flat areas: Focal area has approximately the same elevation as surrounding areas \item Pits: An area is lower than all other surrounding areas @@ -76,5 +76,5 @@ plot(geos, col = col) } } \seealso{ -Module \href{https://grass.osgeo.org/grass84/manuals/r.geomorphon.html}{\code{r.geomorphon}} in \strong{GRASS} +\strong{GRASS} module \code{r.geomorphon} (see \code{grassHelp("r.geomorphon")}) } diff --git a/man/grass.Rd b/man/grassGUI.Rd similarity index 87% rename from man/grass.Rd rename to man/grassGUI.Rd index 9aaf2db3..3359fa25 100644 --- a/man/grass.Rd +++ b/man/grassGUI.Rd @@ -1,11 +1,11 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/grass.r -\name{grass,missing-method} -\alias{grass,missing-method} -\alias{grass} +% Please edit documentation in R/grassGUI.r +\name{grassGUI,missing-method} +\alias{grassGUI,missing-method} +\alias{grassGUI} \title{Start the GRASS GUI (potentially dangerous!)} \usage{ -\S4method{grass}{missing}() +\S4method{grassGUI}{missing}() } \value{ Nothing (starts the \strong{GRASS} GUI). @@ -20,7 +20,7 @@ if (grassStarted()) { # Starting the GRASS GUI and making changes to rasters or vectors can "break" # the GRasters and GVectors they are associated with in R. -if (FALSE) grass() +if (interactive() & FALSE) grassGUI() } } diff --git a/man/grassHelp.Rd b/man/grassHelp.Rd new file mode 100644 index 00000000..cd4c2a19 --- /dev/null +++ b/man/grassHelp.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/grassHelp.r +\name{grassHelp} +\alias{grassHelp} +\title{Open the help page for a GRASS module} +\usage{ +grassHelp(x, online = FALSE) +} +\arguments{ +\item{x}{Character: Any of: +\itemize{ +\item The name of a \strong{GRASS} module (e.g., \code{"r.mapcalc"}). +\item \code{"type"}: Display a page wherein modules are classified by types. +\item \code{"topics"}: Display an index of topics. +}} + +\item{online}{If \code{FALSE} (default), show the manual page that is included with your installation of \strong{GRASS}. If \code{FALSE}, show the manual page online (requires an Internet connection).} +} +\value{ +Nothing (opens a web page). +} +\description{ +This function opens the manual page for a \strong{GRASS} module (function) in your browser. +} +\examples{ +if (grassStarted() & interactive()) { + +# Open help pages for `r.mapcalc` and `r.sun`: +grassHelp("r.mapcalc") +grassHelp("r.sun") + +# Open topics page: +grassHelp("topics") + +# Open index page: +grassHelp("index") + +} +} diff --git a/man/hist.Rd b/man/hist.Rd index 764d60ae..9c5c25a9 100644 --- a/man/hist.Rd +++ b/man/hist.Rd @@ -29,9 +29,6 @@ This function creates a histogram of values in \code{GRaster}. The function is m \examples{ if (grassStarted()) { -# Setup -library(terra) - # Example data madElev <- fastData("madElev") # elevation raster madLANDSAT <- fastData("madLANDSAT") # multi-layer raster diff --git a/man/init.Rd b/man/init.Rd index cb464a56..032641d6 100644 --- a/man/init.Rd +++ b/man/init.Rd @@ -80,7 +80,6 @@ reg <- c(regOdd, regEven) names(reg) <- c("odd", "even") plot(reg) - } } \seealso{ diff --git a/man/interpIDW.Rd b/man/interpIDW.Rd index 940e3371..df1a71a6 100644 --- a/man/interpIDW.Rd +++ b/man/interpIDW.Rd @@ -25,5 +25,5 @@ A \code{GRaster}. This function interpolates values from a set of points to a raster using inverse distance weighting (IDW). } \seealso{ -\code{\link[terra:interpIDW]{terra::interpIDW()}}, \code{\link[=interpSplines]{interpSplines()}}, \code{\link[=fillNAs]{fillNAs()}}, module \href{https://grass.osgeo.org/grass84/manuals/v.surf.idw.html}{\code{v.surf.idw}} in \strong{GRASS} +\code{\link[terra:interpIDW]{terra::interpIDW()}}, \code{\link[=interpSplines]{interpSplines()}}, \code{\link[=fillNAs]{fillNAs()}}, \strong{GRASS} module \code{v.surf.idw} (se \code{grasshelp("v.surf.idw")}) } diff --git a/man/interpSplines.Rd b/man/interpSplines.Rd index d8bb9708..c0214592 100644 --- a/man/interpSplines.Rd +++ b/man/interpSplines.Rd @@ -54,5 +54,5 @@ If you receive the error, "No data within this subregion. Consider increasing sp If cross-validation takes too long, or other warnings/errors persist, you can randomly subsample \code{x} to ~100 points to get an optimum value of \code{lambda} (using \code{interpolate = FALSE}), then use this value in the same function again without cross-validation (setting \code{lambda} equal to this value and \code{interpolate = TRUE}). } \seealso{ -\code{\link[=interpIDW]{interpIDW()}}, \code{\link[=fillNAs]{fillNAs()}}, module \href{https://grass.osgeo.org/grass84/manuals/v.surf.bspline.html}{\code{v.surf.bspline}} in \strong{GRASS} +\code{\link[=interpIDW]{interpIDW()}}, \code{\link[=fillNAs]{fillNAs()}}, \strong{GRASS} module \code{v.surf.bspline} (see \code{grassHelp("v.surf.bspline")}) } diff --git a/man/location.Rd b/man/location.Rd index 21a4dc55..3927fc45 100644 --- a/man/location.Rd +++ b/man/location.Rd @@ -53,33 +53,37 @@ elev <- fast(madElev) chelsa1 <- fast(madChelsa1) # Name of the currently active location -.location() -.location(elev) -.location(chelsa1) +if (FALSE) { + + .location() + .location(elev) + .location(chelsa1) -# All available GRASS locations -.locations() + # All available GRASS locations + .locations() -# Find location of an object among all active locations -.locationFind(elev) -.locationFind(chelsa1) -.locationFind(chelsa1, return = "index") -.locationFind(chelsa1, return = "crs") + # Find location of an object among all active locations + .locationFind(elev) + .locationFind(chelsa1) + .locationFind(chelsa1, return = "index") + .locationFind(chelsa1, return = "crs") -# Switch between locations -.locationRestore(elev) -.locationRestore(chelsa1) + # Switch between locations + .locationRestore(elev) + .locationRestore(chelsa1) -loc <- .location(elev) -.locationRestore(loc) + loc <- .location(elev) + .locationRestore(loc) -# We could use .locationDelete(elev) to delete -# the location where "elev" is stored. + # Delete the location where "elev" is stored. + if (FALSE) .locationDelete(elev) # dangerous! -# Mapsets are always "PERMANENT" in fasterRaster -.mapset() -.mapset(elev) -.mapset(chelsa1) + # Mapsets are always "PERMANENT" in fasterRaster + .mapset() + .mapset(elev) + .mapset(chelsa1) + +} } if (grassStarted()) { @@ -98,33 +102,37 @@ elev <- fast(madElev) chelsa1 <- fast(madChelsa1) # Name of the currently active location -.location() -.location(elev) -.location(chelsa1) +if (FALSE) { + + .location() + .location(elev) + .location(chelsa1) -# All available GRASS locations -.locations() + # All available GRASS locations + .locations() -# Find location of an object among all active locations -.locationFind(elev) -.locationFind(chelsa1) -.locationFind(chelsa1, return = "index") -.locationFind(chelsa1, return = "crs") + # Find location of an object among all active locations + .locationFind(elev) + .locationFind(chelsa1) + .locationFind(chelsa1, return = "index") + .locationFind(chelsa1, return = "crs") -# Switch between locations -.locationRestore(elev) -.locationRestore(chelsa1) + # Switch between locations + .locationRestore(elev) + .locationRestore(chelsa1) -loc <- .location(elev) -.locationRestore(loc) + loc <- .location(elev) + .locationRestore(loc) -# We could use .locationDelete(elev) to delete -# the location where "elev" is stored. + # Delete the location where "elev" is stored. + if (FALSE) .locationDelete(elev) # dangerous! -# Mapsets are always "PERMANENT" in fasterRaster -.mapset() -.mapset(elev) -.mapset(chelsa1) + # Mapsets are always "PERMANENT" in fasterRaster + .mapset() + .mapset(elev) + .mapset(chelsa1) + +} } } diff --git a/man/locationCreate.Rd b/man/locationCreate.Rd index 677798ef..e6c37935 100644 --- a/man/locationCreate.Rd +++ b/man/locationCreate.Rd @@ -53,33 +53,37 @@ elev <- fast(madElev) chelsa1 <- fast(madChelsa1) # Name of the currently active location -.location() -.location(elev) -.location(chelsa1) +if (FALSE) { + + .location() + .location(elev) + .location(chelsa1) -# All available GRASS locations -.locations() + # All available GRASS locations + .locations() -# Find location of an object among all active locations -.locationFind(elev) -.locationFind(chelsa1) -.locationFind(chelsa1, return = "index") -.locationFind(chelsa1, return = "crs") + # Find location of an object among all active locations + .locationFind(elev) + .locationFind(chelsa1) + .locationFind(chelsa1, return = "index") + .locationFind(chelsa1, return = "crs") -# Switch between locations -.locationRestore(elev) -.locationRestore(chelsa1) + # Switch between locations + .locationRestore(elev) + .locationRestore(chelsa1) -loc <- .location(elev) -.locationRestore(loc) + loc <- .location(elev) + .locationRestore(loc) -# We could use .locationDelete(elev) to delete -# the location where "elev" is stored. + # Delete the location where "elev" is stored. + if (FALSE) .locationDelete(elev) # dangerous! -# Mapsets are always "PERMANENT" in fasterRaster -.mapset() -.mapset(elev) -.mapset(chelsa1) + # Mapsets are always "PERMANENT" in fasterRaster + .mapset() + .mapset(elev) + .mapset(chelsa1) + +} } } diff --git a/man/locationFind.Rd b/man/locationFind.Rd index c72056ff..8b010a12 100644 --- a/man/locationFind.Rd +++ b/man/locationFind.Rd @@ -62,33 +62,37 @@ elev <- fast(madElev) chelsa1 <- fast(madChelsa1) # Name of the currently active location -.location() -.location(elev) -.location(chelsa1) +if (FALSE) { + + .location() + .location(elev) + .location(chelsa1) -# All available GRASS locations -.locations() + # All available GRASS locations + .locations() -# Find location of an object among all active locations -.locationFind(elev) -.locationFind(chelsa1) -.locationFind(chelsa1, return = "index") -.locationFind(chelsa1, return = "crs") + # Find location of an object among all active locations + .locationFind(elev) + .locationFind(chelsa1) + .locationFind(chelsa1, return = "index") + .locationFind(chelsa1, return = "crs") -# Switch between locations -.locationRestore(elev) -.locationRestore(chelsa1) + # Switch between locations + .locationRestore(elev) + .locationRestore(chelsa1) -loc <- .location(elev) -.locationRestore(loc) + loc <- .location(elev) + .locationRestore(loc) -# We could use .locationDelete(elev) to delete -# the location where "elev" is stored. + # Delete the location where "elev" is stored. + if (FALSE) .locationDelete(elev) # dangerous! -# Mapsets are always "PERMANENT" in fasterRaster -.mapset() -.mapset(elev) -.mapset(chelsa1) + # Mapsets are always "PERMANENT" in fasterRaster + .mapset() + .mapset(elev) + .mapset(chelsa1) + +} } } diff --git a/man/locationRestore.Rd b/man/locationRestore.Rd index ae7d5694..4a67c1bb 100644 --- a/man/locationRestore.Rd +++ b/man/locationRestore.Rd @@ -49,33 +49,37 @@ elev <- fast(madElev) chelsa1 <- fast(madChelsa1) # Name of the currently active location -.location() -.location(elev) -.location(chelsa1) +if (FALSE) { + + .location() + .location(elev) + .location(chelsa1) -# All available GRASS locations -.locations() + # All available GRASS locations + .locations() -# Find location of an object among all active locations -.locationFind(elev) -.locationFind(chelsa1) -.locationFind(chelsa1, return = "index") -.locationFind(chelsa1, return = "crs") + # Find location of an object among all active locations + .locationFind(elev) + .locationFind(chelsa1) + .locationFind(chelsa1, return = "index") + .locationFind(chelsa1, return = "crs") -# Switch between locations -.locationRestore(elev) -.locationRestore(chelsa1) + # Switch between locations + .locationRestore(elev) + .locationRestore(chelsa1) -loc <- .location(elev) -.locationRestore(loc) + loc <- .location(elev) + .locationRestore(loc) -# We could use .locationDelete(elev) to delete -# the location where "elev" is stored. + # Delete the location where "elev" is stored. + if (FALSE) .locationDelete(elev) # dangerous! -# Mapsets are always "PERMANENT" in fasterRaster -.mapset() -.mapset(elev) -.mapset(chelsa1) + # Mapsets are always "PERMANENT" in fasterRaster + .mapset() + .mapset(elev) + .mapset(chelsa1) + +} } } diff --git a/man/locations.Rd b/man/locations.Rd index e4dd0aee..3c3467b6 100644 --- a/man/locations.Rd +++ b/man/locations.Rd @@ -34,33 +34,37 @@ elev <- fast(madElev) chelsa1 <- fast(madChelsa1) # Name of the currently active location -.location() -.location(elev) -.location(chelsa1) +if (FALSE) { + + .location() + .location(elev) + .location(chelsa1) -# All available GRASS locations -.locations() + # All available GRASS locations + .locations() -# Find location of an object among all active locations -.locationFind(elev) -.locationFind(chelsa1) -.locationFind(chelsa1, return = "index") -.locationFind(chelsa1, return = "crs") + # Find location of an object among all active locations + .locationFind(elev) + .locationFind(chelsa1) + .locationFind(chelsa1, return = "index") + .locationFind(chelsa1, return = "crs") -# Switch between locations -.locationRestore(elev) -.locationRestore(chelsa1) + # Switch between locations + .locationRestore(elev) + .locationRestore(chelsa1) -loc <- .location(elev) -.locationRestore(loc) + loc <- .location(elev) + .locationRestore(loc) -# We could use .locationDelete(elev) to delete -# the location where "elev" is stored. + # Delete the location where "elev" is stored. + if (FALSE) .locationDelete(elev) # dangerous! -# Mapsets are always "PERMANENT" in fasterRaster -.mapset() -.mapset(elev) -.mapset(chelsa1) + # Mapsets are always "PERMANENT" in fasterRaster + .mapset() + .mapset(elev) + .mapset(chelsa1) + +} } } diff --git a/man/longlat.Rd b/man/longlat.Rd index 290e82d8..0497ecaa 100644 --- a/man/longlat.Rd +++ b/man/longlat.Rd @@ -14,7 +14,7 @@ A \code{GRaster} stack. } \description{ -\code{longlat()} creates two rasters, one with cell values equal to the longitude of the cell centers, and one with cell values equal to the latitude of the cell centers. Values will be in decimal degrees, regardless of the projection of the raster. +\code{longlat()} creates two rasters, one with cell values equal to the longitude of the cell centers, and one with cell values equal to the latitude of the cell centers. Values will be in decimal degrees, regardless of the projection of the raster. If you want projected coordinates, use \code{\link[=init]{init()}}. } \examples{ if (grassStarted()) { @@ -34,3 +34,6 @@ ll # note units of cell values! } } +\seealso{ +\code{\link[=init]{init()}} +} diff --git a/man/mapset.Rd b/man/mapset.Rd index 4be3bc8c..b1abcf29 100644 --- a/man/mapset.Rd +++ b/man/mapset.Rd @@ -40,33 +40,37 @@ elev <- fast(madElev) chelsa1 <- fast(madChelsa1) # Name of the currently active location -.location() -.location(elev) -.location(chelsa1) +if (FALSE) { + + .location() + .location(elev) + .location(chelsa1) -# All available GRASS locations -.locations() + # All available GRASS locations + .locations() -# Find location of an object among all active locations -.locationFind(elev) -.locationFind(chelsa1) -.locationFind(chelsa1, return = "index") -.locationFind(chelsa1, return = "crs") + # Find location of an object among all active locations + .locationFind(elev) + .locationFind(chelsa1) + .locationFind(chelsa1, return = "index") + .locationFind(chelsa1, return = "crs") -# Switch between locations -.locationRestore(elev) -.locationRestore(chelsa1) + # Switch between locations + .locationRestore(elev) + .locationRestore(chelsa1) -loc <- .location(elev) -.locationRestore(loc) + loc <- .location(elev) + .locationRestore(loc) -# We could use .locationDelete(elev) to delete -# the location where "elev" is stored. + # Delete the location where "elev" is stored. + if (FALSE) .locationDelete(elev) # dangerous! -# Mapsets are always "PERMANENT" in fasterRaster -.mapset() -.mapset(elev) -.mapset(chelsa1) + # Mapsets are always "PERMANENT" in fasterRaster + .mapset() + .mapset(elev) + .mapset(chelsa1) + +} } } diff --git a/man/mow.Rd b/man/mow.Rd index 9ced6edd..dc14b0c1 100644 --- a/man/mow.Rd +++ b/man/mow.Rd @@ -4,13 +4,15 @@ \alias{mow} \title{Remove unused rasters and vectors from the GRASS cache} \usage{ -mow(x, type = NULL, verbose = TRUE, ask = TRUE) +mow(x, type = NULL, keeps = NULL, verbose = TRUE, ask = TRUE) } \arguments{ \item{x}{Either missing (default) or an environment.} \item{type}{Either \code{NULL} or a character vector. If \code{NULL}, all rasters and vectors in the \strong{GRASS} cache are candidates for deletion. Otherwise, this can be either \code{"raster"}, \code{"vector"}, or both.} +\item{keeps}{Either \code{NULL} (default) or a \code{list()} of \code{GRaster}s and/or \code{GVector}s that you want to retain. The rasters and vectors in \strong{GRASS} pointed to by these objects will not be deleted.} + \item{verbose}{Logical: If \code{TRUE} (default), report progress.} \item{ask}{Logical: If \code{TRUE} (default), prompt for reassurance.} @@ -19,11 +21,9 @@ mow(x, type = NULL, verbose = TRUE, ask = TRUE) Invisibly returns a list with the number of rasters and vectors deleted. } \description{ -\strong{fasterRaster} attempts to remove intermediate rasters and vectors from the \strong{GRASS} cache as they are not needed, but files can still accumulate, especially if you remove \code{GRaster}s or \code{GVector}s from the \strong{R} working environment (e.g., by using \code{\link[=rm]{rm()}} or simply using the same variable name for different rasters/vectors). This function will a) search the \strong{GRASS} cache for all rasters/vectors there; and b) remove any of them that are not pointed to by an object in the active \strong{R} environment. - -Note that calling this function inside another function's environment can be very dangerous, as it will only be able to see objects in that environment, and thus delete any rasters/vectors outside that environment. +\strong{fasterRaster} attempts to delete rasters and vectors in the \strong{GRASS} cache. This function will a) search the current \strong{GRASS} "project/location" cache for all rasters/vectors there; and b) remove any of them that are not pointed to by an object in the active \strong{R} environment. Only objects in the currently active \strong{GRASS} project/location will be removed (see \code{vignette("project_mapset", package = "fasterRaster")}). -Note also that this function will only clean the current \strong{GRASS} "project"/"location" (see \code{vignette("projects_mapsets", package = "fasterRaster")}). +Note that calling this function inside another function's environment without providing an argument for \code{x} can be very \strong{dangerous}, as it will detect objects outside that environment, and thus delete any rasters/vectors outside that environment. } \examples{ if (grassStarted()) { diff --git a/man/plot.Rd b/man/plot.Rd index 0f804ef9..480bbc2c 100644 --- a/man/plot.Rd +++ b/man/plot.Rd @@ -32,9 +32,6 @@ This function is essentially a hack, as it it not possible to dependably call th \examples{ if (grassStarted()) { -# Setup -library(terra) - # Example data madElev <- fastData("madElev") # elevation raster madLANDSAT <- fastData("madLANDSAT") # multi-layer raster diff --git a/man/plotRGB.Rd b/man/plotRGB.Rd index 24726c87..74c3c3f9 100644 --- a/man/plotRGB.Rd +++ b/man/plotRGB.Rd @@ -27,9 +27,6 @@ This function takes as its main argument a \code{GRaster} with at least three la \examples{ if (grassStarted()) { -# Setup -library(terra) - # Example data madElev <- fastData("madElev") # elevation raster madLANDSAT <- fastData("madLANDSAT") # multi-layer raster diff --git a/man/rasterize.Rd b/man/rasterize.Rd index 9eaac06d..483cbd8a 100644 --- a/man/rasterize.Rd +++ b/man/rasterize.Rd @@ -69,5 +69,5 @@ plot(byRiver) } } \seealso{ -\code{\link[terra:rasterize]{terra::rasterize()}}, module \href{https://grass.osgeo.org/grass84/manuals/v.to.rast.html}{\code{v.to.rast}} in \strong{GRASS} +\code{\link[terra:rasterize]{terra::rasterize()}}, \strong{GRASS} module \code{v.to.rast} (see \code{grassHelp("v.to.rast")}) } diff --git a/man/regress.Rd b/man/regress.Rd index 37340da6..4f34709e 100644 --- a/man/regress.Rd +++ b/man/regress.Rd @@ -61,7 +61,7 @@ which.max(chelsa) # Regression # Note the intercept is different for fasterRaster::regress(). regress(chelsa) -regress(madChelsa) +regress(madChelsa, 1:nlyr(madChelsa)) # Note: To get quantiles for each layer, use # global(x, "quantile", probs = 0.2). diff --git a/man/resample.Rd b/man/resample.Rd index 03c08588..330a2999 100644 --- a/man/resample.Rd +++ b/man/resample.Rd @@ -105,5 +105,5 @@ plot(terraLanczos, add=TRUE) } } \seealso{ -\code{\link[terra:resample]{terra::resample()}}, modules \href{https://grass.osgeo.org/grass84/manuals/r.resample.html}{\code{r.resample}} and \href{https://grass.osgeo.org/grass84/manuals/r.resamp.interp.html}{\code{r.resamp.interp}} in \strong{GRASS} +\code{\link[terra:resample]{terra::resample()}}, \strong{GRASS} modules \code{r.resample} and \code{r.resamp.interp} (see \verb{grassHelp("}r.resample\verb{") and }grassHelp("\code{r.resamp.interp}")`) } diff --git a/man/streams.Rd b/man/streams.Rd index 494eb973..d803dd21 100644 --- a/man/streams.Rd +++ b/man/streams.Rd @@ -34,7 +34,7 @@ A \code{GRaster}. } \description{ -This function estimates the course of streams and rivers from an elevation raster. It is based on the \strong{GRASS} module \verb{\\href\{https://grass.osgeo.org/grass84/manuals/r.stream.extract.html\}\{r.stream.extract\}}, where more details can be found. +This function estimates the course of streams and rivers from an elevation raster. It is based on the \strong{GRASS} module \code{r.stream.extract}, where more details can be found (see \code{grassHelp("r.stream.extract")}) } \examples{ if (grassStarted()) { @@ -55,5 +55,5 @@ plot(streams) } } \seealso{ -\code{\link[=flow]{flow()}}, \code{\link[=flowPath]{flowPath()}}, the \verb{\\href\{https://grass.osgeo.org/grass84/manuals/r.stream.extract.html\}\{r.stream.extract\}} module in \strong{GRASS} +\code{\link[=flow]{flow()}}, \code{\link[=flowPath]{flowPath()}}, \strong{GRASS} module \code{r.stream.extract} (see \code{grassHelp("r.stream.extract")}) } diff --git a/man/sun.Rd b/man/sun.Rd index 6c54ab55..06411cb5 100644 --- a/man/sun.Rd +++ b/man/sun.Rd @@ -46,7 +46,7 @@ sun( \item{albedo}{A \code{GRaster} or a numeric value: This is either a raster with values of ground albedo or a numeric value (in which case albedo is assumed to be the same everywhere). Albedo is unit-less, and the default value is 0.2.} -\item{linke}{A \code{GRaster} or a numeric value: This is either a raster with values of the Linke atmospheric turbidity coefficient or a numeric value (in which case the same value is assumed for all locations). The Linke coefficient is unit-less. The default value is 3, but see also the \strong{GRASS} manual page for module \href{https://grass.osgeo.org/grass84/manuals/r.sun.html}{\code{r.sun}}.} +\item{linke}{A \code{GRaster} or a numeric value: This is either a raster with values of the Linke atmospheric turbidity coefficient or a numeric value (in which case the same value is assumed for all locations). The Linke coefficient is unit-less. The default value is 3, but see also the \strong{GRASS} manual page for module \code{r.sun} (\code{grassHelp("r.sun")}).} \item{day}{Positive integer between 1 to 365, inclusive: Day of year for which to calculate ir/radiation. Default is 1 (January 1st).} @@ -83,7 +83,7 @@ A raster or raster stack stack with the same extent, resolution, and coordinate } } \description{ -The \code{sun()} function calculates beam (direct), diffuse and ground reflected solar irradiation for a given day and set of topographic and atmospheric conditions. The function relies on the \strong{GRASS} module \href{https://grass.osgeo.org/grass84/manuals/r.sun.html}{\code{r.sun}}, which contains a detailed explanation. +The \code{sun()} function calculates beam (direct), diffuse and ground reflected solar irradiation for a given day and set of topographic and atmospheric conditions. The function relies on the \strong{GRASS} module \code{r.sun}, the manual page for which contains a detailed explanation (see \code{grassHelp("r.sun")}) } \examples{ if (grassStarted()) { @@ -146,5 +146,5 @@ solar } } \seealso{ -\code{\link[=terrain]{terrain()}}, \code{\link[=horizonHeight]{horizonHeight()}}, module \href{https://grass.osgeo.org/grass84/manuals/r.sun.html}{\code{r.sun}} in \strong{GRASS} +\code{\link[=terrain]{terrain()}}, \code{\link[=horizonHeight]{horizonHeight()}}, \strong{GRASS} module \code{r.sun}(see \code{grassHelp("r.sun")}) } diff --git a/man/wetness.Rd b/man/wetness.Rd index ce44af4c..6f8e2980 100644 --- a/man/wetness.Rd +++ b/man/wetness.Rd @@ -39,5 +39,5 @@ plot(c(elev, twi)) } } \seealso{ -\code{\link[=terrain]{terrain()}}, \code{\link[=ruggedness]{ruggedness()}}, \code{\link[=geomorphons]{geomorphons()}}, module \href{https://grass.osgeo.org/grass84/manuals/r.topidx.html}{\code{r.topidx}} in \strong{GRASS} +\code{\link[=terrain]{terrain()}}, \code{\link[=ruggedness]{ruggedness()}}, \code{\link[=geomorphons]{geomorphons()}}, \strong{GRASS} module \code{r.topidx} (see \code{grassHelp("r.topidx")}) } diff --git a/man/writeRaster.Rd b/man/writeRaster.Rd index 8d3b89a8..de7f1b6f 100644 --- a/man/writeRaster.Rd +++ b/man/writeRaster.Rd @@ -80,7 +80,7 @@ A \code{GRaster} (invisibly). A raster is also saved to disk. \description{ This function saves a \code{GRaster} to disk directly from a \strong{GRASS} session. It is faster than using \code{\link[=rast]{rast()}}, then saving the output of that to disk (because \code{rast()} actually save the raster to disk, anyway). -The function will attempt to ascertain the file type to be ascertained from the file extension, but you can specify the format using the \code{format} argument (see entry for \code{...}). You can see a list of supported formats by simply using this function with no arguments, as in \code{writeRaster()}, or by consulting the online help page for the \strong{GRASS} module \href{https://grass.osgeo.org/grass84/manuals/r.out.gdal.html}{\code{r.out.gdal}}. Only the \code{GeoTIFF} file format is guaranteed to work for multi-layered rasters. +The function will attempt to ascertain the file type to be ascertained from the file extension, but you can specify the format using the \code{format} argument (see entry for \code{...}). You can see a list of supported formats by simply using this function with no arguments, as in \code{writeRaster()}, or by consulting the online help page for the \strong{GRASS} module \code{r.out.gdal} (see \code{grassHelp("r.out.gdal")}). Only the \code{GeoTIFF} file format is guaranteed to work for multi-layered rasters. The function will attempt to optimize the \code{datatype} argument, but this can take a long time. You can speed this up by setting \code{datatype} manually. Note that if you are saving a "stack" of \code{GRaster}s with different \code{datatype}s, the one with the highest information density will be used (e.g., low-bit integer < high-bit integer < floating-point < double-floating point). This can make rasters with lower datatypes much larger on disk. In these cases, it make be best to save rasters with similar \code{datatype}s together. } @@ -128,5 +128,5 @@ chelsaBio1 } } \seealso{ -\code{\link[terra:writeRaster]{terra::writeRaster()}}, module \href{https://grass.osgeo.org/grass84/manuals/r.out.gdal.html}{\code{r.out.gdal}} in \strong{GRASS} +\code{\link[terra:writeRaster]{terra::writeRaster()}}, \strong{GRASS} module \code{r.out.gdal} (see \code{grassHelp("r.out.gdal")}) } diff --git a/man/writeVector.Rd b/man/writeVector.Rd index 9cb0d377..dc2d08cb 100644 --- a/man/writeVector.Rd +++ b/man/writeVector.Rd @@ -37,7 +37,7 @@ \item{attachTable}{Logical: If \code{TRUE} (default), attach the attribute to table to the vector before saving it. If \code{FALSE}, the attribute table will not be attached.} -\item{...}{Additional arguments to send to \strong{GRASS} module \href{https://grass.osgeo.org/grass84/manuals/v.out.ogr.html}{\code{v.out.ogr}} in \strong{GRASS}.} +\item{...}{Additional arguments to send to \strong{GRASS} module \code{v.out.ogr} (see \code{grassHelp("v.out.ogr")}).} } \value{ Invisibly returns a \code{GRaster} (the input, \code{x}). Also saves the vector to disk. @@ -45,7 +45,7 @@ Invisibly returns a \code{GRaster} (the input, \code{x}). Also saves the vector \description{ This function saves a \code{GVector} to disk directly from a \strong{GRASS} session. -By default, files will be of OGC GeoPackage format (extension "\code{.gpkg}"), but this can be changed with the \code{format} argument. You can see a list of supported formats by simply using this function with no arguments, as in \code{writeVector()}, or by consulting the online help page for \strong{GRASS} module \href{https://grass.osgeo.org/grass84/manuals/v.out.ogr.html}{\code{v.out.ogr}}. +By default, files will be of OGC GeoPackage format (extension "\code{.gpkg}"), but this can be changed with the \code{format} argument. You can see a list of supported formats by simply using this function with no arguments, as in \code{writeVector()}, or by consulting the online help page for \strong{GRASS} module \code{v.out.ogr} (see \code{grassHelp("v.out.ogr")}). Note that if the vector has a data table attached and at least one numeric or integer column has an \code{NA} or \code{NaN} value, the function will yield a warning like: @@ -97,7 +97,7 @@ writeVector(rivers, filename) } } \seealso{ -\code{\link[terra:writeVector]{terra::writeVector()}}, \code{\link[sf:st_write]{sf::st_write()}}, module \href{https://grass.osgeo.org/grass84/manuals/v.out.ogr.html}{\code{v.out.ogr}} in \strong{GRASS} +\code{\link[terra:writeVector]{terra::writeVector()}}, \code{\link[sf:st_write]{sf::st_write()}}, \strong{GRASS} module \code{v.out.ogr} (see \code{grassHelp("v.out.ogr")}) \code{\link[terra:writeVector]{terra::writeVector()}} } From 8ab90e24b921ed4dbcaa0624497ff3a6581f67a1 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 22:38:13 -0500 Subject: [PATCH 087/125] Update 01_generics.r --- R/01_generics.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/01_generics.r b/R/01_generics.r index 3b9c0a32..4420a357 100644 --- a/R/01_generics.r +++ b/R/01_generics.r @@ -149,7 +149,7 @@ methods::setGeneric(name = "fragmentation", def = function(x, ...) standardGener methods::setGeneric(name = "geomtype", package = "terra") methods::setGeneric(name = "geomorphons", def = function(x, ...) standardGeneric("geomorphons")) methods::setGeneric(name = "global", package = "terra") -methods::setGeneric(name = "grass", def = function(x, ...) standardGeneric("grass")) +methods::setGeneric(name = "grassGUI", def = function(x, ...) standardGeneric("grassGUI")) methods::setGeneric(name = "grid", def = function(x, ...) standardGeneric("grid")) methods::setGeneric(name = "head", package = "utils") From b9e3a49542a8a094d55817088a6aa9aaac252be1 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 22:38:18 -0500 Subject: [PATCH 088/125] Update complete.cases.r --- R/complete.cases.r | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/complete.cases.r b/R/complete.cases.r index 4fceb462..5a35fea1 100644 --- a/R/complete.cases.r +++ b/R/complete.cases.r @@ -28,7 +28,7 @@ methods::setMethod( function(..., levels = TRUE) { dots <- list(...) - if (length(dots) != 1L) stop("Can only assess complete cases for a single GRaster at a time (though it can be multi-layered).") + if (length(dots) != 1L) stop("This function only assess complete cases for a single GRaster at a time (though it can be multi-layered).") x <- dots[[1L]] if (levels) { @@ -63,7 +63,7 @@ methods::setMethod( function(...) { dots <- list(...) - if (length(dots) != 1L) stop("Can only assess complete cases for a single GVector at a time.") + if (length(dots) != 1L) stop("Tis function can only assess complete cases for a single GVector at a time.") x <- dots[[1L]] table <- x@table @@ -86,7 +86,7 @@ methods::setMethod( function(..., levels = TRUE) { dots <- list(...) - if (length(dots) != 1L) stop("Can only assess complete cases for a single GRaster at a time (though it can be multi-layered).") + if (length(dots) != 1L) stop("This function can only assess complete cases for a single GRaster at a time (though it can be multi-layered).") x <- dots[[1L]] if (levels) { @@ -121,7 +121,7 @@ methods::setMethod( function(...) { dots <- list(...) - if (length(dots) != 1L) stop("Can only assess missing cases for a single GVector at a time.") + if (length(dots) != 1L) stop("This function can only assess missing cases for a single GVector at a time.") x <- dots[[1L]] table <- x@table From f2af6f0204fea94b8e519c51a4c5584e16f7a667 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 22:38:21 -0500 Subject: [PATCH 089/125] Update longlat.r --- R/longlat.r | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/R/longlat.r b/R/longlat.r index 8dbd6318..c352546c 100644 --- a/R/longlat.r +++ b/R/longlat.r @@ -1,10 +1,12 @@ #' Create longitude/latitude rasters #' -#' @description `longlat()` creates two rasters, one with cell values equal to the longitude of the cell centers, and one with cell values equal to the latitude of the cell centers. Values will be in decimal degrees, regardless of the projection of the raster. +#' @description `longlat()` creates two rasters, one with cell values equal to the longitude of the cell centers, and one with cell values equal to the latitude of the cell centers. Values will be in decimal degrees, regardless of the projection of the raster. If you want projected coordinates, use [init()]. #' #' @param x A `GRaster`. #' #' @returns A `GRaster` stack. +#' +#' @seealso [init()] #' #' @example man/examples/ex_longlat.r #' From 399b2378c42e93b7ec54414ca279da723712737f Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 23:28:58 -0500 Subject: [PATCH 090/125] Add progress bar --- R/extract.r | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/R/extract.r b/R/extract.r index e0e35be9..947abd20 100644 --- a/R/extract.r +++ b/R/extract.r @@ -36,7 +36,7 @@ #' #' @param cats Logical (extracting from a raster): If `TRUE` (default) and `x` is a categorical `GRaster`, then return the category labels instead of the values. #' -#' @param verbose Logical: If `TRUE`, display progress (will only function when extracting from points on a `GRaster` when the number of `GRaster`s is large). +#' @param verbose Logical: If `TRUE`, display progress (will only function when extracting from points on a `GRaster` when the number of `GRaster`s is large, or when extracting using a "points" `GVector` with lots of points). #' #' @param ... Arguments to pass to [project()]. This is used only if extracting from a `GRaster` at locations specified by a `GVector`, and they have a different coordinate reference system. In this case, users should specify the `wrap` argument to [project()]. #' @@ -449,14 +449,14 @@ methods::setMethod( methods::setMethod( f = "extract", signature = c(x = "GVector", y = "GVector"), - function(x, y, xy = FALSE) { + function(x, y, xy = FALSE, verbose = TRUE) { if (geomtype(y) != "points") stop("Argument`y` must be a points vector.") if (is.3d(y)) warning("Coordinates in the z-dimension will be ignored.") .locationRestore(x) coords <- crds(y, z = FALSE) - .extractFromVect(x, coords, xy) + .extractFromVect(x, coords, xy, verbose = verbose) } # EOF @@ -467,8 +467,8 @@ methods::setMethod( #' @exportMethod extract methods::setMethod( f = "extract", - signature = c(x = "GVector", y = "data.frame"), - function(x, y, xy = FALSE) .extractFromVect(x, y[ , 1L:2L], xy) + signature = c(x = "GVector", y = "data.frame", verbose = TRUE), + function(x, y, xy = FALSE) .extractFromVect(x, y[ , 1L:2L], xy, verbose = verbose) ) #' @aliases extract @@ -477,10 +477,10 @@ methods::setMethod( methods::setMethod( f = "extract", signature = c(x = "GVector", y = "data.table"), - function(x, y, xy = FALSE) { + function(x, y, xy = FALSE, verbose = TRUE) { y <- as.data.frame(y) - .extractFromVect(x, y[ , 1L:2L], xy) + .extractFromVect(x, y[ , 1L:2L], xy, verbose = verbose) } # EOF ) @@ -490,8 +490,8 @@ methods::setMethod( #' @exportMethod extract methods::setMethod( f = "extract", - signature = c(x = "GVector", y = "matrix"), - function(x, y, xy = FALSE) .extractFromVect(x, y[ , 1L:2L], xy) + signature = c(x = "GVector", y = "matrix", verbose = TRUE), + function(x, y, xy = FALSE) .extractFromVect(x, y[ , 1L:2L], xy, verbose = verbose) ) #' @aliases extract @@ -503,7 +503,7 @@ methods::setMethod( function(x, y, xy = FALSE) { y <- cbind(y) - .extractFromVect(x, y, xy) + .extractFromVect(x, y, xy, verbose = FALSE) } # EOF ) @@ -516,9 +516,9 @@ methods::setMethod( # methods::setMethod( # f = "extract", # signature = c(x = "GVector", y = "character"), -# function(x, y, xy = FALSE) { +# function(x, y, xy = FALSE, verbose = TRUE) { -# y <- .crdsVect(y, z = FALSE, gm = "points") +# y <- .crdsVect(y, z = FALSE, gm = "points", verbose = verbose) # .extractFromVect(x, y, xy) # } # EOF @@ -529,9 +529,10 @@ methods::setMethod( #' @param xy T/F: Return coordinates #' @param nperSet Number of points to extract... too large throws an error. 1000 seems to break, so going with 900. #' @param data.table return as `data.table` +#' @param verbose Logical. #' #' @noRd -.extractFromVect <- function(x, y, xy, nPerSet = 900L, data.table = TRUE) { +.extractFromVect <- function(x, y, xy, nPerSet = 900L, data.table = TRUE, verbose = FALSE) { .locationRestore(x) @@ -542,17 +543,24 @@ methods::setMethod( if (n > nPerSet) { + sets <- ceiling(n / nPerSet) + if (verbose | faster("verbose")) { + pb <- utils::txtProgressBar(min = 0, max = sets, initial = 0, style = 3, width = 30) + } + out <- data.table::data.table() for (set in seq_len(sets)) { - - omnibus::say(set) + + if (verbose | faster("verbose")) utils::setTxtProgressBar(pb, set) yy <- y[(1L + (set - 1L) * nPerSet):min(set * nPerSet, n)] - thisOut <- .extractFromVect(x, y = yy, xy = xy, data.table = TRUE) + thisOut <- .extractFromVect(x, y = yy, xy = xy, data.table = TRUE, verbose = FALSE) out <- rbind(out, thisOut) } + + if (verbose | faster("verbose")) close(pb) } else { @@ -604,9 +612,7 @@ methods::setMethod( if (nrow(x) == 0L) { id.x <- NULL - out <- data.table::data.table(id.y = 1L:n, id.x = NA_integer_) - if (lenNonnas > 0L) out[nonnasIndex, id.x := nons] # vector has data table From b663b8fd09a02bb2299a3c8c05f9497771fbbabc Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 23:29:09 -0500 Subject: [PATCH 091/125] Update locationRestore.r --- R/locationRestore.r | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/locationRestore.r b/R/locationRestore.r index 2b5e96af..68a97e99 100644 --- a/R/locationRestore.r +++ b/R/locationRestore.r @@ -79,7 +79,6 @@ methods::setMethod( ### reconnect to location emptyRast <- terra::rast(matrix(1L), crs = coordRef) - ### start the GRASS session suppressWarnings( session <- rgrass::initGRASS( gisBase = grassDir, @@ -95,7 +94,7 @@ methods::setMethod( ) ) - .fasterRaster$activeLocation <- location + .fasterRaster$activeLocation <<- location } From 7e59d9981be2dd533f353907b4da337b05226dc0 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Mon, 7 Oct 2024 23:29:13 -0500 Subject: [PATCH 092/125] Update mow.Rd --- man/mow.Rd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/mow.Rd b/man/mow.Rd index dc14b0c1..2edbde7f 100644 --- a/man/mow.Rd +++ b/man/mow.Rd @@ -4,14 +4,14 @@ \alias{mow} \title{Remove unused rasters and vectors from the GRASS cache} \usage{ -mow(x, type = NULL, keeps = NULL, verbose = TRUE, ask = TRUE) +mow(x, type = NULL, keep = NULL, verbose = TRUE, ask = TRUE) } \arguments{ \item{x}{Either missing (default) or an environment.} \item{type}{Either \code{NULL} or a character vector. If \code{NULL}, all rasters and vectors in the \strong{GRASS} cache are candidates for deletion. Otherwise, this can be either \code{"raster"}, \code{"vector"}, or both.} -\item{keeps}{Either \code{NULL} (default) or a \code{list()} of \code{GRaster}s and/or \code{GVector}s that you want to retain. The rasters and vectors in \strong{GRASS} pointed to by these objects will not be deleted.} +\item{keep}{Either \code{NULL} (default) or a \code{list()} of \code{GRaster}s and/or \code{GVector}s that you want to retain. The rasters and vectors in \strong{GRASS} pointed to by these objects will not be deleted.} \item{verbose}{Logical: If \code{TRUE} (default), report progress.} From f4a81cb7771a8f681227821bef7b774b82ef0439 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Tue, 8 Oct 2024 09:14:15 -0500 Subject: [PATCH 093/125] Forgot `entry` arg --- R/grassHelp.r | 1 + 1 file changed, 1 insertion(+) diff --git a/R/grassHelp.r b/R/grassHelp.r index 72354d75..c27c7d22 100644 --- a/R/grassHelp.r +++ b/R/grassHelp.r @@ -23,6 +23,7 @@ grassHelp <- function(x, online = FALSE) { args <- list( cmd = "g.manual", + entry = x, flags = .quiet() ) From 8f4d8d69d5eca1a9c57d2a77253d7e88108657c8 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Tue, 8 Oct 2024 09:14:31 -0500 Subject: [PATCH 094/125] Add `verbose` --- R/extract.r | 8 ++++---- man/extract.Rd | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/R/extract.r b/R/extract.r index 947abd20..8d048a2c 100644 --- a/R/extract.r +++ b/R/extract.r @@ -467,8 +467,8 @@ methods::setMethod( #' @exportMethod extract methods::setMethod( f = "extract", - signature = c(x = "GVector", y = "data.frame", verbose = TRUE), - function(x, y, xy = FALSE) .extractFromVect(x, y[ , 1L:2L], xy, verbose = verbose) + signature = c(x = "GVector", y = "data.frame"), + function(x, y, xy = FALSE, verbose = TRUE) .extractFromVect(x, y[ , 1L:2L], xy, verbose = verbose) ) #' @aliases extract @@ -490,8 +490,8 @@ methods::setMethod( #' @exportMethod extract methods::setMethod( f = "extract", - signature = c(x = "GVector", y = "matrix", verbose = TRUE), - function(x, y, xy = FALSE) .extractFromVect(x, y[ , 1L:2L], xy, verbose = verbose) + signature = c(x = "GVector", y = "matrix"), + function(x, y, xy = FALSE, verbose = TRUE) .extractFromVect(x, y[ , 1L:2L], xy, verbose = verbose) ) #' @aliases extract diff --git a/man/extract.Rd b/man/extract.Rd index 08517e70..28965862 100644 --- a/man/extract.Rd +++ b/man/extract.Rd @@ -34,13 +34,13 @@ \S4method{extract}{GRaster,numeric}(x, y, xy = FALSE, cats = TRUE) -\S4method{extract}{GVector,GVector}(x, y, xy = FALSE) +\S4method{extract}{GVector,GVector}(x, y, xy = FALSE, verbose = TRUE) -\S4method{extract}{GVector,data.frame}(x, y, xy = FALSE) +\S4method{extract}{GVector,data.frame}(x, y, xy = FALSE, verbose = TRUE) -\S4method{extract}{GVector,data.table}(x, y, xy = FALSE) +\S4method{extract}{GVector,data.table}(x, y, xy = FALSE, verbose = TRUE) -\S4method{extract}{GVector,matrix}(x, y, xy = FALSE) +\S4method{extract}{GVector,matrix}(x, y, xy = FALSE, verbose = TRUE) \S4method{extract}{GVector,numeric}(x, y, xy = FALSE) } @@ -76,7 +76,7 @@ \item{cats}{Logical (extracting from a raster): If \code{TRUE} (default) and \code{x} is a categorical \code{GRaster}, then return the category labels instead of the values.} -\item{verbose}{Logical: If \code{TRUE}, display progress (will only function when extracting from points on a \code{GRaster} when the number of \code{GRaster}s is large).} +\item{verbose}{Logical: If \code{TRUE}, display progress (will only function when extracting from points on a \code{GRaster} when the number of \code{GRaster}s is large, or when extracting using a "points" \code{GVector} with lots of points).} \item{...}{Arguments to pass to \code{\link[=project]{project()}}. This is used only if extracting from a \code{GRaster} at locations specified by a \code{GVector}, and they have a different coordinate reference system. In this case, users should specify the \code{wrap} argument to \code{\link[=project]{project()}}.} } From 96a0a7048ab29b1eaa7105e20ac18ca6993bdf6a Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Tue, 8 Oct 2024 11:40:22 -0500 Subject: [PATCH 095/125] Different temp dir... `tempdir()` can fail --- R/flow.r | 15 +++++++++++---- man/flow.Rd | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/R/flow.r b/R/flow.r index d0f5f409..bc967107 100644 --- a/R/flow.r +++ b/R/flow.r @@ -25,7 +25,7 @@ #' #' @seealso [flowPath()], [streams()], the **GRASS** module `r.terraflow` (see `grassHelp("r.terraflow")`) #' -#' @param scratchDir Character: Directory in which to store temporary files. The **GRASS** module `r.terraflow` makes a lot of temporary files. The default is given by [tempdir()]. +#' @param scratchDir Character or `NULL` (default): Directory in which to store temporary files. The **GRASS** module `r.terraflow` makes a lot of temporary files. If this is `NULL`, then a temporary folder in the user's working directory will be used (see [getwd()]). #' #' @returns A `GRaster`. #' @@ -42,9 +42,11 @@ methods::setMethod( direction = "multi", return = "accumulation", dirThreshold = Inf, - scratchDir = tempdir() + scratchDir = NULL ) { + if (nlyr(x) > 1L) stop("This function can only use a single-layered GRaster as input.") + returns <- c("accumulation", "basins", "direction", "flooded", "TCI") if (any(return == "*")) return <- returns return <- omnibus::pmatchSafe(return, returns) @@ -52,8 +54,6 @@ methods::setMethod( direction <- omnibus::pmatchSafe(direction, c("single", "multi"), nmax = 1L) - if (nlyr(x) > 1L) stop("This function can only use a single-layered GRaster as input.") - .locationRestore(x) .region(x) @@ -74,6 +74,13 @@ methods::setMethod( if (any(return == "flooded")) args$filled <- .makeSourceName("r_flow_filled", "raster") if (any(return == "TCI")) args$tci <- .makeSourceName("r_flow_tci", "raster") + if (is.null(scratchDir)) { + scratchDir <- getwd() + scratchDir <- paste0(scratchDir, '/flow_temp_files') + omnibus::dirCreate(scratchDir) + on.exit(unlink(scratchDir, recursive = TRUE, force = TRUE, expand = TRUE), add = TRUE) + } + do.call(rgrass::execGRASS, args = args) names <- srcs <- character() diff --git a/man/flow.Rd b/man/flow.Rd index 994e1b09..1ed5194b 100644 --- a/man/flow.Rd +++ b/man/flow.Rd @@ -10,7 +10,7 @@ direction = "multi", return = "accumulation", dirThreshold = Inf, - scratchDir = tempdir() + scratchDir = NULL ) } \arguments{ @@ -30,7 +30,7 @@ \item{dirThreshold}{Numeric (default is \code{Inf}): For the multi-direction flow model, this indicates the amount of accumulated flow above which the single-direction flow rule is used to locate the egress of water from a cell. This is the \code{d8cut} parameter in \code{r.stream.extract}.} -\item{scratchDir}{Character: Directory in which to store temporary files. The \strong{GRASS} module \code{r.terraflow} makes a lot of temporary files. The default is given by \code{\link[=tempdir]{tempdir()}}.} +\item{scratchDir}{Character or \code{NULL} (default): Directory in which to store temporary files. The \strong{GRASS} module \code{r.terraflow} makes a lot of temporary files. If this is \code{NULL}, then a temporary folder in the user's working directory will be used (see \code{\link[=getwd]{getwd()}}).} } \value{ A \code{GRaster}. From cb7813bf2e9f43f38bb0e8c1618d8fda95197296 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 01:00:50 -0500 Subject: [PATCH 096/125] Fix bug related to attaching db --- R/connectors.r | 13 +++++++++---- man/connectors.Rd | 6 +++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/R/connectors.r b/R/connectors.r index 447aa4bf..1e907dbe 100644 --- a/R/connectors.r +++ b/R/connectors.r @@ -5,9 +5,9 @@ #' @param x,y `GVector`s. #' @param minDist,maxDist Either `NULL` (default) or numeric values: Ignore features separated by less than or greater than these distances. #' -#' @return A `GVector`. +#' @returns A `GVector` with a data table that has the length of each connecting line in meters. #' -#' @seealso Module `v.distance` in **GRASS** +#' @seealso **GRASS** module `v.distance` (see `grassHelp("v.distance")`). #' #' @example man/examples/ex_connectors.r #' @@ -30,9 +30,11 @@ methods::setMethod( if (is.null(maxDist)) maxDist <- -1 if (!.vHasDatabase(x)) { + cats <- .vCats(x) + # db <- data.frame(TEMPTEMP_minDist_meters = -1) db <- data.frame(cat = cats, TEMPTEMP_minDist_meters = -1) - .vAttachDatabase(x, table = db, replace = TRUE) + .vAttachDatabase(x, table = db, replace = TRUE, cats = cats) } @@ -54,7 +56,10 @@ methods::setMethod( # # remove column that was added to x # rgrass::execGRASS("v.db.dropcolumn", map = sources(x), columns = "TEMPTEMP_minDist_meters", flags = .quiet()) - .makeGVector(src) + table <- .vAsDataTable(x) + table <- table[ , "TEMPTEMP_minDist_meters"] + names(table) <- "distance_x_to_y_meters" + .makeGVector(src, table = table) } # EOF ) diff --git a/man/connectors.Rd b/man/connectors.Rd index 1046b84b..ae368ef8 100644 --- a/man/connectors.Rd +++ b/man/connectors.Rd @@ -13,7 +13,7 @@ \item{minDist, maxDist}{Either \code{NULL} (default) or numeric values: Ignore features separated by less than or greater than these distances.} } \value{ -A \code{GVector}. +A \code{GVector} with a data table that has the length of each connecting line in meters. } \description{ \code{connectors()} creates a lines \code{GVector} which represent the shortest (Great Circle) paths between each feature of one \code{GVector} and the nearest feature of another \code{GVector}. @@ -29,8 +29,8 @@ madRivers <- fastData("madRivers") madDypsis <- fastData("madDypsis") # Convert sf's to GVectors: -rivers <- fast(madRivers) dypsis <- fast(madDypsis) +rivers <- fast(madRivers) ### Connections from each point to nearest river consFromDypsis <- connectors(dypsis, rivers) @@ -49,5 +49,5 @@ plot(consFromRivers, col = "red", add = TRUE) } } \seealso{ -Module \code{v.distance} in \strong{GRASS} +\strong{GRASS} module \code{v.distance} (see \code{grassHelp("v.distance")}). } From 47eeb3abba5fc4abc966b0ec78ff5554af99ed60 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 01:01:06 -0500 Subject: [PATCH 097/125] Fix big in detecting column "cat" --- R/vAttachDatabase.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/vAttachDatabase.r b/R/vAttachDatabase.r index 472bdc5a..92e6b759 100644 --- a/R/vAttachDatabase.r +++ b/R/vAttachDatabase.r @@ -44,7 +44,7 @@ } # if table does not have a "cat" column - if (!any(names(table) %in% "cat")) { + if (!any("cat" %in% names(table))) { if (is.null(cats)) cats <- .vCats(src, db = FALSE, integer = TRUE) catsRenum <- omnibus::renumSeq(cats) From 34ea991940e4b9ccacdb9312a09ac28710c0d8e9 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 01:01:24 -0500 Subject: [PATCH 098/125] Fix bug in how GRASS reports presence of db --- R/vHasDatabase.r | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/R/vHasDatabase.r b/R/vHasDatabase.r index a98fbf4a..a469e49b 100644 --- a/R/vHasDatabase.r +++ b/R/vHasDatabase.r @@ -27,6 +27,10 @@ intern = TRUE ) - length(info) > 0L + if (length(info) > 1L) { + any(grepl(info, pattern = "is connected by")) + } else { + !grepl(info, pattern = "is not connected to a database") + } } From 098e4329a437f944ee9c46797903ec9e9eda6e7d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 01:01:33 -0500 Subject: [PATCH 099/125] Update grassHelp.r --- R/grassHelp.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/grassHelp.r b/R/grassHelp.r index c27c7d22..beb6e94b 100644 --- a/R/grassHelp.r +++ b/R/grassHelp.r @@ -7,7 +7,7 @@ #' * `"type"`: Display a page wherein modules are classified by types. #' * `"topics"`: Display an index of topics. #' -#' @param online If `FALSE` (default), show the manual page that is included with your installation of **GRASS**. If `FALSE`, show the manual page online (requires an Internet connection). +#' @param online Logical: If `FALSE` (default), show the manual page that was included with your installation of **GRASS** on your computer. If `FALSE`, show the manual page online (requires an Internet connection). In either case, the manual page will display for the version of **GRASS** you have installed. #' #' @returns Nothing (opens a web page). #' From bcc31db9e42822b0bc577840fcfbbed391470d87 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 01:01:37 -0500 Subject: [PATCH 100/125] Update hidden_functions.Rmd --- vignettes/hidden_functions.Rmd | 1 + 1 file changed, 1 insertion(+) diff --git a/vignettes/hidden_functions.Rmd b/vignettes/hidden_functions.Rmd index 7ad6faf0..586f5874 100644 --- a/vignettes/hidden_functions.Rmd +++ b/vignettes/hidden_functions.Rmd @@ -21,6 +21,7 @@ fig.path = 'man/figures/' ## General * `.addLocationProject()`: Add a "location" or "project" argument to a list to be passed to [rgrass::execGRASS()] +* `.backdoor()`: Calls [faster()] and sets **GRASS** folder to "C:/Program Files/GRASS GIS X.Y", plus other options useful for development. * `.fileExt()`: Get file extension * `.ls()`: Lists the `sources` of all objects in the active **GRASS** "project/location" * `.message()`: Display a warning or message if the given warning has not been displayed since **fasterRaster** was attached or if a given number of hours has passed From 7848c42d656f02b496bb193f6fd1c44ea563955e Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 01:01:45 -0500 Subject: [PATCH 101/125] Add juice --- R/backdoor.r | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/R/backdoor.r b/R/backdoor.r index c03b0d5a..2773f398 100644 --- a/R/backdoor.r +++ b/R/backdoor.r @@ -1,6 +1,21 @@ #' Setup fasterRaster for ABS #' -#' This is a secret function for ABS's machine to be used for faster development of **fasterRaster**. +#' This is a secret function to be used for faster development of **fasterRaster**. It assume development is on a Windows machine. #' -#' @noRd -.backdoor <- function() faster(grassDir = "C:/Program Files/GRASS GIS 8.4", memory = 1024 * 8, cores = 2, verbose = TRUE) +#' @param ver Character: **GRASS**: e.g., "83" or "84". +#' +#' @returns `TRUE` (invisibly). +#' +#' @keywords internal +.backdoor <- function(ver = "84") { + + verNice <- paste0(substr(ver, 1L, 1L), ".", substr(ver, 2L, 2L)) + + faster( + grassDir = paste0("C:/Program Files/GRASS GIS ", verNice), + memory = 1024 * 8, + cores = 2, + verbose = TRUE + ) + invisible(TRUE) +} From 860c14ee1e198022345b4a769fcbece7a0d7e34d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 01:01:57 -0500 Subject: [PATCH 102/125] Update --- man/as.contour.Rd | 2 +- man/bioclims.Rd | 2 +- man/dot-backdoor.Rd | 18 ++++++++++++++++++ man/grassHelp.Rd | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 man/dot-backdoor.Rd diff --git a/man/as.contour.Rd b/man/as.contour.Rd index f61de0d1..45776712 100644 --- a/man/as.contour.Rd +++ b/man/as.contour.Rd @@ -32,7 +32,7 @@ elev <- fast(madElev) # Calculate contour lines: conts <- as.contour(elev, nlevels = 10) -plot(madElev) +plot(elev) plot(conts, add = TRUE) } diff --git a/man/bioclims.Rd b/man/bioclims.Rd index 2a323a7f..f67d22e9 100644 --- a/man/bioclims.Rd +++ b/man/bioclims.Rd @@ -138,7 +138,7 @@ tmax <- fast(madTmax) # Takes longer to run compared to SpatRaster version for small rasters, so # just calculate select BIOCLIMs: -bc <- bioclims(ppt, tmin, tmax, bios = c(1, 5, 6, 12, 15)) +bc <- bioclims(ppt, tmin, tmax, bios = c(1, 5, 12)) bc plot(bc) diff --git a/man/dot-backdoor.Rd b/man/dot-backdoor.Rd new file mode 100644 index 00000000..e1982b48 --- /dev/null +++ b/man/dot-backdoor.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/backdoor.r +\name{.backdoor} +\alias{.backdoor} +\title{Setup fasterRaster for ABS} +\usage{ +.backdoor(ver = "84") +} +\arguments{ +\item{ver}{Character: \strong{GRASS}: e.g., "83" or "84".} +} +\value{ +\code{TRUE} (invisibly). +} +\description{ +This is a secret function to be used for faster development of \strong{fasterRaster}. It assume development is on a Windows machine. +} +\keyword{internal} diff --git a/man/grassHelp.Rd b/man/grassHelp.Rd index cd4c2a19..91598896 100644 --- a/man/grassHelp.Rd +++ b/man/grassHelp.Rd @@ -14,7 +14,7 @@ grassHelp(x, online = FALSE) \item \code{"topics"}: Display an index of topics. }} -\item{online}{If \code{FALSE} (default), show the manual page that is included with your installation of \strong{GRASS}. If \code{FALSE}, show the manual page online (requires an Internet connection).} +\item{online}{Logical: If \code{FALSE} (default), show the manual page that was included with your installation of \strong{GRASS} on your computer. If \code{FALSE}, show the manual page online (requires an Internet connection). In either case, the manual page will display for the version of \strong{GRASS} you have installed.} } \value{ Nothing (opens a web page). From 14585b3050e691b30192d0ff43446bb76cd1814d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 01:02:02 -0500 Subject: [PATCH 103/125] Update ex_connectors.r --- man/examples/ex_connectors.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/examples/ex_connectors.r b/man/examples/ex_connectors.r index 220308ac..7bab3954 100644 --- a/man/examples/ex_connectors.r +++ b/man/examples/ex_connectors.r @@ -8,8 +8,8 @@ madRivers <- fastData("madRivers") madDypsis <- fastData("madDypsis") # Convert sf's to GVectors: -rivers <- fast(madRivers) dypsis <- fast(madDypsis) +rivers <- fast(madRivers) ### Connections from each point to nearest river consFromDypsis <- connectors(dypsis, rivers) From 1d4b4ad44adc72a885adcd8f18498a6e0a0a885b Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 01:02:13 -0500 Subject: [PATCH 104/125] Update ex_bioclims.r --- man/examples/ex_bioclims.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/examples/ex_bioclims.r b/man/examples/ex_bioclims.r index c3b44235..c8918c69 100644 --- a/man/examples/ex_bioclims.r +++ b/man/examples/ex_bioclims.r @@ -19,7 +19,7 @@ tmax <- fast(madTmax) # Takes longer to run compared to SpatRaster version for small rasters, so # just calculate select BIOCLIMs: -bc <- bioclims(ppt, tmin, tmax, bios = c(1, 5, 6, 12, 15)) +bc <- bioclims(ppt, tmin, tmax, bios = c(1, 5, 12)) bc plot(bc) From 8439e8ea389e17cbfc861679d209729ed998c745 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 01:02:17 -0500 Subject: [PATCH 105/125] Update ex_asContour.r --- man/examples/ex_asContour.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/examples/ex_asContour.r b/man/examples/ex_asContour.r index 7f29bdbb..cde4fe95 100644 --- a/man/examples/ex_asContour.r +++ b/man/examples/ex_asContour.r @@ -12,7 +12,7 @@ elev <- fast(madElev) # Calculate contour lines: conts <- as.contour(elev, nlevels = 10) -plot(madElev) +plot(elev) plot(conts, add = TRUE) } From 4dc9a16a4d045fc81080dbd00a410b40f769da19 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 08:16:52 -0500 Subject: [PATCH 106/125] Fix bug detecting output of `v.info` --- R/vHasDatabase.r | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/R/vHasDatabase.r b/R/vHasDatabase.r index a469e49b..b60b6afe 100644 --- a/R/vHasDatabase.r +++ b/R/vHasDatabase.r @@ -27,7 +27,9 @@ intern = TRUE ) - if (length(info) > 1L) { + if (length(info) < 1L) { + FALSE + } else if (length(info) > 1L) { any(grepl(info, pattern = "is connected by")) } else { !grepl(info, pattern = "is not connected to a database") From 7f4610bc6d8137fbd71b517d0d3b418d12ad3c86 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 08:16:59 -0500 Subject: [PATCH 107/125] Update --- man/aggregate.Rd | 10 +++++----- man/disagg.Rd | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/man/aggregate.Rd b/man/aggregate.Rd index 6e59b423..823a588e 100644 --- a/man/aggregate.Rd +++ b/man/aggregate.Rd @@ -108,19 +108,19 @@ agg2x3 madCoast4 <- fastData("madCoast4") # Convert: -coast <- fast(madCoast4) +coast4 <- fast(madCoast4) # Aggregate and disaggregate: -aggCoast <- aggregate(coast) -disaggCoast <- disagg(coast) +aggCoast <- aggregate(coast4) +disaggCoast <- disagg(coast4) -ngeom(coast) +ngeom(coast4) ngeom(aggCoast) ngeom(disaggCoast) # plot oldpar <- par(mfrow = c(1, 3)) -plot(coast, main = "Original", col = 1:nrow(coast)) +plot(coast4, main = "Original", col = 1:nrow(coast4)) plot(aggCoast, main = "Aggregated", col = 1:nrow(aggCoast)) plot(disaggCoast, main = "Disaggregated", col = 1:nrow(disaggCoast)) par(oldpar) diff --git a/man/disagg.Rd b/man/disagg.Rd index bb066bde..19f6b885 100644 --- a/man/disagg.Rd +++ b/man/disagg.Rd @@ -72,19 +72,19 @@ agg2x3 madCoast4 <- fastData("madCoast4") # Convert: -coast <- fast(madCoast4) +coast4 <- fast(madCoast4) # Aggregate and disaggregate: -aggCoast <- aggregate(coast) -disaggCoast <- disagg(coast) +aggCoast <- aggregate(coast4) +disaggCoast <- disagg(coast4) -ngeom(coast) +ngeom(coast4) ngeom(aggCoast) ngeom(disaggCoast) # plot oldpar <- par(mfrow = c(1, 3)) -plot(coast, main = "Original", col = 1:nrow(coast)) +plot(coast4, main = "Original", col = 1:nrow(coast4)) plot(aggCoast, main = "Aggregated", col = 1:nrow(aggCoast)) plot(disaggCoast, main = "Disaggregated", col = 1:nrow(disaggCoast)) par(oldpar) From 2ee25d4931ca76002a38e65e6711d324c009449b Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 08:17:04 -0500 Subject: [PATCH 108/125] Update ex_aggregate_disagg.r --- man/examples/ex_aggregate_disagg.r | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/man/examples/ex_aggregate_disagg.r b/man/examples/ex_aggregate_disagg.r index ec458dc5..25d943bc 100644 --- a/man/examples/ex_aggregate_disagg.r +++ b/man/examples/ex_aggregate_disagg.r @@ -51,19 +51,19 @@ agg2x3 madCoast4 <- fastData("madCoast4") # Convert: -coast <- fast(madCoast4) +coast4 <- fast(madCoast4) # Aggregate and disaggregate: -aggCoast <- aggregate(coast) -disaggCoast <- disagg(coast) +aggCoast <- aggregate(coast4) +disaggCoast <- disagg(coast4) -ngeom(coast) +ngeom(coast4) ngeom(aggCoast) ngeom(disaggCoast) # plot oldpar <- par(mfrow = c(1, 3)) -plot(coast, main = "Original", col = 1:nrow(coast)) +plot(coast4, main = "Original", col = 1:nrow(coast4)) plot(aggCoast, main = "Aggregated", col = 1:nrow(aggCoast)) plot(disaggCoast, main = "Disaggregated", col = 1:nrow(disaggCoast)) par(oldpar) From 2e9d878545b1b599741a00823d52e39692fd0b74 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 21:54:16 -0500 Subject: [PATCH 109/125] Update connectors.r --- R/connectors.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/connectors.r b/R/connectors.r index 1e907dbe..9c94c05a 100644 --- a/R/connectors.r +++ b/R/connectors.r @@ -58,7 +58,7 @@ methods::setMethod( table <- .vAsDataTable(x) table <- table[ , "TEMPTEMP_minDist_meters"] - names(table) <- "distance_x_to_y_meters" + names(table) <- "length_meters" .makeGVector(src, table = table) } # EOF From e89f03132c324beec95ee55ded6defb27492a7c3 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 21:54:36 -0500 Subject: [PATCH 110/125] Fix bug parsing distance output from `v.distance` --- R/distance.r | 2 ++ 1 file changed, 2 insertions(+) diff --git a/R/distance.r b/R/distance.r index 0210bfd2..a4411be3 100644 --- a/R/distance.r +++ b/R/distance.r @@ -217,6 +217,7 @@ methods::setMethod( ) info <- info[-1L] + info <- info[grepl(info, pattern = "\\|")] info <- strsplit(info, split="\\|") info <- lapply(info, as.numeric) info <- do.call(rbind, info) @@ -273,6 +274,7 @@ methods::setMethod( ) out <- out[-1L] + out <- out[grepl(out, pattern = "\\|")] out <- strsplit(out, split = "\\|") out <- do.call(rbind, out) n <- nrow(out) From 0899ce695a971be414150321ff98e132565ecfa3 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 21:54:54 -0500 Subject: [PATCH 111/125] Require stronger response when `ask=T` --- R/mow.r | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/mow.r b/R/mow.r index 5f70636f..b11bd96e 100644 --- a/R/mow.r +++ b/R/mow.r @@ -26,8 +26,8 @@ mow <- function(x, type = NULL, keep = NULL, verbose = TRUE, ask = TRUE) { if (ask) { - response <- readline("Are you sure you want to clean the GRASS cache? (y/n) ") - if (response != "y") return(invisible(list(rasters = 0, vectors = 0))) + response <- readline("Are you sure you want to clean the GRASS cache? (Y/n) ") + if (response != "Y") return(invisible(list(rasters = 0, vectors = 0))) } if (!is.null(keep)) { From edc28f82cce5e021deae49d6a29f241eed682859 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 21:54:58 -0500 Subject: [PATCH 112/125] Update ex_distance.r --- man/examples/ex_distance.r | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/man/examples/ex_distance.r b/man/examples/ex_distance.r index db71e7fb..bc0b822b 100644 --- a/man/examples/ex_distance.r +++ b/man/examples/ex_distance.r @@ -40,11 +40,11 @@ plot(distTo3) distToVect <- distance(elev, rivers) plot(distToVect) -plot(st_geometry(madRivers), add = TRUE) +plot(rivers, add = TRUE) ### Case 3: GVector vs GVector -plot(st_geometry(madRivers)) -plot(st_geometry(madDypsis), add = TRUE) +plot(rivers) +plot(dypsis, add = TRUE) distToRivers <- distance(dypsis, rivers, unit = "yd") distToPlants <- distance(rivers, dypsis) From c2364d7b4eb304b736723886792ccb9f2a989683 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 21:55:35 -0500 Subject: [PATCH 113/125] Update NEWS.md --- NEWS.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 47ef1848..94ab1c74 100644 --- a/NEWS.md +++ b/NEWS.md @@ -16,7 +16,8 @@ o `pca()` has been renamed `princomp()`. ### Enhanced functionality and new functions o `extract()` now automatically projects a `GVector` to match the CRS of a `GRaster` from which extraction is being made. -o `grass()` allows users to start the **GRASS** GUI. +o `grassGUI()` allows users to start the **GRASS** GUI. +o `grassHelp()` shows teh manual page for a **GRASS** module. o `layerIndex()` allows a `negate` argument to get the "opposite" indices of a `GRaster`. o `init()` assigns to `GRaster` cells the value of their coordinates, rows, columns, or values in a regular or chessboard-like pattern. o `regress()` replaces individual functions `intercept()`, `slope()`, `r2()`, and `tvalue()`. @@ -24,11 +25,13 @@ o `subset()` subsets layers of a `GRaster` or rows/geometries of a `GVector`. o `segregate()` creates one layer per unique value in an input `GRaster`, with values in the output coded 1 or 0 depending on whether cells in the input had the unique value or not. ### Bug and issue fixes -o `appFuns()` opens a a **shiny** table with `app()` functions. +o `appFuns()` succeds in opening a **shiny** table with `app()` functions. o `categories()` correctly assigns active category column. +o `distance()` correctly parses distance matrix. o `simplifyGeom()` works for 2-dimensional `GVector`s. o `rasterize()` works when `by` is not `NULL`. -o `.layerIndex()` (called by `catergories()` and other functions related to categorical `GRaster`s) does not fail. +o `.layerIndex()` (called by `categories()` and other functions related to categorical `GRaster`s) does not fail. +o `.vHasDatabase()` correctly detects if a vector has a database attached to it. o Removed all instances of `sQuote()`. # fasterRaster 8.3.0.7026 (2024-09-22) From 3feb3d30c403b54a2b4e87ea8e6264f6670d4bcc Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 22:30:18 -0500 Subject: [PATCH 114/125] Fix bug parsing `v.to.db` --- R/crds.r | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/R/crds.r b/R/crds.r index f3acb97c..bde8aa1f 100644 --- a/R/crds.r +++ b/R/crds.r @@ -106,7 +106,7 @@ st_coordinates <- function(x) { } else if (gtype == "points") { - data <- rgrass::execGRASS( + info <- rgrass::execGRASS( cmd = "v.to.db", map = src, flags = c(.quiet(), "p"), @@ -115,13 +115,14 @@ st_coordinates <- function(x) { intern = TRUE ) - # data <- data[-1L] - cutAt <- which(data == "Reading features...") - if (length(cutAt) > 0L) data <- data[1L:(cutAt - 1L)] + info <- info[!grepl(info, pattern = "cat|x|y|z")] + info <- info[grepl(info, pattern = "\\|")] + # cutAt <- which(info == "Reading features...") + # if (length(cutAt) > 0L) info <- info[1L:(cutAt - 1L)] - data <- strsplit(data, split="\\|") - data <- lapply(data, as.numeric) - out <- do.call(rbind, data) + info <- strsplit(info, split = "\\|") + info <- lapply(info, as.numeric) + out <- do.call(rbind, info) if (z) { out <- out[ , 2L:4L, drop = FALSE] From 611fbe40ac2eabc9751bdbc3e93b49c4a7885e64 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 22:30:55 -0500 Subject: [PATCH 115/125] Update NEWS.md --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 94ab1c74..acbb8668 100644 --- a/NEWS.md +++ b/NEWS.md @@ -27,6 +27,7 @@ o `segregate()` creates one layer per unique value in an input `GRaster`, with v ### Bug and issue fixes o `appFuns()` succeds in opening a **shiny** table with `app()` functions. o `categories()` correctly assigns active category column. +o `crds()` correctly returns coordinates from a "points" `GVector`. o `distance()` correctly parses distance matrix. o `simplifyGeom()` works for 2-dimensional `GVector`s. o `rasterize()` works when `by` is not `NULL`. From 8ede09d8e44456c6481ee325dc5ac8d7f4f9f44b Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 22:30:59 -0500 Subject: [PATCH 116/125] Update distance.Rd --- man/distance.Rd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/man/distance.Rd b/man/distance.Rd index b102536a..beb43c73 100644 --- a/man/distance.Rd +++ b/man/distance.Rd @@ -130,11 +130,11 @@ plot(distTo3) distToVect <- distance(elev, rivers) plot(distToVect) -plot(st_geometry(madRivers), add = TRUE) +plot(rivers, add = TRUE) ### Case 3: GVector vs GVector -plot(st_geometry(madRivers)) -plot(st_geometry(madDypsis), add = TRUE) +plot(rivers) +plot(dypsis, add = TRUE) distToRivers <- distance(dypsis, rivers, unit = "yd") distToPlants <- distance(rivers, dypsis) From 2b25416b239793647a76d92061e2242db9896464 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 23:11:40 -0500 Subject: [PATCH 117/125] Fix bug when `scratchDir = NULL` --- R/flow.r | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/R/flow.r b/R/flow.r index bc967107..e3c347a2 100644 --- a/R/flow.r +++ b/R/flow.r @@ -57,6 +57,12 @@ methods::setMethod( .locationRestore(x) .region(x) + if (is.null(scratchDir)) { + scratchDir <- paste0(getwd(), "/flow_temporary_files") + omnibus::dirCreate(scratchDir) + on.exit(unlink(scratchDir, recursive = TRUE, force = TRUE)) + } + args <- list( cmd = "r.terraflow", elevation = sources(x), From 9d214df468e2de36741d2fb3be6be6dfe96cf0f4 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Wed, 9 Oct 2024 23:12:15 -0500 Subject: [PATCH 118/125] Update NEWS.md --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index acbb8668..fd681ed5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -30,6 +30,7 @@ o `categories()` correctly assigns active category column. o `crds()` correctly returns coordinates from a "points" `GVector`. o `distance()` correctly parses distance matrix. o `simplifyGeom()` works for 2-dimensional `GVector`s. +o `flow()` creates a scratch folder when none is provided. o `rasterize()` works when `by` is not `NULL`. o `.layerIndex()` (called by `categories()` and other functions related to categorical `GRaster`s) does not fail. o `.vHasDatabase()` correctly detects if a vector has a database attached to it. From 4065555fd28590b2c72edcb9a4d60d3d2df04ec4 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 10 Oct 2024 08:23:07 -0500 Subject: [PATCH 119/125] Fix bug Correctly assigns `quantile` values to output without error --- R/global.r | 10 +++++++--- man/examples/ex_global.r | 5 +++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/R/global.r b/R/global.r index d208d5df..118ee103 100644 --- a/R/global.r +++ b/R/global.r @@ -145,12 +145,15 @@ methods::setMethod( colNames <- fun isQuant <- which(colNames == "quantile") quantNames <- paste0("quantile_", probs) + nCols <- length(colNames) if (length(fun) == 1L & fun[1L] == "quantile") { colNames <- quantNames } else if (colNames[1L] == "quantile") { - colNames <- c(quantNames, colNames[2L:length(colNames)]) - } else if (colNames[length(colNames)] == "quantile") { - colNames <- c(colNames[1L:(length(colNames) - 1L)], quantNames) + colNames <- c(quantNames, colNames[2L:nCols]) + } else if (colNames[nCols] == "quantile") { + colNames <- c(colNames[1L:(nCols - 1L)], quantNames) + } else { + colNames <- c(colNames[(1L:(isQuant) - 1L)], quantNames, colNames[(isQuant + 1L):nCols]) } names(thisOut) <- colNames @@ -213,6 +216,7 @@ methods::setMethod( # this <- strsplit(this, split=":")[[1L]][2L] # this <- as.numeric(this) + quantInfo <- quantInfo[seq_along(probs)] this <- strsplit(quantInfo, split = ":") this <- lapply(this, as.numeric) this <- do.call(rbind, this) diff --git a/man/examples/ex_global.r b/man/examples/ex_global.r index bb255345..5bddc043 100644 --- a/man/examples/ex_global.r +++ b/man/examples/ex_global.r @@ -9,12 +9,13 @@ madElev <- fastData("madElev") # Convert a SpatRaster to a GRaster: elev <- fast(madElev) +# What functions can we use with global()? +global() + # Calculate global statistics: global(elev, fun = c("mean", "var", "varpop")) global(elev, "quantile", probs = c(0.25, 0.5, 0.75)) global(elev, "*") # calculate all available functions -global() # vector of all functions - } From e9c94cb4c3692b0169104be6c185906ba98d12fc Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Thu, 10 Oct 2024 08:23:14 -0500 Subject: [PATCH 120/125] Update NEWS.md --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index fd681ed5..b37c5176 100644 --- a/NEWS.md +++ b/NEWS.md @@ -31,6 +31,7 @@ o `crds()` correctly returns coordinates from a "points" `GVector`. o `distance()` correctly parses distance matrix. o `simplifyGeom()` works for 2-dimensional `GVector`s. o `flow()` creates a scratch folder when none is provided. +o `global()` does not fail when multiple values of `fun` and `probs` are used and `fun` includes `quantile`. o `rasterize()` works when `by` is not `NULL`. o `.layerIndex()` (called by `categories()` and other functions related to categorical `GRaster`s) does not fail. o `.vHasDatabase()` correctly detects if a vector has a database attached to it. From e98f9ce52e413cc6f46ff4d2b754b97e81dd6f67 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Tue, 15 Oct 2024 09:32:40 -0500 Subject: [PATCH 121/125] Update --- man/global.Rd | 5 +++-- man/locationFind.Rd | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/man/global.Rd b/man/global.Rd index 5bb6d859..b61c4f5b 100644 --- a/man/global.Rd +++ b/man/global.Rd @@ -53,14 +53,15 @@ madElev <- fastData("madElev") # Convert a SpatRaster to a GRaster: elev <- fast(madElev) +# What functions can we use with global()? +global() + # Calculate global statistics: global(elev, fun = c("mean", "var", "varpop")) global(elev, "quantile", probs = c(0.25, 0.5, 0.75)) global(elev, "*") # calculate all available functions -global() # vector of all functions - } } \seealso{ diff --git a/man/locationFind.Rd b/man/locationFind.Rd index 8b010a12..1d64f1a5 100644 --- a/man/locationFind.Rd +++ b/man/locationFind.Rd @@ -26,15 +26,15 @@ \itemize{ \item Missing: Returns names and coordinate reference system strings of all "locations". \item A character representing a coordinate reference system in WKT format -\item A \code{SpatRaster}, \code{SpatVector}, or \code{sf} vector +\item A \code{SpatRaster}, \code{SpatVector}, or \code{sf} vector with a coordinate reference system \item A \code{GSpatial} object (usually a \code{GRaster} or \code{GVector}) }} \item{return}{Either: \itemize{ \item \code{"name"} (default): Returns the name of the "location" with a coordinate reference system the same as \code{x}. -\item \code{"index"}: Returns the index of this "location". -\item \code{"crs"}: Returns the coordinate reference system of this "location". +\item \code{"index"}: Returns the index of this "location" in \code{.fasterRaster$locations} of the \code{.fasterRaster} environment. +\item \code{"crs"}: Returns the coordinate reference system of this "project/location". }} \item{match}{Character: Method used to find the location. If \code{match} is "\code{name}"" (default), then the name of the location is used. If \code{match} is "\code{crs}", then the coordinate reference system of each location is checked for a match.} @@ -43,7 +43,7 @@ Character, integer, or \code{NULL} (if no match is found). } \description{ -The function searches the set of available \strong{GRASS} "locations" for one that has a coordinate reference system matching a \code{GSpatial} object. If none are found, or if no connection with \strong{GRASS} has yet been made, then it returns \code{NULL}. Otherwise, it returns either the index or the name of the matching location. +The function searches the set of available \strong{GRASS} "projects" (previously known as "locations") for one that has a coordinate reference system matching a \code{GSpatial} object. If none are found, or if no connection with \strong{GRASS} has yet been made, then it returns \code{NULL}. Otherwise, it returns either the index or the name of the matching location. } \examples{ if (grassStarted()) { From 93abab9ebb857ced80d5eaf683713be8a66a5e3d Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Tue, 15 Oct 2024 09:33:27 -0500 Subject: [PATCH 122/125] Only work if `interactive()` --- R/grassHelp.r | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/R/grassHelp.r b/R/grassHelp.r index beb6e94b..191bf590 100644 --- a/R/grassHelp.r +++ b/R/grassHelp.r @@ -18,21 +18,32 @@ #' @export grassHelp <- function(x, online = FALSE) { - x <- tolower(x) - if (length(x) != 1L) stop("Only one help page can be opened at a time.") - - args <- list( - cmd = "g.manual", - entry = x, - flags = .quiet() - ) - - if (x == "type") { - args$flags <- c(args$flags, "i") - } else if (x == "index") { - args$flags <- c(args$flags, "t") - } + if (interactive()) { + + if (!grassStarted()) { + omnibus::say("You must start GRASS first by using fast() at least once before opening a manual page.") + return() + } + + x <- tolower(x) + if (length(x) != 1L) stop("Only one help page can be opened at a time.") + + args <- list( + cmd = "g.manual", + entry = x, + flags = .quiet() + ) + + if (x == "type") { + args$flags <- c(args$flags, "i") + } else if (x == "index") { + args$flags <- c(args$flags, "t") + } - do.call(rgrass::execGRASS, args = args) + do.call(rgrass::execGRASS, args = args) + + } else { + warning("You can only open GRASS manual pages in an interactive R session.") + } } From c69f2ba7c0c14e839d9a954b05519fcf5e05b2c6 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Tue, 15 Oct 2024 09:34:42 -0500 Subject: [PATCH 123/125] Update locationFind.r --- R/locationFind.r | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/locationFind.r b/R/locationFind.r index dcab912c..7e1d8e22 100644 --- a/R/locationFind.r +++ b/R/locationFind.r @@ -1,17 +1,17 @@ #' Match CRS of a GSpatial object and an existing "GRASS" location #' -#' @description The function searches the set of available **GRASS** "locations" for one that has a coordinate reference system matching a `GSpatial` object. If none are found, or if no connection with **GRASS** has yet been made, then it returns `NULL`. Otherwise, it returns either the index or the name of the matching location. +#' @description The function searches the set of available **GRASS** "projects" (previously known as "locations") for one that has a coordinate reference system matching a `GSpatial` object. If none are found, or if no connection with **GRASS** has yet been made, then it returns `NULL`. Otherwise, it returns either the index or the name of the matching location. #' #' @param x Either: #' * Missing: Returns names and coordinate reference system strings of all "locations". #' * A character representing a coordinate reference system in WKT format -#' * A `SpatRaster`, `SpatVector`, or `sf` vector +#' * A `SpatRaster`, `SpatVector`, or `sf` vector with a coordinate reference system #' * A `GSpatial` object (usually a `GRaster` or `GVector`) #' #' @param return Either: #' * `"name"` (default): Returns the name of the "location" with a coordinate reference system the same as `x`. -#' * `"index"`: Returns the index of this "location". -#' * `"crs"`: Returns the coordinate reference system of this "location". +#' * `"index"`: Returns the index of this "location" in `.fasterRaster$locations` of the `.fasterRaster` environment. +#' * `"crs"`: Returns the coordinate reference system of this "project/location". #' #' @param match Character: Method used to find the location. If `match` is "`name`"" (default), then the name of the location is used. If `match` is "`crs`", then the coordinate reference system of each location is checked for a match. #' From 734fabfdf0f5beeeb0ae68aebb2382951b68ac00 Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Tue, 15 Oct 2024 09:34:58 -0500 Subject: [PATCH 124/125] `8.3.0.2027`! --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index e260b2fc..a8dfd961 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: fasterRaster Type: Package Title: Faster Raster and Spatial Vector Processing Using 'GRASS GIS' -Version: 8.3.0.7026 -Date: 2024-09-22 +Version: 8.3.0.7027 +Date: 2024-10-15 Authors@R: c( person( From 487c63c8c3711aeebde48e4a28cf623a8a791f3b Mon Sep 17 00:00:00 2001 From: "Adam B. Smith" Date: Tue, 15 Oct 2024 09:49:17 -0500 Subject: [PATCH 125/125] Update NEWS.md --- NEWS.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index b37c5176..681230c1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,7 @@ -# fasterRaster 8.4.0.7027 (2024-09-XX) +# fasterRaster 8.4.0.7027 (2024-10-15) ### Main task for this version -o Test examples with **GRASS 8.4** and update functions as needed. +o Test examples with **GRASS 8.4** and update functions as needed. Upgrade to **fasterRaster** 8.4.X.X. ### Updates for **GRASS 8.4** o `addLocationProject()` adds either a `project` or `location` argument to a `list` to be passed to `rgrass::execGRASS()`. @@ -17,7 +17,7 @@ o `pca()` has been renamed `princomp()`. ### Enhanced functionality and new functions o `extract()` now automatically projects a `GVector` to match the CRS of a `GRaster` from which extraction is being made. o `grassGUI()` allows users to start the **GRASS** GUI. -o `grassHelp()` shows teh manual page for a **GRASS** module. +o `grassHelp()` shows the manual page for a **GRASS** module. o `layerIndex()` allows a `negate` argument to get the "opposite" indices of a `GRaster`. o `init()` assigns to `GRaster` cells the value of their coordinates, rows, columns, or values in a regular or chessboard-like pattern. o `regress()` replaces individual functions `intercept()`, `slope()`, `r2()`, and `tvalue()`. @@ -25,7 +25,7 @@ o `subset()` subsets layers of a `GRaster` or rows/geometries of a `GVector`. o `segregate()` creates one layer per unique value in an input `GRaster`, with values in the output coded 1 or 0 depending on whether cells in the input had the unique value or not. ### Bug and issue fixes -o `appFuns()` succeds in opening a **shiny** table with `app()` functions. +o `appFuns()` succeeds in opening a **shiny** table with `app()` functions. o `categories()` correctly assigns active category column. o `crds()` correctly returns coordinates from a "points" `GVector`. o `distance()` correctly parses distance matrix.