R/input_file_utils.R

Defines functions resample_input DEV_input PFA_debris_flow_input bldgrds_nochannels_input distanceToRoad_input quantiles_input samplePointInput LShunterInput huntLSinput align_input accum_input makegrids_input convert_to_flt get_dem parse_arg get_args get_keyword get_input_file

Documented in accum_input align_input bldgrds_nochannels_input DEV_input distanceToRoad_input get_args get_dem get_input_file get_keyword huntLSinput LShunterInput makegrids_input parse_arg PFA_debris_flow_input quantiles_input resample_input samplePointInput

#' Load an ASCII input file
#'
#' TerrainWorks scripts read instructions and parameter values
#' from an ASCII file. These files are formatted using a "keyword: argument"
#' format. The keywords and associated arguments can come in any order
#' and each keyword may have any number of arguments, including no argument.
#' Arguments use a "parameter = value" format, with each argument separated
#' by a comma. For example, here is the keyword: argument input line for
#' specifying reach length in a synthetic channel network:
#' REACH LENGTH: FIXED=100., BREAK AT JUNCTIONS.
#' Here, "REACH LENGTH" is the keyword, there are two arguments, one of which
#' requires a value.
#'
#' @param infile File name with full path.
#'   If no file is specified, a window opens to choose a file interactively.
#'
#' @return A tibble with each row corresponding to one line in the input file.
#'
#' @examples get_input_file("c:\\data\\Umpqua\\input_bldgrds.txt")
#'   opens the specified file.
#'
#' @examples get_input_file() opens a Windows Explorer window for interactive
#'   file selection.
#'
#' @seealso
#' * [get_keyword()] extracts a keyword from an input file line.
#' * [get_args()] extracts the arguments from an input line.
#' * [parse_arg()] extracts the parameter and value from a single argument.
#'
#' @import tibble
#' @importFrom stringr str_detect

#' @export
#'
get_input_file <- function(infile = "nofile") {
  if (str_detect(infile, "nofile")) {
    input_file <- tibble(readLines(file.choose()))
  } else {
    if (!file.exists(infile)) {
      stop("Input file not found")
    } else {
      input_file <- tibble(readLines(infile))
    }
  }
  return(input_file)
}
#------------------------------------------------------
#' Get the keyword from an input-file line.
#'
#' @param infile, a tibble created by get_input_file.
#' @param line_num, line number in the input file.
#'
#' @return A character vector containing the keyword;
#'   NA if no keyword is present.
#'
#' @examples
#' * infile <- get_input_file() select input file interactively.
#' * keyword <- get_keyword(infile,3) gets the keyword from
#'   line 3 of the selected file.
#'
#' @export
#'
get_keyword <- function(infile, line_num) {
  loc_comment <- str_locate(infile[line_num,], "#")
  loc <- str_locate(infile[line_num,], ":")
  if (is.na(loc_comment[1,1]) == TRUE) {
    keyword <- str_sub(infile[line_num,], start = 1, end = loc[1,1] - 1)
  } else {
    keyword <- NA
  }

  return(keyword)
}
#-----------------------------------------------------
#' Get a character vector of arguments from an input-file line.
#'
#' @param infile; a tibble created by get_input_file.
#' @param line_num; the line of the input file.
#'
#' @return arguments; a character vector of arguments
#'
#' @export
#'
get_args <- function(infile, line_num) {
  loc <- str_locate(infile[line_num,], ":")
  arguments <- str_sub(infile[line_num,] ,start = loc[1,1] + 1, end = str_length(infile[line_num,]))
  return(arguments)
}
#-------------------------------------------------
#' Parse "parameter = value" pairs from a specified element
#'   in a list of arguments.
#'
#' @param arguments; a character vector of arguments output by get_args.
#' @param i; the element in the argument vector to parse.
#'
#' @return A 2-element character vector. The first element gives the
#'   parameter name; the second element gives the parameter value.
#'   If no equal sign is present in the argument, the parameter element
#'   is empty and the argument value is contained in the value element.
#'
#' @export
#'
parse_arg <- function(arguments, i = 1) {
  arg_list <- str_split(arguments, ",")
  this_arg <- arg_list[[1]][[i]]
  out_arg <- vector("list", 2)
  names(out_arg) <- c("Parameter", "Value")
  if (str_detect(this_arg, "=")) {
    param_value <- str_split(this_arg, "=")
    out_arg[["Parameter"]] <- param_value[[1]][[1]]
    out_arg[["Value"]] <- param_value[[1]][[2]]
  } else {
    out_arg[["Parameter"]] <- ""
    out_arg[["Value"]] <- this_arg
  }
  return(out_arg)
}
#-----------------------------------------------
#' Get a list of DEM raster files listed in an ASCII input file.
#'
#' @param infile, a tibble created by get_input_file.
#'
#' @return A list of character strings, each string gives a DEM file name
#'
#' @export
#'
get_dem <- function(infile) {
  # count instances of DEM keyword in input
  if (nrow(infile) == 0) stop("Input file is empty")

  n <- 0
  for (i in 1:nrow(infile)) {
    keyword <- get_keyword(infile, i)
    if (is.na(keyword)) {
      next
    }
    if (str_detect(keyword, "DEM") == TRUE) {
      n <- n + 1
    }
  }

  dem <- vector("list", n)
  n <- 0
  for (i in 1:nrow(infile)) {
    keyword <- get_keyword(infile, i)
    if (is.na(keyword)) {
      next
    }
    if (str_detect(keyword, "DEM") == TRUE) {
      argument <- get_args(infile, i)
      param_value <- parse_arg(argument)
      n <- n + 1
      dem[[n]] <- param_value[[2]]
    }
  }
  return(dem)
}
#--------------------------------------------------
convert_to_flt <- function(in_raster) {
  if (!grepl("\\.flt$", in_raster)) {
    # Strip file extension, if it has one
    in_raster_base <- gsub("\\.\\w+$", "", in_raster)
  }

  if (!file.exists(paste0(in_raster, ".flt"))) {
    # Create raster object from in_raster
    out_raster <- terra::rast(in_raster)

    # Create .flt filename
    out_name <- paste0(in_raster_base, ".flt")

    # Save .flt file
    terra::writeRaster(out_raster, out_name)

    # Convert GDAL BIL flt header file to binary floating point header.
    convert_hdr(paste0(out_name, ".hdr"))
  }
}
#--------------------------------------------------
#' Create an input file for Fortran program makegrids
#'
#' MakeGrids is called by functions in surface_metrics.
#' MakeGrids reads an ASCII input file with a "Keyword: arguments" format.
#'
#' @param dem: File name (full path) to input DEM.
#' @param length_scale: Diameter in meters over which to measure attributes.
#' @param scratch_dir: Directory for storing temporary files. The input
#'   file is written to the scratch_dir.
#' @param rasters: A character vector. Each element contains two strings
#'   separated by a comma. The first specifies the type of attribute,
#'   the second specifies the file name (full path) for the output floating
#'   point binary (.flt) raster.
#' @param overwrite: If TRUE, allows overwriting of an existing input file.
#'
#' @return There is no return object, but an explicit side effect is
#'   writing to disk of the makegrids input file.
#' @export
#'
makegrids_input <- function(dem,
                            length_scale,
                            scratch_dir,
                            rasters,
                            overwrite = TRUE) {

  if (!dir.exists(scratch_dir)) {
    stop("invalid scratch folder: ", scratch_dir)
  }
  if (!is.numeric(length_scale)) {
    stop("length_scale must be numeric")
  }

  # Normalize paths
  dem <- normalizePath(dem)
  scratch_dir <- normalizePath(scratch_dir)

  out_file <- paste0(scratch_dir, "\\makegrids_input.txt")
  if (file.exists(out_file)) {
    if (overwrite) {
      message("overwriting ", out_file)
    } else {
      stop(out_file, " exists. Set overwrite = TRUE to overwrite.")
    }
  }

  # Do not include ".flt" in dem file name
  if (str_detect(dem, ".flt$") == TRUE) {
    n <- str_length(dem)
    dem <- str_sub(dem, 1, n[[1]]-4)
  }

  write_input <- function(...,
                          append = TRUE) {
    cat(..., "\n",
        file = out_file,
        sep = "",
        append = append
    )
  }

  write_input("# Input file for makeGrids\n",
              "# Creating by input_file_utils.R\n",
              "# On ", as.character(Sys.time()),
              append = FALSE
  )

  write_input("DEM: ", dem)
  write_input("SCRATCH DIRECTORY: ", scratch_dir)
  write_input("LENGTH SCALE: ", length_scale)

  for (i in 1:length(rasters)) {
    loc <- str_locate(rasters[[i]], ",")
    grid_type <- str_sub(rasters[[i]], 1, loc[1,1] - 1)
    grid_file <- str_sub(rasters[[i]], loc[1,1] + 1, -1)
    if (str_detect(grid_file, "OUTPUT FILE") == FALSE) {
      grid_file <- paste0("OUTPUT FILE = ", grid_file)
    }
    if (str_detect(grid_file, ".flt")) {
      grid_file <- paste0(grid_file, ".flt")
    }
    write_input(paste0("GRID: ", grid_type, ", ", grid_file))
  }
}
#---------------------------------------------------------
#' Create an input file for Fortran program Partial.
#'
#' Program Partial builds a raster giving the contributing area to each
#' DEM cell for a storm of specified duration. Inputs to Partial are read
#' from an ASCII input file using "Keyword: arguments" format.
#'
#' @param dem Character string: The file name (full path) for the input DEM.
#' @param k Numeric (dbl): Saturated hydraulic conductivity, in meters per hour.
#' @param d Numeric (dbl): Storm duration in hours.
#' @param length_scale Numeric (dbl): Diameter for smoothing the DEM.
#' @param scratch_dir Character string: Directory for temporary files.
#'   The input file is written to the scratch_dir.
#' @param out_raster Character string: File name (full path) for the output
#'   binary floating point (.flt) raster.
#'
#' @return There is no explicit return object, but an explicit side effect
#'   is writing to disk of the partial input file.
#' @export
#'
accum_input <- function(dem,
                        k,
                        d,
                        length_scale,
                        scratch_dir,
                        out_raster) {

  if (!dir.exists(scratch_dir)) {
    stop("invalid scratch folder: ", scratch_dir)
  }
  if (!is.numeric(length_scale)) {
    stop("length_scale must be numeric")
  }

  # Normalize paths
  dem <- normalizePath(dem)
  scratch_dir <- normalizePath(scratch_dir)
  suppressWarnings(out_raster <- normalizePath(out_raster))

  out_file <- paste0(scratch_dir, "\\partial_input.txt")

  # Do not include ".flt" in dem file name
  if (str_detect(dem, ".flt$") == TRUE) {
    n <- str_length(dem)
    dem <- str_sub(dem, 1, n[[1]]-4)
  }

  write_input <- function(...,
                          append = TRUE) {
    cat(..., "\n",
        file = out_file,
        sep = "",
        append = append
    )
  }

  write_input("# Input file for partial\n",
              "# Creating by input_file_utils.R\n",
              "# On ", as.character(Sys.time()),
              append = FALSE
  )

  write_input("DEM: ", dem)
  write_input("SCRATCH DIRECTORY: ", scratch_dir)
  write_input("LENGTH SCALE: ", length_scale)
  write_input("DURATION: ", d)
  write_input("CONDUCTIVITY: ", k)
  write_input("OUTPUT RASTER: ", out_raster)
}

#---------------------------------------------------------
#' Create an input file for Fortran program align.
#'
#' Program align co-registers two overlapping DTMs. It characterizes the 
#' frequency distribution of elevation differences as functions of 
#' slope gradient and aspect, uses that to determine slope- and aspect-dependent
#' thresholds for filtering elevation differences in setting up a set of linear
#' equations to solve for the optimal x-y-z shift of one DTM, solves for the
#' x-y-z shift, generates an aligned DTM, and outputs an elevation difference
#' raster. Inputs to program align are read from an ASCII input file using 
#' "Keyword: arguments" format, built here.
#'
#' @param refDTM Character: The file name (full path) for the input 
#' reference DTM.
#' @param alignDTM Character: The file name (full path) for the input
#' @param refDSM Character: The file name (full path) for the input
#' reference surface height raster (optional).
#' @param alignDSM Character: The file name (full path) for the input
#' DSM raster to align (optional) 
#' @param iterations Numeric (int): Number of iterations to solve for the shift
#' @param k Numeric (dbl): Number of inter-quartile ranges to use with a
#' Tukey's fence to identify outliers
#' @param dampener Numeric (dbl): Dampener for the shift
#' @param outDTM Character: File name (full path) for the output aligned DTM
#' @param tileNx (int): number of tiles in the x direction
#' @param tilesNy (int): number of tiles in the y direction
#' @param overlap (dbl): overlap between tiles
#' @param radius (dbl): radius in meters for measuring slope and aspect
#' @param nslope (int): number of gradient bins
#' @param maxSlope (dbl): maximum gradient for binning
#' @param nAzimuth (int): number of azimuth bins
#' @param outbins Character: output csv file of elevation differences
#' binned by slope and aspect
#' @param outDif Character: output elevation difference raster (.flt)
#' @param outOutlier Character: output outlier raster (.flt)
#' @param scratch_dir Charcter: scratch directory
#'
#' @return There is no explicit return object, but an explicit side effect
#'   is writing to disk of the partial input file.
#' @export
#'
align_input <- function(refDTM,
                        alignDTM,
                        refDSM = 'nofile',
                        alignDSM = 'nofile',
                        iterations,
                        k,
                        dampener,
                        outDTM,
                        tileNx,
                        tileNy,
                        overlap,
                        radius,
                        nslope,
                        maxSlope,
                        nAzimuth,
                        outbins,
                        outDif,
                        outOutlier,
                        scratch_dir) {
  
  if (!dir.exists(scratch_dir)) {
    stop("invalid scratch folder: ", scratch_dir)
  }
  
  # Normalize paths
  refDTM <- normalizePath(refDTM)
  alignDTM <- normalizePath(alignDTM)
  if (!refDSM == 'nofile') {
    suppressWarnings(refDSM <- normalizePath(refDSM))
  }
  if (!alignDSM == 'nofile') {
    suppressWarnings(alignDSM <- normalizePath(alignDSM))
  }
  suppressWarnings(outDTM <- normalizePath(outDTM))
  suppressWarnings(outDif <- normalizePath(outDif))
  suppressWarnings(outbins <- normalizePath(outbins))
  suppressWarnings(outOutlier <- normalizePath(outOutlier))
  scratch_dir <- normalizePath(scratch_dir)
  
  # Do not include ".flt" in raster file names
  if (str_detect(refDTM, ".flt$") == TRUE) {
    n <- str_length(refDTM)
    refDTM <- str_sub(refDTM, 1, n[[1]]-4)
  }
  if (str_detect(alignDTM, ".flt$") == TRUE) {
    n <- str_length(alignDTM)
    alignDTM <- str_sub(alignDTM, 1, n[[1]]-4)
  }

  out_file <- paste0(scratch_dir, "\\input_align.txt")
    
  write_input <- function(...,
                          append = TRUE) {
    cat(..., "\n",
        file = out_file,
        sep = "",
        append = append
    )
  }
  
  write_input("# Input file for align\n",
              "# Creating by input_file_utils.R\n",
              "# On ", as.character(Sys.time()),
              append = FALSE
  )
  
  write_input("REFERENCE DEM: ", refDTM)
  write_input("DEM TO ALIGN: ", alignDTM)
  if (!refDSM == 'nofile') {
    write_input("REFERENCE DSM: ", refDSM)
  }
  if (!alignDSM == 'nofile') {
    write_input("DSM TO ALIGN: ", alignDSM)
  }
  write_input("RADIUS: ", radius)
  write_input("ITERATIONS: ", iterations)
  write_input("K: ", k)
  write_input("DAMPENER: ", dampener)
  write_input("OUTPUT DEM: ", outDTM)
  write_input("TILES: X = ", tileNx, ", Y = ", tileNy, ", OVERLAP = ", overlap)
  write_input("BINS: SLOPE BINS=",nslope,",MAX SLOPE=",maxSlope,",AZIMUTH BINS=",nAzimuth,",OUTPUT=",outbins)
  write_input("OUTPUT DIFFERENCE RASTER: ", outDif)
  write_input("OUTPUT OUTLIER RASTER: ", outOutlier)
  write_input("SCRATCH DIRECTORY: ", scratch_dir)
}

#----------------------------------------------------------
#' Create an input file for Fortran program HuntLS
#' 
#' Program HuntLS reads an "outlier" raster created by program Align.
#' The "outlier" k values indicate the number of interquartile ranges
#' (q3-q1) a DoD de (delta elevation) value falls outside of the 
#' interquartile range: k = (de-q1)/(q3-a1) if k is less than q1 
#' and k = (de-q3)/(q3-q1) if k is greater than q3. Values of de
#' between q1 and q3 are given k values of zero. HuntLS associates 
#' k patches with field-mapped landslide locations surveyed for the
#' Post Mortem (Stewart et al., 2013) study. 
#' 
#' @param DEM Character: The reference DTM used for program Align.
#' @param Outlier Character: The input outlier raster (.flt) created by Align.
#' @param DoD Character: Input DoD raster (.flt) created by Align.
#' @param Accum Character: Input flow accumulation raster created by bldgrds.
#' @param AccumThreshold Numeric (dbl): Contributing area above which landslide
#' patches are precluded.
#' @param LSpnts Character: Input point shapefile of mapped landslide points.
#' @param IDfield Character: LSpnts attributes-table field name for record ID.
#' @param Radius Numeric (dbl): Search radius in meters for matching landslide
#' points to k-value patches.
#' @param AspectLength Numeric (dbl): Length in meters to calculate aspect.
#' @param GradLength Numeric (dbl): Length in meters to calculate gradient.
#' @param OutlierThreshold Numeric (dbl): Minimum absolute k value for a patch.
#' @param ScratchDir Character: Scratch directory.
#' @param OutPatch Character: Output patch raster (.flt).
#' @param OutGrad Character: Output gradient raster (.flt).
#' @param Outcsv Character: Output comma-delimited table.
#' 
#' @return returnCode, a value of zero indicates success
#' @export
#' 
 huntLSinput <- function(DEM,
                         Outlier,
                         DoD,
                         Accum,
                         AccumThreshold,
                         LSpnts,
                         IDfield,
                         Radius,
                         AspectLength,
                         GradLength,
                         OutlierThreshold,
                         ScratchDir,
                         OutPatch,
                         OutGrad,
                         Outcsv) {

  returnCode = -1
  
  if (!dir.exists(ScratchDir)) {
    stop("invalid scratch folder: ", ScratchDir)
  }

# Normalize paths
  suppressWarnings(DEM <- normalizePath(DEM))
  suppressWarnings(DoD <- normalizePath(DoD))
  suppressWarnings(Accum <- normalizePath(Accum))
  suppressWarnings(LSpnts <- normalizePath(LSpnts))
  suppressWarnings(OutPatch <- normalizePath(OutPatch))
  suppressWarnings(OutGrad <- normalizePath(OutGrad))
  suppressWarnings(Outcsv <- normalizePath(Outcsv))
  suppressWarnings(ScratchDir <- normalizePath(ScratchDir))
  
  # Do not include ".flt" in raster file names
  if (str_detect(DEM, ".flt$") == TRUE) {
    n <- str_length(DEM)
    DEM <- str_sub(DEM, 1, n[[1]]-4)
  }
  
  if (str_detect(DoD, ".flt$") == TRUE) {
    n <- str_length(DoD)
    DoD <- str_sub(DoD, 1, n[[1]]-4)
  }
  
  if (str_detect(Outlier, ".flt$") == TRUE) {
    n <- str_length(Outlier)
    Outlier <- str_sub(Outlier, 1, n[[1]]-4)
  }
  
  if (str_detect(Accum, ".flt$") == TRUE) {
    n <- str_length(Accum)
    Accum <- str_sub(Accum, 1, n[[1]]-4)
  }
  
  out_file <- paste0(ScratchDir, "\\input_huntls.txt")
  
  write_input <- function(...,
                          append = TRUE) {
    cat(..., "\n",
        file = out_file,
        sep = "",
        append = append
    )
  }
  
  write_input("# Input file for HuntLS\n",
              "# Creating by input_file_utils.R\n",
              "# On ", as.character(Sys.time()),
              append = FALSE
  )
  
  write_input("DEM: ", DEM)
  write_input("INPUT OUTLIER RASTER: ", Outlier)
  write_input("INPUT ELEVATION DIFFERENCE RASTER: ", DoD)
  write_input("INPUT FLOW ACCUMULATION RASTER: ", Accum)
  write_input("ACCUMULATION THRESHOLD: ", AccumThreshold)
  write_input(paste0("INPUT LANDSLIDE POINT SHAPEFILE: ", LSpnts, ", ID FIELD = ", IDfield))
  write_input("SEARCH RADIUS: ", Radius)
  write_input("ASPECT LENGTH SCALE: ", AspectLength)
  write_input("GRADIENT LENGTH SCALE: ", GradLength)
  write_input("OUTLIER THRESHOLD: ", OutlierThreshold)
  write_input("SCRATCH DIRECTORY: ", ScratchDir)
  write_input("OUTPUT PATCH RASTER: ", OutPatch)
  write_input("OUTPUT GRADIENT RASTER: ", OutGrad)
  write_input("OUTPUT TABLE: ", Outcsv)
  
  returnCode = 0
  return(returnCode)
}
#----------------------------------------------------------
#' Create an input file for Fortran program LShunter
#' 
#' Program LShunter identifies potential landslide sites using 
#' "outlier patches" on a DoD (DEM of Difference) delineated on
#' an "outlier" raster created by Program Align. 
#' 
#' @param DoD Character: The reference DoD (DEM of Difference) raster (.flt) created by Align.
#' @param Outlier Character: The outlier raster (.flt) created by Align.
#' @param threshold1 Numeric (dbl): Maximum k value for a patch (e.g., -5.0), 1st round.
#' @param threshold2 Numeric (dbl): Maximum k value for a patch (e.g., -1.5), 2nd round.
#' @param Gradient Character: An input gradient raster (.flt) created by program MakeGrids.
#' @param min1 Numeric (dbl): Minimum gradient for a patch, 1st round.
#' @param min2 Numeric (dbl): Minimum gradient for a patch, 2nd round.
#' @param Accum Character: Input flow accumulation raster (.flt) created by bldgrds.
#' @param maxAccum1 Numeric (dbl): Maximum flow accumulation for a patch, 1st round.
#' @param maxAccum2 Numeric (dbl): Maximum flow accumulation for a patch, 2nd round.
#' @param MinSize Numeric (dbl): Minimum size of a patch in square meters.
#' @param OutPatch Character: Output patch raster (.flt)
#' @param ScratchDir Character: Scratch directory.
#' 
#' @return returnCode, a value of zero indicates success
#' @export
#' 
LShunterInput <- function(
  DoD,
  Outlier,
  threshold1,
  threshold2,
  Gradient,
  min1,
  min2,
  Accum,
  maxAccum1,
  maxAccum2,
  MinSize,
  OutPatch,
  ScratchDir) {
 
 returnCode = -1
 
 if (!dir.exists(ScratchDir)) {
   stop("invalid scratch folder: ", ScratchDir)
 }
 
 # Normalize paths
 suppressWarnings(DoD <- normalizePath(DoD))
 suppressWarnings(Outlier <- normalizePath(Outlier))
 suppressWarnings(Gradient <- normalizePath(Gradient))
 suppressWarnings(Accum <- normalizePath(Accum)) 
 suppressWarnings(OutPatch <- normalizePath(OutPatch))
 suppressWarnings(ScratchDir <- normalizePath(ScratchDir))
 
 # Do not include ".flt" in raster file names
 if (str_detect(DoD, ".flt$") == TRUE) {
   n <- str_length(DoD)
   DoD <- str_sub(DoD, 1, n[[1]]-4)
 }
 
 if (str_detect(Outlier, ".flt$") == TRUE) {
   n <- str_length(Outlier)
   Outlier <- str_sub(Outlier, 1, n[[1]]-4)
 }
 
 if (str_detect(Gradient, ".flt$") == TRUE) {
   n <- str_length(Gradient)
   Gradient <- str_sub(Gradient, 1, n[[1]]-4)
 }
 
 if (str_detect(Accum, ".flt$") == TRUE) {
   n <- str_length(Accum)
   Accum <- str_sub(Accum, 1, n[[1]]-4)
 }
 
 out_file <- paste0(ScratchDir, "\\input_LShunter.txt")
 
 write_input <- function(...,
                         append = TRUE) {
   cat(..., "\n",
       file = out_file,
       sep = "",
       append = append
   )
 }
 
 write_input("# Input file for LShunter\n",
             "# Creating by input_file_utils.R\n",
             "# On ", as.character(Sys.time()),
             append = FALSE
 )
 
 write_input("DoD: ", DoD)
 write_input("OUTLIER: ", Outlier, ", THRESHOLD1 = ", threshold1, ", THRESHOLD2 = ", threshold2)
 write_input("GRADIENT: ", Gradient, ", MIN1 = ", min1, ", MIN2 = ", min2)
 write_input("FLOW ACCUMULATION: ", Accum, ", MAX1 = ", maxAccum1, ", MAX2 = ", maxAccum2)
 write_input("MINIMUM SIZE: ", MinSize)
 write_input("OUTPUT PATCH RASTER: ", OutPatch)
 write_input("SCRATCH DIRECTORY: ", ScratchDir)
 
 returnCode = 0
 return(returnCode)
}
#-------------------------------------------------------------------------
#' Create an input file for Fortan program SamplePoints
#' 
#' SamplePoints is written to generate point samples for zones inside and
#' outside of mapped landslide initiation zones. It is written specifically
#' for working with output files from LS_poly, which I've been developing for 
#' analysis of the Tongass landslide inventory on Wrangell Island. 
#' The primary output from LS_poly used here is a raster of landslide 
#' initiation zones. These are used with a series of specified input rasters
#' to define the range of predictor values found within the initiation patches.
#' A raster mask is then defined to delineate areas within that range. 
#' Random point samples are then generated inside the initiation zones and 
#' outside the zones within the area of the mask. A buffer is placed around
#' each point to preclude point placements too close to each other. 
#' Bins are generated for increments of each predictor for the entire 
#' initiation zone and mask area and for the inside and outside samples. 
#' These are used to compare the frequency distribution of values both 
#' between inside and outside the initiation zones and between the total area
#' and the sample points.
#' 
#' @param inRaster Character: input initiation zone raster (.flt) generated by LS_poly.
#' @param areaPerSample Numeric (dbl): area in square meters for each sample point. This
#' determines the number of points and the point density.
#' @param buffer Numeric (dbl): distance in meters to buffer around each point.
#' @param margin Numeric (dbl): distance in meters to preclude point placement from the
#' edge of the initiation zones. 
#' @param ratio Numeric (dbl): ratio of the number of points outside the initiation zones
#' to the number inside, i.e., ratio = #outside/#inside.
#' @param nbins Numeric (int): number of bins for each predictor.
#' @param R4rasters (list): a list of real single-precision rasters to use as predictors. 
#' Each element of the list includes a name to assign the raster, the raster-file, 
#' and lower and upper cutoff values.
#' @param I4rasters (list): a list of integer rasters to use as predictors. Each integer
#' represents a nominal class, e.g., landform type.
#' @param minPatch Numeric (dbl): minimum size in square meters for patches delineated in the raster mask.
#' @param inPoints Character: output shapefile name (full path) for the inside points.
#' @param outPoints Character: output shapefile name (full path) for the outside points.
#' @param outMask Character: output raster (.flt) name (full path) for the mask.
#' @param outInit Character: output raster (.flt) name (full path) for the initiation zones sampled.
#' @param table Character: output table name (full path) for the binning results.
#' @param scratchDir Character: scratch directory.
#' 
#' @returns returnCode, a value of zero indicates success
#' @export 
#' 
samplePointInput <- function(
  inRaster,
  areaPerSample,
  buffer,
  margin,
  ratio,
  nbins,
  R4rasters,
  I4rasters,
  minPatch,
  inPoints,
  outPoints,
  outMask,
  outInit,
  table,
  scratchDir) {
  
  returnCode = -1
  
  if (!dir.exists(scratchDir)) {
    stop("invalid scratch folder: ", scratchDir)
  }
  
  # Normalize paths
  suppressWarnings(inRaster <- normalizePath(inRaster))
  suppressWarnings(inPoints <- normalizePath(inPoints))
  suppressWarnings(outPoints <- normalizePath(outPoints))
  suppressWarnings(outMask <- normalizePath(outMask))
  
  if (length(R4rasters) > 0) {
    for (i in 1:length(R4rasters)) {
      suppressWarnings(R4rasters[[i]][[2]] <- normalizePath(R4rasters[[i]][[2]]))
      if (str_detect(R4rasters[[i]][[2]], ".flt$") == TRUE) {
        n <- str_length(R4rasters[[i]][[2]])
        R4rasters[[i]][[2]] <- str_sub(R4rasters[[i]][[2]], 1, n[[1]]-4)
      }
    }
  }
  
  if (length(I4rasters) > 0) {
    for (i in length(I4rasters)) {
      suppressWarnings(I4rasters[[i]][[2]] <- normalizePath(I4rasters[[i]][[2]]))
      if (str_detect(I4rasters[[i]][[2]], ".flt$") == TRUE) {
        n <- str_length(I4rasters[[i]][[2]])
        I4rasters[[i]][[2]] <- str_sub(I4rasters[[i]][[2]], 1, n[[1]]-4)
      }    
    }
  }
  
  # Do not include ".flt" in raster file names
  if (str_detect(inRaster, ".flt$") == TRUE) {
    n <- str_length(inRaster)
    inRaster <- str_sub(inRaster, 1, n[[1]]-4)
  }
  
  out_file <- paste0(scratchDir, "\\input_samplePoints.txt")
  
  write_input <- function(...,
                          append = TRUE) {
    cat(..., "\n",
        file = out_file,
        sep = "",
        append = append
    )
  }
  
  write_input("# Input file for samplePoints\n",
              "# Creating by input_file_utils.R\n",
              "# On ", as.character(Sys.time()),
              append = FALSE
  )
  
  write_input("INITIATION ZONE RASTER: ", inRaster)
  write_input("SAMPLE AREA: ", areaPerSample)
  write_input("BUFFER: ", buffer)
  write_input("MARGIN: ", margin)
  write_input("RATIO: ", ratio)
  write_input("NBINS: ", nbins)
  n <- length(R4rasters)
  if (n > 0) {
    write_input("R4 RASTERS: ", n)
    for (i in 1:n) {
      write_input("  ",R4rasters[[i]][[1]], ": ", R4rasters[[i]][[2]], ", lower = ",
                  R4rasters[[i]][[3]], ", upper = ", R4rasters[[i]][[4]])
    }
  }
  n <- length(I4rasters)
  if (n > 0) {
    write_input("I4 RASTERS: ", n)
    for (i in 1:n) {
      write_input("  ",I4rasters[[i]][[1]], ": ", I4rasters[[i]][[2]])
    }
  }
  write_input("MINIMUM MASK PATCH: ", minPatch)
  write_input("OUTPUT INPOINT SHAPEFILE: ", inPoints)
  write_input("OUTPUT OUTPOINT SHAPEFILE: ", outPoints)
  write_input("OUTPUT MASK RASTER: ", outMask)
  write_input("OUTPUT INITIATION ZONE RASTER: ", outInit)
  write_input("OUTPUT TABLE: ", table)
  write_input("SCRATCH DIRECTORY: ", scratchDir)
  
  returnCode = 0
  return(returnCode)
}
#---------------------------------------------------------
 #' Create an input file for Fortran program Quantiles.
#' 
#' Program Quantiles reads a raster file and computes quantiles
#' over a moving circular window. For each iteration of the window,
#' the interquartile range is determined and outliers, based
#' on a Tukey's fence with k = 1.5, are removed. Quartiles are then
#' recalculated using the remaining values and the minimum and 
#' maximum range 
#' 
#' @param in_raster Character: The file name (full path) for the input raster.
#' @param radius Numeric (dbl): radius in meters for the moving window
#' @param buffer Numeric (dbl): spacign between moving window center points,
#' in raster cells.
#' @param out_outlier Character: output file name (full path) for the 
#' outlier raster (optional)
#' @param out_q1 Character: output file name (full path) for the
#' first-quartile raster (optional)
#' @param out_q2 Character: output file name (full path) for the
#' median raster (optional)
#' @param out_q3 Character: output file name (full path) for the
#' third-quartile raster (optional)
#' @param out_mean Character: output file name (full path) for the mean raster
#' @param out_zscore Character: output file name (full path) for the 
#' z-score raster
#' @param out_prob Character: output file name (full path) for the
#' probability raster (optional)
#' @param scratch_dir Charcter: scratch directory
#'
#' @return There is no explicit return object, but an explicit side effect
#'   is writing to disk of the partial input file.
#' @export
#' 
quantiles_input <- function(in_raster = "nofile",
                            radius = 0.,
                            buffer = 0,
                            out_outlier = "nofile",
                            out_q1 = "nofile",
                            out_q2 = "nofile",
                            out_q3 = "nofile",
                            out_mean = "nofile",
                            out_zscore = "nofile",
                            out_prob = "nofile",
                            scratch_dir = "none") {
  
  if (!dir.exists(scratch_dir)) {
    stop("invalid scratch folder: ", scratch_dir)
  }
  
  # Normalize paths
  in_raster <- normalizePath(in_raster)
  
  if (!out_outlier == "nofile") {
    suppressWarnings(out_outlier <- normalizePath(out_outlier))
  }
  
  if (!out_q1 == "nofile") {
    suppressWarnings(out_q1 <- normalizePath(out_q1))
  }
  
  if (!out_q2 == "nofile") {
    suppressWarnings(out_q2 <- normalizePath(out_q2))
  }
  
  if (!out_q3 == "nofile") {
    suppressWarnings(out_q3 <- normalizePath(out_q3))
  }
  
  if (!out_mean == "nofile") {
    suppressWarnings(out_mean <- normalizePath(out_mean))
  }
  
  if (!out_zscore == "nofile") {
    suppressWarnings(out_zscore <- normalizePath(out_zscore))
  }
  
  if (!out_prob == "nofile") {
    suppressWarnings(out_prob <- normalizePath(out_prob))
  }

  scratch_dir <- normalizePath(scratch_dir)
  
  # Do not include ".flt" in raster file names
  if (str_detect(in_raster, ".flt$") == TRUE) {
    n <- str_length(in_raster)
    in_raster <- str_sub(in_raster, 1, n[[1]]-4)
  }
  
  out_file <- paste0(scratch_dir, "\\input_quantiles.txt")
  
  write_input <- function(...,
                          append = TRUE) {
    cat(..., "\n",
        file = out_file,
        sep = "",
        append = append
    )
  }
  
  write_input("# Input file for quantiles\n",
              "# Creating by input_file_utils.R\n",
              "# On ", as.character(Sys.time()),
              append = FALSE
  )
  
  write_input("INPUT RASTER: ", in_raster)
  write_input("RADIUS: ", radius)
  write_input("BUFFER: ", buffer)
  if (!out_outlier == "nofile") {
    write_input("OUTPUT OUTLIER RASTER: ", out_outlier)
  }
  if (!out_q1 == "nofile") {
    write_input("OUTPUT Q1 RASTER: ", out_q1)
  }
  if (!out_q2 == "nofile") {
    write_input("OUTPUT Q2 RASTER: ", out_q2)
  }
  if (!out_q3 == "nofile") {
    write_input("OUTPUT Q3 RASTER: ", out_q3)
  }
  if (!out_mean == "nofile") {
    write_input("OUTPUT MEAN RASTER: ", out_mean)
  }
  if (!out_zscore == "nofile") {
    write_input("OUTPUT ZSCORE RASTER: ", out_zscore)
  }
  if (!out_prob == "nofile") {
    write_input("OUTPUT PROBABILITY RASTER: ", out_prob)
  }
  write_input("SCRATCH DIRECTORY: ", scratch_dir)
}

#---------------------------------------------------------
#' Create an input file for Fortran program distanceToRoad.
#'
#' Program distanceToRoad builds a raster giving the distance in meters
#' to the nearest road for each DEM grid point.
#' Inputs to distanceToRoad are read from an ASCII input file
#' using "Keyword: arguments" format.
#'
#' @param dem Character string: The file name (full path) for the input DEM.
#' @param radius Double: radius in meters to extend from a road.
#' @param road_file Character string: input road polyline shapefile.
#' @param out_raster Character string: File name (full path) for the output
#'   binary floating point (.flt) raster.
#' @param scratch_dir Character string: Directory for temporary files.
#'   The input file is written to the scratch_dir.
#'
#' @return There is no explicit return object, but an explicit side effect
#'   is writing to disk of the input file.
#' @export
#'
distanceToRoad_input <- function(dem,
                                 radius = 1000.,
                                 road_file,
                                 out_raster,
                                 scratch_dir) {

  if (!dir.exists(scratch_dir)) {
    stop("invalid scratch folder: ", scratch_dir)
  }

  # Normalize paths
  dem <- normalizePath(dem)
  scratch_dir <- normalizePath(scratch_dir)
  suppressWarnings(out_raster <- normalizePath(out_raster))

  out_file <- paste0(scratch_dir, "\\input_distanceToRoad.txt")

  # Do not include ".flt" in dem file name
  if (str_detect(dem, ".flt$") == TRUE) {
    n <- str_length(dem)
    dem <- str_sub(dem, 1, n[[1]]-4)
  }

  write_input <- function(...,
                          append = TRUE) {
    cat(..., "\n",
        file = out_file,
        sep = "",
        append = append
    )
  }

  write_input("# Input file for distanceToRoad\n",
              "# Creating by input_file_utils.R\n",
              "# On ", as.character(Sys.time()),
              append = FALSE
  )

  write_input("DEM: ", dem)
  write_input("ROAD SHAPEFILE: ", road_file)
  write_input("OUTPUT RASTER: ", out_raster)
  write_input("SCRATCH DIRECTORY: ", scratch_dir)
  write_input("RADIUS: ", radius)
}

#---------------------------------------------------------
#' Create an input file for Fortran program bldrds.
#'
#' Program bldgrds does flow routing for a DEM. This function
#' creates an input file specifying that the flow routing will
#' not involve delineation of channels. Therefore, flow directions
#' are entirely based on D-infinity.
#'
#' @param dem Character string: The file name (full path) for the input DEM.
#' @param aspect_length: double, length in meters over which aspect is measured.
#' @param plan_length: double, length in meters over which plan curvature is measured.
#' @param grad_length: double, length in meters over which gradient is measured.
#' @param out_raster Character string: File name (full path) for the output
#'   binary floating point (.flt) raster.
#' @param scratch_dir Character string: Directory for temporary files.
#'   The input file is written to the scratch_dir.
#'
#' @return There is no explicit return object, but an explicit side effect
#'   is writing to disk of the input file.
#' @export
#'
bldgrds_nochannels_input <- function(dem,
                                     aspect_length,
                                     plan_length,
                                     grad_length,
                                     out_raster,
                                     scratch_dir) {

  if (!dir.exists(scratch_dir)) {
    stop("invalid scratch folder: ", scratch_dir)
  }

  # Normalize paths
  dem <- normalizePath(dem)
  scratch_dir <- normalizePath(scratch_dir)
  suppressWarnings(out_raster <- normalizePath(out_raster))

  out_file <- paste0(scratch_dir, "\\input_bldgrds_nochannels.txt")

  # Do not include ".flt" in dem file name
  if (str_detect(dem, ".flt$") == TRUE) {
    n <- str_length(dem)
    dem <- str_sub(dem, 1, n[[1]]-4)
  }

  write_input <- function(...,
                          append = TRUE) {
    cat(..., "\n",
        file = out_file,
        sep = "",
        append = append
    )
  }

  write_input("# Input file for bldgrds_nochannels\n",
              "# Creating by input_file_utils.R\n",
              "# On ", as.character(Sys.time()),
              append = FALSE
  )

  write_input("DEM: ", dem)
  write_input("USE SMOOTHED ASPECT: LENGTH SCALE = ", aspect_length)
  write_input("PLAN CURVATURE LENGTH SCALE: ", plan_length)
  write_input("GRADIENT LENGTH SCALE: ", grad_length)
  write_input("NO CHANNELS:")
  write_input("OUTPUT FLOW ACCUMULATION RASTER: ", out_raster)
  write_input("SCRATCH DIRECTORY: ", scratch_dir)
}

#---------------------------------------------------------
#' Function PFA_debris_flow_input;
#' create an input file for Fortran program PFA_debris_flow.
#'
#' PFA_debris_flow is one of a sequence of programs used to assemble data files
#' for the PFA recalibration of the landslide initiation and debris-flow-runout models.
#' The Quatro file PFA_runout describes calibration of a multinomial model for
#' estimating the probability of debris-flow scour or deposition using the ODF
#' 1996 Storm Study surveys. The coefficients for that model are inputs to PFA_debris_flow.
#' Here, the debris-flow runout tracks from the DOGAMI Special Paper 53 study are
#' associated with lidar-DEM flow paths and terrain attributes associated with runout
#' extent are assembled and output in a format for a Cox survival model with
#' time-dependent covariates.
#'
#' @param dem Character string: The file name (full path) for the input DEM.
#' @param init_points: string: File name for initiation-point shapefile.
#' @param geo_poly: string: File name for rock-type polygon shapefile.
#' @param stand_age: string: File name for LEMMA stand-age flt raster.
#' @param tracks: string: File name for DOGAMI debris-flow-track polyline shapefile.
#' @param radius: dbl: Search radius in meters for matching DEM flow path to DOGAMI track.
#' @param length_scale: dbl: Length in meters to measure elevation derivatives.
#' @param bulk_coef: dbl: Bulking coefficient
#' @param alpha: dbl: Proportion of debris-flow cross-sectional volume deposited per unit length.
#' @param uncensored: string: If TRUE, interpret all endpoint tracks as uncensored.
#' @param scratch_dir Character string: Directory for temporary files.
#'   The input file is written to the scratch_dir.
#' @param out_track: string: Output track point shapefile.
#' @param out_surv: string: Output input file for Cox survival.
#' @param out_point: string: Output initiation point shapefile; includes fields for scour and deposit volume.
#' @param coef: list of dbl values specifying multinomial logistic regression coefficients
#'   for probability of scour, deposition, and transitional flow.
#'
#' @return There is no explicit return object, but an explicit side effect
#'   is writing to disk of the input file.
#' @export
#'
PFA_debris_flow_input <- function(dem,
                                  init_points,
                                  geo_poly,
                                  stand_age,
                                  tracks,
                                  radius,
                                  initRadius,
                                  length_scale,
                                  slope_intercept,
                                  slope_coef,
                                  bulk_coef,
                                  init_width,
                                  init_length,
                                  DF_width,
                                  alpha,
                                  uncensored,
                                  scratch_dir,
                                  out_surv,
                                  out_point,
                                  out_kaplanMeier,
                                  coef) {

  if (!dir.exists(scratch_dir)) {
    stop("invalid scratch folder: ", scratch_dir)
  }

  # Normalize paths
  dem <- normalizePath(dem)
  scratch_dir <- normalizePath(scratch_dir)

  out_file <- paste0(scratch_dir, "\\input_PFA_debris_flow.txt")

  # Do not include ".flt" in dem file name
  if (str_detect(dem, ".flt$") == TRUE) {
    n <- str_length(dem)
    dem <- str_sub(dem, 1, n[[1]] - 4)
  }

  write_input <- function(...,
                          append = TRUE) {
    cat(..., "\n",
        file = out_file,
        sep = "",
        append = append
    )
  }

  write_input("# Input file for PFA_debris_flow\n",
              "# Creating by input_file_utils.R\n",
              "# On ", as.character(Sys.time()),
              append = FALSE
  )

  write_input("DEM: ", dem)
  write_input("INITIATION POINT SHAPEFILE: ", init_points)
  write_input("TRACK LINE SHAPEFILE: ", tracks)
  write_input("ROCK TYPE POLYGON SHAPEFILE: ", geo_poly)
  write_input("STAND AGE RASTER: ", stand_age)
  write_input("RADIUS: ", radius)
  write_input("INITIATION POINT RADIUS: ", initRadius)
  write_input("LENGTH SCALE: ", length_scale)
  write_input("SLOPE: INTERCEPT = ", slope_intercept, ", COEFFICIENT = ", slope_coef)
  write_input("BULKING FACTOR: COEFFICIENT = ", bulk_coef)
  write_input("INITIATION DIMENSION: WIDTH = ", init_width, ", LENGTH = ", init_length)
  write_input("TRACK WIDTH: ",DF_width)
  write_input("ALPHA: ", alpha)
  if (str_detect(uncensored,"TRUE")) {
    write_input("UNCENSORED:")
  }
  write_input("SCRATCH DIRECTORY: ", scratch_dir)
  write_input("OUTPUT SURVIVAL FILE: ", out_surv)
  write_input("OUTPUT INITIATION POINT SHAPEFILE: ", out_point)
  write_input("")
  write_input("MULTINOMIAL LOGISTIC REGRESSION COEFFICIENTS LIST:")
  write_input("  SCOUR INTERCEPT: ", coef[[1]])
  write_input("  SCOUR GRADIENT: ", coef[[2]])
  write_input("  SCOUR NORMAL CURVATURE: ", coef[[3]])
  write_input("  SCOUR TANGENT CURVATURE: ", coef[[4]])
  write_input("  SCOUR STAND AGE: ", coef[[5]])
  write_input("  SCOUR ROCK TYPE: SEDIMENTARY = ", coef[[6]],", VOLCANIC = ", coef[[7]],
              ", IGNEOUS-METAMORPHIC = ", coef[[8]], ", VOLCANICLASTIC = ", coef[[9]],
              ", UNCONSOLIDATED = ", coef[[10]])
  write_input("  TRANSITION INTERCEPT: ", coef[[11]])
  write_input("  TRANSITION GRADIENT: ", coef[[12]])
  write_input("  TRANSITION NORMAL CURVATURE: ", coef[[13]])
  write_input("  TRANSITION TANGENT CURVATURE: ", coef[[14]])
  write_input("  TRANSITION STAND AGE: ", coef[[15]])
  write_input("  TRANSITION ROCK TYPE: SEDIMENTARY = ", coef[[16]],", VOLCANIC = ", coef[[17]],
              ", IGNEOUS-METAMORPHIC = ", coef[[18]], ", VOLCANICLASTIC = ", coef[[19]],
              ", UNCONSOLIDATED = ", coef[[20]])
  write_input("END LIST:")

}

#---------------------------------------------------------
#' Create an input file for Fortran program LocalRelief.
#'
#' Program LocalRelief builds a deviation-from-local-elevation (DEV) raster
#' (and other things as well, DEV is all we need here).
#'
#' @param dem Character string: The file name (full path) for the input DEM.
#' @param radius dbl: Radius in meters for calculating DEV.
#' @param out_raster Character string: File name (full path) for the output
#'   binary floating point (.flt) raster.
#' @param scratch_dir Character string: Directory for temporary files.
#'   The input file is written to the scratch_dir.
#'
#' @return There is no explicit return object, but an explicit side effect
#'   is writing to disk of the input file.
#' @export
#'
DEV_input <- function(dem,
                      radius,
                      out_raster,
                      scratch_dir) {

  if (!dir.exists(scratch_dir)) {
    stop("invalid scratch folder: ", scratch_dir)
  }

  # Normalize paths
  dem <- normalizePath(dem)
  scratch_dir <- normalizePath(scratch_dir)
  suppressWarnings(out_raster <- normalizePath(out_raster))

  out_file <- paste0(scratch_dir, "\\input_DEV.txt")

  # Do not include ".flt" in dem file name
  if (str_detect(dem, ".flt$") == TRUE) {
    n <- str_length(dem)
    dem <- str_sub(dem, 1, n[[1]]-4)
  }

  write_input <- function(...,
                          append = TRUE) {
    cat(..., "\n",
        file = out_file,
        sep = "",
        append = append
    )
  }

  write_input("# Input file for LocalRelief\n",
              "#   getting DEV only.\n",
              "# Creating by input_file_utils.R\n",
              "# On ", as.character(Sys.time()),
              append = FALSE
  )

  write_input("DEM: ", dem)
  write_input("RADIUS: ", radius)
  write_input("DOWN SAMPLE: 1")
  write_input("SAMPLE INTERVAL: 1")
  write_input("OUTPUT DEV RASTER: ", out_raster)
  write_input("SCRATCH DIRECTORY: ", scratch_dir)
}

#---------------------------------------------------------
#' Create an input file for Fortran program resample
#'
#' Resample downsamples a raster using increments of the raster pixel size.
#' It uses no interpolation; output pixel corner locations match input pixel corners,
#' the pixels are just bigger. Resample is primarily used to downsample a DEM,
#' in which case instead of pixel corners, we're dealing with grid points or cells.
#' @param in_raster Character string
#' @param skip integer, number of cells to skip.
#' @param out_raster Character string, resampled input raster
#' @param scratch_dir Character string: Directory for temporary files.
#'   The input file is written to the scratch_dir.
#' @return There is no explicit return object, but an explicit side effect
#'   is writing to disk (in the scratch_dir) of the input file.
#' @export
#'
resample_input <- function(in_raster, skip, out_raster, scratch_dir) {

  if (!dir.exists(scratch_dir)) {
    stop("invalid scratch folder: ", scratch_dir)
  }

  # Normalize paths
  dem <- normalizePath(in_raster)
  scratch_dir <- normalizePath(scratch_dir)
  suppressWarnings(out_raster <- normalizePath(out_raster))

  out_file <- paste0(scratch_dir, "\\input_resample.txt")
  if (str_detect(dem, ".flt$") == TRUE) {
    n <- str_length(dem)
    dem <- str_sub(dem, 1, n[[1]]-4)
  }

  write_input <- function(...,
                          append = TRUE) {
    cat(..., "\n",
        file = out_file,
        sep = "",
        append = append
    )
  }

  write_input("# Input file for program resample\n",
              "# Creating by input_file_utils.R\n",
              "# On ", as.character(Sys.time()),
              append = FALSE
  )

  write_input("INPUT RASTER: ", in_raster)
  write_input("OUTPUT RASTER: ", out_raster)
  write_input("SKIP: ", skip)
  write_input("SCRATCH DIRECTORY: ", scratch_dir)
}
tabrasel/TerrainWorksUtils documentation built on June 1, 2025, 8:05 p.m.