#' Generate a new land use change scenario in SWAT+
#'
#' @param scen.name Object type 'character' with the name of the new scenario to be created.
#' @param textInOut.path Path to the default 'TxtInOut' folder. The new scenario will be created from this file.
#' @param hru.shape Spatial object of type 'SpatVector' that contains the HRUs that were created in the `QSWAT+` project.
#' @param lc Raster object of type 'SpatRaster' that contains the land cover for the years to be analysed.
#' @param lookup.table A three column 'data.frame' object. The first column stores the land use values from the 'lc'
#' object. The second column stores the SWAT_CODE as in the lookup table that is given to `SWAT+`. Finally, the
#' third column represents the land use (lum) identifiers from `SWAT+` as stored in the 'landuse.lum' file.
#' @param skip.copy Logical object. Set tu true only if the files from the default 'TxtInOut' folder have been
#' already copied to the directory of the new scenario.
#' @param change.yr Object type 'character' containing the year(s) when the land use changes take place (in chronological order).
#' @param jday Numerical value that indicates the julian day that the land use change(s) will take place (default jday = 1).
#' @param fact.diss Factor of disaggregation of the land cover maps. Sometimes, when the HRUs are very small,
#' this function can return NAs. Therefore, the LU maps are disaggregated using the nearest neighbour. A value of 1 means
#' no disaggregation. See terra::disaggregate for more details.
#' @param scen.path Path to the directory where the new scenatio will be created. If not given, the new scenario will be
#' created in the same folder of the default scenario.
#' @param verbose Object type 'logical'. Should the progress messages be printed?
#' @param exclude.uses Exclude land use classes from the scenario. Currently, the change of urban and water classes cause errors in `SWAT+`; and therefore, should be avoided.
#'
#' @return
#' @export
#'
#' @examples
LUCscen <- function(scen.name,
textInOut.path,
hru.shape,
lc,
lookup.table,
change.yr,
skip.copy = FALSE,
jday = 1,
fact.diss = NULL,
scen.path = NULL,
verbose = TRUE,
exclude.uses = NULL){
#-------------Checks--------------------------#
if(!is.numeric(change.yr))
stop("The object 'change.yr' is not numeric!")
n <- terra::nlyr(lc)
if(length(change.yr) != n - 1)
stop("The object 'change.yr' has different dimensions than the expected changes!")
# Checking where to save new scenario
if(!is.null(scen.path)){
scenarios <- scen.path
} else {
scenarios <- dirname(dirname(textInOut.path))
}
# Creating new folder to store the new
new.folder <- file.path(scenarios, scen.name)
if(!file.exists(new.folder))
dir.create(new.folder)
# Copying the defauld scenario inside new scenario
if(!skip.copy){
if(verbose){
message("#------ Creating new scenario files in: ------------# \n")
message(new.folder, "\n")
file.copy(textInOut.path, new.folder, recursive = TRUE)
}
} else {
if(verbose)
message("#------ The file was not copied! ------------# \n")
}
if(verbose)
message("#------ Starting the analysis of LU changes --------# \n")
# Setting the path to the new path
textInOut.new <- file.path(new.folder, basename(textInOut.path))
# get decision table file
scen.lu.file <- file.path(textInOut.new, "scen_lu.dtl")
#---------------- Setting the conditions ------------------------#
var <- c(rep("year_cal", n - 1), "jday")
obj <- rep("null", n)
obj_num <- rep(0, n)
lim_var <- obj
lim_op <- rep ("-", n)
lim_const <- c(sprintf('%.5f', change.yr), sprintf('%.5f', jday))
# Setting number of conditions
n.cond <- terra::nlyr(lc)
# Creating the conditions section
conditions <- data.frame(
var = var,
obj = obj,
obj_num = obj_num,
lim_var = lim_var,
lim_op = lim_op,
lim_const = lim_const
)
#---------------- Setting alternatives ---------------------------#
n.alts <- n - 1
name.alts <- paste0("alt", 1:n.alts)
# Creating an identity matrix to set the alternatives of LU change
alts <- diag(n.alts)
alts[alts == 1] <- "="
alts[alts == "0"] <- "-"
# Transform the matrix to data frame and add the jday row
alts <- rbind(alts, rep("=", ncol(alts)))
alts <- data.frame(alts)
names(alts) <- name.alts
# Creating the alternatives section
alternatives <- alts
#---------------- Setting activities -----------------------------#
# Apply function to analyse the changes according to HRU and land covers
changes.df <- extract_changes(hru.shape, lc, fun = terra::modal, fact.diss = fact.diss, lookup.table = lookup.table)
# Generating list to store the analysis per change
activities.list <- list()
# Setting vectod for outcomes
outc.general <- rep("n", n.alts)
# Creating new folder to store the shapefiles
shape.folder <- file.path(new.folder, "Shapefiles_HRU_Changes")
if(!file.exists(shape.folder))
dir.create(shape.folder)
# Read hru-data.hru
hrus.file <- file.path(textInOut.new, "hru-data.hru")
hrus <- read.table(hrus.file, skip = 1, header = TRUE, sep = "")
# keeping only character values of the changes
changes.lum <- changes.df$LUM_ids[,2:ncol(changes.df$LUM_ids)]
# Get changes
for(i in 1:n.alts){
# Storing id's of the HRUs
ids <- changes.df$LU_values[,1]
# Substracting the columns of the change 'i'
subset.df <- data.frame(changes.lum[,i], changes.lum[,i+1])
names(subset.df) <- c("Before", "After")
before <- subset.df[,1]
after <- subset.df[,2]
# Subsetting HRUs according to NAs
exclude <- which(is.na(before))
exclude <- c(exclude, which(is.na(after)))
exclude <- unique(exclude)
if(length(exclude) > 0){
before <- before[-exclude]
after <- after[-exclude]
ids <- ids[-exclude]
if(verbose)
Warning("There are NA values in the analysis, try increasing the 'fact.diss' parameter! \n")
}
# Subsetting HRUs according to exclude cases
exclude <- which(before %in% exclude.uses)
exclude <- c(exclude, which(after %in% exclude.uses))
exclude <- unique(exclude)
if(length(exclude) > 0){
before <- before[-exclude]
after <- after[-exclude]
ids <- ids[-exclude]
}
# Cross-checking the land uses with the HRUs file
lum.hrus <- hrus$lu_mgt[which(hrus$id %in% ids)]
exclude <- which(lum.hrus %in% exclude.uses)
if(length(exclude) > 0){
before <- before[-exclude]
after <- after[-exclude]
ids <- ids[-exclude]
}
# Warning of new clases!!
new.elements <- after[which(!(after %in% before))]
new.elements <- unique(new.elements)
if(length(new.elements) > 0)
warning("Warning! The land cover(s): '", new.elements, "' is(are) not contained in the initial conditions! \n",
"Please make sure that it(they) is(are) defined in the 'landuse.lum', 'cntable.lum', 'plant.ini', and 'ovn_table.lum' files! \n",
"In the case that the new land cover(s) is(are) included in 'landuse.lum' please omit this warning :)")
# Subsetting HRUs that changed
pos <- which(before != after)
act_type <- rep("lu_change", length(pos))
obj <- rep("hru", length(pos))
obj_num <- ids[pos]
name <- after[pos]
option <- rep("null", length(pos))
const <- rep(sprintf('%.5f', 0), length(pos))
const2 <- const
fp <- name
outcome <- outc.general
outcome[i] <- "y"
outcome <- noquote(paste(outcome, collapse = " "))
outcome <- rep(outcome, length(pos))
# Creating the activities section
activities.list[[i]] <- data.frame(
act_type = act_type,
obj = obj,
obj_num = obj_num,
name = name,
option = option,
const = const,
const2 = const2,
fp = fp,
outcome = outcome
)
# Create shapefile of the changes
# Getting numerical values of the HRUs of the shapefile
hrus.nbr <- as.numeric(hru.shape$HRUS)
# Matching them to the HRUs that changed
subset <- which(hrus.nbr %in% obj_num)
# Subsetting shapefile
hru.change <- hru.shape[subset,]
# Getting attribute table of the HRUs shapefile and the specific HRUs
values.hru <- terra::values(hru.change)
hrus.shape <- as.numeric(values.hru$HRUS)
# Creating and ordering the lum values for before and after
before.hru <- data.frame(ids, before)
after.hru <- data.frame(ids, after)
before.hru <- before.hru[match(hrus.shape, before.hru$ids),]
after.hru <- after.hru[match(hrus.shape, after.hru$ids),]
# Assigning values to the shapefile
values.hru$Before <- before.hru$before
values.hru$After <- after.hru$after
terra::values(hru.change) <- values.hru
name.shape <- paste0(shape.folder, "/HRU_change_", change.yr[i])
if(!file.exists(name.shape))
terra::writeVector(hru.change, name.shape, overwrite = TRUE)
}
activities <- activities.list
names(activities) <- paste0("LUchange_", change.yr)
activities <- do.call("rbind", activities)
n.acts <- nrow(activities)
#---------------- Write Desision Table ---------------------------#
# Read the decision tables file
scen.lu <- readLines(scen.lu.file)
scen.lu[2] <- as.numeric(scen.lu[2]) + 1
# Get the maximum numbers of lines
max.lines <- length(scen.lu)
# If the last line of the file is not blank then create it
if(!scen.lu[max.lines] == ""){
max.lines <-max.lines + 1
scen.lu[max.lines] <- ""
}
# Write the firs line of the table
scen.lu[max.lines + 1] <- "name conds alts acts "
# Creating the second line of the file
name <- stringi::stri_pad(scen.name, width = 29, side = "right")
conds <- stringi::stri_pad(n.cond, width = 10, side = "right")
alts <- stringi::stri_pad(n.alts, width = 8, side = "right")
acts <- stringi::stri_pad(n.acts, width = 4, side = "right")
scen.lu[max.lines + 2] <- paste0(name, conds, alts, acts)
# resseting the max.lines
nex.line <- length(scen.lu) + 1
# Add title conditions and alternatives
cond.names <- "var obj obj_num lim_var lim_op lim_const"
alt.names <- unlist(lapply(names(alternatives), stringi::stri_pad, width = 9, side = "left"))
alt.names <- paste(paste0(alt.names), collapse = "")
condalt.names <- paste(cond.names, alt.names)
scen.lu[nex.line] <- condalt.names
nex.line <- nex.line + 1
# Adding conditions and alternatives
cond <- conditions
var <- stringi::stri_pad(cond$var, width = 26, side = "right")
obj <- stringi::stri_pad(cond$obj, width = 13, side = "right")
obj_num <- stringi::stri_pad(cond$obj_num, width = 15, side = "right")
lim_var <- stringi::stri_pad(cond$lim_var, width = 21, side = "right")
lim_op <- stringi::stri_pad(cond$lim_op, width = 5, side = "right")
lim_const <- stringi::stri_pad(cond$lim_const, width = 19, side = "right")
alt <- data.frame(apply(alternatives, 2, stringi::stri_pad,
width = 11, side = "right"))
names(alt) <- names(alternatives)
condalt <- cbind(data.frame(
var = var,
obj = obj,
obj_num = obj_num,
lim_var = lim_var,
lim_op = lim_op,
lim_const = lim_const),
alt
)
for(i in 1:nrow(condalt)){
new.line <- length(scen.lu) + 1
scen.lu[new.line] <- paste(condalt[i,], collapse = "")
}
# Creating header for activities
new.line <- new.line + 1
scen.lu[new.line] <- "act_typ obj obj_num name option const const2 fp outcome "
# Adding conditions and activities
act <- activities
act_typ <- stringi::stri_pad(act$act_type, width = 27, side = "right")
obj <- stringi::stri_pad(act$obj, width = 9, side = "right")
obj_num <- stringi::stri_pad(act$obj_num, width = 14, side = "right")
name <- stringi::stri_pad(act$name, width = 22, side = "right")
option <- stringi::stri_pad(act$option, width = 11, side = "right")
const <- stringi::stri_pad(act$const, width = 14, side = "right")
const2 <- stringi::stri_pad(act$const2, width = 17, side = "right")
fp <- stringi::stri_pad(act$fp, width = 10, side = "right")
out <- act[,9:ncol(act)]
activ <- data.frame(
act_typ = act_typ,
obj = obj,
obj_num = obj_num,
name = name,
option = option,
const = const,
const2 = const2,
fp = fp,
out
)
for(i in 1:nrow(activ)){
new.line <- length(scen.lu) + 1
scen.lu[new.line] <- paste(activ[i,], collapse = "")
}
# Adding last line and saving the table
scen.lu[new.line + 1] <- ""
writeLines(scen.lu, scen.lu.file)
#---------------- Write conditional.upd ---------------------------#
cond.upd <- paste("Conditional.upd: written by swatLUC:", Sys.time(), collapse = "")
cond.upd[2] <- paste("1 !Ccond must match a decision table in scen_lu.dtl", collapse = "")
cond.upd[3] <- paste("type name cond", collapse = "")
type <- stringi::stri_pad("lu_change", width = 11, side = "right")
name <- stringi::stri_pad(scen.name, width = 15, side = "right")
cond <- scen.name
cond.upd[4] <- paste(type, name, cond, collapse = "")
cond.upd[5] <- ""
cond.upd.file <- file.path(dirname(scen.lu.file), "conditional.upd")
writeLines(cond.upd, cond.upd.file)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.