diff --git a/docs/Part1_DummyModel.html b/docs/Part1_DummyModel.html index 96b3574..e9df715 100644 --- a/docs/Part1_DummyModel.html +++ b/docs/Part1_DummyModel.html @@ -400,7 +400,7 @@

## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 ## 3.4.0. ## ℹ Please use `linewidth` instead. -ggarrange(plotlist = lmPlots) +ggarrange(plotlist = lmPlots)

diff --git a/docs/Part2_SDMs.html b/docs/Part2_SDMs.html index 8811e4c..a0858db 100644 --- a/docs/Part2_SDMs.html +++ b/docs/Part2_SDMs.html @@ -352,18 +352,30 @@

## If exact location is required, functions will be: `sim$.mods$<moduleName>$FunctionName`. defineModule(sim, list( name = "speciesAbundanceData", - description = "", - keywords = "", - authors = structure(list(list(given = c("First", "Middle"), family = "Last", role = c("aut", "cre"), email = "email@example.com", comment = NULL)), class = "person"), + description = paste("Data module to prepare tree species cover data for species distribution modelling.", + "Defaults to using Canadian National Forest Inventory data."), + keywords = c("minimal SpaDES example", "species distribution model"), + authors = structure(list(list(given = c("Ceres"), family = "Barros", role = c("aut", "cre"), + email = "ceres.barros@ubc.ca", comment = NULL)), class = "person"), childModules = character(0), - version = list(speciesAbundanceData = "0.0.0.9000"), + version = list(speciesAbundanceData = "1.0.0"), timeframe = as.POSIXlt(c(NA, NA)), timeunit = "year", citation = list("citation.bib"), - documentation = list("NEWS.md", "README.md", "speciesAbundanceData.Rmd"), - reqdPkgs = list("SpaDES.core (>= 2.1.5.9001)", "ggplot2"), + documentation = list("README.md", "speciesAbundanceData.Rmd"), ## same file + reqdPkgs = list("SpaDES.core (>=2.0.2)", + "httr", "terra", "ggplot2", "rasterVis"), parameters = bindrows( #defineParameter("paramName", "paramClass", value, min, max, "parameter description"), + defineParameter("sppAbundURL", "character", + paste0("https://ftp.maps.canada.ca/pub/nrcan_rncan/Forests_Foret/", + "canada-forests-attributes_attributs-forests-canada/", + "2001-attributes_attributs-2001/", + "NFI_MODIS250m_2001_kNN_Species_Pice_Gla_v1.tif"), NA, NA, + paste("URL where the first SpatRaster of species abundance resides.", + "This will be the abundance data used to fit the species ditribution model.", + "Defaults to *Picea glauca* percent cover across Canada, in 2001", + "(from Canadian National Forest Inventory forest attributes)")), defineParameter(".plots", "character", "screen", NA, NA, "Used by Plots function, which can be optionally used here"), defineParameter(".plotInitialTime", "numeric", start(sim), NA, NA, @@ -375,8 +387,7 @@

defineParameter(".saveInterval", "numeric", NA, NA, NA, "This describes the simulation time interval between save events."), defineParameter(".studyAreaName", "character", NA, NA, NA, - "Human-readable name for the study area used - e.g., a hash of the study", - "area obtained using `reproducible::studyAreaName()`"), + "Human-readable name for the study area used. If NA, a hash of studyArea will be used."), ## .seed is optional: `list('init' = 123)` will `set.seed(123)` for the `init` event only. defineParameter(".seed", "list", list(), NA, NA, "Named list of seeds to use for each event (names)."), @@ -385,166 +396,115 @@

), inputObjects = bindrows( #expectsInput("objectName", "objectClass", "input object description", sourceURL, ...), - expectsInput(objectName = NA, objectClass = NA, desc = NA, sourceURL = NA) + expectsInput("studyAreaRas", objectClass = "SpatRaster", + desc = "A binary raster of the study area") ), outputObjects = bindrows( #createsOutput("objectName", "objectClass", "output object description", ...), - createsOutput(objectName = NA, objectClass = NA, desc = NA) + createsOutput("sppAbundanceDT", "data.table", + desc = paste("Species abundance data from `sppAbundanceRas`, with columns 'cell',", + "'x', 'y', 'sppAbund' and 'year' (an integer matching the number in", + "names(`sppAbundanceRas`)." )), + createsOutput("sppAbundanceRas", "SpatRaster", + desc = paste("A species abundance layer used to fit a species distribution model", + "at the start of the simulation. Layers named as:", + "paste('year', start(sim):end(sim), sep = '_')). Data obtained from", + "P(sim)$sppAbundURL")) ) )) -doEvent.speciesAbundanceData = function(sim, eventTime, eventType) { +## event types +# - type `init` is required for initialization + +doEvent.speciesAbundanceData = function(sim, eventTime, eventType, debug = FALSE) { switch( eventType, init = { - ### check for more detailed object dependencies: - ### (use `checkObject` or similar) - - # do stuff for this event - sim <- Init(sim) - - # schedule future event(s) - sim <- scheduleEvent(sim, P(sim)$.plotInitialTime, "speciesAbundanceData", "plot") - sim <- scheduleEvent(sim, P(sim)$.saveInitialTime, "speciesAbundanceData", "save") - }, - plot = { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - - plotFun(sim) # example of a plotting function - # schedule future event(s) - - # e.g., - #sim <- scheduleEvent(sim, time(sim) + P(sim)$.plotInterval, "speciesAbundanceData", "plot") - - # ! ----- STOP EDITING ----- ! # - }, - save = { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - - # e.g., call your custom functions/methods here - # you can define your own methods below this `doEvent` function - - # schedule future event(s) - - # e.g., - # sim <- scheduleEvent(sim, time(sim) + P(sim)$.saveInterval, "speciesAbundanceData", "save") - - # ! ----- STOP EDITING ----- ! # - }, - event1 = { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - - # e.g., call your custom functions/methods here - # you can define your own methods below this `doEvent` function - - # schedule future event(s) - - # e.g., - # sim <- scheduleEvent(sim, time(sim) + increment, "speciesAbundanceData", "templateEvent") - - # ! ----- STOP EDITING ----- ! # + ## do stuff for this event + sim <- abundanceInit(sim) + + ## schedule future event(s) + sim <- scheduleEvent(sim, eventTime = P(sim)$.plotInitialTime, + moduleName = "speciesAbundanceData", eventType = "abundPlot", + eventPriority = .normal()) }, - event2 = { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - - # e.g., call your custom functions/methods here - # you can define your own methods below this `doEvent` function - - # schedule future event(s) - - # e.g., - # sim <- scheduleEvent(sim, time(sim) + increment, "speciesAbundanceData", "templateEvent") - - # ! ----- STOP EDITING ----- ! # + abundPlot = { + ## do stuff for this event + sim <- abundancePlot(sim) }, - warning(noEventWarning(sim)) + warning(paste("Undefined event type: '", current(sim)[1, "eventType", with = FALSE], + "' in module '", current(sim)[1, "moduleName", with = FALSE], "'", sep = "")) ) return(invisible(sim)) } -### template initialization -Init <- function(sim) { - # # ! ----- EDIT BELOW ----- ! # - - # ! ----- STOP EDITING ----- ! # - +## event functions +# - keep event functions short and clean, modularize by calling subroutines from section below. + +## Initialisation Event function +abundanceInit <- function(sim) { + ## download data - prepInputs does all the heavy-lifting of dowloading and pre-processing the layer and caches. + ## there seems to be an issue masking this particular raster with `terra` and `GDAL`, so we'll not use them here. + opts <- options("reproducible.useTerra" = FALSE, + "reproducible.useGDAL" = FALSE) + on.exit(options(opts), add = TRUE) + + httr::with_config(config = httr::config(ssl_verifypeer = 0L), { + sppAbundanceRas <- prepInputs(targetFile = "NFI_MODIS250m_2001_kNN_Species_Pice_Gla_v1.tif", + url = P(sim)$sppAbundURL, + # fun = "terra::rast", + # projectTo = sim$studyAreaRas, + # cropTo = sim$studyAreaRas, + # maskTo = sim$studyAreaRas, + rasterToMatch = raster::raster(sim$studyAreaRas), + maskWithRTM = TRUE, + overwrite = TRUE, + cacheRepo = cachePath(sim)) + }) + + options(opts) + + if (is(sppAbundanceRas, "RasterLayer")) { + sppAbundanceRas <- terra::rast(sppAbundanceRas) + } + + names(sppAbundanceRas) <- paste("year", time(sim), sep = "_") + sppAbundanceDT <- as.data.table(as.data.frame(sppAbundanceRas, xy = TRUE, cells = TRUE)) + sppAbundanceDT[, year := as.integer(sub("year_", "", names(sppAbundanceRas)))] + setnames(sppAbundanceDT, "year_1", "sppAbund") + + ## export to sim + sim$sppAbundanceRas <- sppAbundanceRas + sim$sppAbundanceDT <- sppAbundanceDT + return(invisible(sim)) } -### template for save events -Save <- function(sim) { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - sim <- saveFiles(sim) - - # ! ----- STOP EDITING ----- ! # - return(invisible(sim)) -} - -### template for plot events -plotFun <- function(sim) { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - sampleData <- data.frame("TheSample" = sample(1:10, replace = TRUE)) - Plots(sampleData, fn = ggplotFn) # needs ggplot2 - - # ! ----- STOP EDITING ----- ! # - return(invisible(sim)) -} - -### template for your event1 -Event1 <- function(sim) { - # ! ----- EDIT BELOW ----- ! # - # THE NEXT TWO LINES ARE FOR DUMMY UNIT TESTS; CHANGE OR DELETE THEM. - # sim$event1Test1 <- " this is test for event 1. " # for dummy unit test - # sim$event1Test2 <- 999 # for dummy unit test - # ! ----- STOP EDITING ----- ! # +## Plotting event function +abundancePlot <- function(sim) { + ## plot species abundance + Plots(sim$sppAbundanceRas, fn = plotSpatRaster, types = P(sim)$.plots, + usePlot = TRUE, filename = file.path(outputPath(sim), "figures", "speciesAbundance"), + plotTitle = "Species abundance data", xlab = "Longitude", ylab = "Latitude") + return(invisible(sim)) } -### template for your event2 -Event2 <- function(sim) { - # ! ----- EDIT BELOW ----- ! # - # THE NEXT TWO LINES ARE FOR DUMMY UNIT TESTS; CHANGE OR DELETE THEM. - # sim$event2Test1 <- " this is test for event 2. " # for dummy unit test - # sim$event2Test2 <- 777 # for dummy unit test - - # ! ----- STOP EDITING ----- ! # - return(invisible(sim)) -} .inputObjects <- function(sim) { - # Any code written here will be run during the simInit for the purpose of creating - # any objects required by this module and identified in the inputObjects element of defineModule. - # This is useful if there is something required before simulation to produce the module - # object dependencies, including such things as downloading default datasets, e.g., - # downloadData("LCC2005", modulePath(sim)). - # Nothing should be created here that does not create a named object in inputObjects. - # Any other initiation procedures should be put in "init" eventType of the doEvent function. - # Note: the module developer can check if an object is 'suppliedElsewhere' to - # selectively skip unnecessary steps because the user has provided those inputObjects in the - # simInit call, or another module will supply or has supplied it. e.g., - # if (!suppliedElsewhere('defaultColor', sim)) { - # sim$map <- Cache(prepInputs, extractURL('map')) # download, extract, load file from url in sourceURL - # } - #cacheTags <- c(currentModule(sim), "function:.inputObjects") ## uncomment this if Cache is being used dPath <- asPath(getOption("reproducible.destinationPath", dataPath(sim)), 1) message(currentModule(sim), ": using dataPath '", dPath, "'.") - + # ! ----- EDIT BELOW ----- ! # - + + if (!suppliedElsewhere(sim$studyAreaRas)) { + ## code check: did the user supply a study area? + stop("Please supply a 'studyAreaRas' SpatRaster") + } + # ! ----- STOP EDITING ----- ! # return(invisible(sim)) -} - -ggplotFn <- function(data, ...) { - ggplot2::ggplot(data, ggplot2::aes(TheSample)) + - ggplot2::geom_histogram(...) }

@@ -565,16 +525,18 @@

## If exact location is required, functions will be: `sim$.mods$<moduleName>$FunctionName`. defineModule(sim, list( name = "climateData", - description = "", - keywords = "", - authors = structure(list(list(given = c("First", "Middle"), family = "Last", role = c("aut", "cre"), email = "email@example.com", comment = NULL)), class = "person"), + description = paste("Data module to prepare climate data for species distribution modelling.", + "Defaults to using bioclimatic variables from Worldclim."), + keywords = c("minimal SpaDES example", "species distribution model"), + authors = structure(list(list(given = c("Ceres"), family = "Barros", role = c("aut", "cre"), email = "ceres.barros@ubc.ca", comment = NULL)), class = "person"), childModules = character(0), - version = list(climateData = "0.0.0.9000"), + version = list(climateData = "1.0.0"), timeframe = as.POSIXlt(c(NA, NA)), timeunit = "year", citation = list("citation.bib"), - documentation = list("NEWS.md", "README.md", "climateData.Rmd"), - reqdPkgs = list("SpaDES.core (>= 2.1.5.9001)", "ggplot2"), + documentation = list("README.md", "climateData.Rmd"), ## same file + reqdPkgs = list("SpaDES.core (>=2.0.2)", + "ggplot2", "rasterVis", "terra", "data.table"), parameters = bindrows( #defineParameter("paramName", "paramClass", value, min, max, "parameter description"), defineParameter(".plots", "character", "screen", NA, NA, @@ -588,8 +550,7 @@

defineParameter(".saveInterval", "numeric", NA, NA, NA, "This describes the simulation time interval between save events."), defineParameter(".studyAreaName", "character", NA, NA, NA, - "Human-readable name for the study area used - e.g., a hash of the study", - "area obtained using `reproducible::studyAreaName()`"), + "Human-readable name for the study area used. If NA, a hash of studyArea will be used."), ## .seed is optional: `list('init' = 123)` will `set.seed(123)` for the `init` event only. defineParameter(".seed", "list", list(), NA, NA, "Named list of seeds to use for each event (names)."), @@ -598,166 +559,255 @@

), inputObjects = bindrows( #expectsInput("objectName", "objectClass", "input object description", sourceURL, ...), - expectsInput(objectName = NA, objectClass = NA, desc = NA, sourceURL = NA) + expectsInput("baselineClimateURLs", "data.table", + desc = paste("A table with columns 'vars', 'URL', 'targetFile' and 'year', containing", + "variable names, URLs and raster file names of each climate covariate", + "used in the species distribution models. Year is the first year of the", + "simulation (not the reference climate period). Defaults to Worldclim's", + "'bio1', 'bio4', 'bio12' and 'bio15' bioclimatic variables for the 1970-2000", + "climate period, at 2.5 minutes.")), + expectsInput("projClimateURLs", "data.table", + desc = paste("Same as `baselineClimateURLs` but refering to projected climate layers.", + "Variable names in 'vars' need to the same as in `baselineClimateURLs`", + "and P(sim)$projClimateURLs. Years should correspond to simulation years.", + "Defaults to 2081-2100 projections using the CanESM5 climate model and the", + "SSP 585 climate scenario, at 2.5 minutes, obtained from Worldclim.")), + expectsInput("studyAreaRas", objectClass = "SpatRaster", + desc = "A binary raster of the study area") ), outputObjects = bindrows( #createsOutput("objectName", "objectClass", "output object description", ...), - createsOutput(objectName = NA, objectClass = NA, desc = NA) + createsOutput("climateDT", "data.table", + desc = paste("A data.table with as many columns as the climate covariates", + "used in the species distribution model and 'year' column describing", + "the simulation year to which the data corresponds.")), + createsOutput("baselineClimateRas", "SpatRaster", + desc = paste("Baseline climate layers obtained from `baselineClimateURLs`")), + createsOutput("projClimateRas", "SpatRaster", + desc = paste("Baseline climate layers obtained from `projClimateURLs`")) ) )) -doEvent.climateData = function(sim, eventTime, eventType) { +## event types +# - type `init` is required for initialization + +doEvent.climateData = function(sim, eventTime, eventType, debug = FALSE) { switch( eventType, init = { - ### check for more detailed object dependencies: - ### (use `checkObject` or similar) - - # do stuff for this event - sim <- Init(sim) - - # schedule future event(s) - sim <- scheduleEvent(sim, P(sim)$.plotInitialTime, "climateData", "plot") - sim <- scheduleEvent(sim, P(sim)$.saveInitialTime, "climateData", "save") - }, - plot = { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - - plotFun(sim) # example of a plotting function - # schedule future event(s) - - # e.g., - #sim <- scheduleEvent(sim, time(sim) + P(sim)$.plotInterval, "climateData", "plot") - - # ! ----- STOP EDITING ----- ! # - }, - save = { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - - # e.g., call your custom functions/methods here - # you can define your own methods below this `doEvent` function - - # schedule future event(s) - - # e.g., - # sim <- scheduleEvent(sim, time(sim) + P(sim)$.saveInterval, "climateData", "save") - - # ! ----- STOP EDITING ----- ! # - }, - event1 = { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - - # e.g., call your custom functions/methods here - # you can define your own methods below this `doEvent` function - - # schedule future event(s) - - # e.g., - # sim <- scheduleEvent(sim, time(sim) + increment, "climateData", "templateEvent") - - # ! ----- STOP EDITING ----- ! # + ## do stuff for this event + sim <- climateInit(sim) + + ## schedule future event(s) + sim <- scheduleEvent(sim, eventTime = P(sim)$.plotInitialTime, + moduleName = "climateData", eventType = "climPlot", + eventPriority = .normal()) }, - event2 = { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - - # e.g., call your custom functions/methods here - # you can define your own methods below this `doEvent` function - - # schedule future event(s) - - # e.g., - # sim <- scheduleEvent(sim, time(sim) + increment, "climateData", "templateEvent") - - # ! ----- STOP EDITING ----- ! # + climPlot = { + ## do stuff for this event + sim <- climatePlot(sim) }, - warning(noEventWarning(sim)) + warning(paste("Undefined event type: '", current(sim)[1, "eventType", with = FALSE], + "' in module '", current(sim)[1, "moduleName", with = FALSE], "'", sep = "")) ) return(invisible(sim)) } -### template initialization -Init <- function(sim) { - # # ! ----- EDIT BELOW ----- ! # - - # ! ----- STOP EDITING ----- ! # - - return(invisible(sim)) -} -### template for save events -Save <- function(sim) { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - sim <- saveFiles(sim) - - # ! ----- STOP EDITING ----- ! # - return(invisible(sim)) -} - -### template for plot events -plotFun <- function(sim) { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - sampleData <- data.frame("TheSample" = sample(1:10, replace = TRUE)) - Plots(sampleData, fn = ggplotFn) # needs ggplot2 - - # ! ----- STOP EDITING ----- ! # +## event functions +# - keep event functions short and clean, modularize by calling subroutines from section below. + +## Initialisation Event function +climateInit <- function(sim) { + ## GET BASELINE DATA + ## make a vector of archive (zip) file names if the url points to one. + archiveFiles <- sapply(sim$baselineClimateURLs$URL, function(URL) { + if (grepl("\\.zip$", basename(URL))) { + basename(URL) + } else { + NULL + } + }, USE.NAMES = FALSE) + + ## check that baseline climate data only has one year value + if (length(unique(sim$baselineClimateURLs$year)) != 1) { + stop(paste("'baselineClimateURLs' should all have the same 'year' value,", + "corresponding to the first year of the simulation")) + } + ## download data - prepInputs does all the heavy-lifting of dowloading and pre-processing the layer and caches. + baselineClimateRas <- Cache(Map, + f = prepInputs, + url = sim$baselineClimateURLs$URL, + targetFile = sim$baselineClimateURLs$targetFile, + archive = archiveFiles, + MoreArgs = list( + fun = "terra::rast", + overwrite = TRUE, + projectTo = sim$studyAreaRas, + cropTo = sim$studyAreaRas, + maskTo = sim$studyAreaRas, + rasterToMatch = sim$studyAreaRas, + cacheRepo = cachePath(sim)), + cacheRepo = cachePath(sim)) + + names(baselineClimateRas) <- paste0(sim$baselineClimateURLs$vars, "_year", sim$baselineClimateURLs$year) + + ## make a stack + baselineClimateRas <- rast(baselineClimateRas) + + ## make a data.table + baselineClimateData <- as.data.table(as.data.frame(baselineClimateRas, xy = TRUE, cells = TRUE)) + setnames(baselineClimateData, sub("_year.*", "", names(baselineClimateData))) ## don't need year in names here + baselineClimateData[, year := unique(sim$baselineClimateURLs$year)] + + ## GET PROJECTED DATA + ## make a vector of archive (zip) file names if the url points to one. + archiveFiles <- lapply(sim$projClimateURLs$URL, function(URL) { + if (grepl("\\.zip$", basename(URL))) { + basename(URL) + } else { + NULL + } + }) + + ## download data - prepInputs does all the heavy-lifting of dowloading and pre-processing the layer and caches. + ## workaround Mar 30th 2022 cache issue with terra. + projClimateRas <- Cache(Map, + f = prepInputs, + url = sim$projClimateURLs$URL, + targetFile = sim$projClimateURLs$targetFile, + archive = archiveFiles, + MoreArgs = list( + overwrite = TRUE, + fun = "raster::stack", + projectTo = sim$studyAreaRas, + cropTo = sim$studyAreaRas, + maskTo = sim$studyAreaRas, + rasterToMatch = sim$studyAreaRas, + cacheRepo = cachePath(sim)), + cacheRepo = cachePath(sim)) + if (any(sapply(projClimateRas, function(x) is(x, "RasterLayer") | is(x, "RasterStack")))){ + projClimateRas <- lapply(projClimateRas, terra::rast) + } + + ## these rasters are different. The tif file contains all the variables in different layers + ## so, for each variable, we need to keep only the layer of interest + projClimateRas <- mapply(function(stk, var) { + lyr <- which(sub(".*_", "BIO", names(projClimateRas[[1]])) == var) + return(stk[[lyr]]) + }, stk = projClimateRas, var = sim$projClimateURLs$vars) + names(projClimateRas) <- paste0(sim$projClimateURLs$vars, "_year", sim$projClimateURLs$year) + + ## make a stack + projClimateRas <- rast(projClimateRas) + + ## make a data.table + projClimateData <- as.data.table(as.data.frame(projClimateRas, xy = TRUE, cells = TRUE)) + + ## melt so that year is in a column + projClimateDataMolten <- lapply(unique(sim$projClimateURLs$vars), function(var, projClimateData) { + cols <- grep(paste0(var, "_year"), names(projClimateData), value = TRUE) + idCols <- names(projClimateData)[!grepl("_year", names(projClimateData))] + + moltenDT <- melt(projClimateData, id.vars = idCols, measure.vars = cols, + variable.name = "year", value.name = var) + moltenDT[, year := sub(paste0(var, "_year"), "", year)] + moltenDT[, year := as.integer(year)] + return(moltenDT) + }, projClimateData = projClimateData) + + idCols <- c(names(projClimateData)[!grepl("_year", names(projClimateData))], "year") + ## set keys for merge + projClimateDataMolten <- lapply(projClimateDataMolten, function(DT, cols) { + setkeyv(DT, cols = cols) + return(DT) + }, cols = idCols) + + projClimateData <- Reduce(merge, projClimateDataMolten) + + ## bind the two data.tables + if (!identical(sort(names(baselineClimateData)), sort(names(projClimateData)))) { + stop("Variable names in `projClimateURLs` differ from those in `baselineClimateURLs`") + } + + ## check + if (!compareGeom(baselineClimateRas, projClimateRas, res = TRUE, stopOnError = FALSE)) { + stop("`baselineClimateRas` and `projClimateRas` do not have the same raster properties") + } + + ## export to sim + sim$baselineClimateRas <- baselineClimateRas + sim$projClimateRas <- projClimateRas + sim$climateDT <- rbindlist(list(baselineClimateData, projClimateData), use.names = TRUE) + return(invisible(sim)) } -### template for your event1 -Event1 <- function(sim) { - # ! ----- EDIT BELOW ----- ! # - # THE NEXT TWO LINES ARE FOR DUMMY UNIT TESTS; CHANGE OR DELETE THEM. - # sim$event1Test1 <- " this is test for event 1. " # for dummy unit test - # sim$event1Test2 <- 999 # for dummy unit test - - # ! ----- STOP EDITING ----- ! # +## Plotting event function +climatePlot <- function(sim) { + ## plot climate rasters + allRasters <- rast(list(sim$baselineClimateRas, sim$projClimateRas)) + lapply(sim$baselineClimateURLs$vars, function(var, allRasters) { + lrs <- grep(paste0(var, "_"), names(allRasters)) + file_name <- paste0("climateRas_", var) + Plots(allRasters[[lrs]], + fn = plotSpatRasterStk, types = P(sim)$.plots, + usePlot = FALSE, + filename = file.path(outputPath(sim), "figures", file_name), + xlab = "Longitude", ylab = "Latitude") + }, allRasters = allRasters) + return(invisible(sim)) } -### template for your event2 -Event2 <- function(sim) { - # ! ----- EDIT BELOW ----- ! # - # THE NEXT TWO LINES ARE FOR DUMMY UNIT TESTS; CHANGE OR DELETE THEM. - # sim$event2Test1 <- " this is test for event 2. " # for dummy unit test - # sim$event2Test2 <- 777 # for dummy unit test - - # ! ----- STOP EDITING ----- ! # - return(invisible(sim)) -} .inputObjects <- function(sim) { - # Any code written here will be run during the simInit for the purpose of creating - # any objects required by this module and identified in the inputObjects element of defineModule. - # This is useful if there is something required before simulation to produce the module - # object dependencies, including such things as downloading default datasets, e.g., - # downloadData("LCC2005", modulePath(sim)). - # Nothing should be created here that does not create a named object in inputObjects. - # Any other initiation procedures should be put in "init" eventType of the doEvent function. - # Note: the module developer can check if an object is 'suppliedElsewhere' to - # selectively skip unnecessary steps because the user has provided those inputObjects in the - # simInit call, or another module will supply or has supplied it. e.g., - # if (!suppliedElsewhere('defaultColor', sim)) { - # sim$map <- Cache(prepInputs, extractURL('map')) # download, extract, load file from url in sourceURL - # } - #cacheTags <- c(currentModule(sim), "function:.inputObjects") ## uncomment this if Cache is being used dPath <- asPath(getOption("reproducible.destinationPath", dataPath(sim)), 1) message(currentModule(sim), ": using dataPath '", dPath, "'.") - + # ! ----- EDIT BELOW ----- ! # - + + if (!suppliedElsewhere(sim$studyAreaRas)) { + ## code check: did the user supply a study area? + stop("Please supply a 'studyAreaRas' SpatRaster") + } + + if (!is(sim$studyAreaRas, "SpatRaster")) { + sim$studyAreaRas <- rast(sim$studyAreaRas) + } + + if (!suppliedElsewhere(sim$baselineClimateURLs)) { + sim$baselineClimateURLs <- data.table( + vars = c("BIO1", "BIO4", "BIO12", "BIO15"), + URL = c("https://biogeo.ucdavis.edu/data/worldclim/v2.1/base/wc2.1_2.5m_bio.zip", + "https://biogeo.ucdavis.edu/data/worldclim/v2.1/base/wc2.1_2.5m_bio.zip", + "https://biogeo.ucdavis.edu/data/worldclim/v2.1/base/wc2.1_2.5m_bio.zip", + "https://biogeo.ucdavis.edu/data/worldclim/v2.1/base/wc2.1_2.5m_bio.zip"), + targetFile = c("wc2.1_2.5m_bio_1.tif", "wc2.1_2.5m_bio_4.tif", + "wc2.1_2.5m_bio_12.tif", "wc2.1_2.5m_bio_15.tif"), + year = rep(1L, 4) + ) + } + + if (!suppliedElsewhere(sim$projClimateURLs)) { + sim$projClimateURLs <- data.table( + vars = rep(c("BIO1", "BIO4", "BIO12", "BIO15"), times = 4), + URL = rep(c("https://geodata.ucdavis.edu/cmip6/2.5m/CanESM5/ssp585/wc2.1_2.5m_bioc_CanESM5_ssp585_2021-2040.tif", + "https://geodata.ucdavis.edu/cmip6/2.5m/CanESM5/ssp585/wc2.1_2.5m_bioc_CanESM5_ssp585_2041-2060.tif", + "https://geodata.ucdavis.edu/cmip6/2.5m/CanESM5/ssp585/wc2.1_2.5m_bioc_CanESM5_ssp585_2061-2080.tif", + "https://geodata.ucdavis.edu/cmip6/2.5m/CanESM5/ssp585/wc2.1_2.5m_bioc_CanESM5_ssp585_2081-2100.tif"), + each = 4), + targetFile = rep(c("wc2.1_2.5m_bioc_CanESM5_ssp585_2021-2040.tif", + "wc2.1_2.5m_bioc_CanESM5_ssp585_2041-2060.tif", + "wc2.1_2.5m_bioc_CanESM5_ssp585_2061-2080.tif", + "wc2.1_2.5m_bioc_CanESM5_ssp585_2081-2100.tif"), + each = 4), + year = rep(2L:5L, each = 4) + ) + } + # ! ----- STOP EDITING ----- ! # return(invisible(sim)) -} - -ggplotFn <- function(data, ...) { - ggplot2::ggplot(data, ggplot2::aes(TheSample)) + - ggplot2::geom_histogram(...) } @@ -787,29 +837,35 @@

name = "projectSpeciesDist", description = "", keywords = "", - authors = structure(list(list(given = c("First", "Middle"), family = "Last", role = c("aut", "cre"), email = "email@example.com", comment = NULL)), class = "person"), + authors = structure(list(list(given = c("Ceres"), family = "Barros", + role = c("aut", "cre"), email = "ceres.barros@ubc.ca", comment = NULL)), class = "person"), childModules = character(0), - version = list(projectSpeciesDist = "0.0.0.9000"), + version = list(projectSpeciesDist = "1.0.0"), timeframe = as.POSIXlt(c(NA, NA)), timeunit = "year", citation = list("citation.bib"), - documentation = list("NEWS.md", "README.md", "projectSpeciesDist.Rmd"), - reqdPkgs = list("SpaDES.core (>= 2.1.5.9001)", "ggplot2"), + documentation = list("README.md", "projectSpeciesDist.Rmd"), ## same file + reqdPkgs = list("SpaDES.core (>=2.0.2)", + "caret", "data.table", "dismo", + "ggplot2", "rJava", "rasterVis"), parameters = bindrows( #defineParameter("paramName", "paramClass", value, min, max, "parameter description"), + defineParameter("predVars", "character", c("BIO1", "BIO4", "BIO12", "BIO15"), NA, NA, + "Predictors used in statistical model."), + defineParameter("presThresh", "numeric", 10, 0, NA, + paste("Minimum threshold for the species to be considered present, when", + " `sppAbundanceDT` contains non binary species data (e.g. %, proportions,", + "or abundance data). By default 10% cover.")), + defineParameter("statModel", "character", "MaxEnt", NA, NA, + paste("What statitical algorith to use. Currently only 'MaxEnt' and 'GLM' are", + "supported. 'MaxEnt will fit a MaxEnt model using dismo::maxent; 'GLM'", + "will fit a generalised linear model with a logit link using", + "glm(..., family = 'binomial'). In both cases all predictor variables are used,", + "and for GLM only additive effects are considered." )), defineParameter(".plots", "character", "screen", NA, NA, "Used by Plots function, which can be optionally used here"), defineParameter(".plotInitialTime", "numeric", start(sim), NA, NA, "Describes the simulation time at which the first plot event should occur."), - defineParameter(".plotInterval", "numeric", NA, NA, NA, - "Describes the simulation time interval between plot events."), - defineParameter(".saveInitialTime", "numeric", NA, NA, NA, - "Describes the simulation time at which the first save event should occur."), - defineParameter(".saveInterval", "numeric", NA, NA, NA, - "This describes the simulation time interval between save events."), - defineParameter(".studyAreaName", "character", NA, NA, NA, - "Human-readable name for the study area used - e.g., a hash of the study", - "area obtained using `reproducible::studyAreaName()`"), ## .seed is optional: `list('init' = 123)` will `set.seed(123)` for the `init` event only. defineParameter(".seed", "list", list(), NA, NA, "Named list of seeds to use for each event (names)."), @@ -818,166 +874,256 @@

), inputObjects = bindrows( #expectsInput("objectName", "objectClass", "input object description", sourceURL, ...), - expectsInput(objectName = NA, objectClass = NA, desc = NA, sourceURL = NA) + expectsInput("climateDT", "data.table", + desc = paste("A data.table with as many columns as the climate covariates", + "used in the species distribution model and 'year' column describing", + "the simulation year to which the data corresponds.")), + expectsInput("sppAbundanceDT", "data.table", + desc = paste("A species abundance data. Converted to presence/absence data, if not binary.", + "By default a table with % species cover.")), + expectsInput("studyAreaRas", objectClass = "SpatRaster", + desc = "A binary raster of the study area") ), outputObjects = bindrows( #createsOutput("objectName", "objectClass", "output object description", ...), - createsOutput(objectName = NA, objectClass = NA, desc = NA) + createsOutput(objectName = "sppDistProj", objectClass = "SpatRaster", + desc = paste("Species distribution projections - raw predictions.", + "Each layer corresponds to a prediciton year")), + createsOutput(objectName = "evalOut", objectClass = "ModelEvaluation", + desc = paste("`sdmOut` model evaluation statistics. Model evaluated on the 20% of", + "the data. See `?dismo::evaluation`.")), + createsOutput(objectName = "sdmData", objectClass = "data.table", + desc = "Input data used to fit `sdmOut`."), + createsOutput(objectName = "sdmOut", objectClass = c("MaxEnt", "glm"), + desc = paste("Fitted species distribution model. Model fitted on 80%", + "of `sdmData`, with remaining 20% used for evaluation.")), + createsOutput(objectName = "thresh", objectClass = "numeric", + desc = paste("Threshold of presence that maximises the sum of the sensitivity", + "(true positive rate) and specificity (true negative rate).", + "See `dismo::threshold(..., stat = 'spec_sens')`.")) ) )) +## event types +# - type `init` is required for initialization + doEvent.projectSpeciesDist = function(sim, eventTime, eventType) { switch( eventType, init = { ### check for more detailed object dependencies: ### (use `checkObject` or similar) - + # do stuff for this event - sim <- Init(sim) - + sim <- SDMInit(sim) + # schedule future event(s) - sim <- scheduleEvent(sim, P(sim)$.plotInitialTime, "projectSpeciesDist", "plot") - sim <- scheduleEvent(sim, P(sim)$.saveInitialTime, "projectSpeciesDist", "save") + sim <- scheduleEvent(sim, start(sim), "projectSpeciesDist", "fitSDM") + sim <- scheduleEvent(sim, start(sim), "projectSpeciesDist", "evalSDM", + eventPriority = .normal() + 1) + sim <- scheduleEvent(sim, start(sim), "projectSpeciesDist", "projSDM", + eventPriority = .normal() + 2) + sim <- scheduleEvent(sim, P(sim)$.plotInitialTime, "projectSpeciesDist", "plotProjSDM", + eventPriority = .normal() + 3) + }, - plot = { + fitSDM = { # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - - plotFun(sim) # example of a plotting function - # schedule future event(s) - - # e.g., - #sim <- scheduleEvent(sim, time(sim) + P(sim)$.plotInterval, "projectSpeciesDist", "plot") - + sim <- fitSDMEvent(sim) # ! ----- STOP EDITING ----- ! # }, - save = { + evalSDM = { # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - - # e.g., call your custom functions/methods here - # you can define your own methods below this `doEvent` function - - # schedule future event(s) - - # e.g., - # sim <- scheduleEvent(sim, time(sim) + P(sim)$.saveInterval, "projectSpeciesDist", "save") - + sim <- evalSDMEvent(sim) # ! ----- STOP EDITING ----- ! # }, - event1 = { + projSDM = { # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - - # e.g., call your custom functions/methods here - # you can define your own methods below this `doEvent` function - - # schedule future event(s) - - # e.g., - # sim <- scheduleEvent(sim, time(sim) + increment, "projectSpeciesDist", "templateEvent") - + sim <- projSDMEvent(sim) + + sim <- scheduleEvent(sim, time(sim) + 1L, "projectSpeciesDist", "projSDM") # ! ----- STOP EDITING ----- ! # }, - event2 = { + plotProjSDM = { # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - - # e.g., call your custom functions/methods here - # you can define your own methods below this `doEvent` function - - # schedule future event(s) - - # e.g., - # sim <- scheduleEvent(sim, time(sim) + increment, "projectSpeciesDist", "templateEvent") - + plotProjEvent(sim) + + sim <- scheduleEvent(sim, time(sim) + 1L, "projectSpeciesDist", "plotProjSDM", + eventPriority = .normal() + 1) + # ! ----- STOP EDITING ----- ! # }, - warning(noEventWarning(sim)) + warning(paste("Undefined event type: \'", current(sim)[1, "eventType", with = FALSE], + "\' in module \'", current(sim)[1, "moduleName", with = FALSE], "\'", sep = "")) ) return(invisible(sim)) } +## event functions +# - keep event functions short and clean, modularize by calling subroutines from section below. + ### template initialization -Init <- function(sim) { +SDMInit <- function(sim) { # # ! ----- EDIT BELOW ----- ! # - + ## at this point we can only have the following columns + if (!identical(sort(names(sim$sppAbundanceDT)), sort(c("cell", "x", "y", "sppAbund", "year")))) { + stop(paste("sim$sppAbundanceDT can only have the following columns at the start of year 1:\n", + paste(c("cell", "x", "y", "sppAbund", "year"), collapse = ", "))) + } + + if (length(setdiff(sim$climateDT$cell, sim$sppAbundanceDT$cell)) > 0 || + length(setdiff(sim$sppAbundanceDT$cell, sim$climateDT$cell)) > 0) { + stop("'cell' columns in `climateDT` and `sppAbundanceDT` have different values") + } + + if (!P(sim)$statModel %in% c("MaxEnt", "GLM")) { + stop("'statModel' parameter must be 'MaxEnt' or 'GLM'") + } + + ## a few data cleaning steps to make sure we have presences and absences: + sppAbundanceDT <- copy(sim$sppAbundanceDT) + if (min(range(sppAbundanceDT$sppAbund)) < 0) { + sppAbundanceDT[sppAbund < 0, sppAbund := 0] + } + + if (!all(unique(sppAbundanceDT$sppAbund) %in% c(0, 1))) { + message("Species data is not binary.") + message("Converting values >= P(sim)$presThresh to presences, and < P(sim)$presThresh to absences") + sppAbundanceDT[sppAbund >= P(sim)$presThresh, presAbs := 1] + sppAbundanceDT[sppAbund < P(sim)$presThresh, presAbs := 0] + } + + ## join the two datasets - note that there are no input species abundances beyond year 1 + sim$sdmData <- merge(sim$climateDT, sppAbundanceDT[, .(cell, sppAbund, presAbs, year)], + by = c("cell", "year"), all = TRUE) + # ! ----- STOP EDITING ----- ! # - return(invisible(sim)) } -### template for save events -Save <- function(sim) { - # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - sim <- saveFiles(sim) +fitSDMEvent <- function(sim) { + # ! ----- EDIT BELOW ----- ! # + ## break data into training and testing subsets + dataForFitting <- sim$sdmData[year == time(sim)] + + if (nrow(dataForFitting) == 0) { + stop(paste("No data for year", time(sim), "provided to fit the model")) + } + + group <- createDataPartition(dataForFitting$presAbs, p = 0.8, list = FALSE) + ## save the the split datasets as internal objects to this module + mod$trainData <- dataForFitting[group] + mod$testData <- dataForFitting[-group] + + if (!any(mod$trainData$presAbs == 0)) { + stop("Training dataset contains no absences.") + } + + predVars <- P(sim)$predVars + if (P(sim)$statModel == "MaxEnt") { + sim$sdmOut <- maxent(x = as.data.frame(mod$trainData[, ..predVars]), + p = mod$trainData$presAbs) + } else { + ## make an additive model with all predictors - avoid using as.formula, which drags the whole environment + form <- enquote(paste("presAbs ~", paste(predVars, collapse = "+"))) + sim$sdmOut <- glm(formula = eval(expr = parse(text = form)), + family = "binomial", data = mod$trainData) + } # ! ----- STOP EDITING ----- ! # return(invisible(sim)) } -### template for plot events -plotFun <- function(sim) { +evalSDMEvent <- function(sim) { # ! ----- EDIT BELOW ----- ! # - # do stuff for this event - sampleData <- data.frame("TheSample" = sample(1:10, replace = TRUE)) - Plots(sampleData, fn = ggplotFn) # needs ggplot2 - + ## validate model + predVars <- P(sim)$predVars + sim$evalOut <- evaluate(p = mod$testData[presAbs == 1, ..predVars], + a = mod$testData[presAbs == 0, ..predVars], + model = sim$sdmOut) + ## save the threshold of presence/absence in an internal object to this module + sim$thresh <- threshold(sim$evalOut, 'spec_sens') + # ! ----- STOP EDITING ----- ! # return(invisible(sim)) } -### template for your event1 -Event1 <- function(sim) { +projSDMEvent <- function(sim) { # ! ----- EDIT BELOW ----- ! # - # THE NEXT TWO LINES ARE FOR DUMMY UNIT TESTS; CHANGE OR DELETE THEM. - # sim$event1Test1 <- " this is test for event 1. " # for dummy unit test - # sim$event1Test2 <- 999 # for dummy unit test - + ## predict across the full data and make a map + dataForPredicting <- sim$sdmData[year == time(sim)] + + if (nrow(dataForPredicting) == 0) { + stop(paste("No data for year", time(sim), "provided to calculate predictions")) + } + + predVars <- P(sim)$predVars + preds <- predict(sim$sdmOut, as.data.frame(dataForPredicting[, ..predVars]), + progress = '') + sppDistProj <- replace(sim$studyAreaRas, which(!is.na(sim$studyAreaRas[])), preds) + names(sppDistProj) <- paste0("year", time(sim)) + + if (is.null(sim$sppDistProj)) { + sim$sppDistProj <- sppDistProj + } else { + sim$sppDistProj <- rast(list(sim$sppDistProj, sppDistProj)) + } + # ! ----- STOP EDITING ----- ! # return(invisible(sim)) } -### template for your event2 -Event2 <- function(sim) { +plotProjEvent <- function(sim) { # ! ----- EDIT BELOW ----- ! # - # THE NEXT TWO LINES ARE FOR DUMMY UNIT TESTS; CHANGE OR DELETE THEM. - # sim$event2Test1 <- " this is test for event 2. " # for dummy unit test - # sim$event2Test2 <- 777 # for dummy unit test - + checkPath(file.path(outputPath(sim), "figures"), create = TRUE) + + if (any(!is.na(P(sim)$.plots))) { + + ## response plot + ## we can't use Plots to plot and save SDM predictions with dismo. + ## these are only saved to disk + fileSuffix <- paste0(P(sim)$statModel, ".png") + + notScreen <- setdiff(P(sim)$.plots, "screen") + if (any(notScreen != "png")) { + warning(paste(currentModule(sim), "only saves to PNG at the moment.")) + } + png(file.path(outputPath(sim), "figures", paste0("SDMresponsePlot_", fileSuffix))) + response(sim$sdmOut) + dev.off() + + ## species projections + fileSuffix <- paste0(P(sim)$statModel, "_Year", time(sim)) + clearPlot() + rawValsPlot <- sim$sppDistProj[[paste0("year", time(sim))]] + Plots(rawValsPlot, fn = plotSpatRaster, types = P(sim)$.plots, + usePlot = TRUE, filename = file.path(outputPath(sim), "figures", paste0("projRawVals_", fileSuffix)), + plotTitle = paste("Projected raw values -", "year", time(sim)), + xlab = "Longitude", ylab = "Latitude") + + PAsPlot <- terra::as.int(sim$sppDistProj[[paste0("year", time(sim))]] > sim$thresh) + Plots(PAsPlot, fn = plotSpatRaster, types = P(sim)$.plots, + usePlot = TRUE, filename = file.path(outputPath(sim), "figures", paste0("projPA_", fileSuffix)), + plotTitle = paste("Projected presence/absence -", "year", time(sim)), + xlab = "Longitude", ylab = "Latitude") + } + # ! ----- STOP EDITING ----- ! # return(invisible(sim)) } .inputObjects <- function(sim) { - # Any code written here will be run during the simInit for the purpose of creating - # any objects required by this module and identified in the inputObjects element of defineModule. - # This is useful if there is something required before simulation to produce the module - # object dependencies, including such things as downloading default datasets, e.g., - # downloadData("LCC2005", modulePath(sim)). - # Nothing should be created here that does not create a named object in inputObjects. - # Any other initiation procedures should be put in "init" eventType of the doEvent function. - # Note: the module developer can check if an object is 'suppliedElsewhere' to - # selectively skip unnecessary steps because the user has provided those inputObjects in the - # simInit call, or another module will supply or has supplied it. e.g., - # if (!suppliedElsewhere('defaultColor', sim)) { - # sim$map <- Cache(prepInputs, extractURL('map')) # download, extract, load file from url in sourceURL - # } - #cacheTags <- c(currentModule(sim), "function:.inputObjects") ## uncomment this if Cache is being used dPath <- asPath(getOption("reproducible.destinationPath", dataPath(sim)), 1) message(currentModule(sim), ": using dataPath '", dPath, "'.") - + # ! ----- EDIT BELOW ----- ! # - + ## check that necessary objects are in the simList or WILL BE supplied by another module + if (!suppliedElsewhere("climateDT") | !suppliedElsewhere("sppAbundanceDT") ) { + stop("Please provide `climateDT` and `sppAbundanceDT`") + } + # ! ----- STOP EDITING ----- ! # return(invisible(sim)) -} - -ggplotFn <- function(data, ...) { - ggplot2::ggplot(data, ggplot2::aes(TheSample)) + - ggplot2::geom_histogram(...) } @@ -1138,9 +1284,9 @@

| | | 0% | - |= | 2% + |=== | 6% | - |== | 5% + |=== | 7% | |==== | 7% | @@ -1170,51 +1316,37 @@

| |=================== | 39% | - |===================== | 41% + |====================== | 44% | - |====================== | 44% + |======================= | 46% | - |======================= | 46% + |======================== | 48% | - |=========================== | 53% + |============================ | 56% | - |============================ | 56% + |================================ | 63% | - |============================= | 58% + |================================== | 68% | - |================================== | 68% + |==================================== | 73% | - |=================================== | 70% + |============================================ | 87% | - |==================================== | 73% + |============================================== | 92% | - |====================================== | 75% + |=============================================== | 95% | - |======================================= | 78% + |================================================ | 97% | - |======================================== | 80% + |==================================================| 99% | - |========================================= | 82% - | - |========================================== | 85% - | - |============================================ | 87% - | - |============================================= | 90% - | - |============================================== | 92% - | - |=============================================== | 95% - | - |================================================ | 97% - | - |==================================================| 100% - -## check that rJava can be loaded. -if (!require(rJava, quietly = TRUE)) { - stop(paste("Your Java installation may have problems, please check.\n", - "See https://www.java.com/en/download/manual.jsp for Java installation")) -} + |==================================================| 100% + +## check that rJava can be loaded. +if (!require(rJava, quietly = TRUE)) { + stop(paste("Your Java installation may have problems, please check.\n", + "See https://www.java.com/en/download/manual.jsp for Java installation")) +}

A few things to note about the code above:

@@ -1354,7 +1486,7 @@

-
+
@@ -1364,7 +1496,7 @@

-
+
@@ -1376,7 +1508,7 @@

-
+
@@ -1386,7 +1518,7 @@

-
+
@@ -1406,7 +1538,7 @@

-
+
@@ -1416,7 +1548,7 @@

-
+
@@ -1428,7 +1560,7 @@

-
+
@@ -1438,7 +1570,7 @@

-
+
@@ -1450,7 +1582,7 @@

-
+
@@ -1469,7 +1601,7 @@

-
+
@@ -1479,7 +1611,7 @@

-
+
@@ -1491,7 +1623,7 @@

-
+
@@ -1501,7 +1633,7 @@

-
+
@@ -1513,7 +1645,7 @@

-
+
@@ -1631,7 +1763,7 @@

## organise the plots with mildest scenario first ## It is clear that MaxEnt and GLM do not agree in their prediction -plotAll <- ggpubr::ggarrange( +plotAll <- ggpubr::ggarrange( plotMaxEnt2 + labs(title = expression(bold("Scenario - SSP 126")), y = expression(atop(bold("Raw predictions"), "Latitude"))) + diff --git a/docs/SpaDES-For-Dummies.pdf b/docs/SpaDES-For-Dummies.pdf index 9be93cb..645e139 100644 Binary files a/docs/SpaDES-For-Dummies.pdf and b/docs/SpaDES-For-Dummies.pdf differ diff --git a/docs/appendices/Part1_Rscript.html b/docs/appendices/Part1_Rscript.html index 1eeb52e..4a0a589 100644 --- a/docs/appendices/Part1_Rscript.html +++ b/docs/appendices/Part1_Rscript.html @@ -227,7 +227,8 @@

A
Code
## start from a clean R session
 options(repos = c("https://predictiveecology.r-universe.dev/",
-                  CRAN = "https://cloud.r-project.org"))
+                  "https://cloud.r-project.org", 
+                  getOption("repos")))
 install.packages(c("SpaDES.project", "SpaDES.core"))
 
 library(SpaDES.project)
diff --git a/docs/appendices/Part2_Rscript.html b/docs/appendices/Part2_Rscript.html
index fe760f9..af0e05b 100644
--- a/docs/appendices/Part2_Rscript.html
+++ b/docs/appendices/Part2_Rscript.html
@@ -231,8 +231,9 @@ 

A "See https://github.com/rspatial/dismo/issues/13")) } -repos <- c("https://predictiveecology.r-universe.dev/", - CRAN = "https://cloud.r-project.org") +repos <- c("https://predictiveecology.r-universe.dev/", + "https://cloud.r-project.org", + getOption("repos")) install.packages(c("SpaDES.project", "SpaDES.core"), repos = repos) ## decide where you're working @@ -424,7 +425,7 @@

A ## organise the plots with mildest scenario first ## It is clear that MaxEnt and GLM do not agree in their prediction -plotAll <- ggpubr::ggarrange(plotMaxEnt2 + labs(title = expression(bold("Scenario - SSP 126")), +plotAll <- ggpubr::ggarrange(plotMaxEnt2 + labs(title = expression(bold("Scenario - SSP 126")), y = expression(atop(bold("Raw predictions"), "Latitude"))) + theme(legend.title = element_blank(), legend.key.height = unit(3, "lines"), plot.title = element_text(hjust = 0.5), plot.margin = margin(0,0,0,0)), diff --git a/docs/index.html b/docs/index.html index 8ba3145..9b701e0 100644 --- a/docs/index.html +++ b/docs/index.html @@ -8,7 +8,7 @@ - + SpaDES For Dummies