diff --git a/.Rproj.user/E4971820/cpp-definition-cache b/.Rproj.user/E4971820/cpp-definition-cache index 0637a08..631dd2b 100644 --- a/.Rproj.user/E4971820/cpp-definition-cache +++ b/.Rproj.user/E4971820/cpp-definition-cache @@ -1 +1,80 @@ -[] \ No newline at end of file +[ + { + "file": "/home/eb97ziwi/Proj/enerscape/src/distance.h", + "file_last_write": 1664895205.0, + "definitions": [ + { + "usr": "c:@F@distance#$@N@Rcpp@S@Vector>#VI14#@N@Rcpp@ST>1#T@PreserveStorage#d#d#", + "kind": 6, + "parent_name": "", + "name": "distance(Rcpp::NumericVector, double, double)", + "file": "/home/eb97ziwi/Proj/enerscape/src/distance.h", + "line": 11, + "column": 15, + "hidden": false + } + ], + "hidden": false + }, + { + "file": "/home/eb97ziwi/Proj/enerscape/src/enerscape.cpp", + "file_last_write": 1664895433.0, + "definitions": [ + { + "usr": "c:@F@energy#$@N@Rcpp@S@Vector>#VI14#@N@Rcpp@ST>1#T@PreserveStorage#S0_#d#d#b#", + "kind": 6, + "parent_name": "", + "name": "energy(Rcpp::NumericVector, Rcpp::NumericVector, double, double, bool)", + "file": "/home/eb97ziwi/Proj/enerscape/src/enerscape.cpp", + "line": 16, + "column": 15, + "hidden": false + }, + { + "usr": "c:@F@energyscape#$@N@Rcpp@S@Matrix>#VI14#@N@Rcpp@ST>1#T@PreserveStorage#I#d#d#b#", + "kind": 6, + "parent_name": "", + "name": "energyscape(Rcpp::NumericMatrix, int, double, double, bool)", + "file": "/home/eb97ziwi/Proj/enerscape/src/enerscape.cpp", + "line": 49, + "column": 15, + "hidden": false + } + ], + "hidden": false + }, + { + "file": "/home/eb97ziwi/Proj/enerscape/src/neighbours.h", + "file_last_write": 1664895233.0, + "definitions": [ + { + "usr": "c:@F@neighbours#I#I#I#$@N@Rcpp@S@Matrix>#VI14#@N@Rcpp@ST>1#T@PreserveStorage#", + "kind": 6, + "parent_name": "", + "name": "neighbours(int, int, int, Rcpp::NumericMatrix)", + "file": "/home/eb97ziwi/Proj/enerscape/src/neighbours.h", + "line": 12, + "column": 15, + "hidden": false + } + ], + "hidden": false + }, + { + "file": "/home/eb97ziwi/Proj/enerscape/src/slope.h", + "file_last_write": 1664895257.0, + "definitions": [ + { + "usr": "c:@F@slope#$@N@Rcpp@S@Vector>#VI14#@N@Rcpp@ST>1#T@PreserveStorage#d#d#", + "kind": 6, + "parent_name": "", + "name": "slope(Rcpp::NumericVector, double, double)", + "file": "/home/eb97ziwi/Proj/enerscape/src/slope.h", + "line": 11, + "column": 15, + "hidden": false + } + ], + "hidden": false + } +] \ No newline at end of file diff --git a/.Rproj.user/E4971820/pcs/files-pane.pper b/.Rproj.user/E4971820/pcs/files-pane.pper index 46bed95..7d14db5 100644 --- a/.Rproj.user/E4971820/pcs/files-pane.pper +++ b/.Rproj.user/E4971820/pcs/files-pane.pper @@ -5,5 +5,5 @@ "ascending": true } ], - "path": "~/enerscape" + "path": "~/Proj/enerscape" } \ No newline at end of file diff --git a/.Rproj.user/E4971820/pcs/windowlayoutstate.pper b/.Rproj.user/E4971820/pcs/windowlayoutstate.pper index c59cfe8..c27934a 100644 --- a/.Rproj.user/E4971820/pcs/windowlayoutstate.pper +++ b/.Rproj.user/E4971820/pcs/windowlayoutstate.pper @@ -1,14 +1,14 @@ { "left": { - "splitterpos": 471, + "splitterpos": 337, "topwindowstate": "HIDE", - "panelheight": 1140, - "windowheight": 1178 + "panelheight": 818, + "windowheight": 856 }, "right": { - "splitterpos": 364, + "splitterpos": 431, "topwindowstate": "NORMAL", - "panelheight": 1140, - "windowheight": 1178 + "panelheight": 818, + "windowheight": 856 } } \ No newline at end of file diff --git a/.Rproj.user/E4971820/pcs/workbench-pane.pper b/.Rproj.user/E4971820/pcs/workbench-pane.pper index ab5e950..f398270 100644 --- a/.Rproj.user/E4971820/pcs/workbench-pane.pper +++ b/.Rproj.user/E4971820/pcs/workbench-pane.pper @@ -1,5 +1,5 @@ { "TabSet1": 3, - "TabSet2": 0, + "TabSet2": 3, "TabZoom": {} } \ No newline at end of file diff --git a/.Rproj.user/E4971820/persistent-state b/.Rproj.user/E4971820/persistent-state index ef4f54a..c655c36 100644 --- a/.Rproj.user/E4971820/persistent-state +++ b/.Rproj.user/E4971820/persistent-state @@ -1,6 +1,6 @@ build-last-errors="[]" -build-last-errors-base-dir="~/enerscape/" -build-last-outputs="[{\"type\":0,\"output\":\"==> R CMD INSTALL --no-multiarch --with-keep.source enerscape\\n\\n\"},{\"type\":1,\"output\":\"* installing to library ‘/home/eb97ziwi/R/x86_64-pc-linux-gnu-library/4.1’\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"* installing *source* package ‘enerscape’ ...\\n\"},{\"type\":1,\"output\":\"** using staged installation\\n\"},{\"type\":1,\"output\":\"** R\\n\"},{\"type\":1,\"output\":\"** data\\n\"},{\"type\":1,\"output\":\"*** moving datasets to lazyload DB\\n\"},{\"type\":1,\"output\":\"** byte-compile and prepare package for lazy loading\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"** help\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":2,\"output\":\"Warning: /home/eb97ziwi/enerscape/man/enerscape_gridded.Rd:49: unknown macro '\\\\textit'\\n\"},{\"type\":2,\"output\":\"Warning: /home/eb97ziwi/enerscape/man/enerscape_merge.Rd:27: unknown macro '\\\\textit'\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"*** installing help indices\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"** building package indices\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"** testing if installed package can be loaded from temporary location\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"** testing if installed package can be loaded from final location\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"** testing if installed package keeps a record of temporary installation path\\n\"},{\"type\":1,\"output\":\"* DONE (enerscape)\\n\"},{\"type\":1,\"output\":\"\"}]" +build-last-errors-base-dir="~/Proj/enerscape/" +build-last-outputs="[{\"type\":0,\"output\":\"==> Rcpp::compileAttributes()\\n\\n\"},{\"type\":1,\"output\":\"* Updated R/RcppExports.R\\n\"},{\"type\":1,\"output\":\"\\n\"},{\"type\":0,\"output\":\"==> R CMD INSTALL --preclean --no-multiarch --with-keep.source enerscape\\n\\n\"},{\"type\":1,\"output\":\"* installing to library ‘/home/eb97ziwi/R/x86_64-pc-linux-gnu-library/4.2’\\n\"},{\"type\":1,\"output\":\"* installing *source* package ‘enerscape’ ...\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"g++ -std=gnu++14 -I\\\"/usr/share/R/include\\\" -DNDEBUG -I'/home/eb97ziwi/R/x86_64-pc-linux-gnu-library/4.2/Rcpp/include' -fpic -g -O2 -fdebug-prefix-map=/build/r-base-zYgbYq/r-base-4.2.1=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -c RcppExports.cpp -o RcppExports.o\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"** using staged installation\\n\"},{\"type\":1,\"output\":\"** libs\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"g++ -std=gnu++14 -I\\\"/usr/share/R/include\\\" -DNDEBUG -I'/home/eb97ziwi/R/x86_64-pc-linux-gnu-library/4.2/Rcpp/include' -fpic -g -O2 -fdebug-prefix-map=/build/r-base-zYgbYq/r-base-4.2.1=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -c enerscape.cpp -o enerscape.o\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"g++ -std=gnu++14 -shared -L/usr/lib/R/lib -Wl,-Bsymbolic-functions -Wl,-z,relro -o enerscape.so RcppExports.o enerscape.o -L/usr/lib/R/lib -lR\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"installing to /home/eb97ziwi/R/x86_64-pc-linux-gnu-library/4.2/00LOCK-enerscape/00new/enerscape/libs\\n\"},{\"type\":1,\"output\":\"** R\\n\"},{\"type\":1,\"output\":\"** data\\n\"},{\"type\":1,\"output\":\"*** moving datasets to lazyload DB\\n\"},{\"type\":1,\"output\":\"** byte-compile and prepare package for lazy loading\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"** help\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"*** installing help indices\\n\"},{\"type\":1,\"output\":\"*** copying figures\\n\"},{\"type\":1,\"output\":\"** building package indices\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"** testing if installed package can be loaded from temporary location\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"** checking absolute paths in shared objects and dynamic libraries\\n\"},{\"type\":1,\"output\":\"** testing if installed package can be loaded from final location\\n\"},{\"type\":1,\"output\":\"\"},{\"type\":1,\"output\":\"** testing if installed package keeps a record of temporary installation path\\n\"},{\"type\":1,\"output\":\"* DONE (enerscape)\\n\"},{\"type\":1,\"output\":\"\"}]" compile_pdf_state="{\"tab_visible\":false,\"running\":false,\"target_file\":\"\",\"output\":\"\",\"errors\":[]}" files.monitored-path="" find-in-files-state="{\"handle\":\"\",\"input\":\"\",\"path\":\"\",\"regex\":false,\"ignoreCase\":false,\"results\":{\"file\":[],\"line\":[],\"lineValue\":[],\"matchOn\":[],\"matchOff\":[],\"replaceMatchOn\":[],\"replaceMatchOff\":[]},\"running\":false,\"replace\":false,\"preview\":false,\"gitFlag\":false,\"replacePattern\":\"\"}" diff --git a/.Rproj.user/E4971820/sources/prop/INDEX b/.Rproj.user/E4971820/sources/prop/INDEX index 1c7a385..0c8fd48 100644 --- a/.Rproj.user/E4971820/sources/prop/INDEX +++ b/.Rproj.user/E4971820/sources/prop/INDEX @@ -1,3 +1,17 @@ +~%2FDocuments%2Fenerscape-bench.R="4584E606" +~%2FProj%2Fenerscape%2FDESCRIPTION="2AA39DB9" +~%2FProj%2Fenerscape%2FNAMESPACE="647D670F" +~%2FProj%2Fenerscape%2Fsrc%2FRcppExports.cpp="062A3DA4" +~%2FProj%2Fenerscape%2Fsrc%2Fdistance.h="AA3B434F" +~%2FProj%2Fenerscape%2Fsrc%2Fenergyscape.cpp="B2EED60C" +~%2FProj%2Fenerscape%2Fsrc%2Fenerscape.cpp="35A69DF3" +~%2FProj%2Fenerscape%2Fsrc%2Fneighbours.h="F5033350" +~%2FProj%2Fenerscape%2Fsrc%2Frcpp_hello_world.cpp="D80A2861" +~%2FProj%2Fenerscape%2Fsrc%2Frcpp_neighbours.cpp="BFA94E37" +~%2FProj%2Fenerscape%2Fsrc%2Fslope.cpp="A0D3343E" +~%2FProj%2Fenerscape%2Fsrc%2Fslope.h="8B842BE7" +~%2FProj%2Fenerscape%2Ftests%2Ftestthat%2Ftest-cpp-functions.R="F7EC2347" +~%2FProj%2Fenerscape%2Ftests%2Ftestthat.R="3FFDA157" ~%2Fenerscape%2FDESCRIPTION="0B233340" ~%2Fenerscape%2FR%2Fdata.R="65E8B01C" ~%2Fenerscape%2FR%2Fenerscape_gridded.R="24BBDC9D" diff --git a/.Rproj.user/shared/notebooks/paths b/.Rproj.user/shared/notebooks/paths index 459983b..f4e6b14 100644 --- a/.Rproj.user/shared/notebooks/paths +++ b/.Rproj.user/shared/notebooks/paths @@ -1,3 +1,13 @@ +/home/eb97ziwi/Documents/enerscape-bench.R="D0EAC371" +/home/eb97ziwi/Proj/enerscape/DESCRIPTION="F8FE071B" +/home/eb97ziwi/Proj/enerscape/NAMESPACE="1EE9CDCB" +/home/eb97ziwi/Proj/enerscape/src/RcppExports.cpp="4DCCA75B" +/home/eb97ziwi/Proj/enerscape/src/distance.h="F61D409D" +/home/eb97ziwi/Proj/enerscape/src/enerscape.cpp="E09A4297" +/home/eb97ziwi/Proj/enerscape/src/neighbours.h="DF5E7469" +/home/eb97ziwi/Proj/enerscape/src/slope.h="471F7B9B" +/home/eb97ziwi/Proj/enerscape/tests/testthat.R="945208A1" +/home/eb97ziwi/Proj/enerscape/tests/testthat/test-cpp-functions.R="B788CACF" /home/eb97ziwi/enerscape/DESCRIPTION="841B0F64" /home/eb97ziwi/enerscape/R/data.R="7068BE25" /home/eb97ziwi/enerscape/R/enerscape_gridded.R="05E88254" diff --git a/.gitignore b/.gitignore index 5b6a065..c833a2c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .Rhistory .RData .Ruserdata +inst/doc diff --git a/DESCRIPTION b/DESCRIPTION index 7121d8c..4d2c3ca 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Package: enerscape Type: Package Title: Compute Energy Landscapes -Version: 0.2.0 +Version: 1.0.0 Author: Emilio Berti Maintainer: Emilio Berti Description: Compute energy landscapes using a digital elevation model raster and body mass of animals. Vignette available at: . @@ -11,18 +11,15 @@ Encoding: UTF-8 LazyData: true Imports: Rcpp, - raster, - gdistance, - rgdal, - rgeos, - sp, - Matrix + methods, + terra Suggests: knitr, rmarkdown, testthat (>= 3.0.0) LinkingTo: Rcpp -RoxygenNote: 7.1.1 +RoxygenNote: 7.2.0 Depends: R (>= 2.10) Config/testthat/edition: 3 +VignetteBuilder: knitr diff --git a/NAMESPACE b/NAMESPACE index 5f039f5..533323b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,3 +1,8 @@ useDynLib(enerscape, .registration=TRUE) importFrom(Rcpp, evalCpp) exportPattern("^[[:alpha:]]+") + +importFrom("methods", "is") +importFrom("terra", + "rast", "rasterize", "writeRaster", "aggregate", + "res", "crs", "crs<-", "ext", "ext<-") diff --git a/R/RcppExports.R b/R/RcppExports.R index cb5ba24..a8ef393 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -1,23 +1,58 @@ # Generated by using Rcpp::compileAttributes() -> do not edit by hand # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 -distance <- function(dem, center, res) { - .Call(`_enerscape_distance`, dem, center, res) +#' Spatial distances +#' +#' @param x matrix with values +#' @param center numeric value (double) with the value of the focal cell +#' @param res numeric value (double) of the spatial resolution of the matrix +#' @return Vector with values the distances between x and center +distances <- function(x, center, res) { + .Call(`_enerscape_distances`, x, center, res) } +#' Energy Landscape +#' +#' @param slope vector with slopes +#' @param distance vector with distances +#' @param mass body mass of species (kg) +#' @param res numeric value (double) of the spatial resolution of the matrix +#' @param kcal (boolean) if to return the result in kCal (true) or J (false) +#' @return Vector with the energy cost of locomotion (EnergyScape) energy <- function(slope, distance, mass, res, kcal = TRUE) { .Call(`_enerscape_energy`, slope, distance, mass, res, kcal) } -energyscape <- function(m, n = 4L, mass = 0, res = 0, kcal = TRUE) { - .Call(`_enerscape_energyscape`, m, n, mass, res, kcal) +#' Energy Landscape +#' +#' @param x matrix with values the elevation. +#' @param n (integer) number of neighbours to consider (either 4 or 8). +#' @param mass body mass of species (kg). +#' @param res numeric value (double) of the spatial resolution of the matrix. +#' @param kcal (boolean) if to return the result in kCal (true) or J (false). +#' @return Matrix with the energy cost of locomotion (EnergyScape). +energyscape <- function(x, n = 4L, mass = 0, res = 0, kcal = TRUE) { + .Call(`_enerscape_energyscape`, x, n, mass, res, kcal) } -neighbours <- function(i, j, n, m) { - .Call(`_enerscape_neighbours`, i, j, n, m) +#' Neighbours +#' +#' @param i row index +#' @param j column index +#' @param n number of neighbours (4 or 8) +#' @param x matrix with values +#' @return Vector with values the neighours of x +neighbours <- function(i, j, n, x) { + .Call(`_enerscape_neighbours`, i, j, n, x) } -slope <- function(dem, center, res) { - .Call(`_enerscape_slope`, dem, center, res) +#' Slopes +#' +#' @param x matrix with values +#' @param center numeric value (double) with the value of the focal cell +#' @param res numeric value (double) of the spatial resolution of the matrix +#' @return Vector with values the slopes (degrees) between x and center +slope <- function(x, center, res) { + .Call(`_enerscape_slope`, x, center, res) } diff --git a/R/circuitscape.R b/R/circuitscape.R index ada2540..7f765d2 100644 --- a/R/circuitscape.R +++ b/R/circuitscape.R @@ -12,22 +12,22 @@ circuitscape_skeleton <- function( path = NULL, points = NULL ) { - if (is.null(en) | is.null(path) | is.null(points)) { + if (is.null(en) | is.null(points)) { stop("Missing mandatory input") } - if (class(points)[1] != "data.frame") { - if (class(points)[1] == "matrix") { - points <- as.data.frame(points) + if (is(points, "data.frame")) { + points <- as.matrix(points) } else { stop("points are not data.frame or matrix") } - } - pr <- raster::rasterize(points, en$rasters$EnergyScape, na.rm = TRUE) - raster::writeRaster(en$rasters$EnergyScape, file.path(path, "/work.tif"), - overwrite = TRUE) - raster::writeRaster(pr, file.path(path, "/loc.tif"), - overwrite = TRUE) - loc <- + if (is.null(path)) path <- getwd() + pr <- rasterize(points, en, na.rm = TRUE) + writeRaster(en, + file.path(path, "EnergyScape.tif"), + overwrite = TRUE) + writeRaster(pr, + file.path(path, "loc.tif"), + overwrite = TRUE) cs_file <- file(file.path(path, "circuitscape.ini"), open = "w") if (!isOpen(cs_file)) { stop("Connection to file cannot be established") @@ -41,7 +41,7 @@ circuitscape_skeleton <- function( "version = 5.0.0", "", "[Habitat raster or graph]", - paste0("habitat_file = ", path, "/work.tif"), + paste0("habitat_file = ", path, "/EnergyScape.tif"), "habitat_map_is_resistances = true", "", "[Connection Scheme for raster habitat data]", diff --git a/R/data.R b/R/data.R index a47f774..d4e252c 100644 --- a/R/data.R +++ b/R/data.R @@ -13,3 +13,8 @@ #' #' Pontzer, H. (2016). A unified theory for the energy cost of legged #' locomotion. Biology Letters, 12(2), 20150935. "pontzer" + +#' Monte Sirente Digital Elevation Model +#' +#' A matrix with the digital elevation mode of the Monte Sirente (Central Italy). +"sirente" diff --git a/R/en_extrapolation.R b/R/en_extrapolation.R deleted file mode 100644 index e05991c..0000000 --- a/R/en_extrapolation.R +++ /dev/null @@ -1,57 +0,0 @@ -#' Check if model extrapolates -#' -#' This check if computation of the energy landscape extrapolates from the test -#' set of enerscape::pontzer (2016). -#' @param en an enerscape object. -#' @param plot plot areas where slope is extrapolated. -#' @return A list with booleans if body size or inclines extrapolates and a -#' rasterLayer for where incline extrapolates. The rasterLayer is returned -#' only if extrapolations are present. -#' @details Check if body mass or incline are outside the test range of the -#' model. If slope extrapolations are detected and \code{plot = TRUE}, a plot -#' of where extrapolations occur is displayed. -#' @examples -#' library(raster) -#' library(enerscape) -#' data("volcano") -#' dem <- raster(volcano) -#' en <- enerscape(dem, 10, unit = "kcal", neigh = 16) -#' en_extrapolation(en, plot = TRUE) -#' @export -#' @references enerscape::pontzer, H. (2016). A unified theory for the energy -#' cost of legged locomotion. Biology Letters, 12(2), 20150935. \doi{ -#' https://doi.org/10.1098/rsbl.2015.0935}. -en_extrapolation <- function(en, - plot = TRUE) { - extr_mass <- ifelse(en$mass >= min(enerscape::pontzer$Mass) & - en$mass <= max(enerscape::pontzer$Mass), - FALSE, - TRUE) - v <- raster::values(en$rasters$Slope) - min_slope <- min(v, na.rm = TRUE) - max_slope <- max(v, na.rm = TRUE) - extr_slope <- ifelse(min_slope >= min(enerscape::pontzer$Incline) & - max_slope <= max(enerscape::pontzer$Incline), - FALSE, - TRUE) - extr_rast <- en$rasters$Slope - if (extr_slope) { - extr_rast[extr_rast >= min(enerscape::pontzer$Incline) & - extr_rast <= max(enerscape::pontzer$Incline)] <- NA - extr_rast[!is.na(extr_rast)] <- 1 - } else { - extr_rast <- NULL - } - if (plot & extr_slope) { - raster::plot(en$rasters$Slope) - raster::plot(extr_rast, - add = TRUE, - legend = FALSE, - col = grDevices::adjustcolor("blue", alpha.f = 0.5)) - } else if (plot & !extr_slope) { - message("No slope extrapolation detected") - } - return(list("Mass extrapolated" = extr_mass, - "Slope extrapolated" = extr_slope, - "Slope extrapolation" = extr_rast)) -} diff --git a/R/en_path.R b/R/en_path.R deleted file mode 100644 index 90815cb..0000000 --- a/R/en_path.R +++ /dev/null @@ -1,66 +0,0 @@ -#' Compute the energy costs for a chosen path -#' -#' This returns the distance and energy costs of traveling a chosen path. -#' Optionally, the path can be selected by specifying the number of nodes and -#' clicking on the plot. -#' @param en an enerscape object. -#' @param p path as SpatialLines. -#' @param draw if TRUE the path will be chosen by drawing it on the map/ -#' @param n number of node points for the path. -#' @param plot if TRUE plot the path -#' @return A list with elements the path, its travel distance and energy costs. -#' @examples -#' \donttest{ -#' library(raster) -#' library(enerscape) -#' data("volcano") -#' dem <- raster(volcano) -#' en <- enerscape(dem, 10, unit = "kcal", neigh = 16) -# p <- coordinates(en$rasters$DEM)[sample(1:ncell(en$rasters$DEM), 5), ] -# path <- sp::SpatialLines(list(sp::Lines(sp::Line(p), ID = "path"))) -# en_path(en, path) -#' } -#' @export -#' @references Etten, J. van. (2017). R Package gdistance: Distances and Routes -#' on Geographical Grids. Journal of Statistical Software, 76(1), 1–21. -#' \doi{https://doi.org/10.18637/jss.v076.i13}. -#' -#' Pontzer, H. (2016). A unified theory for the energy cost of legged -#' locomotion. Biology Letters, 12(2), 20150935. \doi{ -#' https://doi.org/10.1098/rsbl.2015.0935}. -#' -#' di Prampero, P. E., Cortili, G., Mognoni, P., & Saibene, F. (1979). -#' Equation of motion of a cyclist. Journal of Applied Physiology, 47(1), -#' 201–206. \doi{https://doi.org/10.1152/jappl.1979.47.1.201} -en_path <- function( - en, - p = NULL, - draw = FALSE, - n = NULL, - plot = TRUE -) { - if (is.null(p)) { - if (draw & is.null(n)) { - stop("Enter number of points to draw the path") - } else if (draw & !is.null(n)) { - graphics::par(mfrow = c(1, 1)) - raster::plot(en$rasters$DEM) - p <- raster::click(n = n) - p <- matrix(p, ncol = 2, byrow = TRUE) - } else { - stop("Specify the path or draw it (draw = TRUE)") - } - p <- sp::SpatialLines(list(sp::Lines(sp::Line(p), 1))) - } else if (class(p)[1] != "SpatialLines") { - stop("Path must be a SpatialLines object") - } - w <- raster::extract(en$rasters$EnergyScape, p)[[1]] - ans <- list(Path = p, - Distance = sp::SpatialLinesLengths(p), - Cost = sum(w)) - if (plot) { - raster::plot(en$rasters$DEM) - graphics::lines(p) - } - return(ans) -} diff --git a/R/enerscape.R b/R/enerscape.R index 61e43e0..1305069 100644 --- a/R/enerscape.R +++ b/R/enerscape.R @@ -1,4 +1,4 @@ -#' Compute the energy landscape +#' Compute Energy Landscapes #' #' This is the main function to compute energy landscapes from a digital #' elevation model and body mass of animals based on the model from Pontzer @@ -9,10 +9,6 @@ #' @param m species body mass (kg). #' @param unit if joules ('joule') or kilocalories ('kcal'). #' @param neigh number of neighbor cells that are connected together. -#' @param method method to use to compute the energy costs. 'ARC' refers to the -#' model from Pontzer (2016) and 'cycling' to the model for cyclist from di -#' Prampero et al. (1979). -#' @param v speed of cyclist (km / h), only for \code{method = 'cycling'}. #' @return A list with elements a rasterStack of the digital elevation model, #' slope, energy landscape, and conductance and the conductance as a transitionLayer for #' path analysis. @@ -20,105 +16,48 @@ #' and conductances (1 / work) are computed based on the model described in #' Pontzer (2016). #' @examples -#' library(raster) +#' library(terra) +#' library(enerscape) +#' #' data("volcano") -#' dem <- raster(volcano) +#' dem <- rast(volcano) #' en <- enerscape(dem, 10, unit = "kcal", neigh = 16) #' @export -#' @references Etten, J. van. (2017). R Package gdistance: Distances and Routes -#' on Geographical Grids. Journal of Statistical Software, 76(1), 1–21. -#' \doi{https://doi.org/10.18637/jss.v076.i13}. -#' +#' @references #' Pontzer, H. (2016). A unified theory for the energy cost of legged #' locomotion. Biology Letters, 12(2), 20150935. \doi{ #' https://doi.org/10.1098/rsbl.2015.0935}. #' -#' di Prampero, P. E., Cortili, G., Mognoni, P., & Saibene, F. (1979). -#' Equation of motion of a cyclist. Journal of Applied Physiology, 47(1), -#' 201–206. \doi{https://doi.org/10.1152/jappl.1979.47.1.201} enerscape <- function( - dem, - m, - unit = "joule", - neigh = 16, - method = "ARC", - v = NULL + dem, + m, + unit = "joule", + neigh = 8 ) { if (is.null(dem) | is.null(m)) { - stop("Missing mandatory input - see ?enerscape::enerscape for details") + stop("Missing mandatory input - see ?enerscape::enerscape for details.") } - if (class(dem) != "RasterLayer") { - stop("Digital elevation model must be a RaterLayer") + if (!is(dem, "SpatRaster")) { + stop("Digital elevation model must be a SpatRaster (terra).") } if (!unit %in% c("joule", "kcal")) { - stop("unit must be one of 'joule' or 'kcal'") + stop("unit must be one of 'joule' or 'kcal'.") } + # check units of DEM work_in_kcal <- ifelse(unit == "kcal", TRUE, FALSE) - # transition layers cannot accept optional arguments. The resolution is saved - # as variable in this environment and called with 'get()' in - # enerscape_internals.R - en_res <- raster::res(dem)[1] - message(" - Raster cells are assumed to have same horizontal and vertical", - " resolution and with planar coordinate reference system (e.g. UTM)") - message(" | Calculating slope") - height <- suppressWarnings( #this warning is printed as message before return - gdistance::transition(dem, - function(x) { - x[2] - x[1] - }, - directions = neigh, - symm = FALSE) - ) - slope <- gdistance::geoCorrection(height, scl = FALSE) - slope <- atan(slope) * 180 / pi #convert slope ratio to degrees - adj <- raster::adjacent(dem, - 1:(raster::ncell(dem)), - pairs = TRUE, - directions = neigh) - message(" | Calculating work") - work <- slope - if (method == "ARC") { - work[adj] <- .calc_arc(slope[adj], m, work_in_kcal) - } else if (method == "cycling") { - if (is.null(v)) { - stop("Argument v must be specified") - } else { - v <- v / 3.6 - } - work[adj] <- .calc_cycling(height[adj], slope[adj], m, v, work_in_kcal) - zeros <- Matrix::which(work@transitionMatrix < 0, arr.ind = TRUE) - rw <- zeros[, 1] - cl <- zeros[, 2] - work@transitionMatrix[rw, cl] <- 0 - } else { - stop("Argument method must be 'ARC' or 'cycling'") - } - message(" | Calculating conductance (1 / work)") - cond <- slope - if (method == "ARC") { - cond[adj] <- .calc_arc_cond(slope[adj], m, work_in_kcal) - } else if (method == "cycling") { - cond[adj] <- .calc_cycling_cond(height[adj], slope[adj], m, v, work_in_kcal) - zeros <- Matrix::which(cond@transitionMatrix < 0, arr.ind = TRUE) - rw <- zeros[, 1] - cl <- zeros[, 2] - val <- max(cond@transitionMatrix) * 10 - } else { - stop("Argument method must be 'ARC' or 'cycling'") - } - s <- gdistance::raster(slope, "colSums") / neigh - w <- gdistance::raster(work, "colSums") / neigh - if (method == "cycling") { - w[w < 0] <- 0 - } - con <- gdistance::raster(cond, "colSums") / neigh - ans <- raster::stack(dem, s, w, con) - names(ans) <- c("DEM", "Slope", "EnergyScape", "Conductance") - ans <- list(neighbors = neigh, - mass = m, - rasters = ans, - cond_tr = cond) - class(ans) <- "enerscape" - message(" - Do not use slope with negative values for distance calculations") - return(ans) + stopifnot(res(dem)[1] == res(dem)[2]) + en_res <- res(dem)[1] + message("DEM is assumed to have planar CRS in meters.") + x <- matrix(dem, nrow = nrow(dem), ncol = ncol(dem), byrow = TRUE) + en <- energyscape(x, + n = neigh, + mass = m, + kcal = work_in_kcal, + res = en_res) + ans <- rast(en) + names(ans) <- "EnergyScape" + crs(ans) <- crs(dem) + ext(ans) <- ext(dem) + ans[ans == 0] <- NA + return (ans) } diff --git a/R/enerscape_gridded.R b/R/enerscape_gridded.R deleted file mode 100644 index 20d3638..0000000 --- a/R/enerscape_gridded.R +++ /dev/null @@ -1,268 +0,0 @@ -#' @title Create a gridPolygon -#' -#' @param mat matrix with coordinates. -#' @param proj4 string, proj4string of the original raster. -#' @param ID integer, ID of the polygon (can be anything as lons as unique). -#' -#' @return a SpatialPolygonsDataFrame. -#' -#' @details gridPolygon() is used within gridRaster() and is mostly for enerscape -#' package internal functioning. -#' -#' @examples -#' mat <- matrix(c(0, 0, 1, 0, 1, 1, 0, 1, 0, 0), ncol = 2, byrow = TRUE) -#' proj4 <- "+proj=longlat +datum=WGS84 +no_defs" -#' gridPolygon(mat, proj4, ID = 1) -gridPolygon <- function( - mat, - proj4, - ID -) { - pol <- sp::SpatialPolygons( - list( - sp::Polygons( - list( - sp::Polygon( - mat - ) - ), - ID = 1 - ) - ), - proj4string = sp::CRS(proj4) - ) - pol <- sp::SpatialPolygonsDataFrame(pol, data = data.frame(ID = ID)) - return(pol) -} -#' @title Create spatial grid -#' -#' @param r RasterLayer. -#' @param split.hor integer, number of horizontal splits. -#' @param split.vert integer, number of vertical splits. -#' -#' @return a SpatialPolyonsDataFrame. -#' -#' @details gridRaster makes a spatial grid that will be used to divide -#' the raster into smaller units for computations. The final grid may not -#' necessarily be split into split.hor times split.vert blocks, but it can happen -#' that the final grid has (split.hor - 1) times (split.vert - 1) blocks. -#' -#' @examples -#' \donttest{ -#' library(raster) -#' data("volcano") -#' r <- raster(volcano) -#' crs(r) <- "+proj=longlat +datum=WGS84 +no_defs" -#' gridRaster(r, split.hor = 3, split.vert = 3) -#' } -gridRaster <- function( - r, - split.hor, - split.vert -) { - # get proj4string of the CRS - proj4 <- sp::proj4string(r) - # get corner points - x <- seq(raster::xmin(r), raster::xmax(r), length.out = split.hor) - y <- seq(raster::ymin(r), raster::ymax(r), length.out = split.vert) - # create polygons - pols <- list() - grids <- c() - id <- 0 - for (i in seq_len(split.hor - 1)) { - for (j in seq_len(split.vert - 1)) { - id <- id + 1 - mat <- matrix(c( - x[i], y[j], - x[i + 1], y[j], - x[i + 1], y[j + 1], - x[i], y[j + 1], - x[i], y[j] - ), - ncol = 2, - byrow = TRUE) - # create the polygon for this coordinates - pol <- gridPolygon(mat, proj4, ID = id) - # create grids or append the polygon to it if it exists already - if (length(grids) == 0) { - grids <- pol - } else { - grids <- rbind(grids, pol) - } - } - } - return(grids) -} - -#' @title Split raster into blocks -#' -#' @param x RasterLayer. -#' @param y SpatialPolygonsDataFrame, polygon grid (as created using -#' gridRaster()), -#' @param buffer TRUE/FALSE, if to create a small buffer around the polygon to -#' avoid missing values at the polygon border when merging back together. -#' @param write TRUE/FALSE, if to write the raster to disk. -#' @param out.dir string, output directory; default to tempdir(). -#' -#' @details as splitting exactly at the spatialPolygons boundaries will create -#' later artifacts at the polygons edge, it is better to always set -#' buffer = TRUE, which will polygons buffered by twice the -#' resolution of the raster to split before cropping. -#' -#' @return an enerscape rasterBlocks objects, which is a list of RasterLayers. -#' -#' @examples -#' \donttest{ -#' library(raster) -#' data("volcano") -#' r <- raster(volcano) -#' crs(r) <- "+proj=longlat +datum=WGS84 +no_defs" -#' gr <- gridRaster(r, split.hor = 3, split.vert = 3) -#' splits <- splitRaster(r, gr) -#' } -splitRaster <- function( - x, - y, - buffer = TRUE, - write = FALSE, - out.dir = tempdir() -) { - if (!write) ans <- list() - for (i in seq_along(y)) { - pol <- y[i, ] - if (buffer) pol <- raster::buffer(pol, max(raster::res(x)) * 2) - cropped <- raster::crop(x, pol) - if (write){ - raster::writeRaster(cropped, paste0(out.dir, "/grid-", i, ".tif")) - } else { - ans[[i]] <- cropped - } - } - if (!write) { - attr(ans, "class") <- "rasterBlocks" - attr(ans, "package") <- "enerscape" - return(ans) - } -} - -#' @title Merge split raster blocks -#' -#' @param x an enerscape rasterBlocks class, which is a list of raster blocks. -#' @param out.dir string, output directory; default to tempdir(). -#' @param pattern string, regular pattern to subset files in out.dir. -#' -#' @details if x == NULL, rasterMerge() loads the rasters save to disk by -#' rasterSplit() -#' -#' @return a RasterLayer. -#' -#' @examples -#' \donttest{ -#' library(raster) -#' data("volcano") -#' r <- raster(volcano) -#' crs(r) <- "+proj=longlat +datum=WGS84 +no_defs" -#' gr <- gridRaster(r, split.hor = 3, split.vert = 3) -#' splits <- splitRaster(r, gr) -#' } -mergeRaster <- function( - x = NULL, - out.dir = NULL, - pattern = "" -) { - if (is.null(x) & is.null(out.dir)) stop("Either of x or out.dir must be specified.") - if (!is.null(x) & !is.null(out.dir)) stop("Only one of x or out.dir must be specified.") - if (is.null(x)) { - files <- list.files(out.dir, pattern = ".tif$", full.names = TRUE) - files <- files[grepl(pattern, files)] - if (length(files) == 0) { - stop("No RasterLayer found with pattern: ", pattern) - } - x <- list() - for (f in files) x[[which(files == f)]] <- raster::raster(f) - } - for (lyr in x) { - if (identical(lyr, x[[1]])) { - merged <- lyr - } else { - merged <- raster::merge(merged, lyr) - } - } - return(merged) -} - -#' @title Runs enerscape on blocks of a RasterLayer. -#' -#' @inherit enerscape -#' @param split.hor integer, number of horizontal splits. -#' @param split.vert integer, number of vertical splits. -#' avoid missing values at the polygon border when merging back together. -#' @param write TRUE/FALSE, if to write the raster to disk. -#' @param out.dir string, output directory; default to tempdir(). -#' -#' @details Split the DEM into blocks and run enerscape for each block -#' sequentially. -#' This is useful when the DEM layer is particularly large. There are two -#' options: the first is to manipulate all layers in memory (write = FALSE); -#' the second is to write each layer to disk and load it when necessary. -#' The former is faster, but consume more RAM. -#' -#' @examples -#' \donttest{ -#' library(raster) -#' library(enerscape) -#' data("volcano") -#' r <- raster(volcano) -#' crs(r) <- "+proj=longlat +datum=WGS84 +no_defs" -#' r <- projectRaster(r, crs = "EPSG:32759", res = c(1e3, 1e3)) -#' en <- enerscape(r, 1)$rasters$EnergyScape -#' en_gr <- enerscape_gridded(r, 1, split.hor = 3, split.vert = 3) -#' } -enerscape_gridded <- function( - dem, - m, - split.hor, - split.vert, - unit = "joule", - neigh = 16, - method = "ARC", - v = NULL, - write = FALSE, - out.dir = raster::tmpDir() -) { - # make spatial grid - gr <- gridRaster(dem, split.hor, split.vert) - if (write) message("Single enerscape grids are written in ", out.dir) - # make raster blocks - if (write) { - blocks <- splitRaster(dem, gr, write = TRUE, out.dir = out.dir) - files <- list.files(out.dir, pattern = ".tif$", full.names = TRUE) - files <- files[grepl("grid-", files)] - files <- files[grepl("tif", files)] - blocks <- list() - for (f in files) blocks[[which(files == f)]] <- raster::raster(f) - } else { - blocks <- splitRaster(dem, gr) - } - # run enerscape for each block - i <- 1 - if (!write) ans <- list() - for (lyr in blocks) { - message(" - Running enerscape for grid ", i, " of ", length(blocks)) - en <- suppressMessages(enerscape(lyr, m, unit, neigh, method, v)) - en <- en$rasters$EnergyScape - en <- raster::crop(en, gr[i, ]) #crop to remove buffers - if (write) { - raster::writeRaster(en, paste0(out.dir, "enerscape-", i, ".tif")) - } else { - ans[[i]] <- en - } - i <- i + 1 - } - if (write) { - en <- mergeRaster(out.dir = out.dir, pattern = "enerscape-") - } else { - en <- mergeRaster(ans) - } - return(en) -} diff --git a/R/enerscape_internals.R b/R/enerscape_internals.R deleted file mode 100644 index 142aa91..0000000 --- a/R/enerscape_internals.R +++ /dev/null @@ -1,114 +0,0 @@ -#' Compute energy costs -#' -#' Internal function for enerscape - calculate work. -#' @param slope slope transition matrix. -#' @param m species body mass (kg). -#' @param work_in_kcal if work should be expressed in kilocalories. -#' @param j_to_kcal joules to kilocalories conversion constant. -#' @return A transition layer with values the energy cost of movement between -#' cells (J or kcal). -#' @details Internal function of enerscape, don't call directly. -.calc_arc <- function( - slope, - m, - work_in_kcal = TRUE, - j_to_kcal = 4184 -) { - en_res <- get("en_res", envir = parent.frame()) - E_ar <- 8 * m ^ (-0.34) - E_mec <- 100 * (1 + sin((2 * slope - 74) / 180 * pi)) * m ^ (-0.12) - work <- (E_ar + E_mec) * m * en_res / abs(cos(slope * pi / 180)) - if (work_in_kcal) { - work <- work / j_to_kcal - } - return(work) -} -#' Compute conductance -#' -#' Internal function for enerscape - calculate conductance -#' @inherit .calc_arc -#' @return A transition layer with values the conductance between cells, i.e. -#' the distance that can be traveled per unit of energy (1 / J or 1 / kcal). -#' @export -.calc_arc_cond <- function( - slope, - m, - work_in_kcal = TRUE, - j_to_kcal = 4184 -) { - en_res <- get("en_res", envir = parent.frame()) - E_ar <- 8 * m ^ (-0.34) - E_mec <- 100 * (1 + sin((2 * slope - 74) / 180 * pi)) * m ^ (-0.12) - work <- (E_ar + E_mec) * m * en_res / abs(cos(slope * pi / 180)) - if (work_in_kcal) { - work <- work / j_to_kcal - } - cond <- 1 / work - return(cond) -} -#' Compute energy costs for a cyclist -#' -#' Internal function for enerscape - calculate work. -#' @param height height transition matrix. -#' @param slope slope transition matrix. -#' @param m species body mass (kg). -#' @param v speed of cyclist. -#' @param work_in_kcal if work should be expressed in kilocalories. -#' @param j_to_kcal joules to kilocalories conversion constant. -#' @return A transition layer with values the energy cost of movement between -#' cells (J or kcal). -#' @details Internal function of enerscape, don't call directly. This assumes no -#' wind, a bike of 7 kg, optimal pedal frequency, and constant mechanical -#' efficiency of 25%. -.calc_cycling <- function( - height, - slope, - m, - v, - work_in_kcal = TRUE, - j_to_kcal = 4184 -) { - en_res <- get("en_res", envir = parent.frame()) - m <- m + 7 #add mass of bike - temp <- (15.04 - 0.00649 * height) #air temperature - atm <- 101.29 * ((temp + 273.1) / 288.08) ^ 5.256 #atmospheric pressure - atm <- atm * 7.500638 #in Torr - temp <- temp + 272.15 #in Kelvin - VO2 <- (8.6 * 10^-3 * m + - 7.8 * 10^-3 * 1.8 * atm / temp * v^2 + - 1.87 * m * sin(slope / 180 * pi)) * v - E <- VO2 * 20.9 #ml O2 to J - E <- E * en_res / abs(cos(slope * pi / 180)) / v - if (work_in_kcal) { - E <- E / j_to_kcal - } - return(E) -} -#' Compute conductance for a cyclist -#' -#' Internal function for enerscape - calculate work. -#' @inherit .calc_cycling -.calc_cycling_cond <- function( - height, - slope, - m, - v, - work_in_kcal = TRUE, - j_to_kcal = 4184 -) { - en_res <- get("en_res", envir = parent.frame()) - m <- m + 7 #add mass of bike - temp <- (15.04 - 0.00649 * height) #air temperature - atm <- 101.29 * ((temp + 273.1) / 288.08) ^ 5.256 #atmospheric pressure - atm <- atm * 7.500638 #in Torr - temp <- temp + 272.15 #in Kelvin - VO2 <- (8.6 * 10^-3 * m + - 7.8 * 10^-3 * 1.8 * atm / temp * v^2 + - 1.87 * m * sin(slope / 180 * pi)) * v - E <- VO2 * 20.9 #ml O2 to J - E <- E * en_res / abs(cos(slope * pi / 180)) / v - if (work_in_kcal) { - E <- E / j_to_kcal - } - return(1 / E) -} diff --git a/R/least-cost_path.R b/R/least-cost_path.R deleted file mode 100644 index 38fd5cf..0000000 --- a/R/least-cost_path.R +++ /dev/null @@ -1,101 +0,0 @@ -#' Compute least-cost paths -#' -#' Calculate the least-cost path (lcp) between origin and destination -#' @param en an enerscape object obtained with \code{enerscape()}. -#' @param or origin point. -#' @param dest destination point. -#' @param simulate_random_points if to simulate least-cost path among random -#' points. default = FALSE. -#' @param rep number or random origin and destination points if -#' \code{simulate_random_points = TRUE}. default = 10. -#' @param plot if to plot the output. -#' @return A list with point locations, least-cost path as SpatialLines, energy -#' costs and distances. -#' @details If \code{or} and \code{dest} are not specified, the least-cost path -#' is specified by setting \code{simulate_random_points = TRUE} and \code{rep} -#' equal to the number of random paths to compute. -#' @examples -#' library(raster) -#' data("volcano") -#' dem <- raster(volcano) -#' en <- enerscape(dem, 10, unit = "kcal", neigh = 16) -#' p <- xyFromCell(dem, sample(ncell(dem), 2)) -#' lcp <- en_lcp(en, or = p[1, ], dest = p[2, ]) -en_lcp <- function( - en, - or, - dest, - simulate_random_points = FALSE, - rep = 10, - plot = TRUE -) { - if (class(en) != "enerscape") { - stop("en must be an enerscape object") - } else { - x <- en$rasters$DEM - work <- en$rasters$EnergyScape - cond <- en$cond_tr - } - ans <- list() #initialize return value - if (simulate_random_points) { #random points - ans[["Paths"]] <- list() - for (i in seq_len(rep)) { - message(i, " of ", rep, " random points ...") - p <- raster::xyFromCell(x, sample(raster::ncell(x), 2)) - while (any(is.na(raster::extract(x, p)))) { - p <- raster::xyFromCell(x, sample(raster::ncell(x), 2)) - } - lcp <- gdistance::shortestPath(cond, p[1, ], p[2, ], - output = "SpatialLines") - cost <- raster::extract(work, lcp) - cost <- sum(cost[[1]], na.rm = TRUE) - ans[["Origins"]] <- rbind(ans[["Origins"]], p[1, ]) - ans[["Destinations"]] <- rbind(ans[["Destinations"]], p[2, ]) - ans[["Paths"]][[i]] <- lcp - ans[["Costs"]] <- rbind(ans[["Costs"]], cost) - } - ans[["Paths"]] <- do.call(rbind, ans[["Paths"]]) - ans[["Distances"]] <- sp::SpatialLinesLengths(ans[["Paths"]]) - ans[["Costs"]] <- as.numeric(ans[["Costs"]]) - if (plot == TRUE) { - raster::plot(x, main = paste0("Least-cost paths between ", rep, - " random points")) - graphics::points(ans[["Origins"]], pch = 20, - col = grDevices::adjustcolor("blue", alpha.f = 0.75)) - graphics::points(ans[["Destinations"]], pch = 20, - col = grDevices::adjustcolor("grey20", alpha.f = 0.75)) - graphics::lines(ans[["Paths"]], lt = 2, - col = grDevices::adjustcolor("grey10", alpha.f = 0.75)) - } - return(ans) - } else { #with defined coordinates - p <- rbind(or, dest) - if (class(p)[1] == "data.frame") { - p <- as.matrix(p) - } else if (class(p)[1] != "matrix") { - stop("Origin and destination must be data.frames, matrices, or vectors") - } - if (any(names(p) != c("x", "y"))) { - names(p) <- c("x", "y") - } - lcp <- gdistance::shortestPath(cond, p[1, ], p[2, ], - output = "SpatialLines") - cost <- raster::extract(work, lcp) - cost <- sum(cost[[1]], na.rm = TRUE) - ans <- list(Origin = p[1, ], - Destination = p[2, ], - Path = lcp, - Cost = cost, - Distance = sp::SpatialLinesLengths(lcp)) - if (plot == TRUE) { - raster::plot(x, main = "Least-cost paths between two points") - graphics::points(p[1, 1], p[1, 2], pch = 20, - col = grDevices::adjustcolor("blue", alpha.f = 0.75)) - graphics::points(p[2, 1], p[2, 2], pch = 20, - col = grDevices::adjustcolor("grey20", alpha.f = 0.75)) - graphics::lines(lcp, lt = 2, - col = grDevices::adjustcolor("grey10", alpha.f = 0.75)) - } - return(ans) - } -} diff --git a/R/omniscape.R b/R/omniscape.R index a5b99e3..e49f62c 100644 --- a/R/omniscape.R +++ b/R/omniscape.R @@ -14,15 +14,16 @@ omniscape_skeleton <- function( radius = NULL, aggr_fact = 1 ) { - if (is.null(en) | is.null(path) | is.null(radius)) { + if (is.null(en) | is.null(radius)) { stop("Missing mandatory input") } + if (is.null(path)) path <- getwd() if (aggr_fact > 1) { - w <- raster::aggregate(en$rasters$EnergyScape, aggr_fact) + w <- aggregate(en, aggr_fact) } else { - w <- en$rasters$EnergyScape + w <- en } - raster::writeRaster(w, file.path(path, "/work.tif"), + writeRaster(w, file.path(path, "EnergyScape.tif"), overwrite = TRUE) omni_file <- file(file.path(path, "omniscape.ini"), open = "w") if (!isOpen(omni_file)) { @@ -30,7 +31,7 @@ omniscape_skeleton <- function( } writeLines(text = c( "[Required arguments]", - paste0("resistance_file = ", path, "/work.tif"), + paste0("resistance_file = ", path, "/EnergyScape.tif"), paste0("radius = ", radius), paste0("block_size = ", 1), paste0("project_name = ", path, "/omniscape"), diff --git a/R/random_passage.R b/R/random_passage.R deleted file mode 100644 index 2c8fdc1..0000000 --- a/R/random_passage.R +++ /dev/null @@ -1,101 +0,0 @@ -#' Compute probability of passage of random walks -#' -#' Calculate the net number of passages of random walks between origin and -#' destination. -#' @param en an enerscape object obtained with \code{enerscape()}. -#' @param or origin point. -#' @param dest destination point. -#' @param theta the degree from which the path randomly deviates from the -#' least-cost path. -#' @param simulate_random_points if to simulate least-cost path among random -#' points. default = FALSE. -#' @param rep number or random origin and destination points if -#' \code{simulate_random_points = TRUE}. default = 10. -#' @param plot if to plot the output. -#' @return A list with point locations, rasterLayer of net passage of random -#' walks, and rasterLayer of cumulative net passage if -#' \code{simulate_random_points = TRUE}. -#' @details If \code{or} and \code{dest} are not specified, the least-cost path -#' is specified by setting \code{simulate_random_points = TRUE} and \code{rep} -#' equal to the number of random paths to compute. -#' @examples -#' library(raster) -#' data("volcano") -#' dem <- raster(volcano) -#' en <- enerscape(dem, 10, unit = "kcal", neigh = 16) -#' p <- xyFromCell(dem, sample(ncell(dem), 2)) -#' walk <- en_passage(en, or = p[1, ], dest = p[2, ], theta = 4) -en_passage <- function( - en, - or, - dest, - theta = 4, - simulate_random_points = FALSE, - rep = 10, - plot = TRUE -) { - if (class(en) != "enerscape") { - stop("en must be an enerscape object") - } else { - x <- en$rasters$DEM - cond <- en$cond_tr - } - ans <- list() #initialize return value - if (simulate_random_points) { #random points - rp <- list() - for (i in seq_len(rep)) { - message(i, " of ", rep, " random points ...") - p <- raster::xyFromCell(x, sample(raster::ncell(x), 2)) - while (any(is.na(raster::extract(x, p)))) { - p <- raster::xyFromCell(x, sample(raster::ncell(x), 2)) - } - rp[[i]] <- gdistance::passage(cond, p[1, ], p[2, ], theta) - ans[["Origins"]] <- rbind(ans[["Origins"]], p[1, ]) - ans[["Destinations"]] <- rbind(ans[["Destinations"]], p[2, ]) - } - rp <- raster::stack(rp) - ans[["Passages"]] <- rp - rp <- sum(rp) - rp <- rp / max(raster::values(rp), na.rm = TRUE) - ans[["Cumulative net passage"]] <- rp - if (plot == TRUE) { - raster::plot(rp, col = grDevices::topo.colors(100), - main = paste0("Cumulative net passage for ", rep, - " random points")) - raster::contour(x, nlevels = 5, add = TRUE, col = "gainsboro", lt = 2) - graphics::points(ans[["Origins"]], - pch = 20, - col = grDevices::adjustcolor("gold", alpha.f = 0.75)) - graphics::points(ans[["Destinations"]], - pch = 20, - col = grDevices::adjustcolor("tomato", alpha.f = 0.75)) - } - return(ans) - } else { #with defined coordinates - p <- rbind(or, dest) - if (class(p)[1] == "data.frame") { - p <- as.matrix(p) - } else if (class(p)[1] != "matrix") { - stop("Origin and destination must be data.frames, matrices, or vectors") - } - if (any(names(p) != c("x", "y"))) { - names(p) <- c("x", "y") - } - rp <- gdistance::passage(cond, p[1, ], p[2, ], theta) - if (plot == TRUE) { - raster::plot(rp, col = grDevices::topo.colors(100), - main = paste0("Cumulative net passage")) - raster::contour(x, nlevels = 5, add = TRUE, col = "gainsboro", lt = 2) - graphics::points(ans[["Origins"]], - pch = 20, - col = grDevices::adjustcolor("gold", alpha.f = 0.75)) - graphics::points(ans[["Destinations"]], - pch = 20, - col = grDevices::adjustcolor("tomato", alpha.f = 0.75)) - } - ans[["Origins"]] <- p[1, ] - ans[["Destination"]] <- p[2, ] - ans[["Passage"]] <- rp - return(rp) - } -} diff --git a/data/sirente.rda b/data/sirente.rda new file mode 100644 index 0000000..e7ea310 Binary files /dev/null and b/data/sirente.rda differ diff --git a/man/distances.Rd b/man/distances.Rd new file mode 100644 index 0000000..660714e --- /dev/null +++ b/man/distances.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{distances} +\alias{distances} +\title{Spatial distances} +\usage{ +distances(x, center, res) +} +\arguments{ +\item{x}{matrix with values} + +\item{center}{numeric value (double) with the value of the focal cell} + +\item{res}{numeric value (double) of the spatial resolution of the matrix} +} +\value{ +Vector with values the distances between x and center +} +\description{ +Spatial distances +} diff --git a/man/dot-calc_arc.Rd b/man/dot-calc_arc.Rd deleted file mode 100644 index 6b48ac7..0000000 --- a/man/dot-calc_arc.Rd +++ /dev/null @@ -1,27 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/enerscape_internals.R -\name{.calc_arc} -\alias{.calc_arc} -\title{Compute energy costs} -\usage{ -.calc_arc(slope, m, work_in_kcal = TRUE, j_to_kcal = 4184) -} -\arguments{ -\item{slope}{slope transition matrix.} - -\item{m}{species body mass (kg).} - -\item{work_in_kcal}{if work should be expressed in kilocalories.} - -\item{j_to_kcal}{joules to kilocalories conversion constant.} -} -\value{ -A transition layer with values the energy cost of movement between - cells (J or kcal). -} -\description{ -Internal function for enerscape - calculate work. -} -\details{ -Internal function of enerscape, don't call directly. -} diff --git a/man/dot-calc_arc_cond.Rd b/man/dot-calc_arc_cond.Rd deleted file mode 100644 index 815e1dc..0000000 --- a/man/dot-calc_arc_cond.Rd +++ /dev/null @@ -1,27 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/enerscape_internals.R -\name{.calc_arc_cond} -\alias{.calc_arc_cond} -\title{Compute conductance} -\usage{ -.calc_arc_cond(slope, m, work_in_kcal = TRUE, j_to_kcal = 4184) -} -\arguments{ -\item{slope}{slope transition matrix.} - -\item{m}{species body mass (kg).} - -\item{work_in_kcal}{if work should be expressed in kilocalories.} - -\item{j_to_kcal}{joules to kilocalories conversion constant.} -} -\value{ -A transition layer with values the conductance between cells, i.e. - the distance that can be traveled per unit of energy (1 / J or 1 / kcal). -} -\description{ -Internal function for enerscape - calculate conductance -} -\details{ -Internal function of enerscape, don't call directly. -} diff --git a/man/dot-calc_cycling.Rd b/man/dot-calc_cycling.Rd deleted file mode 100644 index e23ff88..0000000 --- a/man/dot-calc_cycling.Rd +++ /dev/null @@ -1,33 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/enerscape_internals.R -\name{.calc_cycling} -\alias{.calc_cycling} -\title{Compute energy costs for a cyclist} -\usage{ -.calc_cycling(height, slope, m, v, work_in_kcal = TRUE, j_to_kcal = 4184) -} -\arguments{ -\item{height}{height transition matrix.} - -\item{slope}{slope transition matrix.} - -\item{m}{species body mass (kg).} - -\item{v}{speed of cyclist.} - -\item{work_in_kcal}{if work should be expressed in kilocalories.} - -\item{j_to_kcal}{joules to kilocalories conversion constant.} -} -\value{ -A transition layer with values the energy cost of movement between - cells (J or kcal). -} -\description{ -Internal function for enerscape - calculate work. -} -\details{ -Internal function of enerscape, don't call directly. This assumes no - wind, a bike of 7 kg, optimal pedal frequency, and constant mechanical - efficiency of 25%. -} diff --git a/man/dot-calc_cycling_cond.Rd b/man/dot-calc_cycling_cond.Rd deleted file mode 100644 index 06c46d4..0000000 --- a/man/dot-calc_cycling_cond.Rd +++ /dev/null @@ -1,33 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/enerscape_internals.R -\name{.calc_cycling_cond} -\alias{.calc_cycling_cond} -\title{Compute conductance for a cyclist} -\usage{ -.calc_cycling_cond(height, slope, m, v, work_in_kcal = TRUE, j_to_kcal = 4184) -} -\arguments{ -\item{height}{height transition matrix.} - -\item{slope}{slope transition matrix.} - -\item{m}{species body mass (kg).} - -\item{v}{speed of cyclist.} - -\item{work_in_kcal}{if work should be expressed in kilocalories.} - -\item{j_to_kcal}{joules to kilocalories conversion constant.} -} -\value{ -A transition layer with values the energy cost of movement between - cells (J or kcal). -} -\description{ -Internal function for enerscape - calculate work. -} -\details{ -Internal function of enerscape, don't call directly. This assumes no - wind, a bike of 7 kg, optimal pedal frequency, and constant mechanical - efficiency of 25%. -} diff --git a/man/en_extrapolation.Rd b/man/en_extrapolation.Rd deleted file mode 100644 index 79c140f..0000000 --- a/man/en_extrapolation.Rd +++ /dev/null @@ -1,40 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/en_extrapolation.R -\name{en_extrapolation} -\alias{en_extrapolation} -\title{Check if model extrapolates} -\usage{ -en_extrapolation(en, plot = TRUE) -} -\arguments{ -\item{en}{an enerscape object.} - -\item{plot}{plot areas where slope is extrapolated.} -} -\value{ -A list with booleans if body size or inclines extrapolates and a - rasterLayer for where incline extrapolates. The rasterLayer is returned - only if extrapolations are present. -} -\description{ -This check if computation of the energy landscape extrapolates from the test -set of enerscape::pontzer (2016). -} -\details{ -Check if body mass or incline are outside the test range of the - model. If slope extrapolations are detected and \code{plot = TRUE}, a plot - of where extrapolations occur is displayed. -} -\examples{ -library(raster) -library(enerscape) -data("volcano") -dem <- raster(volcano) -en <- enerscape(dem, 10, unit = "kcal", neigh = 16) -en_extrapolation(en, plot = TRUE) -} -\references{ -enerscape::pontzer, H. (2016). A unified theory for the energy - cost of legged locomotion. Biology Letters, 12(2), 20150935. \doi{ - https://doi.org/10.1098/rsbl.2015.0935}. -} diff --git a/man/en_lcp.Rd b/man/en_lcp.Rd deleted file mode 100644 index e479a50..0000000 --- a/man/en_lcp.Rd +++ /dev/null @@ -1,43 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/least-cost_path.R -\name{en_lcp} -\alias{en_lcp} -\title{Compute least-cost paths} -\usage{ -en_lcp(en, or, dest, simulate_random_points = FALSE, rep = 10, plot = TRUE) -} -\arguments{ -\item{en}{an enerscape object obtained with \code{enerscape()}.} - -\item{or}{origin point.} - -\item{dest}{destination point.} - -\item{simulate_random_points}{if to simulate least-cost path among random -points. default = FALSE.} - -\item{rep}{number or random origin and destination points if -\code{simulate_random_points = TRUE}. default = 10.} - -\item{plot}{if to plot the output.} -} -\value{ -A list with point locations, least-cost path as SpatialLines, energy - costs and distances. -} -\description{ -Calculate the least-cost path (lcp) between origin and destination -} -\details{ -If \code{or} and \code{dest} are not specified, the least-cost path - is specified by setting \code{simulate_random_points = TRUE} and \code{rep} - equal to the number of random paths to compute. -} -\examples{ -library(raster) -data("volcano") -dem <- raster(volcano) -en <- enerscape(dem, 10, unit = "kcal", neigh = 16) -p <- xyFromCell(dem, sample(ncell(dem), 2)) -lcp <- en_lcp(en, or = p[1, ], dest = p[2, ]) -} diff --git a/man/en_passage.Rd b/man/en_passage.Rd deleted file mode 100644 index 50cefbb..0000000 --- a/man/en_passage.Rd +++ /dev/null @@ -1,56 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/random_passage.R -\name{en_passage} -\alias{en_passage} -\title{Compute probability of passage of random walks} -\usage{ -en_passage( - en, - or, - dest, - theta = 4, - simulate_random_points = FALSE, - rep = 10, - plot = TRUE -) -} -\arguments{ -\item{en}{an enerscape object obtained with \code{enerscape()}.} - -\item{or}{origin point.} - -\item{dest}{destination point.} - -\item{theta}{the degree from which the path randomly deviates from the -least-cost path.} - -\item{simulate_random_points}{if to simulate least-cost path among random -points. default = FALSE.} - -\item{rep}{number or random origin and destination points if -\code{simulate_random_points = TRUE}. default = 10.} - -\item{plot}{if to plot the output.} -} -\value{ -A list with point locations, rasterLayer of net passage of random - walks, and rasterLayer of cumulative net passage if - \code{simulate_random_points = TRUE}. -} -\description{ -Calculate the net number of passages of random walks between origin and -destination. -} -\details{ -If \code{or} and \code{dest} are not specified, the least-cost path - is specified by setting \code{simulate_random_points = TRUE} and \code{rep} - equal to the number of random paths to compute. -} -\examples{ -library(raster) -data("volcano") -dem <- raster(volcano) -en <- enerscape(dem, 10, unit = "kcal", neigh = 16) -p <- xyFromCell(dem, sample(ncell(dem), 2)) -walk <- en_passage(en, or = p[1, ], dest = p[2, ], theta = 4) -} diff --git a/man/en_path.Rd b/man/en_path.Rd deleted file mode 100644 index 88073db..0000000 --- a/man/en_path.Rd +++ /dev/null @@ -1,49 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/en_path.R -\name{en_path} -\alias{en_path} -\title{Compute the energy costs for a chosen path} -\usage{ -en_path(en, p = NULL, draw = FALSE, n = NULL, plot = TRUE) -} -\arguments{ -\item{en}{an enerscape object.} - -\item{p}{path as SpatialLines.} - -\item{draw}{if TRUE the path will be chosen by drawing it on the map/} - -\item{n}{number of node points for the path.} - -\item{plot}{if TRUE plot the path} -} -\value{ -A list with elements the path, its travel distance and energy costs. -} -\description{ -This returns the distance and energy costs of traveling a chosen path. -Optionally, the path can be selected by specifying the number of nodes and -clicking on the plot. -} -\examples{ -\donttest{ -library(raster) -library(enerscape) -data("volcano") -dem <- raster(volcano) -en <- enerscape(dem, 10, unit = "kcal", neigh = 16) -} -} -\references{ -Etten, J. van. (2017). R Package gdistance: Distances and Routes - on Geographical Grids. Journal of Statistical Software, 76(1), 1–21. - \doi{https://doi.org/10.18637/jss.v076.i13}. - - Pontzer, H. (2016). A unified theory for the energy cost of legged - locomotion. Biology Letters, 12(2), 20150935. \doi{ - https://doi.org/10.1098/rsbl.2015.0935}. - - di Prampero, P. E., Cortili, G., Mognoni, P., & Saibene, F. (1979). - Equation of motion of a cyclist. Journal of Applied Physiology, 47(1), - 201–206. \doi{https://doi.org/10.1152/jappl.1979.47.1.201} -} diff --git a/man/energy.Rd b/man/energy.Rd new file mode 100644 index 0000000..81457fb --- /dev/null +++ b/man/energy.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{energy} +\alias{energy} +\title{Energy Landscape} +\usage{ +energy(slope, distance, mass, res, kcal = TRUE) +} +\arguments{ +\item{slope}{vector with slopes} + +\item{distance}{vector with distances} + +\item{mass}{body mass of species (kg)} + +\item{res}{numeric value (double) of the spatial resolution of the matrix} + +\item{kcal}{(boolean) if to return the result in kCal (true) or J (false)} +} +\value{ +Vector with the energy cost of locomotion (EnergyScape) +} +\description{ +Energy Landscape +} diff --git a/man/energyscape.Rd b/man/energyscape.Rd new file mode 100644 index 0000000..b4b4ad3 --- /dev/null +++ b/man/energyscape.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{energyscape} +\alias{energyscape} +\title{Energy Landscape} +\usage{ +energyscape(x, n = 4L, mass = 0, res = 0, kcal = TRUE) +} +\arguments{ +\item{x}{matrix with values the elevation.} + +\item{n}{(integer) number of neighbours to consider (either 4 or 8).} + +\item{mass}{body mass of species (kg).} + +\item{res}{numeric value (double) of the spatial resolution of the matrix.} + +\item{kcal}{(boolean) if to return the result in kCal (true) or J (false).} +} +\value{ +Matrix with the energy cost of locomotion (EnergyScape). +} +\description{ +Energy Landscape +} diff --git a/man/enerscape.Rd b/man/enerscape.Rd index a6b66b7..41f3aa0 100644 --- a/man/enerscape.Rd +++ b/man/enerscape.Rd @@ -2,9 +2,9 @@ % Please edit documentation in R/enerscape.R \name{enerscape} \alias{enerscape} -\title{Compute the energy landscape} +\title{Compute Energy Landscapes} \usage{ -enerscape(dem, m, unit = "joule", neigh = 16, method = "ARC", v = NULL) +enerscape(dem, m, unit = "joule", neigh = 8) } \arguments{ \item{dem}{raster file of the digital elevation model, either a raster or a @@ -15,12 +15,6 @@ full path location of the file.} \item{unit}{if joules ('joule') or kilocalories ('kcal').} \item{neigh}{number of neighbor cells that are connected together.} - -\item{method}{method to use to compute the energy costs. 'ARC' refers to the -model from Pontzer (2016) and 'cycling' to the model for cyclist from di -Prampero et al. (1979).} - -\item{v}{speed of cyclist (km / h), only for \code{method = 'cycling'}.} } \value{ A list with elements a rasterStack of the digital elevation model, @@ -39,21 +33,15 @@ From the digital elevation model, transition slopes, energy costs Pontzer (2016). } \examples{ -library(raster) +library(terra) +library(enerscape) + data("volcano") -dem <- raster(volcano) +dem <- rast(volcano) en <- enerscape(dem, 10, unit = "kcal", neigh = 16) } \references{ -Etten, J. van. (2017). R Package gdistance: Distances and Routes - on Geographical Grids. Journal of Statistical Software, 76(1), 1–21. - \doi{https://doi.org/10.18637/jss.v076.i13}. - - Pontzer, H. (2016). A unified theory for the energy cost of legged +Pontzer, H. (2016). A unified theory for the energy cost of legged locomotion. Biology Letters, 12(2), 20150935. \doi{ https://doi.org/10.1098/rsbl.2015.0935}. - - di Prampero, P. E., Cortili, G., Mognoni, P., & Saibene, F. (1979). - Equation of motion of a cyclist. Journal of Applied Physiology, 47(1), - 201–206. \doi{https://doi.org/10.1152/jappl.1979.47.1.201} } diff --git a/man/enerscape_gridded.Rd b/man/enerscape_gridded.Rd deleted file mode 100644 index ff2cef7..0000000 --- a/man/enerscape_gridded.Rd +++ /dev/null @@ -1,88 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/enerscape_gridded.R -\name{enerscape_gridded} -\alias{enerscape_gridded} -\title{Runs enerscape on blocks of a RasterLayer.} -\usage{ -enerscape_gridded( - dem, - m, - split.hor, - split.vert, - unit = "joule", - neigh = 16, - method = "ARC", - v = NULL, - write = FALSE, - out.dir = raster::tmpDir() -) -} -\arguments{ -\item{dem}{raster file of the digital elevation model, either a raster or a -full path location of the file.} - -\item{m}{species body mass (kg).} - -\item{split.hor}{integer, number of horizontal splits.} - -\item{split.vert}{integer, number of vertical splits. -avoid missing values at the polygon border when merging back together.} - -\item{unit}{if joules ('joule') or kilocalories ('kcal').} - -\item{neigh}{number of neighbor cells that are connected together.} - -\item{method}{method to use to compute the energy costs. 'ARC' refers to the -model from Pontzer (2016) and 'cycling' to the model for cyclist from di -Prampero et al. (1979).} - -\item{v}{speed of cyclist (km / h), only for \code{method = 'cycling'}.} - -\item{write}{TRUE/FALSE, if to write the raster to disk.} - -\item{out.dir}{string, output directory; default to tempdir().} -} -\value{ -A list with elements a rasterStack of the digital elevation model, - slope, energy landscape, and conductance and the conductance as a transitionLayer for - path analysis. -} -\description{ -This is the main function to compute energy landscapes from a digital -elevation model and body mass of animals based on the model from Pontzer -(2016). The core of the computations are done using the \emph{gdistance} -(Etten, 2017) package. -} -\details{ -Split the DEM into blocks and run enerscape for each block - sequentially. -This is useful when the DEM layer is particularly large. There are two -options: the first is to manipulate all layers in memory (write = FALSE); -the second is to write each layer to disk and load it when necessary. -The former is faster, but consume more RAM. -} -\examples{ -\donttest{ -library(raster) -library(enerscape) -data("volcano") -r <- raster(volcano) -crs(r) <- "+proj=longlat +datum=WGS84 +no_defs" -r <- projectRaster(r, crs = "EPSG:32759", res = c(1e3, 1e3)) -en <- enerscape(r, 1)$rasters$EnergyScape -en_gr <- enerscape_gridded(r, 1, split.hor = 3, split.vert = 3) -} -} -\references{ -Etten, J. van. (2017). R Package gdistance: Distances and Routes - on Geographical Grids. Journal of Statistical Software, 76(1), 1–21. - \doi{https://doi.org/10.18637/jss.v076.i13}. - - Pontzer, H. (2016). A unified theory for the energy cost of legged - locomotion. Biology Letters, 12(2), 20150935. \doi{ - https://doi.org/10.1098/rsbl.2015.0935}. - - di Prampero, P. E., Cortili, G., Mognoni, P., & Saibene, F. (1979). - Equation of motion of a cyclist. Journal of Applied Physiology, 47(1), - 201–206. \doi{https://doi.org/10.1152/jappl.1979.47.1.201} -} diff --git a/man/gridPolygon.Rd b/man/gridPolygon.Rd deleted file mode 100644 index 8324ad2..0000000 --- a/man/gridPolygon.Rd +++ /dev/null @@ -1,30 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/enerscape_gridded.R -\name{gridPolygon} -\alias{gridPolygon} -\title{Create a gridPolygon} -\usage{ -gridPolygon(mat, proj4, ID) -} -\arguments{ -\item{mat}{matrix with coordinates.} - -\item{proj4}{string, proj4string of the original raster.} - -\item{ID}{integer, ID of the polygon (can be anything as lons as unique).} -} -\value{ -a SpatialPolygonsDataFrame. -} -\description{ -Create a gridPolygon -} -\details{ -gridPolygon() is used within gridRaster() and is mostly for enerscape -package internal functioning. -} -\examples{ -mat <- matrix(c(0, 0, 1, 0, 1, 1, 0, 1, 0, 0), ncol = 2, byrow = TRUE) -proj4 <- "+proj=longlat +datum=WGS84 +no_defs" -gridPolygon(mat, proj4, ID = 1) -} diff --git a/man/gridRaster.Rd b/man/gridRaster.Rd deleted file mode 100644 index 37187bf..0000000 --- a/man/gridRaster.Rd +++ /dev/null @@ -1,36 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/enerscape_gridded.R -\name{gridRaster} -\alias{gridRaster} -\title{Create spatial grid} -\usage{ -gridRaster(r, split.hor, split.vert) -} -\arguments{ -\item{r}{RasterLayer.} - -\item{split.hor}{integer, number of horizontal splits.} - -\item{split.vert}{integer, number of vertical splits.} -} -\value{ -a SpatialPolyonsDataFrame. -} -\description{ -Create spatial grid -} -\details{ -gridRaster makes a spatial grid that will be used to divide -the raster into smaller units for computations. The final grid may not -necessarily be split into split.hor times split.vert blocks, but it can happen -that the final grid has (split.hor - 1) times (split.vert - 1) blocks. -} -\examples{ -\donttest{ -library(raster) -data("volcano") -r <- raster(volcano) -crs(r) <- "+proj=longlat +datum=WGS84 +no_defs" -gridRaster(r, split.hor = 3, split.vert = 3) -} -} diff --git a/man/mergeRaster.Rd b/man/mergeRaster.Rd deleted file mode 100644 index 672f90a..0000000 --- a/man/mergeRaster.Rd +++ /dev/null @@ -1,35 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/enerscape_gridded.R -\name{mergeRaster} -\alias{mergeRaster} -\title{Merge split raster blocks} -\usage{ -mergeRaster(x = NULL, out.dir = NULL, pattern = "") -} -\arguments{ -\item{x}{an enerscape rasterBlocks class, which is a list of raster blocks.} - -\item{out.dir}{string, output directory; default to tempdir().} - -\item{pattern}{string, regular pattern to subset files in out.dir.} -} -\value{ -a RasterLayer. -} -\description{ -Merge split raster blocks -} -\details{ -if x == NULL, rasterMerge() loads the rasters save to disk by -rasterSplit() -} -\examples{ -\donttest{ -library(raster) -data("volcano") -r <- raster(volcano) -crs(r) <- "+proj=longlat +datum=WGS84 +no_defs" -gr <- gridRaster(r, split.hor = 3, split.vert = 3) -splits <- splitRaster(r, gr) -} -} diff --git a/man/neighbours.Rd b/man/neighbours.Rd new file mode 100644 index 0000000..d16b6d6 --- /dev/null +++ b/man/neighbours.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{neighbours} +\alias{neighbours} +\title{Neighbours} +\usage{ +neighbours(i, j, n, x) +} +\arguments{ +\item{i}{row index} + +\item{j}{column index} + +\item{n}{number of neighbours (4 or 8)} + +\item{x}{matrix with values} +} +\value{ +Vector with values the neighours of x +} +\description{ +Neighbours +} diff --git a/man/sirente.Rd b/man/sirente.Rd new file mode 100644 index 0000000..9191067 --- /dev/null +++ b/man/sirente.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{sirente} +\alias{sirente} +\title{Monte Sirente Digital Elevation Model} +\format{ +An object of class \code{matrix} (inherits from \code{array}) with 393 rows and 594 columns. +} +\usage{ +sirente +} +\description{ +A matrix with the digital elevation mode of the Monte Sirente (Central Italy). +} +\keyword{datasets} diff --git a/man/slope.Rd b/man/slope.Rd new file mode 100644 index 0000000..1eea20b --- /dev/null +++ b/man/slope.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{slope} +\alias{slope} +\title{Slopes} +\usage{ +slope(x, center, res) +} +\arguments{ +\item{x}{matrix with values} + +\item{center}{numeric value (double) with the value of the focal cell} + +\item{res}{numeric value (double) of the spatial resolution of the matrix} +} +\value{ +Vector with values the slopes (degrees) between x and center +} +\description{ +Slopes +} diff --git a/man/splitRaster.Rd b/man/splitRaster.Rd deleted file mode 100644 index aa57e9e..0000000 --- a/man/splitRaster.Rd +++ /dev/null @@ -1,43 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/enerscape_gridded.R -\name{splitRaster} -\alias{splitRaster} -\title{Split raster into blocks} -\usage{ -splitRaster(x, y, buffer = TRUE, write = FALSE, out.dir = tempdir()) -} -\arguments{ -\item{x}{RasterLayer.} - -\item{y}{SpatialPolygonsDataFrame, polygon grid (as created using -gridRaster()),} - -\item{buffer}{TRUE/FALSE, if to create a small buffer around the polygon to -avoid missing values at the polygon border when merging back together.} - -\item{write}{TRUE/FALSE, if to write the raster to disk.} - -\item{out.dir}{string, output directory; default to tempdir().} -} -\value{ -an enerscape rasterBlocks objects, which is a list of RasterLayers. -} -\description{ -Split raster into blocks -} -\details{ -as splitting exactly at the spatialPolygons boundaries will create -later artifacts at the polygons edge, it is better to always set -buffer = TRUE, which will polygons buffered by twice the -resolution of the raster to split before cropping. -} -\examples{ -\donttest{ -library(raster) -data("volcano") -r <- raster(volcano) -crs(r) <- "+proj=longlat +datum=WGS84 +no_defs" -gr <- gridRaster(r, split.hor = 3, split.vert = 3) -splits <- splitRaster(r, gr) -} -} diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index f42985a..9a67323 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -10,16 +10,16 @@ Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); #endif -// distance -NumericVector distance(NumericVector dem, double center, double res); -RcppExport SEXP _enerscape_distance(SEXP demSEXP, SEXP centerSEXP, SEXP resSEXP) { +// distances +NumericVector distances(NumericVector x, double center, double res); +RcppExport SEXP _enerscape_distances(SEXP xSEXP, SEXP centerSEXP, SEXP resSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< NumericVector >::type dem(demSEXP); + Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP); Rcpp::traits::input_parameter< double >::type center(centerSEXP); Rcpp::traits::input_parameter< double >::type res(resSEXP); - rcpp_result_gen = Rcpp::wrap(distance(dem, center, res)); + rcpp_result_gen = Rcpp::wrap(distances(x, center, res)); return rcpp_result_gen; END_RCPP } @@ -39,50 +39,50 @@ BEGIN_RCPP END_RCPP } // energyscape -NumericMatrix energyscape(NumericMatrix m, int n, double mass, double res, bool kcal); -RcppExport SEXP _enerscape_energyscape(SEXP mSEXP, SEXP nSEXP, SEXP massSEXP, SEXP resSEXP, SEXP kcalSEXP) { +NumericMatrix energyscape(NumericMatrix x, int n, double mass, double res, bool kcal); +RcppExport SEXP _enerscape_energyscape(SEXP xSEXP, SEXP nSEXP, SEXP massSEXP, SEXP resSEXP, SEXP kcalSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< NumericMatrix >::type m(mSEXP); + Rcpp::traits::input_parameter< NumericMatrix >::type x(xSEXP); Rcpp::traits::input_parameter< int >::type n(nSEXP); Rcpp::traits::input_parameter< double >::type mass(massSEXP); Rcpp::traits::input_parameter< double >::type res(resSEXP); Rcpp::traits::input_parameter< bool >::type kcal(kcalSEXP); - rcpp_result_gen = Rcpp::wrap(energyscape(m, n, mass, res, kcal)); + rcpp_result_gen = Rcpp::wrap(energyscape(x, n, mass, res, kcal)); return rcpp_result_gen; END_RCPP } // neighbours -NumericVector neighbours(int i, int j, int n, NumericMatrix m); -RcppExport SEXP _enerscape_neighbours(SEXP iSEXP, SEXP jSEXP, SEXP nSEXP, SEXP mSEXP) { +NumericVector neighbours(int i, int j, int n, NumericMatrix x); +RcppExport SEXP _enerscape_neighbours(SEXP iSEXP, SEXP jSEXP, SEXP nSEXP, SEXP xSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< int >::type i(iSEXP); Rcpp::traits::input_parameter< int >::type j(jSEXP); Rcpp::traits::input_parameter< int >::type n(nSEXP); - Rcpp::traits::input_parameter< NumericMatrix >::type m(mSEXP); - rcpp_result_gen = Rcpp::wrap(neighbours(i, j, n, m)); + Rcpp::traits::input_parameter< NumericMatrix >::type x(xSEXP); + rcpp_result_gen = Rcpp::wrap(neighbours(i, j, n, x)); return rcpp_result_gen; END_RCPP } // slope -NumericVector slope(NumericVector dem, double center, double res); -RcppExport SEXP _enerscape_slope(SEXP demSEXP, SEXP centerSEXP, SEXP resSEXP) { +NumericVector slope(NumericVector x, double center, double res); +RcppExport SEXP _enerscape_slope(SEXP xSEXP, SEXP centerSEXP, SEXP resSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< NumericVector >::type dem(demSEXP); + Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP); Rcpp::traits::input_parameter< double >::type center(centerSEXP); Rcpp::traits::input_parameter< double >::type res(resSEXP); - rcpp_result_gen = Rcpp::wrap(slope(dem, center, res)); + rcpp_result_gen = Rcpp::wrap(slope(x, center, res)); return rcpp_result_gen; END_RCPP } static const R_CallMethodDef CallEntries[] = { - {"_enerscape_distance", (DL_FUNC) &_enerscape_distance, 3}, + {"_enerscape_distances", (DL_FUNC) &_enerscape_distances, 3}, {"_enerscape_energy", (DL_FUNC) &_enerscape_energy, 5}, {"_enerscape_energyscape", (DL_FUNC) &_enerscape_energyscape, 5}, {"_enerscape_neighbours", (DL_FUNC) &_enerscape_neighbours, 4}, diff --git a/src/RcppExports.o b/src/RcppExports.o new file mode 100644 index 0000000..9f50358 Binary files /dev/null and b/src/RcppExports.o differ diff --git a/src/distance.h b/src/distance.h index ab0993a..a58880c 100644 --- a/src/distance.h +++ b/src/distance.h @@ -1,25 +1,30 @@ #include using namespace Rcpp; +//' Spatial distances +//' +//' @param x matrix with values +//' @param center numeric value (double) with the value of the focal cell +//' @param res numeric value (double) of the spatial resolution of the matrix +//' @return Vector with values the distances between x and center // [[Rcpp::export]] -NumericVector distance ( NumericVector dem , double center , double res ) { - const double PI = 3.1415926535; - int n = dem.size(); +NumericVector distances ( NumericVector x , double center , double res ) { + int n = x.size(); NumericVector ans(n); double h; for (int i = 0; i < n; i++) { - h = center - dem[i]; + h = center - x[i]; ans[i] = sqrt(pow(res, 2) + pow(h, 2)); } //diagonal corrections if (n == 8) { - h = center - dem[0]; + h = center - x[0]; ans[0] = sqrt(2 * pow(res, 2) + pow(h, 2)); - h = center - dem[2]; + h = center - x[2]; ans[2] = sqrt(2 * pow(res, 2) + pow(h, 2)); - h = center - dem[5]; + h = center - x[5]; ans[5] = sqrt(2 * pow(res, 2) + pow(h, 2)); - h = center - dem[7]; + h = center - x[7]; ans[7] = sqrt(2 * pow(res, 2) + pow(h, 2)); } return ans; diff --git a/src/enerscape.cpp b/src/enerscape.cpp index 787cc61..c9629c1 100644 --- a/src/enerscape.cpp +++ b/src/enerscape.cpp @@ -4,6 +4,14 @@ #include "distance.h" using namespace Rcpp; +//' Energy Landscape +//' +//' @param slope vector with slopes +//' @param distance vector with distances +//' @param mass body mass of species (kg) +//' @param res numeric value (double) of the spatial resolution of the matrix +//' @param kcal (boolean) if to return the result in kCal (true) or J (false) +//' @return Vector with the energy cost of locomotion (EnergyScape) // [[Rcpp::export]] NumericVector energy ( NumericVector slope , @@ -29,20 +37,27 @@ NumericVector energy ( return work; } - +//' Energy Landscape +//' +//' @param x matrix with values the elevation. +//' @param n (integer) number of neighbours to consider (either 4 or 8). +//' @param mass body mass of species (kg). +//' @param res numeric value (double) of the spatial resolution of the matrix. +//' @param kcal (boolean) if to return the result in kCal (true) or J (false). +//' @return Matrix with the energy cost of locomotion (EnergyScape). // [[Rcpp::export]] NumericMatrix energyscape ( - NumericMatrix m , - int n = 4 , - double mass = 0 , + NumericMatrix x, + int n = 4, + double mass = 0, double res = 0, bool kcal = true ) { if (mass == 0 || res == 0) { return 0; } - int rows = m.nrow(); - int cols = m.ncol(); + int rows = x.nrow(); + int cols = x.ncol(); NumericVector neigh(n); NumericVector sl(n); NumericVector dist(n); @@ -50,12 +65,12 @@ NumericMatrix energyscape ( NumericMatrix ans(rows, cols); for (int i = 0 ; i < rows ; i++) { for (int j = 0 ; j < cols ; j++) { - neigh = neighbours(i, j, n, m); + neigh = neighbours(i, j, n, x); if (neigh.size() == 0) { continue; } - sl = slope(neigh, m(i, j), res); - dist = distance(neigh, m(i, j), res); + sl = slope(neigh, x(i, j), res); + dist = distances(neigh, x(i, j), res); en = energy(sl, dist, mass, res, kcal); ans(i, j) = mean(en); } diff --git a/src/enerscape.o b/src/enerscape.o new file mode 100644 index 0000000..168690a Binary files /dev/null and b/src/enerscape.o differ diff --git a/src/enerscape.so b/src/enerscape.so new file mode 100755 index 0000000..5c0fa8c Binary files /dev/null and b/src/enerscape.so differ diff --git a/src/neighbours.h b/src/neighbours.h index cd083df..e0aae54 100644 --- a/src/neighbours.h +++ b/src/neighbours.h @@ -1,11 +1,18 @@ #include using namespace Rcpp; +//' Neighbours +//' +//' @param i row index +//' @param j column index +//' @param n number of neighbours (4 or 8) +//' @param x matrix with values +//' @return Vector with values the neighours of x // [[Rcpp::export]] -NumericVector neighbours ( int i , int j , int n , NumericMatrix m ) { +NumericVector neighbours ( int i , int j , int n , NumericMatrix x ) { NumericVector ans(n); - int rows = m.nrow(); - int cols = m.ncol(); + int rows = x.nrow(); + int cols = x.ncol(); if (j == (cols - 1) || j == 0) { return NULL; } @@ -13,23 +20,22 @@ NumericVector neighbours ( int i , int j , int n , NumericMatrix m ) { return NULL; } if (n == 4) { - ans[0] = m(i - 1, j); - ans[1] = m(i, j - 1); - ans[2] = m(i, j + 1); - ans[3] = m(i + 1, j); + ans[0] = x(i - 1, j); + ans[1] = x(i, j - 1); + ans[2] = x(i, j + 1); + ans[3] = x(i + 1, j); return ans; } else if (n == 8) { - ans[0] = m(i - 1, j - 1); //diag - ans[1] = m(i, j - 1); - ans[2] = m(i + 1, j - 1); //diag - ans[3] = m(i - 1, j); - ans[4] = m(i + 1, j); - ans[5] = m(i - 1, j + 1); //diag - ans[6] = m(i, j + 1); - ans[7] = m(i + 1, j + 1); //diag + ans[0] = x(i - 1, j - 1); //diag + ans[1] = x(i, j - 1); + ans[2] = x(i + 1, j - 1); //diag + ans[3] = x(i - 1, j); + ans[4] = x(i + 1, j); + ans[5] = x(i - 1, j + 1); //diag + ans[6] = x(i, j + 1); + ans[7] = x(i + 1, j + 1); //diag return ans; } else { - std::cout << "Number of neighoours must be 4 or 8."; return NULL; } } diff --git a/src/slope.h b/src/slope.h index 8d2c354..3e393a5 100644 --- a/src/slope.h +++ b/src/slope.h @@ -1,25 +1,31 @@ #include using namespace Rcpp; +//' Slopes +//' +//' @param x matrix with values +//' @param center numeric value (double) with the value of the focal cell +//' @param res numeric value (double) of the spatial resolution of the matrix +//' @return Vector with values the slopes (degrees) between x and center // [[Rcpp::export]] -NumericVector slope ( NumericVector dem , double center , double res ) { +NumericVector slope ( NumericVector x , double center , double res ) { const double PI = 3.1415926535; - int n = dem.size(); + int n = x.size(); NumericVector ans(n); double h; for (int i = 0; i < n; i++) { - h = center - dem[i]; + h = center - x[i]; ans[i] = atan(h / res) * 180 / PI; } //diagonal corrections if (n == 8) { - h = center - dem[0]; + h = center - x[0]; ans[0] = atan(h / (sqrt(2) * res)) * 180 / PI; - h = center - dem[2]; + h = center - x[2]; ans[2] = atan(h / (sqrt(2) * res)) * 180 / PI; - h = center - dem[5]; + h = center - x[5]; ans[5] = atan(h / (sqrt(2) * res)) * 180 / PI; - h = center - dem[7]; + h = center - x[7]; ans[7] = atan(h / (sqrt(2) * res)) * 180 / PI; } return ans; diff --git a/tests/testthat.R b/tests/testthat.R index c41effa..d6fe19a 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -7,6 +7,7 @@ # * https://testthat.r-lib.org/reference/test_package.html#special-files library(testthat) +library(terra) library(enerscape) test_check("enerscape") diff --git a/tests/testthat/test-cpp-functions.R b/tests/testthat/test-cpp-functions.R index 206a7e2..4166ef3 100644 --- a/tests/testthat/test-cpp-functions.R +++ b/tests/testthat/test-cpp-functions.R @@ -30,7 +30,7 @@ test_that("Distance is correct", { set.seed(1234) m <- matrix(1:100, 10, 10) n <- neighbours(1, 1, 4, m) - dist <- distance(n, m[2, 2], 1) + dist <- distances(n, m[2, 2], 1) s <- m[2, 2] - c(11, 2, 22, 13) s <- atan(s) d <- 1 / cos(s) @@ -42,7 +42,7 @@ test_that("Energy works", { m <- matrix(1:100, 10, 10) n <- neighbours(1, 1, 4, m) sl <- slope(n, m[2, 2], 1) - dist <- distance(n, 1, 10) + dist <- distances(n, 1, 10) mass <- 1000 en <- energy(sl, dist, mass, 1, FALSE) ar <- 8.0 * mass ^ -0.34 @@ -50,13 +50,3 @@ test_that("Energy works", { work <- (ar + mec) * mass * dist; expect_equal(en, work, tolerance = 1e-6) }) - -test_that("Energy is correct", { - set.seed(1234) - m <- matrix(1:100, 10, 10) - e <- energyscape(m, 4, 10, .1) - dem <- raster(m) - en <- enerscape(dem, 10, "kcal", 4) - a <- as.matrix(en$rasters$EnergyScape) - expect_equal(a[(2:8), (2:8)], e[(2:8), (2:8)], tolerance = 1e-6) -}) diff --git a/vignettes/.gitignore b/vignettes/.gitignore new file mode 100644 index 0000000..097b241 --- /dev/null +++ b/vignettes/.gitignore @@ -0,0 +1,2 @@ +*.html +*.R diff --git a/vignettes/enerscape.Rmd b/vignettes/enerscape.Rmd new file mode 100644 index 0000000..427c471 --- /dev/null +++ b/vignettes/enerscape.Rmd @@ -0,0 +1,90 @@ +--- +title: "enerscape" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{enerscape} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +# Changelog + +## 1.0.0 + +This follows the transfer of functionalists of old GIS packages to _terra_, with _rgdal_, _rgeos_ scheduled to be retired in 2023: . + +I removed dependencies on gDistance, raster, sp, rgeos, and rgdal. +Energy landscapes are now calculated using a zonal (kernel) based method, implemented in C++ functions. +This is faster that previous versions, but it does not return the transition matrix or the conductance matrix. +Because of this, least cost paths functions are not supported any more. +Instead, use circuitscape or omniscape; see *circuitscape_skeleton()* and *omniscape_skeleton()* to generate the initialization files to run in Julia. + +The cyclist model is also not supported any more; custom models must be written in the C++ functions. +I plan to add a customizable function soon to do that. + +# Energy Landscapes With Enerscape + +Energy landscapes are spatially explicit projections of the energy cost of travel (kcal or J) for an animal. +In enerscape, energy landscapes depend on the body mass of the animal and the incline between locations, following the ARC model from Pontzer (2016): . +For a general overview of energy landscapes, refer to Berti et al. (2021): . + +```{r setup} +library(terra) +library(enerscape) +``` + +Enerscape comes with the default dataset _sirente_; this is a matrix with values the digital elevation model (DEM) for Monte Sirente in Central Italy: + +```{r sirente, fig.width=6, fig.height=3.65} +data("sirente") +dem <- rast(sirente, + crs = "+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs", + extent = ext(879340, 885280, 4672880, 4676810)) +plot(dem) +``` + +I provided the correct coordinate reference system (CRS) and the extent of the DEM; in most cases, this will already be present in downloaded raster layers. + +Enerscape assumes that the coordinate system is in meters: + +```{r crs} +crs(dem, proj = TRUE) +``` + +You **have** to check this by yourself to make sure _+units=m_ or enerscape results will not be meaningful. +Enerscape checks if this is the case and returns a warning if _+units_ does not match _m_, but it will still return its output. + +The energy landscapes can be obtained with the _enerscape()_ function. +There are two mandatory inputs: _dem_, which is the SpatRaster of the DEM, and _mass_, which is the body mass of the animal (kg). + +```{r enerscape} +en <- enerscape(dem, 10, neigh = 4, unit = "kcal") +``` + +Before visualizing it, it is worth nothing that, from my experience, energy landscape values are usually right-skewed, i.e. with most cells having low values and few cells having high values. +In such cases, it is useful to log-transform energy landscape for visualization: + +```{r visualize, fig.width=6, fig.height=3.65} +en_log <- log(en) +plot(en_log) +terra::contour(dem, add = TRUE, nlevels = 5) +``` + +```{r zoom, fig.width=6, fig.height=3.65} +en_log <- crop(en_log, ext(881306, 882853, 4674928, 4675984)) +plot(en_log) +terra::contour(dem, add = TRUE, nlevels = 20) +``` + +# References + +Pontzer, H. (2016). A unified theory for the energy cost of legged locomotion. Biology letters, 12(2), 20150935. + +Berti, E., Davoli, M., Buitenwerf, R., Dyer, A., Hansen, O. L., Hirt, M., ... & Vollrath, F. (2022). The r package enerscape: A general energy landscape framework for terrestrial movement ecology. Methods in Ecology and Evolution, 13(1), 60-67.